fix: naming a page style is a page break, same master or not
Repeating the current master was read as "no switch, no break", so a book whose chapter heading style names its own page style ran the chapters together — a fixture lost the break before every chapter. An explicitly empty name now ends the parent walk, so nothing inherits a break it never asked for; the section itself still starts only where the master changes.
This commit is contained in:
1 parent
361ce14864
commit
12f2e7a623
3 files changed
+13
-8
No files matched your search
@@ -25,7 +25,7 @@ The **Open .odt** button (`App.svelte`) parses an uploaded file with `importOdt(
|
||||
- **Content controls** (`inlineChildren`, DOCX): a `w:sdt` inside a paragraph — Word wraps every citation and bibliography entry in one — is walked through to its `w:sdtContent`, or the runs inside it never reach the paragraph (the thesis fixture lost every `[7]`-style reference and broke its lines one page short).
|
||||
- **Formulas** (`convertFormulaFrame`): a `<draw:frame>` whose `<draw:object>` resolves to a sub-document with a `math:math` root (or holds one inline) → a `formula` node. Our own `<annotation encoding="application/x-tex">` is preferred; LibreOffice strips it, so the MathML is parsed back to LaTeX instead. **`display` comes from the paragraph** (`aloneInParagraph`), not the object — LibreOffice writes `display="block"` on every formula it re-saves. DOCX: `m:oMath`/`m:oMathPara` sit beside the `w:r` runs in their own namespace, so `convertInline` probes them ahead of its `namespaceURI !== W` guard (that guard silently dropped every Word formula). See `docs/architecture/formulas.md`.
|
||||
- **Bookmarks & cross-references**: `text:bookmark-start`/`-end` (ODF) and `w:bookmarkStart`/`End` (DOCX) become a `bookmark` **mark** on the text they cover; the open ranges live in `Ctx` because Word writes them beside paragraphs as well as beside runs, and a range may close in a later paragraph. A **point** bookmark has no range to mark and is dropped, `_GoBack` with it. `text:bookmark-ref` and `REF`/`PAGEREF` fields (the `fldChar` machine and `w:fldSimple` alike, including a field Word left unresolved) become `crossRef` nodes carrying the producer's cached text. A `w:hyperlink w:anchor` with no `r:id` is an internal link → `#name`. See `docs/architecture/formatting.md`.
|
||||
- **Page geometry is the *body's* master page**, not the one named `Standard`: the importer counts the blocks each `style:master-page-name` governs and re-reads geometry (and the app's own header/footer) from the winner. A document whose Standard master no paragraph references would otherwise take margins — and a footer band — no page of it has. A **master switch always breaks the page** (LibreOffice's "page break with page style"), except for the master the document opens with; `fo:break-after="page"` becomes the next block's `breakBefore`, the only break the editor stores. A master with a `style:next-style-name` is itself the different-first-page flag: its own (possibly absent) zones govern page one.
|
||||
- **Page geometry is the *body's* master page**, not the one named `Standard`: the importer counts the blocks each `style:master-page-name` governs and re-reads geometry (and the app's own header/footer) from the winner. A document whose Standard master no paragraph references would otherwise take margins — and a footer band — no page of it has. **Naming a master breaks the page** (LibreOffice's "page break with page style") — probed: even a style naming the master the page already uses breaks, which is how a book starts each chapter fresh, so only the document's first block is exempt. The *section* (its own header/footer set) starts only where the master really changes. An empty `style:master-page-name=""` is LibreOffice's explicit "no change" and ends the parent-chain walk, so an ancestor's master can't break every child style. `fo:break-after="page"` becomes the next block's `breakBefore`, the only break the editor stores. A master with a `style:next-style-name` is itself the different-first-page flag: its own (possibly absent) zones govern page one.
|
||||
- **A page-anchored frame** (`text:anchor-type="page"`) is out of the flow: it rides a paragraph of its own that collapses to nothing, and the node view places it from its page's corner behind the text (`anchorPage`, `image.ts`). In a cell it falls back to an ordinary floating frame.
|
||||
- **A header/footer zone is fitted to its schema** (`fitZoneSchema`): the zone editor has fewer marks and nodes than the body, and one it doesn't know makes `generateHTML` throw — which renders the whole zone blank. Unknown marks are dropped, unknown nodes leave their text.
|
||||
- **A hoisted text box keeps what its anchor paragraph gave it**: an as-char figure frame is centred by the paragraph it sits in, so `wrapAlign` carries that (or the frame's own `style:horizontal-pos`) onto the box. Its `fo:padding` is read too — the editor's own 0.15cm ring is only the default.
|
||||
|
||||
@@ -1250,12 +1250,15 @@ function convertParaLike(el: Element, ctx: Ctx, kind: BlockKind, boldByDefault =
|
||||
// A style:master-page-name switches the page master, which is how ODF gives a section
|
||||
// its own header/footer; the block that does it opens that section.
|
||||
const master = kind === 'body' ? resolver.masterPageOf(styleName) : null;
|
||||
if (master && ctx.masterPages[ctx.masterPages.length - 1] !== master) {
|
||||
ctx.masterPages.push(master);
|
||||
attrs.sectionBreak = true;
|
||||
// Switching the page master starts a new page in LibreOffice, always — except for
|
||||
// the master the document opens with, which is where the text already is.
|
||||
if (ctx.masterPages.length > 1) attrs.breakBefore = 'page';
|
||||
if (master) {
|
||||
// Naming a master *is* a page break, even where the page already uses that one
|
||||
// (probed) — which is how a book starts every chapter on a fresh page. Only the
|
||||
// document's first block has nothing above it to break from.
|
||||
if (ctx.masterBlocks.size) attrs.breakBefore = 'page';
|
||||
if (ctx.masterPages[ctx.masterPages.length - 1] !== master) {
|
||||
ctx.masterPages.push(master);
|
||||
attrs.sectionBreak = true;
|
||||
}
|
||||
}
|
||||
// '' = the file's own default master, which the blocks before the first switch use.
|
||||
const governing = kind === 'body' ? ctx.masterPages[ctx.masterPages.length - 1] ?? '' : null;
|
||||
|
||||
@@ -519,8 +519,10 @@ export class StyleResolver {
|
||||
while (cur && !seen.has(cur)) {
|
||||
seen.add(cur);
|
||||
const el = this.paraStyleEls.get(cur);
|
||||
// Present but empty is LibreOffice's explicit "no page style change", so it ends
|
||||
// the walk instead of letting an ancestor's break through to every child style.
|
||||
const name = el?.getAttributeNS(NS.style, 'master-page-name');
|
||||
if (name) return name;
|
||||
if (name != null) return name || null;
|
||||
cur = this.styles.get(`paragraph\0${cur}`)?.parent ?? null;
|
||||
}
|
||||
return null;
|
||||
|
||||
Reference in new issue
Block a user