跪拜 Guibai
← All articles
Frontend

Pixel-Perfect Thermal Label Printing from the Browser with Vue 3 and QZ Tray

By 皮蛋小精灵 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Browser-based label printing usually forces a choice between proprietary command languages that lock you into one printer brand or brittle HTML rendering that breaks across drivers. This approach uses a standard Windows GDI driver and bitmap output to sidestep both problems, giving any web app reliable, high-fidelity thermal output without vendor lock-in.

Summary

A complete web-to-thermal pipeline renders labels as 300 DPI bitmaps using html2canvas and sends them silently to a Yiwei A42 printer through QZ Tray's WebSocket service. The system maps 1 mm to 5 px on a 400×300 px canvas for an 80×60 mm label, then scales output to match the physical media exactly. A visual drag-and-drop designer generates JSON templates with dynamic variable interpolation, making layout changes a configuration edit rather than a rewrite of low-level printer commands. Six hard-won fixes address QZ Tray's auto-orientation flip, Windows spooler media-size mismatches, spurious blank pages, broken thin strokes from thermal binarization, double-border artifacts, and pixel drift between the editor and print preview.

Takeaways
QZ Tray's `orientation` parameter silently swaps width and height; removing it entirely prevents single labels from spanning two sheets.
Windows Print Spooler enforces its own default media size, so the printer's preferences must be set to the exact label dimensions (80×60 mm) with zero margins.
A `page-break-after: always` rule in single-page HTML causes WebKit to append a blank second page; `page-break-after: avoid !important` eliminates the ghost page.
Thin font strokes vanish on thermal printers because anti-aliasing grays get binarized to white; forcing pure black text (`color: #000000 !important`) and rendering at 3x scale (300 DPI) restores legibility.
Double table borders come from CSS border declarations on both `th/td` elements and inner `div` wrappers combined with `cellspacing="1px"`; collapsing borders and removing redundant rules fixes it.
Hardcoded padding and a 30px placeholder block in the designer components caused pixel misalignment between the editing canvas and the printed output; removing both achieves exact alignment.
Setting the printer's hardware darkness to 12–14 and speed to 2.0–3.0 in/s gives the thermal head enough dwell time to produce solid blacks from bitmap data.
Conclusions

The core insight is that thermal printers are binary devices — they either heat a pixel or they don't. Every web rendering assumption about grayscale anti-aliasing, subpixel smoothing, and translucent borders breaks at the print head. The fix is not better CSS but brute-force resolution: render at 3x the target DPI so that the binarization step has enough samples to preserve thin strokes.

QZ Tray's auto-orientation behavior is undocumented at the configuration level but well-known to anyone who has debugged a Java PrintService. The fact that passing `orientation: 'landscape'` causes a dimension swap inside the Java layer, not in QZ Tray itself, means the bug survives across QZ versions and printer models. The only safe configuration is to omit the field entirely.

The Windows print stack has two independent sources of truth for media size: the application's print job and the driver's default form. When they disagree, the spooler silently clips or paginates the output. This is not a QZ Tray problem — it is the same class of bug that affects POS systems and label printers regardless of the software stack, and the fix is always the same: align the driver's default form with the job's declared size.

Concepts & terms
QZ Tray
An open-source Java application that runs a local WebSocket server, allowing browser JavaScript to send raw print jobs to system printers without a print dialog. It acts as a bridge between web apps and the native print spooler.
html2canvas
A JavaScript library that renders a DOM subtree into a canvas element by traversing the computed styles of each node. It does not take a screenshot but reconstructs the visual output, which makes it sensitive to cross-origin media and CSS layout quirks.
Thermal Binarization
The process by which a thermal printer converts grayscale or anti-aliased pixel data into pure black or white dots. Because thermal paper darkens through heat, there are no intermediate tones; any pixel below the printer's darkness threshold prints as white, which can fragment thin strokes.
Windows Print Spooler
The system service that manages print jobs queued to a printer. It applies the printer's default form settings (paper size, margins) to incoming jobs, and can clip or paginate output that does not match those settings, overriding what the application requested.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗