ShowTable of Contents
Introduction
IBM® Web Experience Factory (hereafter called WEF) is a powerful and flexible development tool that allows developers to rapidly create, build, customize, and deploy portlets to IBM WebSphere® Portal. Creating a PDF file on the fly often becomes a challenging task, especially when a wide variety of PDFs must be generated with conditional text, images, and repetitive blocks.
The builder described in this article generates PDFs on the fly, using XSL files (*.fo) and the
Apache Formatting Objects Processor (FOP) library. Basically FOP is great for template-based PDF generation, but in our builder, XSL-FO syntaxes are extended with custom XML elements that allow us to add dynamic behavior during the generation of the PDF.
This article illustrates a scenario in which values are picked up from variables defined in the FOP model, or conditional text is displayed by use of a variable or iterated over the XML to display the repeated blocks.
We also put the image generated from flash or charts into a PDF, using WEF's back-channel access URL and servlet streaming. There is no limit to the features you can get while generating a PDF; you just need to extend the logic accordingly to accommodate the features you require.
This builder adds the generated document to the ServletResponseOutputStream, ready to view or download from the Web page, and also supports links, buttons, or image buttons to trigger the generation of the PDF.
This article is divided into following sections:
(1) Installing custom builders in WEF Designer
(2) Using the builder with a given XSL-FO
(3) Adding dynamic capabilities to the XSL-FO file
(4) Builder implementation details
(5) Source, examples and learning
It is assumed that readers have experience with deploying portlets in WebSphere Portal and are aware of basic concepts involved in portlet development using WEF.
Installing custom builders
Download the PDFBuilder.zip file attached to this article and use a compression utility to extract all the files onto your hard disk, for example, into the directory C:\temp. This creates a folder called PDFBuilder in the C:\temp folder. The file structure is shown in figure 1.
Figure 1. PDFBuilder.zip file structure
The PDF_Builder directory contains two folders. The first folder is install_files, in which there are seven archive files:
- PDFBuilder.jar: Contains the custom builder classes
- fop.jar
- xmlgraphics-commons-1.4.jar
- xalan-2.7.0.jar
- serializer-2.7.0.jar
- batik-all-1.7.jar
- avalon-framework-4.2.0.jar
- custom_pdf_builder.zip: Contains the custom builder definitions
The second folder, sample_model, contains the file PDFBuilderSample.model. There are two options for installing the custom builders, either (1) into a project in the workspace or (2) into WEF Designer so that the custom builders are available to all models.
With the first option, only one project is affected; new projects, other projects in the workspace, and projects in other workspaces are not affected. If, however, you install into WEF Designer, then all projects created by WEF Designer thereafter have the files copied into their workspaces.
Installing into a project in the workspace
Import the attached PDF_Builder_Archive.zip archive or place:
- All .jar files in the following location: {{\\WebContent\WEB-INF\lib}}}
- Custom_pdf_builder.zip in <workspace_home>\<project>\WebContent\WEB-INF\builders
- Sample_model directory in <workspace_home>\<project>\WebContent\WEB-INF\ models\
- Images directory in <workspace_home>\<project>\WebContent
- pdf_templates directory in <workspace_home>\<project>\WebContent\
After you restart WEF Designer, a custom builder is available in the builder call list.
Installing into WEF Designer
Installing the custom builders into WEF Designer makes the custom builders available to all models created thereafter with that version of WEF Designer.
Close WEF (if it is open) and place:
- All .jar files in <Designer_home>\FeatureSets\ Web-App_8.0.0 \Templates\Project\wpf.war\WEB-INF\lib
- Custom_ custom_pdf_builder.zip in <Designer_home>\FeatureSets\ Web-App_8.0.0 \Templates\Project\wpf.war\WEB-INF\builders
- Images directory in <Designer_home>\FeatureSets\ Web-App_8.0.0 \Templates\Project\wpf.war
- pdf_templates directory in <Designer_home>\FeatureSets\ Web-App_8.0.0 \Templates\Project\wpf.war
Restart WEF Designer and verify that the custom builder is present.
Verifying custom builders
After successfully adding the .jar files and builders.zip, you should see "PDF Builder" in the category "Custom Builders", as shown in figure 2.
Figure 2. Builder Picker window

Using the builder with a given XSL-FO
Figure 3 shows the PDF Builder window, which has the following inputs:
- Name. Required. This is the name of the builder and the generated LJO.
- XSL-FO Source Type. Required. Specifies how to retrieve the XSL-FO content, either an indirect reference coming from a WEF variable/method or a file within the project. Use the capability of XSL-FO Source by indirect reference when you need to retrieve the content at runtime, for example, from a Service, IBM Web Content ManagerTM (WCM), or any other mechanism.
- XSL-FO Data Reference or XSL-FO File Name. Required. Depending on the selection of XSL-FO Source, one of these two inputs displays to configure the indirect reference or the path of the file name.
- PDF Attachment File Name. Optional. Name of the PDF file when it is generated (this is the name that the file will take in order to download by the client). The default name is "document.pdf" and can also be customized with the indirect reference.
- Advanced. In this section we are able to add a UI control to trigger the generation of the PDF. “None” will add no control, while “Image Button/Button/Link” will add a control in the provided page location, and it is possible to configure the Label or Image path. When a control is selected, the action is straightaway executed to generate and view/download the resulted PDF.
Figure 3. PDF Builder window

Adding dynamic capabilities to the XSL-FO file
One of the main features of the builder is the ability to retrieve dynamic content from the WEF model that will be included at runtime in the resulting PDF. It also supports the possibility to define conditional and iterative blocks of PDF content.
We can achieve this functionality by adding custom elements within the XSL-FO, under a different namespace that does not interfere with the existing FO elements. This also provides the ability to read images (Base64-encoded string) and content for the PDF from WCM or any other Web content management system.
The builder can also consume an XSL-FO that does not contain any dynamic data (that is, static), and the output will be the direct result of the definition in the XSL-FO content.
Adding custom elements within the XSL-FO requires the definition of a namespace and prefix in the root element of the document:
xmlns:dynamic-data="http://www.ibm.com/dynamic.fo. All custom elements will have now the prefix
"dynamic-data". Below is a description of all possible elements/attributes.
Common attributes applicable to all elements
value / compValue. This is the value for the parameter or comparison/iteration elements. It can be a constant value or retrieved from a variable (required). Possible values and format:
- var={varName}@{xpath}. Value is retrieved from a variable in the WEF model. @xpath is optional and used only if the variable is of XML type and it is required to select an element or text.
- const={value}. A constant value. If it is empty it is treated as NULL.
- action={actionName}(param1,param2,...,paramN). Value retrieved from the execution of an action/method in the WEF model. It must return an object. Optionally it supports input parameters of type String and defined as constants (e.g. action=testAction(param1,param2)
Examples of these attributes:
value="var=varTestData@RowSet: Row/HomeAddress: FirstName"
value="var=varAmontWithDouble"
value="const=Constant 1"
value="action=getValueMethod"
value="action=getValueMethodWithParams(paramValue1,true)"
compValue="const=56,567"
compValue="const=" |
valueType / compValueType. Data type of value (optional; if not present, the default type is String). Possible values are:
- String
- Boolean
- Double
- Float
- Integer
- Short
- Long
- Date
- XML (Valid to retrieve FOP content. Namespaces and prefix must match with the FOP template)
Allows you to add optional type properties after the type name:
{Type}[{propName}={propValue}][...]
Supported properties:
parseExpr: Expression used to parse the value when retrieved as String (for example, from XML content)
formatExpr: Expression used to format the value when written into the PDF
These properties are valid for types Double, Float, Integer, Short, Long, Date. For example:
Date[parseExpr=yyyyMMdd][formatExpr=dd.MM.yyyy]
Examples of these attributes:
valueType="String"
valueType="Double[parseExpr=#,##0.00][formatExpr=#,##0]"
valueType="Date[parseExpr=dd.MM.yyyy HH:mm][formatExpr=HH:mm yyyy.MM.dd]"
compValueType="Boolean"
compValueType="Long" |
compType. This attribute is applicable only to
< dynamic-data:condition> and
< dynamic-data:while>. This is the type of comparison to apply between value and compValue: value compType compValue (for example, value neq value2). Possible values are:
eq: equals
neq: not equals
gt: greater than
gteq: greater than or equal
lt: less than
lteq: less than or equal
matchRegex: match the regular expression provided in compValue (supported only for String data types)
Dynamic content, parameters.
< dynamic-data:parameter> Adds the value retrieved from the attribute value at the current location in the XSL-FO file. The value will appear in the generated PDF document. Attributes are:
prefix: string to add as a prefix after the evaluation of the parameter
suffix: string to add as a suffix after the evaluation of the parameter
The final resulting string will be:
prefix + parameterValue + suffix
Examples of these attributes:
<dynamic-data:parameter value="var=varStringWithDouble" valueType="Double[parseExpr=#,###.#][formatExpr=0.00]"/>
<dynamic-data:parameter value="var=sampleData@test:testData/test:strValue3" prefix=" " suffix=" SUFFIX "/>
<dynamic-data:parameter value="var=sampleData2@test:test-data/test:dateValue1" valueType="Date[parseExpr=yyyyMMdd]"/>
<dynamic-data:parameter value="const=45" valueType="Long[formatExpr=0000]"/>
<dynamic-data:parameter value="const=Constant 1" prefix=" "/>
<dynamic-data:parameter value="var=varStringWithDouble" valueType="Double[parseExpr=#,###.#][formatExpr=0.00]"/>
<dynamic-data:parameter value="var=varLong" valueType="Double[formatExpr=#,##0]"/>
Conditional block. < dynamic-data:condition > Defines whether the content inside this element should be displayed or not, based on the result of the evaluation of the condition:
evaluates true: display the content
evaluates false: not display the content
The evaluated condition is: value compType compValue (see below example). This element allows us to have nested condition and parameter elements without limit in the nested level.
Listing 1. Example value compType compValue
<dynamic-data:condition value="var=varYourAge" valueType="Integer" compType="gteq" compValue="const=30" compValueType="Integer">
<fo:block margin-top="10pt">
This text will be displayed when variable varYourAge is greater than or equal to 30.
</fo:block>
</dynamic-data:condition>
<dynamic-data:condition value="var=varYourWifeAge" valueType="Integer" compType="lt" compValue="const=25" compValueType="Integer">
<fo:block margin-top="10pt">
This text will be displayed when variable varYourWifeAge is less than 25.
</fo:block>
</dynamic-data:condition>
<fo:block>String Regular Expression comparison:</fo:block>
<dynamic-data:condition value="var=varRegExCompString" compType="matchRegex" compValue="var=varRegex">
<fo:block>This text will be displayed when variable varRegExCompString match as per the regular expression defined at varRegex.</fo:block>
</dynamic-data:condition>
<dynamic-data:condition value="action=callMethodWithParams(value1,value2)" valueType="Boolean" compType="eq" compValue="const=true" compValueType="Boolean">
<fo:block margin-top="10pt">
This text will be displayed after comparison of Boolean value which returned from the method/LJO.
</fo:block>
</dynamic-data:condition>
Iterative For-each block.
<dynamic-data:for-each> Iterates over a list of objects defined in the iterator attribute. The content is added for each occurrence of the object, and the current object in the iteration can be accessed via the iterator variable (iteratorVarName). Attributes are:
iterator: Expression to retrieve the iterator of the list of objects to apply the for-each (follows the same syntax as value). This expression should return an java.util.Iterator object (via a JavaTM method, or the iteratorType ListXML returns the Iterator object for the XML elements by default).
iteratorType: Type of the list returned by the iterator. An additional type called ListXML is defined when the iterator returns a list of XML elements.
iteratorVarName: Name given name to the variable that will hold the current object in the iteration (this name is defined by the user and is valid only for the scope of the for-each element). Nested for-each elements must have a different iteratorVarName.
Here are some examples:
<dynamic-data:for-each iterator="var=varTestData2@RowSet/Row" iteratorType="ListXML" iteratorVarName="varTest">
<fo:table-row background-color="#FFFFFF">
<fo:table-cell border-style="solid">
<fo:block ><dynamic-data:parameter value="iter=varTest@Row/FirstName" valueType="String"/></fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid">
<fo:block ><dynamic-data:parameter value="iter=varTest@fund/LastName" valueType="String"/></fo:block>
</fo:table-cell>
</fo:table-row>
</dynamic-data:for-each>
Iterative while block. <dynamic-data:while> Loop while the condition defined in the attributes evaluates to true. The definition of the condition and comparison uses the same syntax as the
element. The content inside the element is added each time the condition evaluates to true.
Examples:
<dynamic-data:while value="var=varSequence" valueType="Integer" compType="lteq" compValue="const=5" compValueType="Integer">
<fo:block>INSIDE OF A WHILE=<dynamic-data:parameter value="var=varSequence" valueType="Integer"/></fo:block>
<dynamic-data:action value="action=increaseVarSequence"/>
</dynamic-data:while>
Execution of WEF action without producing output. <dynamic-data:action> Execution of an action in the WEF model. It does not produce any output in the PDF. The syntax for the value attribute is the same as the common value attribute for actions.
Examples:
<dynamic-data:action value="action=myCustomAction"/>
<dynamic-data:action value="action=increaseVarSequence"/>
Images in PDF. There are two types of images in a PDF: Static and Dynamic.
- Static images: Can be referred to as “servlet-context:/path of image”. For example:
<fo:external-graphic src="servlet-context:/images/ibm-logo.jpg"/>
- Dynamic images: In Apache FOP it's not possible to assign a byte array in the <fo:external-graphic > element, so we need to a get byte array, convert it into Base64-encoded string, and then assign it to element. By using this functionality you can retrieve an image from WCM or any other system and add it to the PDF.
Example:
<fo:external-graphic src="dynamic-image:varBase64EncodedImageString"/>
Examples and learning
There is a set of examples under the attached project archive. This archive can be imported at any WEF project. It contains one model to test static (without extended elements) and dynamic generation of PDFs. The dynamic PDF contains a good set of test cases of the extension elements (parameters, conditions, iterations, etc.).
Table 1 lists the resources within the Samples project and their descriptions (paths are relative to /WebContent):
Table 1. Resources within Samples project
Resource | Description |
/pdf_templates/ DynamicPDF.fo | XSL-FO file which generates a PDF with dynamic content from the WEF model.
|
/WEB-INF/models/samples/ PDFCreatorSample.model | Sample model that uses the PDF Creator builder. Demonstrate the capabilities of the builder and how to use it. |
Troubleshooting
If the custom builders are not in the list of all builders when you verify their presence, repeat the installation steps to ensure that all the files are copied into the correct locations or archive imported properly.
If you find multiple errors in the Problems view after you import the sample model, such as “BuilderDef not found,” verify that PDF Builder custom builders is in the builder call list.
Some other troubleshooting tips include restarting WEF Designer, creating a new workspace, and creating a new model.
Removing the custom builders
To do this, just remove the files that were copied or extracted during the installation.
Conclusion
You should now be able to create custom solutions for building PDFs effectively, using Web Experience Factory builders. This article has provided the detailed instructions on how to use PDF builder and PDF generation with dynamic data.
Tell us what you think
Please visit this link to take a one-question survey about this article:
Resources
developerWorks IBM Web Experience Factory product page:
http://www.ibm.com/developerworks/websphere/zones/portal/portletfactory/
IBM Web Experience Factory customer discussion forums:
http://www-01.ibm.com/support/docview.wss?rs=3044&uid=swg27011853
About the author
Vinayak Patil is a WebSphere Portal Specialist with IBM India Software Labs, currently working for the Lab Services division. He specializes in WebSphere Portal, IBM Mobile Portal Accelerator, WEF, IBM Web Content Manager, and IBM Forms and participates in various customer-facing engagements. You can reach Vinayak at vinayspa@in.ibm.com.
Disclaimer
The custom builders and sample model furnished with this article are provided as-is and are not supported by IBM. For this reason, IBM Technical Support is no able to answer questions about these custom builders.