feat(html): honour the manual page break, and stop calling the automatic one a break - #809
Merged
Conversation
…tic one a break `ElementType::page_break` came from ODF's `text:soft-page-break`, which [OpenDocument] 5.1.1 makes a record of where the *producer's* layout broke pages — not a break the author set. Rendering it would re-impose one writer's pagination on a viewport that has nothing to do with it, and it accounted for 240 of the 312 elements the renderer silently dropped. It is no longer parsed. The real manual break was not modelled at all: it is `fo:break-before` / `fo:break-after` on the paragraph style in ODF and `w:pageBreakBefore` in OOXML, so docx produced no break element whatsoever. New `BreakType` and `ParagraphStyle::break_before`/`break_after` carry it, with an explicit `none` for the break a style turns off — `sample1.docx`'s `TOCHeading` derives from a heading that breaks and says `w:val="0"` to stop. The text view then closes its page box and opens the next one at a break, whether it arrives as the style property or, for rtf and `.doc`, as the `page_break` node those formats write. Not pagination: nothing computes where a page *ends*, so the boxes are of unequal height and a document with no manual break is the one box it always was. `.odr-pages` already stacks and centres them. `break-before:page` goes out alongside, which is what print and PDF export need. Mirrored in the JNI, Apple and Python bindings. Reference output regenerated: 39 files, all odt/docx/doc. Their text is byte-identical; 13 gained page boxes, 14 gained a css break where the box could not be split, and 12 differ only in the `data-odr-path` indices the removed soft breaks used to occupy. Towards #174. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018cMYRXLJdiCkH65Jm2W9B5
andiwand
force-pushed
the
feat/manual-page-breaks
branch
from
September 2, 2026 19:06
c55628a to
6c65ce5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Generated with Claude Code
Steps 1 and 2 of the plan in #174 (comment).
The signal was wrong
ElementType::page_breakcame from ODF'stext:soft-page-break, which [OpenDocument] 5.1.1 makes a record of where the producer's layout engine broke pages — informational, not authored. Inabout.odtone sits mid-heading, between adraw:frameand the word "Features". Rendering it would re-impose LibreOffice's pagination for LibreOffice's page size on a viewport that has nothing to do with it. It was 240 of the 312 elements #805 measured the renderer dropping. It is no longer parsed.The real break was not modelled
fo:break-before/fo:break-afteron the paragraph stylew:pageBreakBeforeinw:pPr\page\x0CNew
BreakType { none, page, column }andParagraphStyle::break_before/break_after. The explicitnoneis load-bearing:sample1.docx'sTOCHeadingisw:basedOn="Heading1", which breaks, and says<w:pageBreakBefore w:val="0"/>to stop — so "off" has to be told from "unsaid" or the derived style inherits a break it turned off.What the renderer does
The text view wrote one
.odr-page-outersheet. It now closes it and opens the next at a manual break, from either source. This is not pagination: nothing computes where a page ends, so the boxes are of unequal height. A box means "what the author put on a page of its own", never "what fits on a page" — which is why it does not wait for #76, it sidesteps it. A document with no manual break is the one box it always was, and.odr-pagesalready stacks and centres them with a 16px gap, so no CSS changed.break-before:pagegoes out on the paragraph alongside, which is what print and PDF export need.BreakType::nonewrites nothing — it is the CSS initial value.mixed-layout.odt, the fixture the JNI, Apple and wasm suites share, now renders as the four sheets it is:Verification
auto→none, the split, no empty leading sheet, soft break is not content, reflow gets css only); the OOXML one proves the on/off inheritance chain.BreakTypeand the two fields are mirrored in each.Reference output
39 files changed, all odt/docx/doc — nothing else moved. Pins advanced to
3dcc4fb/9e9cc58.785KB.doc1→65,Vektoranalysis Zusammenfassung.odt1→107).data-odr-pathsibling indices the removed soft breaks used to occupy. No pixel or behaviour change —frontend.cppselects on the attribute's presence, never its value.Not in this PR
<w:br w:type="page"/>(66 corpus occurrences) sits inside a run, so honouring it visually means splitting the paragraph.break-before:pagefrom step 1 already handles it for print.mixed-layout.odtgenuinely has two page geometries wheretext_root_first_master_pagegives us one. Per-sheet page layout is its own issue.