Skip to content

Commit 96ae77a

Browse files
authored
Merge pull request #40 from u8array/refactor/per-type-bwip-scale
refactor(canvas): compute 1D bwip scale per case instead of via cast
2 parents 7a6e703 + cbda99a commit 96ae77a

2 files changed

Lines changed: 42 additions & 18 deletions

File tree

‎src/components/Canvas/BarcodeObject.tsx‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,13 @@ export function BarcodeObject({
175175
!ObjectRegistry[obj.type]?.interpretationLocked &&
176176
!!(obj.props as { printInterpretation?: boolean }).printInterpretation;
177177
const printInterp = isUpright && printInterpEnabled;
178+
// Cross-type access: this block runs for every barcode and reads moduleWidth
179+
// generically (textFontSize is computed for HRI text rendering downstream).
180+
// Unlike buildBwipOptions, this function isn't switch-structured by obj.type,
181+
// so per-case narrowing would require restructuring the whole block. The
182+
// cast + fallback stays as the documented boundary between cross-type code
183+
// and the per-type required-typed schemas (2D types without moduleWidth fall
184+
// through with the default; their branches don't render HRI text anyway).
178185
const moduleWidth =
179186
(obj.props as { moduleWidth?: number }).moduleWidth ?? 2;
180187
const textFontSize = Math.max(dotsToPx(moduleWidth * 10, scale, dpmm), 6);

‎src/components/Canvas/bwipHelpers.ts‎

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,23 @@ export function get1DBwipScale(
188188
return Math.max(1, Math.round(dotsToPx(moduleWidth, scale, dpmm)));
189189
}
190190

191+
/**
192+
* Render-scale for 1D barcodes: integer-aligned per-module scale when the
193+
* caller supplies the canvas scale + dpmm, otherwise the BWIP_SCALE default.
194+
* Wrapping the branch lets per-type cases pass their own (narrowed,
195+
* required-typed) `moduleWidth` instead of going through a cast at the
196+
* generic top of buildBwipOptions.
197+
*/
198+
function bwipScale1D(
199+
moduleWidth: number,
200+
renderScale: number | undefined,
201+
renderDpmm: number | undefined,
202+
): number {
203+
return renderScale != null && renderDpmm != null
204+
? get1DBwipScale(moduleWidth, renderScale, renderDpmm)
205+
: BWIP_SCALE;
206+
}
207+
191208
export function eanCheckDigit(digits: string, w0: number, w1: number): string {
192209
let sum = 0;
193210
for (let i = 0; i < digits.length; i++)
@@ -232,14 +249,6 @@ export function buildBwipOptions(
232249
const bcid = BCID[obj.type];
233250
if (!bcid) return null;
234251

235-
// For 1D barcodes, choose an integer-aligned scale so module widths map
236-
// exactly to display pixels (no fractional upscaling / anti-aliasing).
237-
const mw = (obj.props as { moduleWidth?: number }).moduleWidth ?? 2;
238-
const scale1D =
239-
renderScale != null && renderDpmm != null
240-
? get1DBwipScale(mw, renderScale, renderDpmm)
241-
: BWIP_SCALE;
242-
243252
// bwip-js takes the same N/R/I/B letters ZPL does for symbol orientation;
244253
// emitting it post-build means the produced bitmap is already rotated and
245254
// its dimensions are the post-rotation extents — no Konva-side rotation math
@@ -254,26 +263,28 @@ export function buildBwipOptions(
254263
case "upca":
255264
case "upce": {
256265
const p = obj.props;
266+
const scale = bwipScale1D(p.moduleWidth, renderScale, renderDpmm);
257267
let text: string;
258268
if (obj.type === "upce") {
259269
const r = p.content || "000000";
260270
text = r.length === 6 ? `0${r}` : r;
261271
} else {
262272
text = p.content || "0";
263273
}
264-
opts = { bcid, text, scale: scale1D, height: 10 };
274+
opts = { bcid, text, scale, height: 10 };
265275
break;
266276
}
267277
case "code128": {
268-
const p = obj.props as { content?: string };
278+
const p = obj.props;
279+
const scale = bwipScale1D(p.moduleWidth, renderScale, renderDpmm);
269280
const text = p.content || "0";
270281
// Note: ZPL ^BC e=Y (checkDigit) only prints the MOD-10 digit in the
271282
// interpretation line — it does NOT append it to the encoded barcode data.
272283
const rawB = toCode128BRaw(text);
273284
if (rawB) {
274-
opts = { bcid, text: rawB, raw: true, scale: scale1D, height: 10 };
285+
opts = { bcid, text: rawB, raw: true, scale, height: 10 };
275286
} else {
276-
opts = { bcid, text, scale: scale1D, height: 10 };
287+
opts = { bcid, text, scale, height: 10 };
277288
}
278289
break;
279290
}
@@ -286,6 +297,7 @@ export function buildBwipOptions(
286297
case "codabar":
287298
case "plessey": {
288299
const p = obj.props;
300+
const scale = bwipScale1D(p.moduleWidth, renderScale, renderDpmm);
289301
// Code39/Codabar/Plessey only encode uppercase letters. Zebra firmware
290302
// (and Labelary) silently uppercase lowercase input; bwip-js does not and
291303
// throws instead, which would crash the canvas for any imported ZPL with
@@ -294,35 +306,39 @@ export function buildBwipOptions(
294306
const needsUpper = obj.type === "code39" || obj.type === "codabar" || obj.type === "plessey";
295307
const raw = p.content || "0";
296308
const text = needsUpper ? raw.toUpperCase() : raw;
297-
opts = { bcid, text, scale: scale1D, height: 10 };
309+
opts = { bcid, text, scale, height: 10 };
298310
break;
299311
}
300312
case "msi": {
301313
const p = obj.props;
314+
const scale = bwipScale1D(p.moduleWidth, renderScale, renderDpmm);
302315
// Zebra always encodes a Mod10 check digit in MSI regardless of the ^BM e=N
303316
// parameter (which only suppresses it in the interpretation line).
304-
opts = { bcid, text: p.content || "0", scale: scale1D, height: 10, includecheck: true };
317+
opts = { bcid, text: p.content || "0", scale, height: 10, includecheck: true };
305318
break;
306319
}
307320
case "postal": {
308321
const p = obj.props;
309-
opts = { bcid, text: p.content || "0", scale: scale1D, height: 10 };
322+
const scale = bwipScale1D(p.moduleWidth, renderScale, renderDpmm);
323+
opts = { bcid, text: p.content || "0", scale, height: 10 };
310324
break;
311325
}
312326
case "logmars": {
313327
const p = obj.props;
328+
const scale = bwipScale1D(p.moduleWidth, renderScale, renderDpmm);
314329
// LOGMARS is a Code39 subset; same uppercase rule applies.
315330
opts = {
316331
bcid,
317332
text: (p.content || "0").toUpperCase(),
318-
scale: scale1D,
333+
scale,
319334
height: 10,
320335
includecheck: true,
321336
};
322337
break;
323338
}
324339
case "gs1databar": {
325340
const p = obj.props;
341+
const scale = bwipScale1D(p.moduleWidth, renderScale, renderDpmm);
326342
const sym = p.symbology;
327343
const isExpanded = GS1_DATABAR_EXPANDED_SYMBOLOGIES.has(sym);
328344
// bwip-js needs (AI)data parens; canonical model stores raw digits.
@@ -333,7 +349,7 @@ export function buildBwipOptions(
333349
opts = {
334350
bcid: GS1_DATABAR_BCID[sym],
335351
text,
336-
scale: scale1D,
352+
scale,
337353
height: 10,
338354
paddingheight: 2,
339355
...(sym === 7 ? { segments: p.segments ?? GS1_DATABAR_DEFAULT_SEGMENTS } : {}),
@@ -342,13 +358,14 @@ export function buildBwipOptions(
342358
}
343359
case "planet": {
344360
const p = obj.props;
361+
const scale = bwipScale1D(p.moduleWidth, renderScale, renderDpmm);
345362
let raw = (p.content || "0").replace(/\D/g, "");
346363
if (raw.length < 11) raw = raw.padStart(11, "0");
347364
else if (raw.length === 12) raw = raw.padStart(13, "0");
348365
opts = {
349366
bcid,
350367
text: raw,
351-
scale: scale1D,
368+
scale,
352369
height: 10,
353370
includecheck: true,
354371
};

0 commit comments

Comments
 (0)