Overview
CQ has a built in rewriter that automatically renders a page as a PDF through Apache FOP (with
composition defined in XSL) whenever the extension .pdf is requested. Creating a script in a template
component with the name pdf.<script extension>, such as pdf.jsp, can override this, but then the pdf
must be manually created and delivered though a library such as Apache PDFBox. This document
instructs you how to use the out of the box functionality of CQ to configure the PDF rewriter.
composition defined in XSL) whenever the extension .pdf is requested. Creating a script in a template
component with the name pdf.<script extension>, such as pdf.jsp, can override this, but then the pdf
must be manually created and delivered though a library such as Apache PDFBox. This document
instructs you how to use the out of the box functionality of CQ to configure the PDF rewriter.
Quickstart
The easiest method is to simply update the .xsl file used by default with CQ. This is found in the
repository at /libs/wcm/core/content/pdf/page2fo.xsl. Write the xslfo
and the xslt the way you would
normally. See the XSL section for more details.
repository at /libs/wcm/core/content/pdf/page2fo.xsl. Write the xslfo
and the xslt the way you would
normally. See the XSL section for more details.
OSGI
The actual code that runs the CQ PDF rewriter is contained in the OSGI bundle called "Day
Communique 5 Rewriter" (com.day.cq.cqrewriter).
Specifically, the package com.day.cq.rewriter.xml
within the bundle holds the classes relevant to PDF rewriting. These classes are called through two
different services in Apache Felix. One service controls the ComponentFactory and the other controls
the TransformerFactory. To do some of the customizations described here, you will need to modify
these bundles or services or create new bundles and services similar to these.
Communique 5 Rewriter" (com.day.cq.cqrewriter).
Specifically, the package com.day.cq.rewriter.xml
within the bundle holds the classes relevant to PDF rewriting. These classes are called through two
different services in Apache Felix. One service controls the ComponentFactory and the other controls
the TransformerFactory. To do some of the customizations described here, you will need to modify
these bundles or services or create new bundles and services similar to these.
Rewriter Configuration
The rewriter configuration is found in the repository at /libs/cq/config/rewriter/pdf. More documentation
on this configuration can be found here. If you have added new bundles or services, or have renamed
the default services, this is where you must reconfigure sling to use the modified services. Note also
that, by default, the configuration node has two children, most importantly the child "transformerxslt."
on this configuration can be found here. If you have added new bundles or services, or have renamed
the default services, this is where you must reconfigure sling to use the modified services. Note also
that, by default, the configuration node has two children, most importantly the child "transformerxslt."
This node contains a property called "source" which defines the path in which the transformer
Adobe CQ Blueprints
How do I Render a Page as PDF? | Adobe CQ (AEM) Blueprints
http://www.cqblueprints.com/tipsandtricks/pdfrendering.html 2/4
searches for the .xsl file. The SourceResolver that controls the path search for this field is
com.day.cq.rewriter.xml.SourceResolverImpl, and allows the following namespaces:
file: Uses the org.apache.excalibur.source.factories.FileSourceFactory class, which searches the
server's file system.
resource: Uses the org.apache.excalibur.source.factories.ResourceSourceFactory class, which
searches the resources from the classloader.
sling: Uses the com.day.cq.rewriter.xml.SlingResourceSourceFactory class, which searches the
repository.
searches for the .xsl file. The SourceResolver that controls the path search for this field is
com.day.cq.rewriter.xml.SourceResolverImpl, and allows the following namespaces:
file: Uses the org.apache.excalibur.source.factories.FileSourceFactory class, which searches the
server's file system.
resource: Uses the org.apache.excalibur.source.factories.ResourceSourceFactory class, which
searches the resources from the classloader.
sling: Uses the com.day.cq.rewriter.xml.SlingResourceSourceFactory class, which searches the
repository.
*: Uses the org.apache.excalibur.source.factories.URLSourceFactory class, which searches a url.
Note that, by default, you are only able to set a single .xsl file that will be used for all PDFs. See the
Allowing Multiple XSL Files for details on changing this.
XSL
The default location for the .xsl file controlling the pdf is /libs/wcm/core/content/pdf/page2fo.xsl. This
can be changed by modifying the rewriter configuration. The only parameter set by default is
"resource", which is a string that holds the current resource's path. If you need to get simply the
resource name, you can use this .xsl code to retrieve it.
//CALLED USING <xsl:calltemplate
name="trim"> //NOTE THAT THE 11
IS TO ELIMINATE THE
"/jcr_content" AT THE END OF THE STRING <xsl:withparam
name="str"><xsl:valueof
select="substring($resource,0,stringlength($
resource)11)"/></
xsl:withparam>
</xsl:calltemplate>
//USED FOR RECURSIVELY TRIMMING THE RESOURCE PARAM TO GET THE PAGE NAME
<xsl:template name="trim"> <xsl:param name="str" /> <xsl:choose> <xsl:when test="contains($str, '/')">
<xsl:calltemplate
name="trim"> <xsl:withparam
name="str"><xsl:valueof
select="substringafter($
str,
'/')"/></xsl:withparam>
</xsl:calltemplate>
</xsl:when> <xsl:otherwise> <xsl:valueof
select="$str"/>
</xsl:otherwise> </xsl:choose> </xsl:template>
To send additional parameters to the xsl, see the Adding Parameters section.
Allowing Multiple XSL Files
The CQ code controlling the rewriter have been hardcoded to look for the "source" property on the
"transformerxslt"
node (found at /libs/cq/config/rewriter/pdf/transformerxslt
by default). To allow
multiple .xsl files to be used based on some other consideration (notably the current resource's
"cq:template" or "sling:resourceType" properties), you must modify the
com.day.cq.rewriter.xml.XSLTTransformer class. Specifically for basing the xsl on the template, the init
method must change from simply getting "source" to finding out what template is used and searching
for the config for that template. For example,
public void init(ProcessingContext context, ProcessingComponentConfiguration config) thr
ows IOException
{
Node currentNode = context.getRequest().getResource().adaptTo(Node.class);
String template = currentNode.hasProperty("cq:template") ? currentNode.getPropert
4/21/2015 How do I Render a Page as PDF? | Adobe CQ (AEM) Blueprints
http://www.cqblueprints.com/tipsandtricks/pdfrendering.html 3/4
y("cq:template").getString() : "source";
template = template.substring(template.lastIndexOf("/")+1);
String src = (String)config.getConfiguration().get(template);
if (src == null)
src = (String)config.getConfiguration().get("source");
if (src == null)
throw new RuntimeException("Source is missing.");
this.resolver = new SourceResolverImpl(context.getRequest().getResourceResolver(), c
ontext.getRequest(), context.getResponse());
this.inputSource = this.resolver.resolveURI(src);
this.xsltProcessor = new XSLTProcessorImpl(this.resolver);
this.resource = context.getRequest().getResource();
}
Adding Parameters
The only parameter sent in the xsl request is the parameter "resource," which holds a string of the
current resource's path. In order to add additional parameters to the xsl request, you must modify the
com.day.cq.rewriter.xml.XSLTTransformer class. At the end of the "setContentHandler" method,
simply add code similar to this:
this.transformerHandler.getTransformer().setParameter("resourceName",
this.resource.adaptTo(Node.class).getName());
Then, in the .xsl file, simply include another parameter.
<xsl:param name="resourceName"/>
current resource's path. In order to add additional parameters to the xsl request, you must modify the
com.day.cq.rewriter.xml.XSLTTransformer class. At the end of the "setContentHandler" method,
simply add code similar to this:
this.transformerHandler.getTransformer().setParameter("resourceName",
this.resource.adaptTo(Node.class).getName());
Then, in the .xsl file, simply include another parameter.
<xsl:param name="resourceName"/>