Update, September 2026The "half a page per image" drift described here was the FO layout differing from Word's, as I said at the time. That has since been addressed, so an update for anyone with the same symptom.
17.1.0 reworked docx4j's PDF/XSL-FO output to follow Word's layout rules by default - spacing, line boxes, keeps, tables, and pictures (inline, anchored, a line holding only a picture) - and measured it against Word's own page counts over a corpus of real documents. Page numbers in a PDF's table of contents should now match what Word would print for the same document, rather than sliding with every image. The background-image workaround floated above is no longer needed.
For the "PDF before Word ever opens it" case there are two ways to get correct numbers, and one is cheaper:
- Docx4J.toPDF(..., Docx4J.FLAG_EXPORT_UPDATE_TOC) updates the TOC in the package (entries and page numbers) and then renders. Two layouts, so roughly twice the time, but the package is left with correct numbers as well as the PDF.
- Docx4J.updateToc(pkg, true) (entries only, no page numbers) followed by an ordinary toPDF. The TOC's PAGEREF fields become fo:page-number-citation, which FOP resolves as it renders, so the PDF's numbers are exactly right for that rendering, from a single layout. Use this if you only care about the PDF.
From
17.1.1, the TOC generator's page numbers for the docx itself come from the same FOP layout (via the new org.docx4j.model.pagination.Paginate), so the numbers written into the docx and the numbers printed in the PDF agree.
The usual caveat: the layout is FOP's, with the fonts FOP can see, so make sure the fonts your templates use are available to it - a substituted font is the most common remaining cause of a page shift.
On the hyperlink error in the later posts: unchanged - the fo:basic-link needs a destination, which comes from the hyperlink's r:id resolving to a relationship, so add the relationship to the part's relationships part (which assigns the id) and put that id on the w:hyperlink, as in HyperlinkTest.java.