Feat/barcode rotation#19
Conversation
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.
There was a problem hiding this comment.
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.
…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.
|
/gemini review |
There was a problem hiding this comment.
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.
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.
No description provided.