XSL-FO and PDF generation pipelines
XSL-FO is a page description language driven by XSLT. It still produces the best paginated output, at the cost of a second transform and a heavier toolchain.
The two-stage pipeline
The usual shape is: source XML plus a stylesheet produce a formatting-object document, and a formatting-object processor turns that into PDF. The XSLT stage has no knowledge of pagination; the FO processor decides where pages break.
# Stage 1: XML to XSL-FO
xsltproc --output book.fo to-fo.xsl book.xml
# Stage 2: XSL-FO to PDF with Apache FOP
fop -fo book.fo -pdf book.pdf
# One command, two stages, for a build script
fop -xml book.xml -xsl to-fo.xsl -pdf book.pdfThe split matters for debugging: a wrong glyph or a missing table border is a problem in the first stage, while a bad page break, a running header that appears on the wrong page, or content pushed onto a blank final page is a problem in the second. Inspect book.fo directly when in doubt — it is a readable XML document, and most FO bugs are visible in it.
Formatting objects and engine choice
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="A4" page-height="297mm" page-width="210mm"
margin="20mm">
<fo:region-body margin-bottom="15mm"/>
<fo:region-after extent="10mm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="A4">
<fo:static-content flow-name="xsl-region-after">
<fo:block text-align="center" font-size="8pt">
Page <fo:page-number/> of <fo:page-number-citation ref-id="end"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="16pt" font-weight="bold" space-after="6mm">
<xsl:value-of select="//title"/>
</fo:block>
<fo:block font-size="10pt" text-align="justify">
<xsl:value-of select="//summary"/>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>| Engine | Licence | Strengths | Weaknesses |
|---|---|---|---|
| Apache FOP | Apache 2.0 | Free, predictable, good for tables and long documents | No hyphenation by default, limited font handling |
| Antenna House | Commercial | Excellent typography and CJK support | Cost, per-server licensing |
| RenderX XEP | Commercial | Fast, mature, good SVG support | Cost |
| Prince XML | Commercial | Takes HTML plus CSS instead of FO | Different input model |
FAQ
Do I have to write XSL-FO by hand?
Why is my font missing in the PDF?
Related
Output methods and real use cases Calling XSLT from Java, .NET and Python
Last refreshed 2026-09-18.