From ddc81d482ed85f856e6058cbdeb7d38334b59b68 Mon Sep 17 00:00:00 2001 From: Eduardo Dorado Date: Fri, 22 May 2026 00:34:29 -0300 Subject: [PATCH] fix: render reversed fields and code128 barcodes --- server.js | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/server.js b/server.js index 848ecbc..1ee9487 100644 --- a/server.js +++ b/server.js @@ -167,6 +167,7 @@ function createInitialState() { font: { name: "0", orientation: "N", height: 30, width: 30 }, block: null, fieldHex: false, + fieldReverse: false, barcode: null, barcodeDefaults: { moduleWidth: 2, ratio: 3, height: 10 }, }; @@ -194,6 +195,10 @@ function estimateGraphicHeight(operation) { return operation.size; } + if (operation.type === "code128") { + return operation.height; + } + return estimateTextHeight(operation); } @@ -211,10 +216,12 @@ function parseZpl(zpl, renderContext) { case "^XZ": case "^FS": case "^CI": + case "^FX": case "^MC": if (token.code === "^FS") { state.block = null; state.fieldHex = false; + state.fieldReverse = false; state.barcode = null; } break; @@ -257,6 +264,9 @@ function parseZpl(zpl, renderContext) { case "^FH": state.fieldHex = true; break; + case "^FR": + state.fieldReverse = true; + break; case "^BY": { const [moduleWidth, ratio, height] = splitParams(params); state.barcodeDefaults = { @@ -275,6 +285,18 @@ function parseZpl(zpl, renderContext) { }; break; } + case "^BC": { + const [, height, printText, textAbove, checkDigit] = splitParams(params); + state.barcode = { + type: "code128", + height: parseInteger(height, 120), + printText: String(printText || "Y").toUpperCase() === "Y", + textAbove: String(textAbove || "N").toUpperCase() === "Y", + checkDigit: String(checkDigit || "N").toUpperCase() === "Y", + moduleWidth: state.barcodeDefaults.moduleWidth, + }; + break; + } case "^FD": { const data = decodeFieldData(params, state.fieldHex); @@ -289,6 +311,17 @@ function parseZpl(zpl, renderContext) { scale: state.barcode.magnification, size, }); + } else if (state.barcode && state.barcode.type === "code128") { + addOperation(operations, { + type: "code128", + x: state.x, + y: state.y, + text: data.replace(/^>[:;>]?/, ""), + height: state.barcode.height, + printText: state.barcode.printText, + textAbove: state.barcode.textAbove, + moduleWidth: state.barcode.moduleWidth, + }); } else { addOperation(operations, { type: "text", @@ -297,10 +330,12 @@ function parseZpl(zpl, renderContext) { text: data, font: { ...state.font }, block: state.block ? { ...state.block } : null, + reverse: state.fieldReverse, }); } state.fieldHex = false; + state.fieldReverse = false; state.block = null; state.barcode = null; break; @@ -483,9 +518,11 @@ function fillScaledText(ctx, text, x, y, font, renderContext) { function drawText(ctx, operation, renderContext) { configureTextContext(ctx, operation.font); + ctx.fillStyle = operation.reverse ? "#FFFFFF" : "#000000"; if (!operation.block) { fillScaledText(ctx, operation.text, operation.x, operation.y, operation.font, renderContext); + ctx.fillStyle = "#000000"; return; } @@ -505,6 +542,8 @@ function drawText(ctx, operation, renderContext) { fillScaledText(ctx, line, x, operation.y + (index * lineHeight), operation.font, renderContext); }); + + ctx.fillStyle = "#000000"; } function drawBox(ctx, operation) { @@ -520,7 +559,7 @@ function drawBox(ctx, operation) { return; } - if (operation.width === operation.thickness && operation.height === operation.thickness) { + if (operation.thickness >= Math.min(operation.width, operation.height)) { ctx.fillRect(operation.x, operation.y, operation.width, operation.height); return; } @@ -569,6 +608,27 @@ async function drawQr(ctx, operation) { ctx.drawImage(qrCanvas, operation.x, operation.y, operation.size, operation.size); } +async function drawCode128(ctx, operation) { + const bwip = await loadBwip(); + const targetWidth = Math.max(260, operation.text.length * operation.moduleWidth * 11); + const targetHeight = operation.height; + const barcodeCanvas = createCanvas(targetWidth, targetHeight); + + bwip.render({ + bcid: "code128", + text: operation.text, + scaleX: Math.max(1, operation.moduleWidth), + scaleY: 2, + height: Math.max(8, operation.height / 12), + includetext: operation.printText, + paddingwidth: 0, + paddingheight: 0, + textxalign: "center", + }, bwip.drawingCanvas(barcodeCanvas)); + + ctx.drawImage(barcodeCanvas, operation.x, operation.y, targetWidth, targetHeight); +} + async function renderZplToCanvas(zpl, options = {}) { const renderContext = createRenderContext(options); const { operations, warnings } = parseZpl(zpl, renderContext); @@ -588,6 +648,8 @@ async function renderZplToCanvas(zpl, options = {}) { drawGraphic(ctx, operation); } else if (operation.type === "qr") { await drawQr(ctx, operation); + } else if (operation.type === "code128") { + await drawCode128(ctx, operation); } }