Update, September 2026Revisiting this, because the premise has changed: docx4j now has a page layout model, so "which content is on page N" is something it can tell you.
From 17.1.1,
org.docx4j.model.pagination.Paginate lays the document out with Apache FOP (docx4j-export-fo on the classpath; since 17.1.0 that layout follows Word's rules) and reports, for every paragraph of the main document part, the page it starts on and, for paragraphs that span pages, the character offsets where each new page begins:
- Code: Select all
PaginationMap map = Paginate.compute(wordMLPackage, null);
for (String paraId : map.getKeys()) { // document order
int page = map.getPageIndex(paraId); // 1-based
int[] breaks = map.getBreaks(paraId); // empty unless the paragraph spans pages
}
Or, closer to what was suggested back in 2014, let it write the result into the document:
- Code: Select all
Paginate.paginate(wordMLPackage, null);
That rewrites the document's w:lastRenderedPageBreak markers from a fresh layout - one at the start of each page, splitting a run mid-paragraph where a page begins inside it, exactly as Word records its own last rendering. The markers you were struggling with are then trustworthy, rather than a leftover from whenever Word last saved the file.
Either way, "one page -> one XHTML file" becomes a split-then-export:
- paginate;
- for each page, take a copy of the package (XmlUtils.deepCopy of the body, or clone the whole package), keep only the content between that page's marker and the next, and drop the rest;
- Docx4J.toHTML that copy.
Paragraph granularity (everything from the paragraph containing the marker) is the simple version and is usually what people want for HTML; if you need the exact character, getBreaks gives it, and the markers are already at the right character in the runs. A table that spans a page boundary needs a decision on your part - HTML has no pages, so most people keep the whole table on the page it starts.
The usual caveat: it is FOP's layout, with the fonts FOP can see, so page boundaries are close to Word's rather than identical. That's still a much better basis than the docx -> PDF -> XHTML route floated above, which loses the document structure the HTML export preserves.