Skip to content

Feat/barcode rotation#19

Merged
u8array merged 20 commits into
mainfrom
feat/barcode-rotation
May 6, 2026
Merged

Feat/barcode rotation#19
u8array merged 20 commits into
mainfrom
feat/barcode-rotation

Conversation

@u8array
Copy link
Copy Markdown
Owner

@u8array u8array commented May 6, 2026

No description provided.

u8array added 11 commits May 6, 2026 18:06
Every barcode symbology gains a rotation field of type 'N'|'R'|'I'|'B'
that maps directly to the orientation slot of its ZPL command (the
hardcoded N is replaced with p.rotation). Defaults to N so existing
labels behave unchanged.

The Properties panel exposes a shared RotationSelect that reuses the
text rotation i18n keys (rotationN/R/I/B), so no new locale strings.
Test fixtures and registry assertions are updated to carry rotation.
mkBarcode and the inline barcode handlers (BM/BR/BQ/BX/B0/BB/BF/B7)
now capture the orientation parameter from p[0] instead of dropping
it. Unknown values fall back to N to keep imports resilient. Each
barcode's makeObj call carries rotation alongside the existing fields,
closing the round-trip with the generator.
Pass the rotation through to bwip-js as the 'rotate' option so the
produced bitmap is already rotated; its width/height are post-rotation
and Konva can place it without extra rotation math.

The manual EAN/UPC and Code39-family text overlays in BarcodeObject
assume an upright bitmap, so they are skipped for non-N rotation.
bwip-js is told to includetext for those cases so the rotated barcode
still carries its HRI line.
The bwip-js options builder and the canvas overlay gating both pulled
rotation from obj.props with their own cast and 'N' fallback. Hoist
the access into registry/rotation.ts so the default lives in one
place and cannot drift between the render layer and the overlay
gate.
…tations

bwip-js exposes orientations as N/R/I/L (L = 90° CCW = 270° CW); ZPL
uses N/R/I/B for the same set. Translate B to L when forwarding the
option so 270° rotation actually rotates instead of silently rendering
upright.

After rotation the bitmap dimensions swap, but getDisplaySize was
deriving width/height from canvas.width assuming an upright bitmap.
Compute the upright result and swap once at the boundary so every
per-symbology formula stays unchanged.
…er drift

Add Labelary reference renderings for code128 in N/R/I/B and for one
each of QR and DataMatrix rotated R. Bounds are measured from the PNGs
via tests/scripts/measure_bbox.mjs (added) — confirming code128 R/B
swap to 100x202 and QR keeps the +10 dot Y offset Zebra firmware adds
to ^FO QR codes regardless of rotation.

The 1D rotated cases match Labelary pixel-for-pixel under the existing
500-pixel tolerance. The 2D rotated cases are skipped in the visual
regression list because bwip-js and Zebra firmware diverge at the
encoding layer for QR/DataMatrix; the same divergence flagged for
the upright DataMatrix case carries through rotation.

labelarySync's bar-height assertion only holds for upright 1D codes;
gate it on the rotation prop so quarter-rotated bars do not trip it.
…ation in tests

getUprightDisplaySize previously took a fake-cast HTMLCanvasElement so
the rotation wrapper could swap width/height. Take two numbers (cw, ch)
instead — no type lie, clearer signature, and the wrapper expresses the
swap as plain destructuring.

labelarySync rebuilt the rotation default inline; route it through the
shared objectRotation helper so test and production read the prop the
same way.
Previously the script wrote testCases.ts contents into fixtures.json
on every run. fixtures.json is the source of truth for Labelary-
measured bounds, so this clobbered hand-refined values whenever the
script was re-run for new test cases. Read fixtures.json first, only
append entries whose id is not already present.
Add R/B variants for code39 (^B3 param order) and ean13 (^BE) so the
rotation pipeline is exercised on different ZPL command shapes than
code128's ^BC. All four match Labelary pixel-for-pixel under the
existing 500-pixel visual tolerance.

EAN13 reveals a wrinkle: extended guard bars rotate with the symbol
and end up LEFT of the FO anchor under R rotation (bbox.x = 87 with
FO=100). The labelarySync x-equality check is dropped for rotated
EAN/UPC, and the height/width text-zone adjustment moves to the
width axis under quarter rotation.
…face

labelarySync had two parallel `includes` checks for the EAN/UPC type
list under different names (isEanUpcType / isEanUpc). Hoist the single
`isEanUpc` declaration up next to the rotation flags so all later
branches share one source.

The fetch script's local `Existing` interface lived inside main();
move it to module scope as FixtureMapping for the same reason — one
place to read the fixture file's shape.
- isZplRotation: accepts ZPL N/R/I/B, rejects bwip's L and other strings
- objectRotation: returns valid prop, falls back to N for missing/garbage
- buildBwipOptions: rotate option absent for N, forwarded for R/I,
  translated B → L for bwip-js
- getDisplaySize: swaps W/H for R and B, leaves I unchanged

Locks the contract that previously broke silently when ZPL B was passed
to bwip-js as-is (treated as 'no rotation') and when getDisplaySize
read post-rotation canvas dimensions through upright formulas.
Copy link
Copy Markdown

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

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 implements barcode rotation support (Normal, Rotated 90°, Inverted 180°, and Bottom-up 270°) across all supported barcode types. Key changes include updating the ZPL parser to extract rotation parameters, modifying the bwip-js integration to handle rotated bitmaps and swapped dimensions, and adding a RotationSelect UI component. The implementation also ensures that manual HRI overlays are only applied to upright barcodes, delegating text rendering to bwip-js for rotated symbols. Extensive unit and visual regression tests have been added to verify the rotation logic against Labelary benchmarks. I have no feedback to provide as there were no review comments.

u8array added 6 commits May 6, 2026 19:01
…text

Using bwip includetext for rotated barcodes embedded the text into the
bitmap at bwip's internal scale, making the bitmap larger than what
getDisplaySize computed (bars only), so KImage stretched/squished the
pixel buffer — appearing blurry and distorted.

Fix: always render a bar-only bitmap (no includetext), then add a
rotated Konva Text node positioned at the correct edge for each rotation:
  R  → right of barcode, rotation=90
  I  → above barcode (upside-down), rotation=180
  B  → left of barcode, rotation=-90

EAN/UPC HRI on rotated symbols is intentionally skipped; the digit
layout requires per-rotation coordinate transforms that are out of scope.
Anchor offsets for R and B rotations were off by textFontSize because
Konva's rotation pivot means the font-height dimension extends *away*
from the anchor, not toward the bars. Fixed by deriving screen-space
bounding boxes from the full transform (rot=90: fh spans leftward,
width spans downward; rot=-90: fh spans rightward, width spans upward).

EAN/UPC types are now included in showRotatedText (the plain-text
overlay). LOGMARS text position is mirrored for quarter rotations
(text-above-bars upright → left for R, right for B) and mirrored for
180° (below instead of above).
R (90°CW) renders text LEFT, B (270°CW) renders text RIGHT, matching
Zebra firmware and Labelary reference. LOGMARS is mirrored (text-above
upright → right for R, left for B). For rotated EAN/UPC the displayText
now includes the computed check digit instead of raw input content.
… digit

Rotated EAN/UPC barcodes now show the same grouped digit string as the
upright layout: EAN-13 "5  901234 123457", EAN-8 "5901 2345",
UPC-A "1  23456 78901 2", UPC-E "0  123456 7".
… layout

For rotated EAN/UPC barcodes the HRI text now uses getEanUpcLayout to
place each digit group at the correct position along the rotated encoding
axis, mirroring the upright layout: system digit floated before the start
guard, left group centered at ~1/4, right group at ~3/4. Applies to all
three rotation cases (R/I/B) with correct axis mapping per rotation.
… displayText block

Three copies of the UPC-E expansion+check-digit logic collapsed into a single
exported helper. Dead displayText branch for EAN/UPC types (unreachable after
rotated positioned-node rendering was added) deleted.
@u8array
Copy link
Copy Markdown
Owner Author

u8array commented May 6, 2026

/gemini review

Copy link
Copy Markdown

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

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 comprehensive support for barcode rotation (N, R, I, B) across various 1D and 2D barcode types. Key changes include updating the ZPL parser to extract rotation parameters, adding a RotationSelect UI component to the properties panel, and implementing complex rendering logic in BarcodeObject.tsx to handle rotated text overlays and dimension swapping. The bwipHelpers.ts utility was also enhanced to interface correctly with bwip-js rotation options. Feedback was provided regarding code duplication in the new rendering logic within BarcodeObject.tsx, suggesting a refactor to unify the Group and KImage wrappers for different barcode categories.

Comment thread src/components/Canvas/BarcodeObject.tsx
u8array added 3 commits May 6, 2026 22:38
Addresses Gemini finding: two nearly-identical return statements collapsed
into one. Text content (EAN/UPC nodes or plain Text) is determined first,
then rendered in a single shared Group.
…node

Labelary does not render the check digit outside the right guard bars.
Right block now shows 6 digits (d6-d11) over 42 modules (halfWidth * 6/5),
matching upright N layout. Applied to both upright and rotated rendering.
Labelary omits the check digit from the human-readable line entirely.
Right block reverted to 5 digits (d6-d10) over 35 modules, matching
the upright N layout: system-digit | left-5 | right-5.
@u8array u8array merged commit ef8cf0c into main May 6, 2026
2 checks passed
@u8array u8array deleted the feat/barcode-rotation branch May 6, 2026 20:49
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