Add native label printing (ZPL + TSPL) - #4
Merged
Merged
Conversation
Add a generic, dialect-independent Label content model (Barcode CODE128/QR, Text, positioned by dots) plus NetworkLabelDriver as a PrinterDriver variant, and a ZplRenderer that emits a complete ZPL II stream for it. User-supplied text and barcode data is hex-escaped (^FH + _5E/_7E/_5C) so control characters in item names can't corrupt the stream. Adding NetworkLabelDriver to the sealed PrinterDriver made every PrintEngine actual's driver dispatch non-exhaustive; each now returns a Failure for it, since label content isn't renderable through the HTML/execute path (printLabel dispatch lands in a later change).
ZplRenderer.hexEscape escaped backslash, caret, and tilde but not underscore, which is ZPL's own hex-escape trigger under ^FH. Any literal underscore in barcode data or text (e.g. SKU ITEM_1) would be misread by the printer as the start of a stray _XX escape, silently corrupting the payload. Escape underscore first so it round-trips as _5F before the other substitutions run. TsplRenderer's TEXT command hard-coded the size multiplier to 1,1, dropping fontHeightDots entirely, while ZplRenderer honored it via ^A0N. Derive the TSPL multiplier from fontHeightDots against a base glyph height so both renderers scale text in the same direction for the same Label. Extend both renderer test suites to pin the exact escaped/sized tokens rather than loose substrings.
Adds renderLabel(label, dialect) to dispatch onto the ZPL/TSPL renderers, and printLabel(label, target) on PrintEngine so labels actually leave the process. Desktop/Android/iOS send NetworkLabelDriver targets over the same TCP transport used for ESC/POS, reject other drivers, and default SaveToFile to ZPL since PrintTarget carries no dialect. Web has no raw-socket transport, so it always fails.
Mark LabelRenderer, ZplRenderer, and TsplRenderer internal: the only intended entry points are renderLabel(label, dialect) and printLabel, so the renderers were never meant to be public surface. Also reorder NetworkLabelDriver and Label constructor params so no required argument follows a defaulted one, matching the existing NetworkEscPosDriver convention and making positional construction usable.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds native label printing to spooler as a second content model alongside HTML documents. A device-agnostic Label — positioned Barcode (Code128 or QR) and Text elements plus label geometry — renders to a printer's native command language and prints over the raw TCP 9100 transport spooler already uses. Two dialects ship together: ZplRenderer (Zebra) and TsplRenderer (TSC), reached through renderLabel(label, dialect); a new PrintEngine.printLabel(label, target) and NetworkLabelDriver(host, dialect, port) send them. The HTML/ESC/POS pipeline is untouched — labels are never routed through HTML, which would discard the native ^BC/^BQ barcodes. Shipping ZPL and TSPL together keeps the Label model genuinely device-agnostic rather than ZPL-shaped. The public surface stays small (Label, LabelElement, BarcodeSymbology, LabelDialect, NetworkLabelDriver, renderLabel, printLabel); the renderers are internal. A content/driver mismatch returns PrintResult.Failure rather than throwing; web returns Failure (no raw sockets); SaveToFile writes the ZPL for inspection.
Verification: both emitted command streams were reviewed against the ZPL II and TSPL2 references and signed off as valid. Unit tests cover the ZPL and TSPL output (Code128, QR, text, coordinates, ^FH hex-escaping incl. underscore, TSPL quote-escaping and font sizing), the renderLabel dispatch, and printLabel's SaveToFile and driver-mismatch paths; all targets compile (Android, iOS, Desktop, JS/Wasm) and :spooler:desktopTest is green. The one thing not verifiable in code is a physical Zebra/TSC rendering the stream and a scanner reading the label — that is the on-hardware check. README updated with a Labels section, snippet, and the API/transport tables.
More dialects (EPL, DPL, CPCL) can be added later through the same renderer seam; USB/serial/Bluetooth label transports are out of scope (network first, matching the ESC/POS driver).