{"id":515,"date":"2020-09-05T10:46:23","date_gmt":"2020-09-05T00:46:23","guid":{"rendered":"https:\/\/www.docx4java.org\/blog\/?p=515"},"modified":"2020-09-05T10:46:23","modified_gmt":"2020-09-05T00:46:23","slug":"fast-pdf-templating-using-xsl-fo","status":"publish","type":"post","link":"https:\/\/www.docx4java.org\/blog\/2020\/09\/fast-pdf-templating-using-xsl-fo\/","title":{"rendered":"Fast PDF templating using XSL FO"},"content":{"rendered":"<p>If your objective is to generate PDF documents only (ie you have no need for docx or HTML output), then you might consider an XSL FO templating approach.<\/p>\n<p>That is, you do variable replacement in the XSL FO document.   <\/p>\n<p>This could be faster than generating a docx file first each time. <\/p>\n<p>You could use any of the templating libraries for this.  Google &#8220;java xml templating -sap -hana&#8221;<\/p>\n<p>Using a templating library is probably better than working with a lower level Java XML API so you don&#8217;t reinvent the wheel.<\/p>\n<p>For example, https:\/\/www.thymeleaf.org\/  (especially if you are using Spring).<\/p>\n<p>Below is a quick demo of using a template library called pebble to create an invoice:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n  &lt;dependency&gt;\r\n    &lt;groupId&gt;io.pebbletemplates&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;pebble&lt;\/artifactId&gt;\r\n    &lt;version&gt;3.1.4&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n<\/pre>\n<p>This demo shows simple variable replacement, repeating content, and conditional content.  Java and XSL FO template attached.<\/p>\n<p>How do you get the XSL FO in the first place?  You can create a docx document using Microsoft Word, then convert that to XSL FO using docx4j.  <\/p>\n<p>Then you add the templating commands to the XSL FO file using your favourite text editor.<\/p>\n<p>Java code: <\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport java.io.IOException;\r\nimport java.io.StringWriter;\r\nimport java.io.Writer;\r\nimport java.util.ArrayList;\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\nimport com.mitchellbosecke.pebble.PebbleEngine;\r\nimport com.mitchellbosecke.pebble.template.PebbleTemplate;\r\n\r\npublic class PebbleFoTemplating {\r\n\r\npublic static void main(String&#x5B;] args) throws IOException {\r\n\r\nPebbleEngine engine = new PebbleEngine.Builder().build();\r\nPebbleTemplate compiledTemplate = engine.getTemplate(System.getProperty(&quot;user.dir&quot;) + &quot;\/invoice_fo.xml&quot;);\r\n\r\nMap&lt;String, Object&gt; context = new HashMap&lt;&gt;();\r\n\r\ncontext.put(&quot;name&quot;, &quot;Mitchell&quot;);\r\n\r\n\/\/ repeat demo, see https:\/\/pebbletemplates.io\/wiki\/tag\/for\/\r\nList&lt;Fruit&gt; repeat_fruit = new ArrayList&lt;Fruit&gt;();\r\nrepeat_fruit.add(new Fruit(&quot;apples&quot;, &quot;$20&quot;));\r\nrepeat_fruit.add(new Fruit(&quot;oranges&quot;, &quot;$40&quot;));\r\ncontext.put(&quot;fruitList&quot;, repeat_fruit);\r\n\r\n\/\/ condition, see https:\/\/pebbletemplates.io\/wiki\/tag\/if\/\r\ncontext.put(&quot;condition1&quot;, Boolean.FALSE);\r\n\r\n\r\nWriter writer = new StringWriter();\r\ncompiledTemplate.evaluate(writer, context);\r\n\r\nString output = writer.toString();\r\nSystem.out.println(output);\r\n\r\n}\r\n\r\nstatic class Fruit {\r\n\r\nFruit(String name, String price) {\r\nthis.name=name;\r\nthis.price=price;\r\n}\r\n\r\npublic String name;\r\npublic String price;\r\n}\r\n\r\n}\r\n<\/pre>\n<p>Pebble XSL FO template: <\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\r\n&lt;fo:root xmlns:fo=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nxmlns:mc=&quot;http:\/\/schemas.openxmlformats.org\/markup-compatibility\/2006&quot;&gt;\r\n&lt;layout-master-set\r\nxmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;&gt;\r\n&lt;simple-page-master margin-bottom=&quot;12mm&quot;\r\nmargin-left=&quot;1in&quot; margin-right=&quot;1in&quot; margin-top=&quot;12mm&quot;\r\nmaster-name=&quot;s1-simple&quot; page-height=&quot;297mm&quot; page-width=&quot;210mm&quot;&gt;\r\n&lt;region-body column-count=&quot;1&quot; column-gap=&quot;12mm&quot;\r\nmargin-bottom=&quot;36.0pt&quot; margin-left=&quot;0mm&quot; margin-right=&quot;0mm&quot;\r\nmargin-top=&quot;36.0pt&quot; \/&gt;\r\n&lt;region-before extent=&quot;0.0pt&quot;\r\nregion-name=&quot;xsl-region-before-simple&quot; \/&gt;\r\n&lt;region-after extent=&quot;0.0pt&quot;\r\nregion-name=&quot;xsl-region-after-simple&quot; \/&gt;\r\n&lt;\/simple-page-master&gt;\r\n&lt;page-sequence-master master-name=&quot;s1&quot;&gt;\r\n&lt;repeatable-page-master-alternatives&gt;\r\n&lt;conditional-page-master-reference\r\nmaster-reference=&quot;s1-simple&quot; \/&gt;\r\n&lt;\/repeatable-page-master-alternatives&gt;\r\n&lt;\/page-sequence-master&gt;\r\n&lt;\/layout-master-set&gt;\r\n&lt;fo:page-sequence force-page-count=&quot;no-force&quot;\r\nid=&quot;section_s1&quot; format=&quot;&quot; master-reference=&quot;s1&quot;&gt;\r\n&lt;fo:flow flow-name=&quot;xsl-region-body&quot;&gt;\r\n\r\n&lt;fo:block font-size=&quot;11.0pt&quot; line-height=&quot;115%&quot;\r\nspace-after=&quot;4mm&quot; white-space-treatment=&quot;preserve&quot;&gt;\r\n&lt;\/fo:block&gt;\r\n&lt;fo:block font-size=&quot;11.0pt&quot; line-height=&quot;115%&quot;\r\nspace-after=&quot;4mm&quot; text-align=&quot;center&quot;&gt;\r\n&lt;inline xmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nfont-family=&quot;Calibri&quot;&gt;INVOICE&lt;\/inline&gt;\r\n&lt;\/fo:block&gt;\r\n&lt;fo:block font-size=&quot;11.0pt&quot; line-height=&quot;115%&quot;\r\nspace-after=&quot;4mm&quot;&gt;\r\n&lt;inline xmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nfont-family=&quot;Calibri&quot;&gt;{{ name }}&lt;\/inline&gt;\r\n&lt;\/fo:block&gt;\r\n&lt;fo:block font-size=&quot;11.0pt&quot; line-height=&quot;115%&quot;\r\nspace-after=&quot;4mm&quot; white-space-treatment=&quot;preserve&quot;&gt;\r\n&lt;\/fo:block&gt;\r\n\r\n&lt;fo:table border-bottom-color=&quot;#000000&quot;\r\nborder-bottom-style=&quot;solid&quot; border-bottom-width=&quot;0.5pt&quot;\r\nborder-collapse=&quot;collapse&quot; border-left-color=&quot;#000000&quot;\r\nborder-left-style=&quot;solid&quot; border-left-width=&quot;0.5pt&quot;\r\nborder-right-color=&quot;#000000&quot; border-right-style=&quot;solid&quot;\r\nborder-right-width=&quot;0.5pt&quot; border-top-color=&quot;#000000&quot;\r\nborder-top-style=&quot;solid&quot; border-top-width=&quot;0.5pt&quot;\r\ndisplay-align=&quot;before&quot; start-indent=&quot;0in&quot; table-layout=&quot;fixed&quot;\r\nwidth=&quot;159mm&quot;&gt;\r\n&lt;fo:table-column column-number=&quot;1&quot;\r\ncolumn-width=&quot;119mm&quot; \/&gt;\r\n&lt;fo:table-column column-number=&quot;2&quot;\r\ncolumn-width=&quot;1.58in&quot; \/&gt;\r\n&lt;fo:table-body start-indent=&quot;0in&quot;&gt;\r\n&lt;fo:table-row&gt;\r\n&lt;fo:table-cell border-bottom-color=&quot;#000000&quot;\r\nborder-bottom-style=&quot;solid&quot; border-bottom-width=&quot;0.5pt&quot;\r\nborder-left-color=&quot;#000000&quot; border-left-style=&quot;solid&quot;\r\nborder-left-width=&quot;0.5pt&quot; border-right-color=&quot;#000000&quot;\r\nborder-right-style=&quot;solid&quot; border-right-width=&quot;0.5pt&quot;\r\nborder-top-color=&quot;#000000&quot; border-top-style=&quot;solid&quot;\r\nborder-top-width=&quot;0.5pt&quot; padding-bottom=&quot;0mm&quot;\r\npadding-left=&quot;1.91mm&quot; padding-right=&quot;1.91mm&quot; padding-top=&quot;0mm&quot;&gt;\r\n&lt;block xmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nfont-size=&quot;11.0pt&quot; line-height=&quot;100%&quot; space-after=&quot;0in&quot;&gt;\r\n&lt;inline font-family=&quot;Calibri&quot;&gt;Item&lt;\/inline&gt;\r\n&lt;\/block&gt;\r\n&lt;\/fo:table-cell&gt;\r\n&lt;fo:table-cell border-bottom-color=&quot;#000000&quot;\r\nborder-bottom-style=&quot;solid&quot; border-bottom-width=&quot;0.5pt&quot;\r\nborder-left-color=&quot;#000000&quot; border-left-style=&quot;solid&quot;\r\nborder-left-width=&quot;0.5pt&quot; border-right-color=&quot;#000000&quot;\r\nborder-right-style=&quot;solid&quot; border-right-width=&quot;0.5pt&quot;\r\nborder-top-color=&quot;#000000&quot; border-top-style=&quot;solid&quot;\r\nborder-top-width=&quot;0.5pt&quot; padding-bottom=&quot;0mm&quot;\r\npadding-left=&quot;1.91mm&quot; padding-right=&quot;1.91mm&quot; padding-top=&quot;0mm&quot;&gt;\r\n&lt;block xmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nfont-size=&quot;11.0pt&quot; line-height=&quot;100%&quot; space-after=&quot;0in&quot;&gt;\r\n&lt;inline font-family=&quot;Calibri&quot;&gt;Price&lt;\/inline&gt;\r\n&lt;\/block&gt;\r\n&lt;\/fo:table-cell&gt;\r\n&lt;\/fo:table-row&gt;\r\n{% for fruit in fruitList %}\r\n&lt;fo:table-row&gt;\r\n&lt;fo:table-cell border-bottom-color=&quot;#000000&quot;\r\nborder-bottom-style=&quot;solid&quot; border-bottom-width=&quot;0.5pt&quot;\r\nborder-left-color=&quot;#000000&quot; border-left-style=&quot;solid&quot;\r\nborder-left-width=&quot;0.5pt&quot; border-right-color=&quot;#000000&quot;\r\nborder-right-style=&quot;solid&quot; border-right-width=&quot;0.5pt&quot;\r\nborder-top-color=&quot;#000000&quot; border-top-style=&quot;solid&quot;\r\nborder-top-width=&quot;0.5pt&quot; padding-bottom=&quot;0mm&quot;\r\npadding-left=&quot;1.91mm&quot; padding-right=&quot;1.91mm&quot; padding-top=&quot;0mm&quot;&gt;\r\n&lt;block xmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nfont-size=&quot;11.0pt&quot; line-height=&quot;100%&quot; space-after=&quot;0in&quot;&gt;\r\n&lt;inline font-family=&quot;Calibri&quot;&gt;{{ fruit.name }}&lt;\/inline&gt;\r\n&lt;\/block&gt;\r\n&lt;\/fo:table-cell&gt;\r\n&lt;fo:table-cell border-bottom-color=&quot;#000000&quot;\r\nborder-bottom-style=&quot;solid&quot; border-bottom-width=&quot;0.5pt&quot;\r\nborder-left-color=&quot;#000000&quot; border-left-style=&quot;solid&quot;\r\nborder-left-width=&quot;0.5pt&quot; border-right-color=&quot;#000000&quot;\r\nborder-right-style=&quot;solid&quot; border-right-width=&quot;0.5pt&quot;\r\nborder-top-color=&quot;#000000&quot; border-top-style=&quot;solid&quot;\r\nborder-top-width=&quot;0.5pt&quot; padding-bottom=&quot;0mm&quot;\r\npadding-left=&quot;1.91mm&quot; padding-right=&quot;1.91mm&quot; padding-top=&quot;0mm&quot;&gt;\r\n&lt;block xmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nfont-size=&quot;11.0pt&quot; line-height=&quot;100%&quot; space-after=&quot;0in&quot;&gt;\r\n&lt;inline font-family=&quot;Calibri&quot;&gt;{{ fruit.price }}&lt;\/inline&gt;\r\n&lt;\/block&gt;\r\n&lt;\/fo:table-cell&gt;\r\n&lt;\/fo:table-row&gt;\r\n{% endfor %}\r\n&lt;\/fo:table-body&gt;\r\n&lt;\/fo:table&gt;\r\n&lt;fo:block font-size=&quot;11.0pt&quot; line-height=&quot;115%&quot;\r\nspace-after=&quot;4mm&quot;&gt;\r\n&lt;inline xmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nfont-size=&quot;8.0pt&quot; \/&gt;\r\n&lt;\/fo:block&gt;\r\n&lt;fo:block font-size=&quot;11.0pt&quot; line-height=&quot;115%&quot;\r\nspace-after=&quot;4mm&quot;&gt;\r\n&lt;inline xmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nfont-family=&quot;Calibri&quot;&gt;Please remit funds to ABC Bank, account number 123 456\r\n789. &lt;\/inline&gt;\r\n&lt;inline xmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nfont-size=&quot;8.0pt&quot; \/&gt;\r\n&lt;\/fo:block&gt;\r\n{% if condition1 %}\r\n&lt;fo:block font-size=&quot;11.0pt&quot; line-height=&quot;115%&quot;\r\nspace-after=&quot;4mm&quot;&gt;\r\n&lt;inline xmlns=&quot;http:\/\/www.w3.org\/1999\/XSL\/Format&quot;\r\nfont-family=&quot;Calibri&quot;&gt;This paragraph should be included.&lt;\/inline&gt;\r\n&lt;\/fo:block&gt;\r\n{% endif %}\r\n\r\n\r\n&lt;\/fo:flow&gt;\r\n&lt;\/fo:page-sequence&gt;\r\n&lt;\/fo:root&gt;\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>If your objective is to generate PDF documents only (ie you have no need for docx or HTML output), then you might consider an XSL FO templating approach. That is, you do variable replacement in the XSL FO document. This could be faster than generating a docx file first each time. You could use any [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-515","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.docx4java.org\/blog\/wp-json\/wp\/v2\/posts\/515","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.docx4java.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.docx4java.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.docx4java.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.docx4java.org\/blog\/wp-json\/wp\/v2\/comments?post=515"}],"version-history":[{"count":3,"href":"https:\/\/www.docx4java.org\/blog\/wp-json\/wp\/v2\/posts\/515\/revisions"}],"predecessor-version":[{"id":518,"href":"https:\/\/www.docx4java.org\/blog\/wp-json\/wp\/v2\/posts\/515\/revisions\/518"}],"wp:attachment":[{"href":"https:\/\/www.docx4java.org\/blog\/wp-json\/wp\/v2\/media?parent=515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.docx4java.org\/blog\/wp-json\/wp\/v2\/categories?post=515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.docx4java.org\/blog\/wp-json\/wp\/v2\/tags?post=515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}