Match the editor's spacing in PDF/DOCX/ODT and fix the toolbar dropdown

Exports now use the same typography as the Full Book editor — Georgia
serif at 12pt, 1.7 line spacing, and a blank-line gap between paragraphs -
so the on-screen editor and every exported format look consistent. DOCX/
ODT also gain a chapter break before each chapter after the first.
This commit is contained in:
avi 2026-08-18 18:39:21 -05:00
commit 0df26d4ce6
6 changed files with 143 additions and 27 deletions

View file

@ -96,17 +96,18 @@ const origManifest = fs.readFileSync(path.join(bookDir, "folio.json"));
const docxBuf = await mod.buildDocx(bookDir);
const docxZip = await JSZip.loadAsync(docxBuf);
const docxXml = await docxZip.file("word/document.xml").async("string");
const docxXml = await docxZip.file("word/document.xml").async("string");
const docxStyles = await docxZip.file("word/styles.xml").async("string");
check("docx is a zip (PK magic)", docxBuf.subarray(0, 2).toString("utf-8") === "PK");
check("docx has [Content_Types].xml", !!docxZip.file("[Content_Types].xml"));
check("docx has word/document.xml", !!docxZip.file("word/document.xml"));
check("docx is a zip (PK magic)", docxBuf.subarray(0, 2).toString("utf-8") === "PK");
check("docx has [Content_Types].xml", !!docxZip.file("[Content_Types].xml"));
check("docx has word/document.xml", !!docxZip.file("word/document.xml"));
const stripTags = (xml) => xml.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
const stripTags = (xml) => xml.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
const docxText = stripTags(docxXml);
check("docx contains book title", docxText.includes("Office Test Book"));
check("docx contains byline", docxText.includes("A book compiled with Folio._"));
const docxText = stripTags(docxXml);
check("docx contains book title", docxText.includes("Office Test Book"));
check("docx contains byline", docxText.includes("A book compiled with Folio."));
const idxAlpha = docxText.indexOf("Alpha");
const idxBeta = docxText.indexOf("Beta");
@ -129,6 +130,15 @@ check("docx has heading 1", /w:val="Heading1"/.test(docxXml));
check("docx has table", /<w:tbl>/.test(docxXml));
check("docx has code font", /Consolas/.test(docxXml));
check("docx has centered alignment", /w:val="center"/.test(docxXml));
// Body font/size come from word/styles.xml (document defaults), mirroring the
// editor's Georgia / 12pt choice.
check("docx uses Georgia body font", /<w:rFonts w:ascii="Georgia"/.test(docxStyles));
check("docx body size is 12pt (24 half-points)", /<w:sz w:val="24"\/>/.test(docxStyles));
// Matches the editor: 1.7 line spacing and a blank line (408 twips) between
// paragraphs. Attributes may be emitted in any order, so match loosely.
check("docx has 1.7 line spacing", /<w:spacing[^>]*w:line="408"[^>]*w:lineRule="auto"[^>]*\/>/.test(docxXml));
check("docx has blank-line paragraph spacing", /<w:spacing[^>]*w:after="408"/.test(docxXml));
check("docx has chapter break before later chapters", (docxXml.match(/w:before="320"/g) || []).length >= 4);
check("docx has callout shading", /w:fill="F5F2ED"/.test(docxXml));
check("docx renders ==highlight== (no raw markup)", !docxText.includes("==highlight=="));
check("docx renders **bold** (no raw markup)", !docxText.includes("**bold**"));