Skip to content

feat(barcode): EAN/UPC extended guard tails via bwip-raw canvas - #142

Merged
u8array merged 1 commit into
mainfrom
feat/upc-ean-guard-extensions
Jun 7, 2026
Merged

u8array merged 1 commit into
mainfrom
feat/upc-ean-guard-extensions

Conversation

@u8array

@u8array u8array commented Jun 7, 2026

Copy link
Copy Markdown
Owner

No description provided.

@u8array
u8array merged commit f9518ba into main Jun 7, 2026
1 check passed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces native EAN/UPC barcode rendering via a raw canvas to support extending guard tails when Human Readable Interpretation (HRI) is enabled, matching Zebra firmware behavior. It also refactors cross-type property reads and adjusts selection bounding boxes and strokes for EAN/UPC barcodes. The review feedback highlights two critical issues: first, when interpretation is disabled, the canvas is vertically stretched and blurred because the canvas height does not match the image height; second, sub-pixel rendering can cause anti-aliasing blurriness. The reviewer suggests passing the full text zone height along with a boolean flag to control guard tail extension, rounding pixel heights to integers, and adding defensive checks.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +103 to +115
const eanHeight = (obj.props as { height: number }).height;
const modulePxInt = get1DBwipScale(moduleWidth, scale, dpmm);
const barHeightPx = dotsToPx(eanHeight, scale, dpmm);
const tailHeightPx = printInterpEnabled
? dotsToPx(EAN_TEXT_ZONE_DOTS, scale, dpmm)
: 0;
barcodeCanvas = renderEanUpcRawCanvas(
obj.type as EanUpcType,
rawContent,
modulePxInt,
barHeightPx,
tailHeightPx,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When printInterpretation is disabled (printInterpEnabled is false), the EAN/UPC layout still reserves the 13-dot text zone at the bottom of the barcode to match the firmware-reserved footprint. However, passing tailHeightPx = 0 to renderEanUpcRawCanvas results in a canvas height of only barHeightPx. Since the KImage height is unconditionally set to ub.barH + EAN_TEXT_ZONE_DOTS, the canvas image gets vertically stretched by ~13%, causing blurry rendering and overlapping the reserved text zone.

To fix this, we should always pass the full EAN_TEXT_ZONE_DOTS height as tailHeightPx to ensure the canvas height matches the KImage height, and pass printInterpEnabled as a boolean flag to control whether the guard tails actually extend into this zone.

    const eanHeight = (obj.props as { height: number }).height;
    const modulePxInt = get1DBwipScale(moduleWidth, scale, dpmm);
    const barHeightPx = dotsToPx(eanHeight, scale, dpmm);
    const tailHeightPx = dotsToPx(EAN_TEXT_ZONE_DOTS, scale, dpmm);
    barcodeCanvas = renderEanUpcRawCanvas(
      obj.type as EanUpcType,
      rawContent,
      modulePxInt,
      barHeightPx,
      tailHeightPx,
      printInterpEnabled,
    );

Comment on lines +211 to +250
export function renderEanUpcRawCanvas(
type: EanUpcType,
text: string,
modulePxInt: number,
barHeightPx: number,
tailHeightPx: number,
): HTMLCanvasElement | null {
if (modulePxInt <= 0 || barHeightPx <= 0) return null;
let stack: BwipRawLinear[];
try {
stack = bwipjs.raw({ bcid: type, text, includetext: true } as never) as BwipRawLinear[];
} catch {
return null;
}
const g = stack?.[0];
if (!g?.sbs) return null;
let totalModules = 0;
for (const w of g.sbs) totalModules += w;
const canvas = document.createElement("canvas");
canvas.width = totalModules * modulePxInt;
canvas.height = barHeightPx + Math.max(0, tailHeightPx);
const ctx = canvas.getContext("2d");
if (!ctx) return null;
ctx.fillStyle = "#000000";
let cx = 0;
let isBar = true;
let barIdx = 0;
for (const w of g.sbs) {
const wPx = w * modulePxInt;
if (isBar) {
const isGuard = (g.bbs?.[barIdx] ?? 0) < 0;
const h = isGuard ? canvas.height : barHeightPx;
ctx.fillRect(cx, 0, wPx + RAW_BAR_SEAM, h);
barIdx++;
}
cx += wPx;
isBar = !isBar;
}
return canvas;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Update renderEanUpcRawCanvas to accept the extendGuards boolean flag to control guard tail extension. Additionally, round barHeightPx and tailHeightPx to integers to prevent sub-pixel rendering (anti-aliasing) which causes blurry barcode edges and scanning failures. Also, add a defensive check to ensure totalModules is greater than 0 before creating the canvas.

export function renderEanUpcRawCanvas(
  type: EanUpcType,
  text: string,
  modulePxInt: number,
  barHeightPx: number,
  tailHeightPx: number,
  extendGuards: boolean,
): HTMLCanvasElement | null {
  const barH = Math.round(barHeightPx);
  const tailH = Math.round(tailHeightPx);
  if (modulePxInt <= 0 || barH <= 0) return null;
  let stack: BwipRawLinear[];
  try {
    stack = bwipjs.raw({ bcid: type, text, includetext: true } as never) as BwipRawLinear[];
  } catch {
    return null;
  }
  const g = stack?.[0];
  if (!g?.sbs) return null;
  let totalModules = 0;
  for (const w of g.sbs) totalModules += w;
  if (totalModules <= 0) return null;
  const canvas = document.createElement("canvas");
  canvas.width = totalModules * modulePxInt;
  canvas.height = barH + Math.max(0, tailH);
  const ctx = canvas.getContext("2d");
  if (!ctx) return null;
  ctx.fillStyle = "#000000";
  let cx = 0;
  let isBar = true;
  let barIdx = 0;
  for (const w of g.sbs) {
    const wPx = w * modulePxInt;
    if (isBar) {
      const isGuard = extendGuards && (g.bbs?.[barIdx] ?? 0) < 0;
      const h = isGuard ? canvas.height : barH;
      ctx.fillRect(cx, 0, wPx + RAW_BAR_SEAM, h);
      barIdx++;
    }
    cx += wPx;
    isBar = !isBar;
  }
  return canvas;
}

@u8array
u8array deleted the feat/upc-ean-guard-extensions branch June 7, 2026 23:06
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