Can a macro know, if it is invoked from a PDF export?

Hi all …

I have a macro that should render structurally different for pdf exports.

I could write some flag somewhere in the metadata script of the pdf template, that is executed for every exported script. I tried to write to xcontext as follows:

{{velocity output="false"}}
$xcontext.put('data-export': 'true')
{{/velocity}}

and then inside the macro, I tried to read it:

{{groovy}}
if (xcontext.get("data-export"))
  println("export")
else
  println("no export")
{{/groovy}}

But it seems, that the xcontext is not shared between the template script and the macro script.

Are there any alternative approaches?

Thanks in advance

Holger

Hi, the xcontext is shared for the same HTTP request.

For saving data, see https://www.xwiki.org/xwiki/bin/view/Documentation/DevGuide/Tutorials/SavingData/

Also note that you should be able to use the export action, and check the query string for the PDF export, as in:

#if ($xcontext.action == 'export' && $request.format == 'pdf')
...

See https://www.xwiki.org/xwiki/bin/view/Documentation/UserGuide/Features/Exports#HAdvanced-2

Thanks for your help!

For whatever reason, the metadata script seems not to be executed in the same context as the macros!?

However, xcontext.action == "export" && request.format == "pdf" does the job :blush:

Greets Holger

Hi … me again on this topic … with a new requirement

Background: I have one template for a normal pdf with clickable links and another one for a printable pdf without links but with qr-codes. Some macro needs to know whether it shall render links or qr-codes.

The ExecutionContext is actually not shared between scripts that are called from the template (header, footer, metadata) and the exported page. How can the macro determine, from which template it is actually called?

Yes, because the export is done in two steps:

  • the wiki pages included in the PDF export are rendered async in a background low-priority daemon thread (job) and the rendering results are collected
  • at the end, the PDF sheet is called (HTTP request) which:
    • renders the PDF template fields (cover, table of contents, header, footer)
    • aggregates the rendering results collected in the first step

This means that when the PDF template fields (cover, table of contents, header, footer) are evaluated, the wiki pages (including the macros) were already rendered (and in a different context: background thread vs. actual HTTP request).

There is an exception though: the metadata field from the template is rendered for each wiki page included in the export, before that page is rendered, and in the same execution context normally. So one option you have is to set some (execution) context flag from the metadata field of the template (using a script macro) and then to check that flag in the code of your macros.

Let me know if this helps,
Marius

1 Like

Thanks for your answer. This is exactly, what I thought, should work. I put this script in the metadata field:

{{groovy}}
import org.xwiki.context.*
def context = services.component.getInstance(Execution.class).getContext()
context.setProperty("printing", "true")
{{/groovy}}

Then I try to read the property inside the page content like so:

{{groovy}}
import org.xwiki.context.*
def context = services.component.getInstance(Execution.class).getContext()
def printing = context.getProperty("printing")
println("lala " + printing);
{{/groovy}}

It simply renders “lala null”, regardless if the pages is rendered in view mode or if it is rendered for printing.

Regards Holger

Have you tried to mark the execution property you set as inheritable. The execution keeps a stack of execution contexts. During document rendering it’s often the case that an execution context is pushed on the stack and then popped. Properties that should be seen in the new contexts must be marked as inherited. So you need to check if the property exists, and if not then declare it, using the dedicated builder, calling inherited().

Let me know if this works.

Thanks,
Marius

Hi Marius,

I didn’t know, that properties have to be declared :see_no_evil: … if I understand it correctly, the following should work, right?

{{groovy}}
import org.xwiki.context.*
def context = services.component.getInstance(Execution.class).getContext()
context.newProperty("printing").type(java.lang.Boolean).initial(true).inherited().declare()
{{/groovy}}

Well, it doesn’t :thinking:

Is it possible that the contexts are siblings? Or maybe the context executing the metadata is popped right after?

That may be the case, but I have to debug to confirm. Next try is to use the XWikiContext instead. Can you try using xcontext.put("printing", true) ?

Hi Marius,

please excuse my delayed response. I tried that, too. It does not work :-/

I was wrong actually… the metadata field is rendered after the document is rendered. The right solution that worked for me is this:

{{velocity}}
#set ($expectedTemplateReference = $services.model.resolveDocument('Some.CustomPDFTemplate'))
#set ($actualTemplateReference = $services.job.getCurrentJobStatus(['export', 'pdf']).request.template)
#if ($expectedTemplateReference.equals($actualTemplateReference))
  Using my custom PDF template.
#else
  Either no PDF export or using another PDF template.
#end
{{/velocity}}

Hope this helps,
Marius

@mflorea Would be interesting to document on the client-side PDF export page on exo, in a developer section :slight_smile:

2 Likes

That works :slight_smile:

Thanks a lot for your help!

I added some documentation here https://extensions.xwiki.org/xwiki/bin/view/Extension/PDF%20Export%20Application/#HAdaptthecontentforPDFexport .

This is great (and a great content)! thanks