diff --git a/src/_types.ts b/src/_types.ts index 8d9591a..47cba13 100644 --- a/src/_types.ts +++ b/src/_types.ts @@ -474,8 +474,15 @@ export type PaperSizeName = export interface PageSetup { paperSize?: PaperSize orientation?: "portrait" | "landscape" + /** + * Scale the sheet to fit `fitToWidth` × `fitToHeight` pages instead of + * printing at `scale`. The writer turns this on when either count is + * set; pass `false` to keep the counts out of the file. + */ fitToPage?: boolean + /** Pages wide to fit the sheet to. `0` means as many as needed. Implies `fitToPage`. */ fitToWidth?: number + /** Pages tall to fit the sheet to. `0` means as many as needed. Implies `fitToPage`. */ fitToHeight?: number scale?: number margins?: PageMargins diff --git a/src/xlsx/worksheet-writer.ts b/src/xlsx/worksheet-writer.ts index 381cc64..f4623e7 100644 --- a/src/xlsx/worksheet-writer.ts +++ b/src/xlsx/worksheet-writer.ts @@ -332,8 +332,9 @@ export function writeWorksheetXml( // `` alone does nothing in Excel — the scaling // mode is switched by this flag, and the fit counts are read only // once it is on. It also carries `fitToPage: true` on its own, which - // previously emitted no XML whatsoever. See #407. - if (sheet.pageSetup?.fitToPage) { + // previously emitted no XML whatsoever. See #407. Either count also + // implies the flag; see `isFitToPage` (#596). + if (sheet.pageSetup && isFitToPage(sheet.pageSetup)) { sheetPrChildren.push(xmlSelfClose("pageSetUpPr", { fitToPage: 1 })) } if (sheetPrChildren.length > 0) { @@ -1398,6 +1399,17 @@ function serializePrintOptions(ps: PageSetup | undefined): string { // ── Page Setup Serialization ───────────────────────────────────────── +/** + * Whether the sheet prints in fit-to-page mode. The page counts do nothing + * without the mode, so writing them without it would be a field that looks + * set and prints at 100% — the counts imply the mode unless the caller + * says otherwise. The reader already treats either count as turning it on. + * See #596. + */ +function isFitToPage(ps: PageSetup): boolean { + return ps.fitToPage ?? (ps.fitToWidth !== undefined || ps.fitToHeight !== undefined) +} + function serializePageSetup(ps: PageSetup): string { const attrs: Record = {} @@ -1416,7 +1428,7 @@ function serializePageSetup(ps: PageSetup): string { attrs["scale"] = ps.scale } - if (ps.fitToPage) { + if (isFitToPage(ps)) { if (ps.fitToWidth !== undefined) { attrs["fitToWidth"] = ps.fitToWidth } diff --git a/test/print-settings.test.ts b/test/print-settings.test.ts index 8441360..80a1328 100644 --- a/test/print-settings.test.ts +++ b/test/print-settings.test.ts @@ -151,6 +151,49 @@ describe("page setup — writing", () => { expect(findChild(sheetPr, "pageSetUpPr").attrs["fitToPage"]).toBe("1") }) + it("turns on fit to page when only the page counts are given", () => { + // The counts used to be dropped without `fitToPage: true`, so the + // sheet printed at 100%. See #596. + const doc = parseSheet( + writeXml({ name: "Test", rows: [["Data"]], pageSetup: { fitToWidth: 1, fitToHeight: 0 } }), + ) + + const ps = findChild(doc, "pageSetup") + expect(ps).toBeDefined() + expect(ps.attrs["fitToWidth"]).toBe("1") + expect(ps.attrs["fitToHeight"]).toBe("0") + const sheetPr = findChild(doc, "sheetPr") + expect(sheetPr).toBeDefined() + expect(findChild(sheetPr, "pageSetUpPr").attrs["fitToPage"]).toBe("1") + }) + + it("turns on fit to page when only fitToHeight is given", () => { + const doc = parseSheet( + writeXml({ name: "Test", rows: [["Data"]], pageSetup: { fitToHeight: 2 } }), + ) + + const ps = findChild(doc, "pageSetup") + expect(ps).toBeDefined() + expect(ps.attrs["fitToHeight"]).toBe("2") + expect(ps.attrs["fitToWidth"]).toBeUndefined() + const sheetPr = findChild(doc, "sheetPr") + expect(sheetPr).toBeDefined() + expect(findChild(sheetPr, "pageSetUpPr").attrs["fitToPage"]).toBe("1") + }) + + it("lets an explicit fitToPage: false override the page counts", () => { + const doc = parseSheet( + writeXml({ + name: "Test", + rows: [["Data"]], + pageSetup: { fitToPage: false, fitToWidth: 1, fitToHeight: 0 }, + }), + ) + + expect(findChild(doc, "sheetPr")).toBeUndefined() + expect(findChild(doc, "pageSetup")).toBeUndefined() + }) + it("writes all paper sizes correctly", () => { const paperSizes: Array<[string, string]> = [ ["letter", "1"], @@ -458,6 +501,17 @@ describe("page setup — round-trip", () => { expect(ps!.fitToHeight).toBeUndefined() }) + it("round-trips the page counts without fitToPage (#596)", async () => { + const data = await writeXlsx({ + sheets: [{ name: "Sheet1", rows: [["Data"]], pageSetup: { fitToWidth: 1, fitToHeight: 0 } }], + }) + + const ps = (await readXlsx(data)).sheets[0].pageSetup + expect(ps!.fitToPage).toBe(true) + expect(ps!.fitToWidth).toBe(1) + expect(ps!.fitToHeight).toBe(0) + }) + it("round-trips all paper sizes", async () => { const sizes = ["letter", "legal", "a3", "a4", "a5", "b4", "b5", "executive", "tabloid"] as const