Skip to content

Commit 8f64d0b

Browse files
authored
Merge pull request #194 from u8array/fix/single-line-text-rotated-bounds
fix(canvas): correct rotated single-line text bounds
2 parents f13c29d + 98f6fa0 commit 8f64d0b

2 files changed

Lines changed: 37 additions & 4 deletions

File tree

‎src/lib/objectBounds.test.ts‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,24 @@ describe("objectBoundsDots", () => {
110110
expect(objectBoundsDots(t, ctx(measured))).toEqual({ x: 12, y: 24, width: 77, height: 30 });
111111
});
112112

113-
it("single-line text: measured footprint is used verbatim (producer already rotated it)", () => {
113+
it("single-line text: rotated footprint size is verbatim, top-left shifts for R", () => {
114114
const t = leaf("text", 12, 24, { content: "Hello", fontHeight: 30, fontWidth: 0, rotation: "R" });
115115
const measured = new Map([[t.id, { width: 30, height: 77 }]]); // already rotated
116-
expect(objectBoundsDots(t, ctx(measured))).toEqual({ x: 12, y: 24, width: 30, height: 77 });
116+
// Size used verbatim (not re-rotated); R rotates about the anchor, so the
117+
// visual top-left moves left by the rotated width (mirrors blockBoundsDots).
118+
expect(objectBoundsDots(t, ctx(measured))).toEqual({ x: -18, y: 24, width: 30, height: 77 });
119+
});
120+
121+
it("single-line text: B rotates about the anchor, top-left moves up", () => {
122+
const t = leaf("text", 12, 24, { content: "Hello", fontHeight: 30, fontWidth: 0, rotation: "B" });
123+
const measured = new Map([[t.id, { width: 30, height: 77 }]]); // already rotated
124+
expect(objectBoundsDots(t, ctx(measured))).toEqual({ x: 12, y: -53, width: 30, height: 77 });
125+
});
126+
127+
it("single-line text: I rotates about the anchor, top-left moves left and up", () => {
128+
const t = leaf("text", 12, 24, { content: "Hello", fontHeight: 30, fontWidth: 0, rotation: "I" });
129+
const measured = new Map([[t.id, { width: 77, height: 30 }]]); // already rotated
130+
expect(objectBoundsDots(t, ctx(measured))).toEqual({ x: -65, y: -6, width: 77, height: 30 });
117131
});
118132

119133
it("single-line text: fallback estimate swaps axes for R", () => {

‎src/lib/objectBounds.ts‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@ function fallbackSizeDots(
6060
return def ? resolveDefaultSizeDots(def, label) : { width: 0, height: 0 };
6161
}
6262

63+
/** Anchor to visual-top-left shift for one rotated line: the renderer rotates
64+
* the node about obj.x/obj.y, so R/I/B move the AABB off the anchor. Takes the
65+
* already-rotated footprint; mirrors blockBoundsDots. */
66+
function rotatedLineOffset(
67+
rotation: ZplRotation,
68+
fpWidth: number,
69+
fpHeight: number,
70+
): { x: number; y: number } {
71+
switch (rotation) {
72+
case "R": return { x: -fpWidth, y: 0 };
73+
case "I": return { x: -fpWidth, y: -fpHeight };
74+
case "B": return { x: 0, y: -fpHeight };
75+
case "N":
76+
default: return { x: 0, y: 0 };
77+
}
78+
}
79+
6380
/** Single-line text/serial footprint estimate from props when no measured
6481
* size exists. Width is the Font-0 calibrated advance sum; height is the
6582
* font height. Rotation swaps the axes. */
@@ -166,11 +183,13 @@ export function objectBoundsDots(obj: LabelObject, ctx: ObjectBoundsCtx): Boundi
166183
// Measured is the already-rotated footprint (the producer rotates it); the
167184
// fallback estimate computes upright and rotates itself.
168185
const fp = ctx.measured?.get(obj.id) ?? singleLineEstimate(obj);
169-
return { x: obj.x, y: obj.y, width: fp.width, height: fp.height };
186+
const off = rotatedLineOffset(p.rotation, fp.width, fp.height);
187+
return { x: obj.x + off.x, y: obj.y + off.y, width: fp.width, height: fp.height };
170188
}
171189
case "serial": {
172190
const fp = ctx.measured?.get(obj.id) ?? singleLineEstimate(obj);
173-
return { x: obj.x, y: obj.y, width: fp.width, height: fp.height };
191+
const off = rotatedLineOffset(obj.props.rotation, fp.width, fp.height);
192+
return { x: obj.x + off.x, y: obj.y + off.y, width: fp.width, height: fp.height };
174193
}
175194
default: {
176195
if (isBarcode(obj)) {

0 commit comments

Comments
 (0)