From 3ed3b9cc41090e5286ec826d79fb1886f8cdfaf9 Mon Sep 17 00:00:00 2001 From: u8array Date: Fri, 15 May 2026 22:47:43 +0200 Subject: [PATCH 1/8] feat(font): PrintLab ZPL Bold + ZPL-correct text positioning Add a PrintLab ZPL Bold font (Roboto Condensed derivative, Apache-2.0) with per-glyph advance widths and vertical scale fitted to Zebra's CG Triumvirate so editor layout matches the printed label. Family is renamed to avoid collision with upstream Roboto. License and NOTICE included in src/assets/fonts/. Rebuild text positioning to bridge two anchor semantics: Konva anchors at the EM-top-left and rotates the whole node around that point, while ZPL anchors at the cap-top (^FO) or baseline (^FT) and keeps the cell extending right-down regardless of rotation. The new shift table in textPositionTransforms maps every rotation x positionType to the screen offset needed so the rendered bbox lands where Labelary draws it. I and B FO branches additionally consume the measured ink width because Konva's rotation pivots at the anchor. Add textBoxMatch (our font vs Labelary default) and textVisualRegression (same font on both sides) suites with Labelary-generated fixtures. --- .gitignore | 3 +- index.html | 2 +- src/assets/fonts/LICENSE-APACHE-2.0.txt | 202 ++++++++++++++++++ src/assets/fonts/NOTICE.md | 27 +++ src/assets/fonts/PrintLabZPL-Bold.ttf | Bin 0 -> 140640 bytes src/assets/fonts/PrintLabZPL-Bold.woff2 | Bin 0 -> 63512 bytes src/components/Canvas/KonvaObject.tsx | 66 ++++-- src/components/Canvas/measureTextDots.ts | 43 ++++ .../Canvas/textPositionTransforms.test.ts | 77 +++++-- .../Canvas/textPositionTransforms.ts | 110 +++++++--- src/index.css | 14 ++ src/test/textBoxMatch.test.ts | 141 ++++++++++++ src/test/textVisualRegression.test.ts | 116 ++++++++++ .../labelary_text_default_images/h20_0.png | Bin 0 -> 6812 bytes .../labelary_text_default_images/h20_5.png | Bin 0 -> 6883 bytes .../labelary_text_default_images/h20_9.png | Bin 0 -> 6935 bytes .../labelary_text_default_images/h20_A.png | Bin 0 -> 6906 bytes .../labelary_text_default_images/h20_M.png | Bin 0 -> 6883 bytes .../labelary_text_default_images/h20_W.png | Bin 0 -> 7205 bytes .../labelary_text_default_images/h20_dot.png | Bin 0 -> 6497 bytes .../labelary_text_default_images/h20_eq.png | Bin 0 -> 6585 bytes .../labelary_text_default_images/h20_lcA.png | Bin 0 -> 6810 bytes .../labelary_text_default_images/h20_lcI.png | Bin 0 -> 6535 bytes .../labelary_text_default_images/h20_lcP.png | Bin 0 -> 6863 bytes .../h20_minus.png | Bin 0 -> 6522 bytes .../labelary_text_default_images/h30_0.png | Bin 0 -> 7059 bytes .../labelary_text_default_images/h30_5.png | Bin 0 -> 7182 bytes .../labelary_text_default_images/h30_9.png | Bin 0 -> 7324 bytes .../labelary_text_default_images/h30_A.png | Bin 0 -> 7281 bytes .../labelary_text_default_images/h30_M.png | Bin 0 -> 7190 bytes .../labelary_text_default_images/h30_W.png | Bin 0 -> 7936 bytes .../labelary_text_default_images/h30_dot.png | Bin 0 -> 6505 bytes .../labelary_text_default_images/h30_eq.png | Bin 0 -> 6588 bytes .../labelary_text_default_images/h30_lcA.png | Bin 0 -> 7064 bytes .../labelary_text_default_images/h30_lcI.png | Bin 0 -> 6576 bytes .../labelary_text_default_images/h30_lcP.png | Bin 0 -> 7125 bytes .../h30_minus.png | Bin 0 -> 6515 bytes .../h30_w60_alpha.png | Bin 0 -> 7314 bytes .../h39_w57_long.png | Bin 0 -> 9992 bytes .../labelary_text_default_images/h50_0.png | Bin 0 -> 7384 bytes .../labelary_text_default_images/h50_5.png | Bin 0 -> 7617 bytes .../labelary_text_default_images/h50_9.png | Bin 0 -> 7873 bytes .../labelary_text_default_images/h50_A.png | Bin 0 -> 7822 bytes .../labelary_text_default_images/h50_M.png | Bin 0 -> 7678 bytes .../labelary_text_default_images/h50_W.png | Bin 0 -> 9022 bytes .../labelary_text_default_images/h50_dot.png | Bin 0 -> 6502 bytes .../labelary_text_default_images/h50_eq.png | Bin 0 -> 6583 bytes .../labelary_text_default_images/h50_lcA.png | Bin 0 -> 7490 bytes .../labelary_text_default_images/h50_lcI.png | Bin 0 -> 6567 bytes .../labelary_text_default_images/h50_lcP.png | Bin 0 -> 7463 bytes .../h50_minus.png | Bin 0 -> 6521 bytes .../h50_w30_num.png | Bin 0 -> 7951 bytes .../labelary_text_default_images/h80_0.png | Bin 0 -> 7907 bytes .../labelary_text_default_images/h80_5.png | Bin 0 -> 8226 bytes .../labelary_text_default_images/h80_9.png | Bin 0 -> 8647 bytes .../labelary_text_default_images/h80_A.png | Bin 0 -> 8607 bytes .../labelary_text_default_images/h80_M.png | Bin 0 -> 8460 bytes .../labelary_text_default_images/h80_W.png | Bin 0 -> 10674 bytes .../labelary_text_default_images/h80_dot.png | Bin 0 -> 6501 bytes .../labelary_text_default_images/h80_eq.png | Bin 0 -> 6589 bytes .../labelary_text_default_images/h80_lcA.png | Bin 0 -> 8043 bytes .../labelary_text_default_images/h80_lcI.png | Bin 0 -> 6565 bytes .../labelary_text_default_images/h80_lcP.png | Bin 0 -> 8017 bytes .../h80_minus.png | Bin 0 -> 6523 bytes .../h87_w72_201.png | Bin 0 -> 7726 bytes .../labelary_text_images/h20_n_num.png | Bin 0 -> 7060 bytes .../labelary_text_images/h30_n_alpha.png | Bin 0 -> 7499 bytes .../labelary_text_images/h30_n_date.png | Bin 0 -> 8259 bytes .../labelary_text_images/h30_n_decimal.png | Bin 0 -> 7237 bytes .../labelary_text_images/h30_n_num.png | Bin 0 -> 7426 bytes .../labelary_text_images/h39_n_long.png | Bin 0 -> 9775 bytes .../labelary_text_images/h50_n_num.png | Bin 0 -> 8011 bytes .../labelary_text_images/h80_n_alpha.png | Bin 0 -> 8952 bytes .../labelary_text_images/h80_n_num.png | Bin 0 -> 8891 bytes tests/fixtures/textBoxMatchCases.ts | 82 +++++++ tests/fixtures/textTestCases.ts | 43 ++++ .../fetch_labelary_default_text_fixtures.ts | 64 ++++++ tests/scripts/fetch_labelary_text_fixtures.ts | 100 +++++++++ 78 files changed, 1030 insertions(+), 60 deletions(-) create mode 100644 src/assets/fonts/LICENSE-APACHE-2.0.txt create mode 100644 src/assets/fonts/NOTICE.md create mode 100644 src/assets/fonts/PrintLabZPL-Bold.ttf create mode 100644 src/assets/fonts/PrintLabZPL-Bold.woff2 create mode 100644 src/components/Canvas/measureTextDots.ts create mode 100644 src/test/textBoxMatch.test.ts create mode 100644 src/test/textVisualRegression.test.ts create mode 100644 tests/fixtures/labelary_text_default_images/h20_0.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_5.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_9.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_A.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_M.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_W.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_dot.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_eq.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_lcA.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_lcI.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_lcP.png create mode 100644 tests/fixtures/labelary_text_default_images/h20_minus.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_0.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_5.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_9.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_A.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_M.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_W.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_dot.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_eq.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_lcA.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_lcI.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_lcP.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_minus.png create mode 100644 tests/fixtures/labelary_text_default_images/h30_w60_alpha.png create mode 100644 tests/fixtures/labelary_text_default_images/h39_w57_long.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_0.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_5.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_9.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_A.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_M.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_W.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_dot.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_eq.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_lcA.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_lcI.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_lcP.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_minus.png create mode 100644 tests/fixtures/labelary_text_default_images/h50_w30_num.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_0.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_5.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_9.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_A.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_M.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_W.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_dot.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_eq.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_lcA.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_lcI.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_lcP.png create mode 100644 tests/fixtures/labelary_text_default_images/h80_minus.png create mode 100644 tests/fixtures/labelary_text_default_images/h87_w72_201.png create mode 100644 tests/fixtures/labelary_text_images/h20_n_num.png create mode 100644 tests/fixtures/labelary_text_images/h30_n_alpha.png create mode 100644 tests/fixtures/labelary_text_images/h30_n_date.png create mode 100644 tests/fixtures/labelary_text_images/h30_n_decimal.png create mode 100644 tests/fixtures/labelary_text_images/h30_n_num.png create mode 100644 tests/fixtures/labelary_text_images/h39_n_long.png create mode 100644 tests/fixtures/labelary_text_images/h50_n_num.png create mode 100644 tests/fixtures/labelary_text_images/h80_n_alpha.png create mode 100644 tests/fixtures/labelary_text_images/h80_n_num.png create mode 100644 tests/fixtures/textBoxMatchCases.ts create mode 100644 tests/fixtures/textTestCases.ts create mode 100644 tests/scripts/fetch_labelary_default_text_fixtures.ts create mode 100644 tests/scripts/fetch_labelary_text_fixtures.ts diff --git a/.gitignore b/.gitignore index 37613dc5..4258aa9c 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ coverage/ # Test output tests/fixtures/__diffs__/ tests/fixtures/__shape_diffs__/ +tests/fixtures/__text_diffs__/ # Editor directories and files .vscode/* @@ -51,6 +52,4 @@ tests/fixtures/__shape_diffs__/ .aider.conf.yml *.instructions.* -!tests/fixtures/labelary_images/*.png -!tests/fixtures/labelary_shape_images/*.png CLAUDE.md diff --git a/index.html b/index.html index 35010f95..dc7e4791 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ ZebraPrintLab - +