Skip to content

feat(export): Brother P-touch .lbx export (image-wrapped, single + batch) - #47

Draft
kamilpajak wants to merge 10 commits into
masterfrom
feature/lbx-export-phase1
Draft

kamilpajak wants to merge 10 commits into
masterfrom
feature/lbx-export-phase1

Conversation

@kamilpajak

@kamilpajak kamilpajak commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Add a Brother P-touch .lbx export next to the PNG export, for both single labels and batches.

How it works today / problem

  • The app only exported PNG. A PNG must be re-imported and re-laid-out by hand in P-touch Editor.
  • A first attempt generated native P-touch text objects. P-touch re-rendered them in a Brother font that did not match the app's label — wrong font, no hardware icon, primary text only. It looked wrong.

Goal

Give users a .lbx that opens in P-touch Editor on the correct tape and looks exactly like the app's label, with one click — for a single label and for a whole batch as one file.

How

  • Image-wrapped, not native text. Render the label with the same canvas pipeline as the PNG export, bake it into a 1-bit BMP, and embed it as a single image:image object (modelled on the shipped "Address2" template). Pixel-perfect copy; the × survives because it is pixels, not text. Trade-off: text is not editable in P-touch — accepted for fidelity.
  • Batch = one long strip .lbx. The batch strip canvas → one BMP → one image:image on a single long sheet. One file prints all labels side-by-side. Chosen over P-touch's database/CSV route (needs a physical printer, no file output, drops ×) and over multi-sheet (unconfirmed XML) — see docs/plan-lbx-export.md "Batch route investigation".
  • Compact XML is required (a pretty-printed .lbx crashes P-touch's parser).
  • UI: two compact export buttons per mode — Export PNG / Export .lbx (single) and Export PNG Strip / Export .lbx (batch).
  • No official Brother SDK exists for client-side .lbx generation (b-PAC is Windows COM, print-only for templates); hand-rolling is the only browser-side path.

Links

  • Related: docs/plan-lbx-export.md

Test plan

  • bmp.test.ts — 1-bit BMP encoder (header, DPI, row padding, threshold, MSB packing, transparency)
  • lbx-export.test.ts — image label.xml (one image object, no text object, compact, tape geometry), prop.xml, binary ZIP entry, filename slug, downloadBlob
  • e2e/lbx-export.test.ts — single label download is a ZIP with Object0.bmp + <image:image>, no <text:text>
  • e2e/batch-lbx-export.test.ts — batch download is one strip .lbx with a single image object
  • pnpm check 0 errors, unit suite 754+ passing
  • Manual (maintainer): opened generated single + batch .lbx in P-touch Editor 5.3.23 on 12 mm tape
  • Known gap: 9 mm tape geometry is best-effort (unvalidated); QR/secondary-text as separate objects are out of scope (baked into the raster)

Risks / rollback

  • .lbx is reverse-engineered (not officially documented); future P-touch versions could change the schema. Mitigated by modelling on shipped templates + tests + manual validation.
  • 1-bit conversion uses a fixed threshold (128); thin strokes can thin slightly. Revert = drop the .lbx buttons; PNG export is untouched.

Export a single label as an editable Brother P-touch .lbx (opens natively in
P-touch Editor) alongside the PNG export. Validated in Phase 0: a from-scratch
.lbx opens and renders on 12mm tape. New src/lib/utils/lbx/ modules generate
compact label.xml + prop.xml (compact is required — pretty-printed XML crashes
P-touch's parser) and zip them with fflate; label-lbx-exporter builds and
downloads the file. Adds an 'Export for P-touch (.lbx)' button in single mode.
Covered by unit tests (XML/zip/units) and an e2e download test. 9mm tape and
QR/images/secondary text are follow-up phases.
… text

Strip C0 control characters (except tab/CR/LF) in xmlEscape: XML 1.0
forbids them and P-touch rejects a .lbx whose text carries raw control
bytes. Also disable the P-touch export button when the primary text is
empty, matching the handler guard so general-mode labels with only
secondary text cannot trigger an empty export.
Add unit tests for downloadBlob and exportSingleLabelAsLbx by mocking
document and URL, so the browser-download path is covered without an
e2e run. Restores new-code coverage above the 80% quality gate.
Investigated P-touch's database (CSV mail-merge) feature as the batch
route. It does not fit: the CSV must be single-byte (not UTF-8) so the
multiplication sign is lost, multi-record printing needs a physical
Brother printer, and there is no way to export the merged labels to a
file. Adopt one long single-sheet .lbx strip (N text objects along the
tape), mirroring the existing PNG batch strip, for Phase 3.
Replace the native-text .lbx export with an image-wrapped one: the app's
rendered label is baked into a 1-bit BMP and embedded as a single
image:image object. P-touch rendered the text version in a Brother font
that did not match the label design (wrong font, no hardware icon, only
the primary text). The image version is a pixel-perfect copy on the
correct tape and keeps the multiplication sign (pixels, not text).

- bmp.ts: pure canvas/ImageData -> 1-bit BMP encoder
- label-xml.ts: image:image object modelled on the Address2 template
- lbx-zip.ts: accept binary (BMP) entries alongside XML
- label-exporter.ts: shared renderLabelToExportCanvas for PNG and .lbx
- batch-lbx-exporter.ts: batch -> one long single-sheet strip .lbx
- ui: two compact export buttons (Export PNG / Export .lbx) in single
  and batch mode

Text is no longer editable in P-touch, an accepted trade for fidelity.
@kamilpajak kamilpajak changed the title feat(export): Brother P-touch .lbx export (single label, Phase 1) feat(export): Brother P-touch .lbx export (image-wrapped, single + batch) Jul 6, 2026
Add browser-mode tests for buildSingleLabelLbx/buildBatchLbx (mocked
renderers) to restore new-code coverage above the 80% gate. Drop the
label count from the batch export button, so wait on the label row
instead of the button text to confirm a label was added.
The type lives in label-exporter, not label-lbx-exporter; the wrong
import path failed the type check.
Apply zen review findings. Compute marginRight/marginBottom in label.xml
from the space left after the image instead of mirroring the leading
margin, so the paper block is correct for any image rectangle (identical
output for the current symmetric callers). Escape the embedded bitmap
file name for defense-in-depth, though it is a fixed constant today.
The .lbx export always embeds the exact PNG the app renders (the Export
PNG / Export PNG Strip download), never native P-touch objects. QR,
images, and secondary text are already baked into that raster, so native
QR/text phases are cancelled — there is nothing to add per label feature.
@sonarqubecloud

sonarqubecloud Bot commented Jul 6, 2026

Copy link
Copy Markdown

@kamilpajak
kamilpajak marked this pull request as draft July 7, 2026 09:59
@kamilpajak

Copy link
Copy Markdown
Owner Author

Parking this as a draft until I can verify it on a real Brother printer.

Before merging I want to print the same label two ways and compare:

  1. Export PNG, then import and print it in P-touch Editor.
  2. Export .lbx and print it directly.

Both should look the same on tape (the .lbx embeds the exact PNG). Once the print test passes on 12 mm (and ideally 9 mm) tape, I'll mark this ready and merge.

Test files generated from this branch are on my desktop: gridscribe-app-real.png, gridscribe-app-real.lbx (single), gridscribe-batch-real.lbx (batch).

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