diff --git a/src/lib/import/docx.ts b/src/lib/import/docx.ts index a1530e8..362e23e 100644 --- a/src/lib/import/docx.ts +++ b/src/lib/import/docx.ts @@ -776,7 +776,7 @@ function blockAttrs(ppr: Element | null, kind: BlockKind, headingLevel: number | if (kind === 'body') { const pb = fc(ppr, 'pageBreakBefore'); - if (pb && wVal(pb) !== 'false' && wVal(pb) !== '0') attrs.breakBefore = 'page'; + if (pb && onOff(pb)) attrs.breakBefore = 'page'; } // Paragraph background ("colored field", w:shd) + per-side borders ("rule line", w:pBdr). @@ -1461,7 +1461,8 @@ function docHasEvenOddHeaders(files: Record): boolean { const bytes = files['word/settings.xml']; if (!bytes) return false; try { - return !!parseXml(strFromU8(bytes)).getElementsByTagNameNS(W, 'evenAndOddHeaders')[0]; + const el = parseXml(strFromU8(bytes)).getElementsByTagNameNS(W, 'evenAndOddHeaders')[0]; + return !!el && onOff(el); } catch { return false; } @@ -1489,7 +1490,8 @@ function parseSectPr(sect: Element | null, ctx: Ctx, oddEven = false): { : null; // Different first page: w:titlePg turns on the "first"-type refs for page 1. - const titlePg = !!fc(sect, 'titlePg'); + const titlePgEl = fc(sect, 'titlePg'); + const titlePg = !!titlePgEl && onOff(titlePgEl); const refId = (type: string, variant: 'default' | 'first' | 'even' = 'default') => { const ref = fcAll(sect, `${type}Reference`).find((r) => (r.getAttributeNS(W, 'type') ?? 'default') === variant); return ref?.getAttributeNS(R, 'id') ?? null; diff --git a/tests/unit/docx-onoff.test.ts b/tests/unit/docx-onoff.test.ts new file mode 100644 index 0000000..1ac2a91 --- /dev/null +++ b/tests/unit/docx-onoff.test.ts @@ -0,0 +1,53 @@ +// OOXML toggles are ST_OnOff: the element being present does not mean "on", since +// w:val="false"/"0"/"off" turns it off. Word and the docx lib both write the explicit +// false form. jsdom (vitest `environment`) supplies the global DOMParser. +import { describe, it, expect } from 'vitest'; +import { zipSync, strToU8 } from 'fflate'; +import { importDocx } from '../../src/lib/import/docx'; + +const CT = ` + + +`; + +const RELS = ` + +`; + +const DOC_RELS = ` + +`; + +const W = 'xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"'; +const HEADER = `Running head`; +const DOCUMENT = ` +Body + +`; + +const settings = (val: string) => + ``; + +const build = (settingsXml: string) => zipSync({ + '[Content_Types].xml': strToU8(CT), + '_rels/.rels': strToU8(RELS), + 'word/_rels/document.xml.rels': strToU8(DOC_RELS), + 'word/document.xml': strToU8(DOCUMENT), + 'word/header1.xml': strToU8(HEADER), + 'word/settings.xml': strToU8(settingsXml), +}); + +describe('DOCX on/off toggles', () => { + it('reads an explicit false as off', () => { + const r = importDocx(build(settings('false'))); + expect(r.header).not.toBeNull(); + // Both off, so the one header repeats on every page instead of odd/even variants. + expect(r.differentOddEven).toBe(false); + expect(r.differentFirstPage).toBe(false); + }); + + it('still reads a bare element as on', () => { + const r = importDocx(build(``)); + expect(r.differentOddEven).toBe(true); + }); +});