Repository navigation
Feat/zpl gs symbol - #97
Merged
Merged
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for the ZPL ^GS (Graphic Symbol) command, enabling the rendering of symbols such as ®, ©, and ™. The implementation includes a new symbol object type in the registry, ZPL parsing logic, and a Konva-based rendering component that utilizes vector paths for high-fidelity display. Feedback focuses on optimizing performance by memoizing Path2D objects within the render cycle, using idiomatic Konva methods to access the native canvas context, and ensuring that new locale strings are properly translated across all supported languages.
Adds the Zebra ^GS graphic-symbol command as a first-class object
type: A=Registered ®, B=Copyright ©, C=Trademark ™, D=UL approval,
E=CSA approval. Used for legal markings on retail / certification
labels.
- New `symbol` registry entry (group: 'text'), props: code, height,
width, rotation; appears automatically in the text palette.
- Parser: replaces the prior `mkBrowserLimit('GS')` stub with a
real handler that stashes ^GS params, consumes the next ^FD as
the symbol code, and produces a symbol object. Round-trips for
every code × rotation combination.
- Canvas: initial Courier-bold text-glyph stand-in with placeholder
boxes for UL/CSA.
- Locale: top-level Symbol label + per-prop sub-block injected
into all 32 locales.
- Tests: registry + parser cases including ^GS-without-^FD state
leak protection.
Also includes the unrelated UPC-A / UPC-E check-digit fix
(printCheckDigit hardcoded `N` → `Y`) flagged by the same review
round, plus rel-data measurement + Konva-Group anchor patterns
adopted from the rotation refactor.
Replaces the Courier-bold Unicode stand-in for ^GS symbols with
vector paths sourced directly from Labelary at 8 dpmm:
- A/B/C render via a custom Konva.Shape that builds a Path2D from
the d-string and fills with the native even-odd rule, so the
inner-ring holes of ® and © cut through correctly while ™ stays
filled.
- D/E (UL/CSA) stay as clearly-placeholder dashed light-gray boxes
with grey letters — both are trademarked logos we can't ship, so
the canvas signals "preview" without faking the mark.
Per-rotation glyph placement is measured directly from Labelary
renders (Zebra anchors the glyph at different relative positions
per rotation; no closed-form formula matched), stored as
`Record<'N'|'R'|'I'|'B', {x,y,w,h}>` in GS_SYMBOL_PATHS and applied
via translate + rotate + scale inside sceneFunc.
The field bbox itself does not rotate — Zebra keeps ^FO + (w,h)
fixed and rotates only the inner glyph. Field bbox stays equal to
the user-set (w × h) for selection / transformer / smart-align.
Tooling:
- scripts/generate_gs_svgs.local.ts fetches the four rotations of
each code from Labelary, runs potrace at turdSize=50 to drop
speckle subpaths, and writes src/registry/gsSymbolPaths.ts.
- scripts/regen_gs_from_cache.local.ts replays from cached PNGs
(Labelary's free-tier rate limit makes the live fetcher
unreliable for 12 sequential requests).
- potrace added as devDependency for the one-time generator.
A transparent hit-Rect under the visible Shape gives single-click
selection on the whole bbox even when the glyph occupies less of
it.
Three rounds of review feedback consolidated: - Single hit-Rect at the symbol Group level: the click-target Rect previously lived inside the vector branch only, so D/E logo placeholders weren't selectable. Hoisting to the Group level gives both branches one consistent hit area. - GS_VECTOR_CODES set + GsVectorCode type replace the magic `'A' | 'B' | 'C'` cast at the canvas lookup. Adding D/E to the vector set later is a one-line change rather than a silently- broken cast. - DEFAULT_GS_SYMBOL_META switches from positional `GS_SYMBOLS[1]` to find() on the canonical code so reordering the symbol table doesn't break the fallback. - Defensive `rel[p.rotation] ?? rel.N` in the sceneFunc handles malformed JSON imports with an unexpected rotation string. - gsSymbolPaths.ts (and the regen-from-cache template) reuse ZplRotation from registry/rotation.ts and ship an AUTO-GENERATED header so the next maintainer doesn't hand-edit them. - KonvaObject reaches the native 2D context via Konva's idiomatic `ctx._context.fill(path, "evenodd")` instead of the unknown-cast. - All 32 locale files get proper symbol-block translations — English fallback broke the convention every other entry follows. Height and Width pulled from each locale's existing barcode entries to keep unit terminology consistent. Deferred (separate concern): memoise `new Path2D` outside sceneFunc — perf nit at very high symbol counts, not a correctness issue.
u8array
force-pushed
the
feat/zpl-gs-symbol
branch
from
May 25, 2026 15:41
ffeef1a to
bc8ee7b
Compare
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.
No description provided.