From e17e833d6bb6da94718c475e6cd4e1ac502189b7 Mon Sep 17 00:00:00 2001 From: Paul Kilmurray Date: Mon, 6 Jul 2026 13:13:39 +0200 Subject: [PATCH] fix(printer): use size: auto so thermal receipts print full-size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The system/HTML print path emitted `@page { size: 80mm }` (or 58mm) for thermal receipt templates. A single CSS length in the `size` descriptor sets *both* page dimensions, so the page box became an 80mm×80mm square. Electron prints via the interactive `window.print()` dialog, which honours `@page`, then shrinks that square onto the printer's roll paper (e.g. 80×297mm), rendering the receipt tiny and unreadable. The previous value `${paperWidth} auto` is not a fix either: the `@page size` grammar is `{1,2} | auto | `, so `80mm auto` is invalid and browsers ignore it, falling back to Letter/A4 (this was the P1 Codex finding on #429 that a499ded9c changed to the square `80mm`). CSS cannot express a fixed-width, content-height page. Emit `size: auto` for thermal so the browser uses the thermal printer's own roll paper and shrink-fits the content to the roll width. Update the print-html tests to assert `size: auto` and guard against both broken forms; add 58mm coverage. Ships broken in v1.9.0–v1.9.6. Final on-device confirmation on a real thermal printer still recommended. --- .../printer/src/__tests__/print-html.test.ts | 22 ++++++++++++++++--- packages/printer/src/print-html.ts | 12 +++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/printer/src/__tests__/print-html.test.ts b/packages/printer/src/__tests__/print-html.test.ts index ac335eee2b..a4ea53d1cd 100644 --- a/packages/printer/src/__tests__/print-html.test.ts +++ b/packages/printer/src/__tests__/print-html.test.ts @@ -32,17 +32,33 @@ describe('buildPrintableReceiptHtml', () => { expect(html).toContain('
Hello
'); }); - it('wraps 80mm thermal output with physical page size and no A4 width rule', () => { + it('defers 80mm thermal output to the printer roll paper via size: auto', () => { const html = buildPrintableReceiptHtml({ bodyHtml: '
Thermal
', paperWidth: '80mm', }); - expect(html).toContain('@page { size: 80mm; margin: 0; }'); + // Must be `size: auto` — NOT `80mm` (a valid but square 80mm×80mm page) and + // NOT `80mm auto` (invalid ` auto`, silently ignored → Letter/A4). + // Both broken forms render the receipt tiny. + expect(html).toContain('@page { size: auto; margin: 0; }'); + expect(html).not.toContain('@page { size: 80mm; margin: 0; }'); + expect(html).not.toContain('@page { size: 80mm auto; margin: 0; }'); expect(html).not.toContain('body > * { width: 210mm; }'); expect(html).toContain('
Thermal
'); }); + it('defers 58mm thermal output to the printer roll paper via size: auto', () => { + const html = buildPrintableReceiptHtml({ + bodyHtml: '
Narrow
', + paperWidth: '58mm', + }); + + expect(html).toContain('@page { size: auto; margin: 0; }'); + expect(html).not.toContain('@page { size: 58mm; margin: 0; }'); + expect(html).not.toContain('@page { size: 58mm auto; margin: 0; }'); + }); + it('keeps print mechanics outside user template content', () => { const html = buildPrintableReceiptHtml({ bodyHtml: '
User CSS
', @@ -71,7 +87,7 @@ describe('prepareSystemPrintHtml', () => { paperWidth: '80mm', }); - expect(html).toContain('@page { size: 80mm; margin: 0; }'); + expect(html).toContain('@page { size: auto; margin: 0; }'); expect(html).toContain(''); expect(html).toContain('
From iframe
'); }); diff --git a/packages/printer/src/print-html.ts b/packages/printer/src/print-html.ts index 3f32e97ca2..ef50be29f7 100644 --- a/packages/printer/src/print-html.ts +++ b/packages/printer/src/print-html.ts @@ -18,7 +18,17 @@ export function buildPrintableReceiptHtml({ paperWidth, }: BuildPrintableReceiptHtmlOptions): string { const normalizedPaperWidth = normalizeReceiptPaperWidth(paperWidth); - const pageSize = normalizedPaperWidth === 'a4' ? 'A4' : normalizedPaperWidth; + // Thermal receipts defer to the printer's selected roll paper via `size: auto`. + // + // The CSS `@page size` descriptor grammar is `{1,2} | auto | ` + // — there is no "fixed width, flowing height" form: + // - `80mm auto` is INVALID (length + keyword); browsers ignore it and fall + // back to the default paper (Letter/A4), printing the receipt tiny. + // - `80mm` is valid but makes BOTH dimensions 80mm — an 80mm×80mm square — + // which the print dialog shrinks onto the roll, also printing it tiny. + // `auto` lets the browser use the thermal printer's own paper (e.g. 80×297mm) and + // shrink-fits the content to the roll width, which is what we actually want. + const pageSize = normalizedPaperWidth === 'a4' ? 'A4' : 'auto'; const widthRule = normalizedPaperWidth === 'a4' ? '\nbody > * { width: 210mm; }' : ''; return `