Skip to content

fix(print): accented/non-Latin receipts refused or silently missing rows on pack, merchant and WebUSB paths #1350

Open

Description

@khaira777

Parent: #830 (chat leftovers: "accent and non-Latin text blocking the whole receipt", re-reported recently). Root cause reproduced on main @ 8e39053.

Bug

A receipt with accented or non-Latin item text (for example "Burrito de Camarón") can be refused entirely: Receipt not printed: a financial row contains unsupported printer text ... (makeFinancialPrintRefusalMessage, main/printers/thermal.ts ~L151). The expected behavior is that unsupported lines print as a raster image (#643, #659).

What works (verified)

On the default setup (core classic or compact template, any shipped profile, desktop app after the window is ready), accented, CJK, Arabic, Hindi and Thai item names print through raster. This was confirmed end to end with the real Chromium renderer over TCP. Every shipped profile has raster enabled (main/printers/profiles.ts ~L45-116).

Root causes (each reproduced)

  1. Country-pack bill templates skip raster. receiptDocumentLines returns null for pack and merchant (thermal.ts ~L1681), and formatReceipt forces raster.enabled=false for both (~L2032-2035). Item rows are financial, so any character outside the code page refuses the whole receipt. This has been the case since feat(printers): add capability-gated raster printing for non-Latin thermal output #643 (5 Sep). No bundled tax pack ships print templates, so this only hits stores that installed a signed pack template.
  2. Merchant templates silently drop the item row (worse). document-merchant.ts finish() calls buildEscPos without financialLineRanges, so the unsupported item row is classified as a plain line and skipped. Subtotal and TOTAL still print, which is a silent financial omission (the thing fix(printers): prevent silent financial receipt omissions #603 was meant to prevent).
  3. Renderer not ready means refusal. Any raster failure turns into a financial refusal. A reproduced trigger: printing before the Electron app is ready ("Cannot create BrowserWindow before app is ready"). Timeouts (15 s, raster-renderer.ts ~L19) or GPU/offscreen issues on Windows or MAS would look identical. Not verified on those OSes.
  4. WebUSB from a browser or LAN client has no raster. frontend/src/hooks/usePrinter.ts (~L431-497) rasterizes only through window.electronAPI.rasterizePrintDocument, only for core templates, and only with exactly one WebUSB printer. Otherwise it falls back to the native encoder and refuses (frontend/src/lib/printer/warnings.ts ~L72).

Not a regression in the default path. Before #603 (2 Sep), accented text was sent raw instead of being refused, which is likely why it "used to work".

Decisions (maintainer, 23 Oct)

  • Raster fallback on every path: core, merchant and pack templates, desktop and browser/WebUSB. Refuse only when the raster image itself cannot be produced.
  • A refusal must stay fail-closed: never print a receipt with a missing financial row.

Fix plan

  • Merchant: build raster groups through receiptDocumentLines, stop disabling raster for merchant, and pass financialLineRanges in finish() so the native fallback refuses instead of dropping rows.
  • Pack: add raster grouping and financial ranges to the pack (escpos-line-template-v1) renderer, and stop disabling raster for pack.
  • Renderer: wait for app.whenReady() before creating the raster window instead of failing.
  • Browser/WebUSB: when the Electron rasterizer is unavailable, rasterize unsupported lines in the browser (canvas) using the same raster grouping, and apply this to core, merchant and pack templates.
  • Clearer refusal text when raster genuinely fails (which document, which line, what to try).

Tests

  • Pack and merchant templates with an accented item rasterize with no financial warning.
  • Merchant native fallback emits a financial warning (no silent drop). Add this case to tests/printer.test.ts too.
  • Core classic/compact on generic-80/58 with "Burrito de Camarón" rasterizes.
  • Renderer requested before ready waits instead of refusing.
  • Browser canvas rasterization path (unit plus e2e with a WebUSB stub).
  • Run npm run test:printer, npm run test:raster, tests/receipt-column-oracle.test.ts, then npm test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:backendBackend (Express/SQLite)area:frontendFrontend (React/Next.js)priority:highHigh priority against the backlogstate:readyScoped and ready for implementationtype:bugA confirmed defect in existing behavior

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions