I need to generate PDF documents based on XML model and XSLT style sheet. The Apach FOP framework is a good solution, but the XSL-FO the framework uses is hard to be prepared.
Therefore I created this libary to help me quickly create a temporary XSL-FO template which meets 60% of the requirements. Then I edited it to meet 100% requirements. Finally I used Apahe FOP framework to generate PDF files.
Due to my poor technology, this library is not a full solution. It just saves the time to initialize templates, you need the XSLT/XSL-FO knowledge to edit them to meet all your requirements.
The libary can export XSL-FO of Microsot Word. Maybe you can try to use Microsot Word to design documents and use this libary output XSL-FO result.
The tutorial will create the document below.
- Choose content builders.
- Form
- Table
- PageNumber
- ... (depending on the requirements)
-
Design XSL-FO template and output XML result.
-
Integrated with XSLT.
-
Ouput the PDF file.
-
Thinking of Layout
row \ col 0 1 row: 0 Name order/name WorkGroup order/workGroup 1 Description order/description QA Group order/qaGroup 2 Begin Time order/runBeginTime MA Type freqName 3 End Time order/runEndTime -
The Java Code
FormBuilder from = new FormBuilder(2) .addInfo("Name", 0, 0, "order/name") .addInfo("Description", 1, 0, "order/description") .addInfo("Begin Time", 2, 0, "order/runBeginTime") .addInfo("End Time", 3, 0, "order/runEndTime") .addInfo("Work Group", 0, 1, "order/workGroup") .addInfo("QA Group", 1, 1, "order/qaGroup") .addInfo("MA Type", 2, 1, "order/freqName");
-
Thinking of Layout
Seq No Item Name Result Remark seqNo itemName maResult maRemark -
The Java Code
TableBuilder table = new TableBuilder(4) .createHeaders(new String[] { "Seq No", "Item Name", "Result", "Remark" }) .createRowTemplate(new String[] { "seqNo", "itemName", "maResult", "maRemark" });
-
Thinking of Layout
left center right Page Number -
The Java Code
PageNumberBuilder pn = new PageNumberBuilder()
-
The Java Code
// 1. carete a XML builder (fo:root) FoXML xml = new FoXML(); // 2. create a layout (fo:simple-page-master) xml.createLayout("MA_ORDER", Paper.A4) .enableHeader() .enableFooter(); // 3. create a page (fo:page-sequence) FoPage page = xml.addPage("MA_ORDER"); // 4. 'form' as part of the body of the page (fo:flow) form.applyToBody(page); // 5. 'table' as part of the body of the page (fo:flow) table.applyToBody(page); // 6. 'pageNumber' at the footer of the page (fo:static-content) pn.applyToFooter(page); // 7. generate the XSL-FO content String result = xml.build();
-
Save XSL-FO Content
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="MA_ORDER_REPORT" page-height="29.7cm" page-width="21.0cm" margin="2cm"> <fo:region-body region-name="region-body" margin-top="1cm" margin-bottom="1cm"/> <fo:region-before region-name="region-header"/> <fo:region-after region-name="region-footer"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="MA_ORDER_REPORT" font-family="Arial,sans-serif,SimHei"> <fo:static-content flow-name="region-header"> <fo:table margin-bottom="1cm"> <fo:table-column column-width="33%"/> <fo:table-column column-width="33%"/> <fo:table-column column-width="33%"/> <fo:table-body> <fo:table-row> <fo:table-cell> <fo:block margin-right="0.1mm"><xsl:value-of select="order/id" /></fo:block> </fo:table-cell> <fo:table-cell> <fo:block margin-right="0.1mm">MA Order</fo:block> </fo:table-cell> <fo:table-cell> <fo:block margin-right="0.1mm"><xsl:value-of select="order/runBeginTime" /></fo:block> </fo:table-cell> </fo:table-row> </fo:table-body> </fo:table> </fo:static-content> <fo:static-content flow-name="region-footer"> <fo:table margin-bottom="1cm"> <fo:table-column column-width="40%"/> <fo:table-column column-width="20%"/> <fo:table-column column-width="40%"/> <fo:table-body> <fo:table-row> <fo:table-cell> <fo:block/> </fo:table-cell> <fo:table-cell> <fo:block text-align="center"> <fo:page-number/> </fo:block> </fo:table-cell> <fo:table-cell> <fo:block text-align="right"/> </fo:table-cell> </fo:table-row> </fo:table-body> </fo:table> </fo:static-content> <fo:flow flow-name="region-body"> <fo:table margin-bottom="1cm"> <fo:table-column column-width="20%"/> <fo:table-column column-width="30%"/> <fo:table-column column-width="20%"/> <fo:table-column column-width="30%"/> <fo:table-body> <fo:table-row> <fo:table-cell> <fo:block font-weight="bold" text-align="right" margin-right="0.1mm">Name</fo:block> </fo:table-cell> <fo:table-cell> <fo:block><xsl:value-of select="order/name" /></fo:block> </fo:table-cell> <fo:table-cell> <fo:block font-weight="bold" text-align="right" margin-right="0.1mm">Work Group</fo:block> </fo:table-cell> <fo:table-cell> <fo:block><xsl:value-of select="order/workGroup" /></fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block font-weight="bold" text-align="right" margin-right="0.1mm">Description</fo:block> </fo:table-cell> <fo:table-cell> <fo:block><xsl:value-of select="order/description" /></fo:block> </fo:table-cell> <fo:table-cell> <fo:block font-weight="bold" text-align="right" margin-right="0.1mm">QA Group</fo:block> </fo:table-cell> <fo:table-cell> <fo:block><xsl:value-of select="order/qaGroup" /></fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block font-weight="bold" text-align="right" margin-right="0.1mm">Begin Time</fo:block> </fo:table-cell> <fo:table-cell> <fo:block><xsl:value-of select="order/runBeginTime" /></fo:block> </fo:table-cell> <fo:table-cell> <fo:block font-weight="bold" text-align="right" margin-right="0.1mm">Type</fo:block> </fo:table-cell> <fo:table-cell> <fo:block><xsl:value-of select="order/freqName" /></fo:block> </fo:table-cell> </fo:table-row> <fo:table-row> <fo:table-cell> <fo:block font-weight="bold" text-align="right" margin-right="0.1mm">End Time</fo:block> </fo:table-cell> <fo:table-cell> <fo:block><xsl:value-of select="order/runEndTime" /></fo:block> </fo:table-cell> <fo:table-cell/> <fo:table-cell/> </fo:table-row> </fo:table-body> </fo:table> <fo:table table-layout="auto" border="solid 0.3mm black" margin="0.1mm"> <fo:table-column column-width="25%" border="solid 0.1mm black"/> <fo:table-column column-width="25%" border="solid 0.1mm black"/> <fo:table-column column-width="25%" border="solid 0.1mm black"/> <fo:table-column column-width="25%" border="solid 0.1mm black"/> <fo:table-header> <fo:table-row border="solid 0.1mm black" background-color="#eeeeee"> <fo:table-cell> <fo:block font-weight="bold" text-align="center">Seq No</fo:block> </fo:table-cell> <fo:table-cell> <fo:block font-weight="bold" text-align="center">Item Name</fo:block> </fo:table-cell> <fo:table-cell> <fo:block font-weight="bold" text-align="center">Result</fo:block> </fo:table-cell> <fo:table-cell> <fo:block font-weight="bold" text-align="center">Remark</fo:block> </fo:table-cell> </fo:table-row> </fo:table-header> <fo:table-body> <fo:table-row border="solid 0.1mm black"> <fo:table-cell> <fo:block><xsl:value-of select="seqNo" /></fo:block> </fo:table-cell> <fo:table-cell> <fo:block><xsl:value-of select="itemName" /></fo:block> </fo:table-cell> <fo:table-cell> <fo:block><xsl:value-of select="maResult" /></fo:block> </fo:table-cell> <fo:table-cell> <fo:block><xsl:value-of select="maRemark" /></fo:block> </fo:table-cell> </fo:table-row> </fo:table-body> </fo:table> </fo:flow> </fo:page-sequence> </fo:root>
Commonly used tags of XSLT are xsl:for-each
and value-of
.
-
Create a new XSLT file and manually add XSL-FO created above.
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:template match="/"> <!-- XSL-FO template here --> </xsl:template> </xsl:stylesheet>
-
Add
xsl:for-each
to bind data forfo:table-row
.<xsl:for-each select="order/items/item"> <fo:table-row> <!-- fo:table-cell --> </fo:table-row> </xsl:for-each>
-
Add
value-of
to bind data in rightblock
<fo:block> <xsl:value-of select="order/id" /> </fo:block>
-
Modify more to meet the requirements ...
-
Prepare XML data
<order> <id>MA-00419-191023-01</id> <name>MA-00419</name> <dscription>Line A</dscription> <runBeginTime>2019-10-23 08:00</runBeginTime> <runEndTime>2019-10-23 18:00</runEndTime> <workGroup>Line A WG009</workGroup> <qaGroup>QA 002</qaGroup> <freqName>Monthly</freqName> <items> <item> <seqNo>10</seqNo> <itemName>Item A</itemName> <maResult>OK</maResult> <maRemark></maRemark> </item> <item> <seqNo>20</seqNo> <itemName>Item B</itemName> <maResult>NG</maResult> <maRemark>Broken</maRemark> </item> <item> <seqNo>30</seqNo> <itemName>Item C</itemName> <maResult>OK</maResult> <maRemark></maRemark> </item> <item> <seqNo>40</seqNo> <itemName>Item D</itemName> <maResult>OK</maResult> <maRemark></maRemark> </item> </items> </order>
-
The Java Code
PdfFile.newInstance().save( new File("output.pdf"), new File("data.xml"), new File("data.xslt"));
-
XSL-FO Tutorial of W3Schools - knowledge based.
-
Apche FOP - framework used to output PDF files.
-
XSF-FO XML Schema - XSF-FO schema description.
-
docx4j - Microsoft Word handler.
-
docx4j-export-FO - Microsoft Word XSL-FO exporter.