Skip to content

Feat/zpl gs symbol - #97

Merged
u8array merged 3 commits into
mainfrom
feat/zpl-gs-symbol
May 25, 2026
Merged

u8array merged 3 commits into
mainfrom
feat/zpl-gs-symbol

Conversation

@u8array

@u8array u8array commented May 25, 2026

Copy link
Copy Markdown
Owner

No description provided.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/components/Canvas/KonvaObject.tsx
Comment thread src/components/Canvas/KonvaObject.tsx Outdated
Comment thread src/locales/ar.ts Outdated
u8array added 3 commits May 25, 2026 17:40
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
u8array force-pushed the feat/zpl-gs-symbol branch from ffeef1a to bc8ee7b Compare May 25, 2026 15:41
@u8array
u8array merged commit 3100216 into main May 25, 2026
2 checks passed
@u8array
u8array deleted the feat/zpl-gs-symbol branch May 27, 2026 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant