Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/components/Canvas/KonvaObject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,14 @@ function KonvaObjectInner({
// Use the measured ink width (already in dot space, mirrored to
// CSS px via dotsToPx) so the inverted background bbox tracks the
// actual rendered text rather than a length-based guess.
// Match the printer's ^GB knockout-background exactly: width =
// measured ink width, height = font height in CSS px. Earlier
// versions padded the height to 1.3× for visual breathing room
// but that hid what the printer actually emits — the canvas now
// mirrors the same `^GB inkW, fontHeight` shape the generator
// produces, so the preview matches Labelary and the real device.
const approxW = dotsToPx(textMetrics.inkWidthDots, scale, dpmm);
const approxH = fontSizePx * 1.3;
const approxH = fontSizePx;
return (
<Group
id={obj.id}
Expand Down Expand Up @@ -309,7 +315,7 @@ function KonvaObjectInner({
fontStyle="bold"
scaleX={fontScaleX}
fill="#ffffff"
y={approxH * 0.1}
y={0}
/>
</Group>
);
Expand Down
21 changes: 21 additions & 0 deletions src/lib/zplGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,27 @@ describe('generateZPL — structure', () => {
expect(zpl).toContain('^LS5');
});

it('reverse text round-trips: emit ^GB+^FR, parse collapses back to one reverse text', () => {
// Generator emits a filled black ^GB knockout-background followed by
// an ^FR text at the same anchor. The parser detects that pair and
// collapses it back into a single text object with reverse:true —
// so the editor never sees two objects after a save/load cycle.
const objs = [
{ id: 'r', type: 'text', x: 50, y: 50, rotation: 0,
props: { content: 'Hi', fontHeight: 30, fontWidth: 0, rotation: 'N', reverse: true } },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
] as any;
const zpl = generateZPL(BASE_LABEL, objs);
expect(zpl).toContain('^GB');
expect(zpl).toContain('^FR^FD');
expect(zpl).not.toContain('^LRY');
const { objects } = parseZPL(zpl, 8);
expect(objects).toHaveLength(1);
expect(objects[0]?.type).toBe('text');
expect(props(objects[0]).reverse).toBe(true);
expect(props(objects[0]).content).toBe('Hi');
});

it('omits objects with includeInExport=false', () => {
const objs = [
{ id: 'a', type: 'text', x: 10, y: 10, rotation: 0, props: { content: 'KEEP', fontHeight: 30, fontWidth: 0, rotation: 'N', reverse: false } },
Expand Down
13 changes: 13 additions & 0 deletions src/lib/zplParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,19 @@ describe('parseZPL — ^FR field reverse', () => {
const { objects } = parseZPL('^XA^FO0,0^A0N,30,0^FDNormal^FS^XZ', 8);
expect(props(objects[0]).reverse).toBeFalsy();
});

it('keeps an unrelated filled box + ^FR text at a different anchor as two objects', () => {
// Anchor mismatch ⇒ no collapse. Hand-written ZPL where a black box
// and an ^FR text happen to coexist must round-trip unchanged.
const { objects } = parseZPL(
'^XA^FO10,10^GB60,30,60,B,0^FS^FO200,200^A0N,30,0^FR^FDHi^FS^XZ',
8,
);
expect(objects).toHaveLength(2);
expect(objects[0]?.type).toBe('box');
expect(objects[1]?.type).toBe('text');
expect(props(objects[1]).reverse).toBe(true);
});
});

// ── shapes ────────────────────────────────────────────────────────────────────
Expand Down
Loading