@ai-matrx/print 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/dist/barcode.cjs +182 -0
- package/dist/barcode.cjs.map +1 -0
- package/dist/barcode.d.cts +94 -0
- package/dist/barcode.d.ts +94 -0
- package/dist/barcode.js +151 -0
- package/dist/barcode.js.map +1 -0
- package/dist/booklet.cjs +202 -0
- package/dist/booklet.cjs.map +1 -0
- package/dist/booklet.d.cts +90 -0
- package/dist/booklet.d.ts +90 -0
- package/dist/booklet.js +179 -0
- package/dist/booklet.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/qr-styled.cjs +219 -0
- package/dist/qr-styled.cjs.map +1 -0
- package/dist/qr-styled.d.cts +150 -0
- package/dist/qr-styled.d.ts +150 -0
- package/dist/qr-styled.js +188 -0
- package/dist/qr-styled.js.map +1 -0
- package/package.json +34 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — 2026-08-30
|
|
4
|
+
|
|
5
|
+
Wave 2 — three new entries, all import-inert and RSC-safe:
|
|
6
|
+
|
|
7
|
+
- **/barcode** — 1-D barcode generation: Code 128, EAN-13, UPC-A via lazy
|
|
8
|
+
`bwip-js` (`bwip-js/browser`, pure-JS SVG in Node AND browsers).
|
|
9
|
+
`normalizeBarcodeValue` is the one door: EAN/UPC check digits are appended
|
|
10
|
+
(12/11 digits) or verified (13/12), Code 128 restricted to printable ASCII.
|
|
11
|
+
`generateBarcodeSvg` everywhere; `generateBarcodeDataUrl` browser-only with
|
|
12
|
+
a clear error elsewhere. Heights are millimetres (GS1 nominal defaults).
|
|
13
|
+
- **/booklet** — saddle-stitch imposition: `imposeBooklet` pads to a multiple
|
|
14
|
+
of 4 and yields the exact fold-order sheet spreads (front `[N−2i+2, 2i−1]`,
|
|
15
|
+
back `[2i, N−2i+1]`); `requiresShortEdgeDuplex: true` encodes the long-edge
|
|
16
|
+
flip trap in the type; `bookletSizeWarning` flags bulky (>48pp) booklets;
|
|
17
|
+
`printBooklet`/`buildBookletDocument` compose a 2-up 11×8.5″ print window
|
|
18
|
+
with the exact duplex settings banner (flip on SHORT edge, fold the stack,
|
|
19
|
+
staple the spine).
|
|
20
|
+
- **/qr-styled** — branded QR codes (center logo, dot/corner styles, colors)
|
|
21
|
+
via lazy `qr-code-styling` (browser-only at CALL time; the module imports
|
|
22
|
+
fine anywhere and throws a clear browser-required error in Node). Error
|
|
23
|
+
correction restricted to Q/H (default H; lower levels clamp up — a logo
|
|
24
|
+
occludes modules). Logos accepted as `data:` URLs ONLY (print windows are
|
|
25
|
+
unauthenticated and remote logos would taint the canvas); logo size clamped
|
|
26
|
+
≤0.3 of the symbol.
|
|
27
|
+
|
|
3
28
|
## 0.1.0 — 2026-08-29
|
|
4
29
|
|
|
5
30
|
First release. Extracted from the AI Matrx frontend's print system with
|
package/dist/barcode.cjs
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/barcode.ts
|
|
31
|
+
var barcode_exports = {};
|
|
32
|
+
__export(barcode_exports, {
|
|
33
|
+
DEFAULT_BARCODE_HEIGHT: () => DEFAULT_BARCODE_HEIGHT,
|
|
34
|
+
DEFAULT_BARCODE_SCALE: () => DEFAULT_BARCODE_SCALE,
|
|
35
|
+
generateBarcodeDataUrl: () => generateBarcodeDataUrl,
|
|
36
|
+
generateBarcodeSvg: () => generateBarcodeSvg,
|
|
37
|
+
normalizeBarcodeValue: () => normalizeBarcodeValue
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(barcode_exports);
|
|
40
|
+
var DEFAULT_BARCODE_SCALE = 2;
|
|
41
|
+
var DEFAULT_BARCODE_HEIGHT = {
|
|
42
|
+
code128: 12,
|
|
43
|
+
ean13: 22.85,
|
|
44
|
+
upca: 22.85
|
|
45
|
+
};
|
|
46
|
+
var GS1_DIGIT_LENGTHS = {
|
|
47
|
+
ean13: [12, 13],
|
|
48
|
+
upca: [11, 12]
|
|
49
|
+
};
|
|
50
|
+
var SYMBOLOGIES = ["code128", "ean13", "upca"];
|
|
51
|
+
function assertSymbology(symbology) {
|
|
52
|
+
if (!SYMBOLOGIES.includes(symbology)) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Unknown barcode symbology "${String(symbology)}". Expected one of ${SYMBOLOGIES.map((s) => `"${s}"`).join(", ")}.`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return symbology;
|
|
58
|
+
}
|
|
59
|
+
function gs1CheckDigit(payloadDigits) {
|
|
60
|
+
let sum = 0;
|
|
61
|
+
let weight = 3;
|
|
62
|
+
for (let i = payloadDigits.length - 1; i >= 0; i -= 1) {
|
|
63
|
+
sum += (payloadDigits.charCodeAt(i) - 48) * weight;
|
|
64
|
+
weight = weight === 3 ? 1 : 3;
|
|
65
|
+
}
|
|
66
|
+
return (10 - sum % 10) % 10;
|
|
67
|
+
}
|
|
68
|
+
function normalizeCode128(value) {
|
|
69
|
+
if (value.length === 0) {
|
|
70
|
+
throw new Error("Code 128 barcode value must be a non-empty string.");
|
|
71
|
+
}
|
|
72
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
73
|
+
const code = value.charCodeAt(i);
|
|
74
|
+
if (code < 32 || code > 126) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Code 128 barcode value contains a non-printable-ASCII character ${JSON.stringify(value.charAt(i))} at index ${i} (char code ${code}). Only ASCII 0x20-0x7E can be printed and read back by a human.`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
function normalizeGs1(value, symbology) {
|
|
83
|
+
const raw = value.trim();
|
|
84
|
+
if (raw.length === 0) {
|
|
85
|
+
throw new Error(`${symbology === "ean13" ? "EAN-13" : "UPC-A"} barcode value must be a non-empty string.`);
|
|
86
|
+
}
|
|
87
|
+
if (!/^[0-9]+$/.test(raw)) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`${symbology === "ean13" ? "EAN-13" : "UPC-A"} barcode value "${raw}" must contain digits only (no spaces, dashes, or letters).`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const lengths = GS1_DIGIT_LENGTHS[symbology];
|
|
93
|
+
const [withoutCheck, withCheck] = lengths;
|
|
94
|
+
const label = symbology === "ean13" ? "EAN-13" : "UPC-A";
|
|
95
|
+
if (raw.length === withoutCheck) {
|
|
96
|
+
return `${raw}${gs1CheckDigit(raw)}`;
|
|
97
|
+
}
|
|
98
|
+
if (raw.length === withCheck) {
|
|
99
|
+
const expected = gs1CheckDigit(raw.slice(0, -1));
|
|
100
|
+
const actual = raw.charCodeAt(raw.length - 1) - 48;
|
|
101
|
+
if (expected !== actual) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`${label} barcode value "${raw}" has an invalid GS1 check digit: expected ${expected}, got ${actual}. Pass the first ${withoutCheck} digits instead and the check digit will be computed for you.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return raw;
|
|
107
|
+
}
|
|
108
|
+
throw new Error(
|
|
109
|
+
`${label} barcode value "${raw}" must be ${withoutCheck} digits (check digit appended) or ${withCheck} digits (check digit verified), got ${raw.length}.`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
function normalizeBarcodeValue(value, symbology) {
|
|
113
|
+
assertSymbology(symbology);
|
|
114
|
+
if (typeof value !== "string") {
|
|
115
|
+
throw new Error(`Barcode value must be a string, got ${String(value)}.`);
|
|
116
|
+
}
|
|
117
|
+
if (symbology === "code128") {
|
|
118
|
+
return normalizeCode128(value);
|
|
119
|
+
}
|
|
120
|
+
return normalizeGs1(value, symbology);
|
|
121
|
+
}
|
|
122
|
+
function resolveScale(scale) {
|
|
123
|
+
if (scale === void 0) return DEFAULT_BARCODE_SCALE;
|
|
124
|
+
if (typeof scale !== "number" || !Number.isFinite(scale) || scale <= 0) {
|
|
125
|
+
throw new Error(`Barcode scale must be a positive number, got ${String(scale)}.`);
|
|
126
|
+
}
|
|
127
|
+
return scale;
|
|
128
|
+
}
|
|
129
|
+
function resolveHeight(heightModules, symbology) {
|
|
130
|
+
if (heightModules === void 0) return DEFAULT_BARCODE_HEIGHT[symbology];
|
|
131
|
+
if (typeof heightModules !== "number" || !Number.isFinite(heightModules) || heightModules <= 0) {
|
|
132
|
+
throw new Error(`Barcode heightModules must be a positive number, got ${String(heightModules)}.`);
|
|
133
|
+
}
|
|
134
|
+
return heightModules;
|
|
135
|
+
}
|
|
136
|
+
function buildRenderOptions(value, symbology, opts) {
|
|
137
|
+
const normalized = normalizeBarcodeValue(value, symbology);
|
|
138
|
+
return {
|
|
139
|
+
bcid: symbology,
|
|
140
|
+
text: normalized,
|
|
141
|
+
scale: resolveScale(opts?.scale),
|
|
142
|
+
height: resolveHeight(opts?.heightModules, symbology),
|
|
143
|
+
includetext: opts?.includeText === void 0 ? true : opts.includeText === true,
|
|
144
|
+
textxalign: "center"
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
async function generateBarcodeSvg(value, symbology, opts) {
|
|
148
|
+
const options = buildRenderOptions(value, symbology, opts);
|
|
149
|
+
const bwipjs = await import("bwip-js/browser");
|
|
150
|
+
return bwipjs.toSVG(options);
|
|
151
|
+
}
|
|
152
|
+
var USE_SVG_INSTEAD = "Use generateBarcodeSvg() instead \u2014 SVG renders identically, works in Node and the browser, and prints at the printer's resolution rather than the canvas's.";
|
|
153
|
+
async function generateBarcodeDataUrl(value, symbology, opts) {
|
|
154
|
+
const options = buildRenderOptions(value, symbology, opts);
|
|
155
|
+
if (typeof document === "undefined") {
|
|
156
|
+
throw new Error(`generateBarcodeDataUrl() needs a DOM canvas and cannot run in Node. ${USE_SVG_INSTEAD}`);
|
|
157
|
+
}
|
|
158
|
+
const bwipjs = await import("bwip-js/browser");
|
|
159
|
+
const toCanvas = bwipjs.toCanvas;
|
|
160
|
+
if (typeof toCanvas !== "function") {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`bwip-js resolved to its non-browser build, which has no toCanvas() \u2014 barcode PNG rendering is unavailable in this environment. ${USE_SVG_INSTEAD}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
let canvas;
|
|
166
|
+
try {
|
|
167
|
+
canvas = toCanvas(document.createElement("canvas"), options);
|
|
168
|
+
} catch (cause) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`This environment's canvas could not render the barcode (${cause instanceof Error ? cause.message : String(cause)}). ${USE_SVG_INSTEAD}`,
|
|
171
|
+
{ cause }
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
const dataUrl = canvas.toDataURL("image/png");
|
|
175
|
+
if (typeof dataUrl !== "string" || !dataUrl.startsWith("data:image/png")) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`This environment's canvas produced no PNG data URL (got ${String(dataUrl)}) \u2014 barcode PNG rendering is unavailable here. ${USE_SVG_INSTEAD}`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
return dataUrl;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=barcode.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/barcode.ts"],"sourcesContent":["/**\n * @ai-matrx/print/barcode — 1-D BARCODE GENERATION for retail and warehouse labels.\n *\n * New code (no port source). Scanning/decoding is NOT here. This module only\n * ever turns a value into a linear symbol, in the three symbologies that\n * actually show up on the commerce lane's labels:\n *\n * - `code128` — the general-purpose workhorse. The `manufacturer_serial`\n * identifier kind is usually printed as Code 128: arbitrary printable ASCII,\n * dense, and every warehouse scanner reads it.\n * - `ean13` / `upca` — retail GTINs. These are NOT free-form strings: they\n * carry a GS1 mod-10 check digit, and a label printed with the wrong one is\n * a label that gets rejected at the point of sale.\n *\n * Two rules that are NOT style choices:\n * - EVERY generate call routes through `normalizeBarcodeValue` first. A caller\n * that hands us 12 digits gets the check digit APPENDED; a caller that hands\n * us 13 gets it VERIFIED and throws on a mismatch. There is no path that\n * prints an unvalidated retail symbol.\n * - The human-readable line defaults ON (`includeText`). On a retail or\n * warehouse label the printed digits are the fallback when the scanner\n * fails, and a keyer at a register cannot read bars.\n *\n * The module is import-inert: `bwip-js` is only ever pulled in via\n * `await import(\"bwip-js\")` inside a function body, so importing this file from\n * a React Server Component (or any bundle that never draws a barcode) costs\n * nothing. No module-level mutable state, no React, no DOM at import time.\n *\n * SVG works EVERYWHERE — bwip-js `toSVG` is pure JS with no canvas dependency,\n * so it is the lane for Node (server-side PDF/label composition) as much as for\n * the browser. The PNG data-URL lane is browser-only by design: it uses\n * bwip-js's canvas rendering rather than dragging `node-canvas` into a package\n * that mostly runs in a print window. In Node it throws and names the fix.\n */\n\n/**\n * We import the \"bwip-js/browser\" SUBPATH rather than the bare \"bwip-js\"\n * specifier, deliberately and for two reasons:\n * 1. bwip-js's export map has no `default` condition — only `browser`, `node`,\n * `electron` and `react-native` — so a bundler-style TypeScript resolver\n * cannot resolve the bare specifier at all. The subpath resolves cleanly.\n * 2. The browser build is the SUPERSET for our purposes: its `toSVG` is pure\n * JS and imports/runs fine under Node (verified — nothing touches `document`\n * at module scope), and it is the only build that carries `toCanvas`, which\n * the PNG lane needs. The node build would give us `toBuffer` instead and\n * no canvas lane at all.\n */\nimport type { RenderOptions } from \"bwip-js/browser\";\n\n// ─── Contract ────────────────────────────────────────────────────────────────\n\n/** The three 1-D symbologies the platform prints. Values are bwip-js `bcid`s verbatim. */\nexport type BarcodeSymbology = \"code128\" | \"ean13\" | \"upca\";\n\nexport interface BarcodeOptions {\n /**\n * Bar height. This is bwip-js's `height`, whose unit is MILLIMETRES (it is\n * not a module count — the name follows the package's option contract).\n * Defaults per symbology: 12 for Code 128, 22.85 for EAN-13/UPC-A, the GS1\n * nominal symbol height at 100% magnification.\n */\n heightModules?: number;\n /** Human-readable line under the bars. Default TRUE — it is the fallback when a scan fails. */\n includeText?: boolean;\n /** Module width multiplier (bwip-js `scale`). Default 2. */\n scale?: number;\n}\n\n/** Default module-width multiplier for every Matrx-printed barcode. */\nexport const DEFAULT_BARCODE_SCALE = 2;\n\n/**\n * Default bar height in millimetres, per symbology.\n * 22.85mm is the GS1 nominal EAN-13/UPC-A symbol height at 100% magnification;\n * Code 128 has no spec height, and 12mm is a comfortable warehouse-label bar.\n */\nexport const DEFAULT_BARCODE_HEIGHT: Readonly<Record<BarcodeSymbology, number>> = {\n code128: 12,\n ean13: 22.85,\n upca: 22.85,\n};\n\n/** Digit counts each GS1 symbology accepts: [without check digit, with check digit]. */\nconst GS1_DIGIT_LENGTHS: Readonly<Record<\"ean13\" | \"upca\", readonly [number, number]>> = {\n ean13: [12, 13],\n upca: [11, 12],\n};\n\nconst SYMBOLOGIES: readonly BarcodeSymbology[] = [\"code128\", \"ean13\", \"upca\"];\n\nfunction assertSymbology(symbology: BarcodeSymbology): BarcodeSymbology {\n if (!SYMBOLOGIES.includes(symbology)) {\n throw new Error(\n `Unknown barcode symbology \"${String(symbology)}\". Expected one of ${SYMBOLOGIES.map((s) => `\"${s}\"`).join(\", \")}.`,\n );\n }\n return symbology;\n}\n\n// ─── Validation ──────────────────────────────────────────────────────────────\n\n/**\n * GS1 mod-10 check digit over the payload digits (i.e. WITHOUT the check\n * digit): weights 3 and 1 alternating from the right. Length-independent, so\n * UPC-A's 11-digit payload and EAN-13's 12-digit payload use the same routine.\n */\nfunction gs1CheckDigit(payloadDigits: string): number {\n let sum = 0;\n let weight = 3;\n for (let i = payloadDigits.length - 1; i >= 0; i -= 1) {\n sum += (payloadDigits.charCodeAt(i) - 48) * weight;\n weight = weight === 3 ? 1 : 3;\n }\n return (10 - (sum % 10)) % 10;\n}\n\n/**\n * Code 128 encodes ASCII 0–127, but a label only prints what a human can read\n * back, so we accept the printable range 0x20 (space) through 0x7E (tilde).\n * Control characters and anything non-ASCII (smart quotes pasted from a\n * spreadsheet are the usual culprit) are rejected loudly rather than silently\n * mangled by the encoder.\n */\nfunction normalizeCode128(value: string): string {\n if (value.length === 0) {\n throw new Error(\"Code 128 barcode value must be a non-empty string.\");\n }\n for (let i = 0; i < value.length; i += 1) {\n const code = value.charCodeAt(i);\n if (code < 0x20 || code > 0x7e) {\n throw new Error(\n `Code 128 barcode value contains a non-printable-ASCII character ${JSON.stringify(value.charAt(i))} ` +\n `at index ${i} (char code ${code}). Only ASCII 0x20-0x7E can be printed and read back by a human.`,\n );\n }\n }\n return value;\n}\n\nfunction normalizeGs1(value: string, symbology: \"ean13\" | \"upca\"): string {\n const raw = value.trim();\n if (raw.length === 0) {\n throw new Error(`${symbology === \"ean13\" ? \"EAN-13\" : \"UPC-A\"} barcode value must be a non-empty string.`);\n }\n if (!/^[0-9]+$/.test(raw)) {\n throw new Error(\n `${symbology === \"ean13\" ? \"EAN-13\" : \"UPC-A\"} barcode value \"${raw}\" must contain digits only ` +\n `(no spaces, dashes, or letters).`,\n );\n }\n\n const lengths = GS1_DIGIT_LENGTHS[symbology];\n const [withoutCheck, withCheck] = lengths;\n const label = symbology === \"ean13\" ? \"EAN-13\" : \"UPC-A\";\n\n if (raw.length === withoutCheck) {\n // Caller gave us the payload — APPEND the check digit we compute.\n return `${raw}${gs1CheckDigit(raw)}`;\n }\n if (raw.length === withCheck) {\n // Caller gave us a full code — VERIFY the check digit they already have.\n const expected = gs1CheckDigit(raw.slice(0, -1));\n const actual = raw.charCodeAt(raw.length - 1) - 48;\n if (expected !== actual) {\n throw new Error(\n `${label} barcode value \"${raw}\" has an invalid GS1 check digit: expected ${expected}, got ${actual}. ` +\n `Pass the first ${withoutCheck} digits instead and the check digit will be computed for you.`,\n );\n }\n return raw;\n }\n throw new Error(\n `${label} barcode value \"${raw}\" must be ${withoutCheck} digits (check digit appended) or ` +\n `${withCheck} digits (check digit verified), got ${raw.length}.`,\n );\n}\n\n/**\n * Validate and normalize a value for a symbology, returning the FULL value that\n * will actually be encoded.\n *\n * - `ean13` / `upca`: digits only. 12 (EAN-13) or 11 (UPC-A) digits get the\n * GS1 mod-10 check digit APPENDED; 13 or 12 digits get theirs VERIFIED and\n * throw on a mismatch. Either way the return value is the complete code.\n * - `code128`: printable ASCII (0x20-0x7E) passes through untouched —\n * including leading/trailing spaces, which are legal Code 128 data.\n *\n * Throws an Error with a message naming the exact problem. Every generator in\n * this module routes through here first; call it directly when you need to show\n * the normalized value (e.g. the digits under the bars) before printing.\n */\nexport function normalizeBarcodeValue(value: string, symbology: BarcodeSymbology): string {\n assertSymbology(symbology);\n if (typeof value !== \"string\") {\n throw new Error(`Barcode value must be a string, got ${String(value)}.`);\n }\n if (symbology === \"code128\") {\n return normalizeCode128(value);\n }\n return normalizeGs1(value, symbology);\n}\n\n// ─── Option resolution ───────────────────────────────────────────────────────\n\nfunction resolveScale(scale: number | undefined): number {\n if (scale === undefined) return DEFAULT_BARCODE_SCALE;\n if (typeof scale !== \"number\" || !Number.isFinite(scale) || scale <= 0) {\n throw new Error(`Barcode scale must be a positive number, got ${String(scale)}.`);\n }\n return scale;\n}\n\nfunction resolveHeight(heightModules: number | undefined, symbology: BarcodeSymbology): number {\n if (heightModules === undefined) return DEFAULT_BARCODE_HEIGHT[symbology];\n if (typeof heightModules !== \"number\" || !Number.isFinite(heightModules) || heightModules <= 0) {\n throw new Error(`Barcode heightModules must be a positive number, got ${String(heightModules)}.`);\n }\n return heightModules;\n}\n\n/**\n * Build the bwip-js render options. Note the object is fully populated here —\n * under `exactOptionalPropertyTypes` we never assign `undefined` to an optional\n * key, so every field is either a real value or absent.\n */\nfunction buildRenderOptions(value: string, symbology: BarcodeSymbology, opts: BarcodeOptions | undefined): RenderOptions {\n const normalized = normalizeBarcodeValue(value, symbology);\n return {\n bcid: symbology,\n text: normalized,\n scale: resolveScale(opts?.scale),\n height: resolveHeight(opts?.heightModules, symbology),\n includetext: opts?.includeText === undefined ? true : opts.includeText === true,\n textxalign: \"center\",\n };\n}\n\n// ─── Rendering ───────────────────────────────────────────────────────────────\n\n/**\n * SVG markup string — the lane that works EVERYWHERE (Node and browser), since\n * bwip-js `toSVG` is pure JS with no canvas dependency. Lazy-imports `bwip-js`\n * at call time, so this module stays import-inert.\n *\n * The value is validated and normalized first; a bad GS1 check digit throws\n * here rather than printing a label nobody can sell.\n */\nexport async function generateBarcodeSvg(\n value: string,\n symbology: BarcodeSymbology,\n opts?: BarcodeOptions,\n): Promise<string> {\n const options = buildRenderOptions(value, symbology, opts);\n const bwipjs = await import(\"bwip-js/browser\");\n return bwipjs.toSVG(options);\n}\n\n/**\n * `toCanvas` only exists in bwip-js's browser build (the node build ships\n * `toBuffer` instead), so even on the browser subpath we PROBE for it rather\n * than assume which condition a host bundler ultimately resolved.\n */\ninterface BwipCanvasLane {\n toCanvas?: (canvas: HTMLCanvasElement, opts: RenderOptions) => HTMLCanvasElement;\n}\n\nconst USE_SVG_INSTEAD =\n \"Use generateBarcodeSvg() instead — SVG renders identically, works in Node and the browser, \" +\n \"and prints at the printer's resolution rather than the canvas's.\";\n\n/**\n * PNG data URL, for embedding straight into a print window's `<img src>`.\n *\n * BROWSER ONLY. In Node this throws immediately and names the fix rather than\n * dragging `node-canvas` (a native build) into a package that mostly runs in a\n * print window. The same clear error is thrown in a DOM environment whose\n * canvas is a stub (jsdom without the optional `canvas` package returns `null`\n * from `toDataURL`) — we would rather fail loudly than hand back a broken\n * `src` that silently renders as a missing image.\n */\nexport async function generateBarcodeDataUrl(\n value: string,\n symbology: BarcodeSymbology,\n opts?: BarcodeOptions,\n): Promise<string> {\n const options = buildRenderOptions(value, symbology, opts);\n\n if (typeof document === \"undefined\") {\n throw new Error(`generateBarcodeDataUrl() needs a DOM canvas and cannot run in Node. ${USE_SVG_INSTEAD}`);\n }\n\n const bwipjs = (await import(\"bwip-js/browser\")) as unknown as BwipCanvasLane;\n const toCanvas = bwipjs.toCanvas;\n if (typeof toCanvas !== \"function\") {\n throw new Error(\n `bwip-js resolved to its non-browser build, which has no toCanvas() — barcode PNG rendering is ` +\n `unavailable in this environment. ${USE_SVG_INSTEAD}`,\n );\n }\n\n let canvas: HTMLCanvasElement;\n try {\n canvas = toCanvas(document.createElement(\"canvas\"), options);\n } catch (cause) {\n // A DOM without a real 2-D context (jsdom without the optional `canvas`\n // package is the common one) fails somewhere inside bwip-js's drawing\n // code. Re-throw with the fix instead of a stray TypeError.\n throw new Error(\n `This environment's canvas could not render the barcode (${cause instanceof Error ? cause.message : String(cause)}). ` +\n `${USE_SVG_INSTEAD}`,\n { cause },\n );\n }\n const dataUrl: unknown = canvas.toDataURL(\"image/png\");\n if (typeof dataUrl !== \"string\" || !dataUrl.startsWith(\"data:image/png\")) {\n throw new Error(\n `This environment's canvas produced no PNG data URL (got ${String(dataUrl)}) — barcode PNG rendering ` +\n `is unavailable here. ${USE_SVG_INSTEAD}`,\n );\n }\n return dataUrl;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ai-matrx/print/barcode — 1-D BARCODE GENERATION for retail and warehouse labels.
|
|
3
|
+
*
|
|
4
|
+
* New code (no port source). Scanning/decoding is NOT here. This module only
|
|
5
|
+
* ever turns a value into a linear symbol, in the three symbologies that
|
|
6
|
+
* actually show up on the commerce lane's labels:
|
|
7
|
+
*
|
|
8
|
+
* - `code128` — the general-purpose workhorse. The `manufacturer_serial`
|
|
9
|
+
* identifier kind is usually printed as Code 128: arbitrary printable ASCII,
|
|
10
|
+
* dense, and every warehouse scanner reads it.
|
|
11
|
+
* - `ean13` / `upca` — retail GTINs. These are NOT free-form strings: they
|
|
12
|
+
* carry a GS1 mod-10 check digit, and a label printed with the wrong one is
|
|
13
|
+
* a label that gets rejected at the point of sale.
|
|
14
|
+
*
|
|
15
|
+
* Two rules that are NOT style choices:
|
|
16
|
+
* - EVERY generate call routes through `normalizeBarcodeValue` first. A caller
|
|
17
|
+
* that hands us 12 digits gets the check digit APPENDED; a caller that hands
|
|
18
|
+
* us 13 gets it VERIFIED and throws on a mismatch. There is no path that
|
|
19
|
+
* prints an unvalidated retail symbol.
|
|
20
|
+
* - The human-readable line defaults ON (`includeText`). On a retail or
|
|
21
|
+
* warehouse label the printed digits are the fallback when the scanner
|
|
22
|
+
* fails, and a keyer at a register cannot read bars.
|
|
23
|
+
*
|
|
24
|
+
* The module is import-inert: `bwip-js` is only ever pulled in via
|
|
25
|
+
* `await import("bwip-js")` inside a function body, so importing this file from
|
|
26
|
+
* a React Server Component (or any bundle that never draws a barcode) costs
|
|
27
|
+
* nothing. No module-level mutable state, no React, no DOM at import time.
|
|
28
|
+
*
|
|
29
|
+
* SVG works EVERYWHERE — bwip-js `toSVG` is pure JS with no canvas dependency,
|
|
30
|
+
* so it is the lane for Node (server-side PDF/label composition) as much as for
|
|
31
|
+
* the browser. The PNG data-URL lane is browser-only by design: it uses
|
|
32
|
+
* bwip-js's canvas rendering rather than dragging `node-canvas` into a package
|
|
33
|
+
* that mostly runs in a print window. In Node it throws and names the fix.
|
|
34
|
+
*/
|
|
35
|
+
/** The three 1-D symbologies the platform prints. Values are bwip-js `bcid`s verbatim. */
|
|
36
|
+
type BarcodeSymbology = "code128" | "ean13" | "upca";
|
|
37
|
+
interface BarcodeOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Bar height. This is bwip-js's `height`, whose unit is MILLIMETRES (it is
|
|
40
|
+
* not a module count — the name follows the package's option contract).
|
|
41
|
+
* Defaults per symbology: 12 for Code 128, 22.85 for EAN-13/UPC-A, the GS1
|
|
42
|
+
* nominal symbol height at 100% magnification.
|
|
43
|
+
*/
|
|
44
|
+
heightModules?: number;
|
|
45
|
+
/** Human-readable line under the bars. Default TRUE — it is the fallback when a scan fails. */
|
|
46
|
+
includeText?: boolean;
|
|
47
|
+
/** Module width multiplier (bwip-js `scale`). Default 2. */
|
|
48
|
+
scale?: number;
|
|
49
|
+
}
|
|
50
|
+
/** Default module-width multiplier for every Matrx-printed barcode. */
|
|
51
|
+
declare const DEFAULT_BARCODE_SCALE = 2;
|
|
52
|
+
/**
|
|
53
|
+
* Default bar height in millimetres, per symbology.
|
|
54
|
+
* 22.85mm is the GS1 nominal EAN-13/UPC-A symbol height at 100% magnification;
|
|
55
|
+
* Code 128 has no spec height, and 12mm is a comfortable warehouse-label bar.
|
|
56
|
+
*/
|
|
57
|
+
declare const DEFAULT_BARCODE_HEIGHT: Readonly<Record<BarcodeSymbology, number>>;
|
|
58
|
+
/**
|
|
59
|
+
* Validate and normalize a value for a symbology, returning the FULL value that
|
|
60
|
+
* will actually be encoded.
|
|
61
|
+
*
|
|
62
|
+
* - `ean13` / `upca`: digits only. 12 (EAN-13) or 11 (UPC-A) digits get the
|
|
63
|
+
* GS1 mod-10 check digit APPENDED; 13 or 12 digits get theirs VERIFIED and
|
|
64
|
+
* throw on a mismatch. Either way the return value is the complete code.
|
|
65
|
+
* - `code128`: printable ASCII (0x20-0x7E) passes through untouched —
|
|
66
|
+
* including leading/trailing spaces, which are legal Code 128 data.
|
|
67
|
+
*
|
|
68
|
+
* Throws an Error with a message naming the exact problem. Every generator in
|
|
69
|
+
* this module routes through here first; call it directly when you need to show
|
|
70
|
+
* the normalized value (e.g. the digits under the bars) before printing.
|
|
71
|
+
*/
|
|
72
|
+
declare function normalizeBarcodeValue(value: string, symbology: BarcodeSymbology): string;
|
|
73
|
+
/**
|
|
74
|
+
* SVG markup string — the lane that works EVERYWHERE (Node and browser), since
|
|
75
|
+
* bwip-js `toSVG` is pure JS with no canvas dependency. Lazy-imports `bwip-js`
|
|
76
|
+
* at call time, so this module stays import-inert.
|
|
77
|
+
*
|
|
78
|
+
* The value is validated and normalized first; a bad GS1 check digit throws
|
|
79
|
+
* here rather than printing a label nobody can sell.
|
|
80
|
+
*/
|
|
81
|
+
declare function generateBarcodeSvg(value: string, symbology: BarcodeSymbology, opts?: BarcodeOptions): Promise<string>;
|
|
82
|
+
/**
|
|
83
|
+
* PNG data URL, for embedding straight into a print window's `<img src>`.
|
|
84
|
+
*
|
|
85
|
+
* BROWSER ONLY. In Node this throws immediately and names the fix rather than
|
|
86
|
+
* dragging `node-canvas` (a native build) into a package that mostly runs in a
|
|
87
|
+
* print window. The same clear error is thrown in a DOM environment whose
|
|
88
|
+
* canvas is a stub (jsdom without the optional `canvas` package returns `null`
|
|
89
|
+
* from `toDataURL`) — we would rather fail loudly than hand back a broken
|
|
90
|
+
* `src` that silently renders as a missing image.
|
|
91
|
+
*/
|
|
92
|
+
declare function generateBarcodeDataUrl(value: string, symbology: BarcodeSymbology, opts?: BarcodeOptions): Promise<string>;
|
|
93
|
+
|
|
94
|
+
export { type BarcodeOptions, type BarcodeSymbology, DEFAULT_BARCODE_HEIGHT, DEFAULT_BARCODE_SCALE, generateBarcodeDataUrl, generateBarcodeSvg, normalizeBarcodeValue };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ai-matrx/print/barcode — 1-D BARCODE GENERATION for retail and warehouse labels.
|
|
3
|
+
*
|
|
4
|
+
* New code (no port source). Scanning/decoding is NOT here. This module only
|
|
5
|
+
* ever turns a value into a linear symbol, in the three symbologies that
|
|
6
|
+
* actually show up on the commerce lane's labels:
|
|
7
|
+
*
|
|
8
|
+
* - `code128` — the general-purpose workhorse. The `manufacturer_serial`
|
|
9
|
+
* identifier kind is usually printed as Code 128: arbitrary printable ASCII,
|
|
10
|
+
* dense, and every warehouse scanner reads it.
|
|
11
|
+
* - `ean13` / `upca` — retail GTINs. These are NOT free-form strings: they
|
|
12
|
+
* carry a GS1 mod-10 check digit, and a label printed with the wrong one is
|
|
13
|
+
* a label that gets rejected at the point of sale.
|
|
14
|
+
*
|
|
15
|
+
* Two rules that are NOT style choices:
|
|
16
|
+
* - EVERY generate call routes through `normalizeBarcodeValue` first. A caller
|
|
17
|
+
* that hands us 12 digits gets the check digit APPENDED; a caller that hands
|
|
18
|
+
* us 13 gets it VERIFIED and throws on a mismatch. There is no path that
|
|
19
|
+
* prints an unvalidated retail symbol.
|
|
20
|
+
* - The human-readable line defaults ON (`includeText`). On a retail or
|
|
21
|
+
* warehouse label the printed digits are the fallback when the scanner
|
|
22
|
+
* fails, and a keyer at a register cannot read bars.
|
|
23
|
+
*
|
|
24
|
+
* The module is import-inert: `bwip-js` is only ever pulled in via
|
|
25
|
+
* `await import("bwip-js")` inside a function body, so importing this file from
|
|
26
|
+
* a React Server Component (or any bundle that never draws a barcode) costs
|
|
27
|
+
* nothing. No module-level mutable state, no React, no DOM at import time.
|
|
28
|
+
*
|
|
29
|
+
* SVG works EVERYWHERE — bwip-js `toSVG` is pure JS with no canvas dependency,
|
|
30
|
+
* so it is the lane for Node (server-side PDF/label composition) as much as for
|
|
31
|
+
* the browser. The PNG data-URL lane is browser-only by design: it uses
|
|
32
|
+
* bwip-js's canvas rendering rather than dragging `node-canvas` into a package
|
|
33
|
+
* that mostly runs in a print window. In Node it throws and names the fix.
|
|
34
|
+
*/
|
|
35
|
+
/** The three 1-D symbologies the platform prints. Values are bwip-js `bcid`s verbatim. */
|
|
36
|
+
type BarcodeSymbology = "code128" | "ean13" | "upca";
|
|
37
|
+
interface BarcodeOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Bar height. This is bwip-js's `height`, whose unit is MILLIMETRES (it is
|
|
40
|
+
* not a module count — the name follows the package's option contract).
|
|
41
|
+
* Defaults per symbology: 12 for Code 128, 22.85 for EAN-13/UPC-A, the GS1
|
|
42
|
+
* nominal symbol height at 100% magnification.
|
|
43
|
+
*/
|
|
44
|
+
heightModules?: number;
|
|
45
|
+
/** Human-readable line under the bars. Default TRUE — it is the fallback when a scan fails. */
|
|
46
|
+
includeText?: boolean;
|
|
47
|
+
/** Module width multiplier (bwip-js `scale`). Default 2. */
|
|
48
|
+
scale?: number;
|
|
49
|
+
}
|
|
50
|
+
/** Default module-width multiplier for every Matrx-printed barcode. */
|
|
51
|
+
declare const DEFAULT_BARCODE_SCALE = 2;
|
|
52
|
+
/**
|
|
53
|
+
* Default bar height in millimetres, per symbology.
|
|
54
|
+
* 22.85mm is the GS1 nominal EAN-13/UPC-A symbol height at 100% magnification;
|
|
55
|
+
* Code 128 has no spec height, and 12mm is a comfortable warehouse-label bar.
|
|
56
|
+
*/
|
|
57
|
+
declare const DEFAULT_BARCODE_HEIGHT: Readonly<Record<BarcodeSymbology, number>>;
|
|
58
|
+
/**
|
|
59
|
+
* Validate and normalize a value for a symbology, returning the FULL value that
|
|
60
|
+
* will actually be encoded.
|
|
61
|
+
*
|
|
62
|
+
* - `ean13` / `upca`: digits only. 12 (EAN-13) or 11 (UPC-A) digits get the
|
|
63
|
+
* GS1 mod-10 check digit APPENDED; 13 or 12 digits get theirs VERIFIED and
|
|
64
|
+
* throw on a mismatch. Either way the return value is the complete code.
|
|
65
|
+
* - `code128`: printable ASCII (0x20-0x7E) passes through untouched —
|
|
66
|
+
* including leading/trailing spaces, which are legal Code 128 data.
|
|
67
|
+
*
|
|
68
|
+
* Throws an Error with a message naming the exact problem. Every generator in
|
|
69
|
+
* this module routes through here first; call it directly when you need to show
|
|
70
|
+
* the normalized value (e.g. the digits under the bars) before printing.
|
|
71
|
+
*/
|
|
72
|
+
declare function normalizeBarcodeValue(value: string, symbology: BarcodeSymbology): string;
|
|
73
|
+
/**
|
|
74
|
+
* SVG markup string — the lane that works EVERYWHERE (Node and browser), since
|
|
75
|
+
* bwip-js `toSVG` is pure JS with no canvas dependency. Lazy-imports `bwip-js`
|
|
76
|
+
* at call time, so this module stays import-inert.
|
|
77
|
+
*
|
|
78
|
+
* The value is validated and normalized first; a bad GS1 check digit throws
|
|
79
|
+
* here rather than printing a label nobody can sell.
|
|
80
|
+
*/
|
|
81
|
+
declare function generateBarcodeSvg(value: string, symbology: BarcodeSymbology, opts?: BarcodeOptions): Promise<string>;
|
|
82
|
+
/**
|
|
83
|
+
* PNG data URL, for embedding straight into a print window's `<img src>`.
|
|
84
|
+
*
|
|
85
|
+
* BROWSER ONLY. In Node this throws immediately and names the fix rather than
|
|
86
|
+
* dragging `node-canvas` (a native build) into a package that mostly runs in a
|
|
87
|
+
* print window. The same clear error is thrown in a DOM environment whose
|
|
88
|
+
* canvas is a stub (jsdom without the optional `canvas` package returns `null`
|
|
89
|
+
* from `toDataURL`) — we would rather fail loudly than hand back a broken
|
|
90
|
+
* `src` that silently renders as a missing image.
|
|
91
|
+
*/
|
|
92
|
+
declare function generateBarcodeDataUrl(value: string, symbology: BarcodeSymbology, opts?: BarcodeOptions): Promise<string>;
|
|
93
|
+
|
|
94
|
+
export { type BarcodeOptions, type BarcodeSymbology, DEFAULT_BARCODE_HEIGHT, DEFAULT_BARCODE_SCALE, generateBarcodeDataUrl, generateBarcodeSvg, normalizeBarcodeValue };
|
package/dist/barcode.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// src/barcode.ts
|
|
2
|
+
var DEFAULT_BARCODE_SCALE = 2;
|
|
3
|
+
var DEFAULT_BARCODE_HEIGHT = {
|
|
4
|
+
code128: 12,
|
|
5
|
+
ean13: 22.85,
|
|
6
|
+
upca: 22.85
|
|
7
|
+
};
|
|
8
|
+
var GS1_DIGIT_LENGTHS = {
|
|
9
|
+
ean13: [12, 13],
|
|
10
|
+
upca: [11, 12]
|
|
11
|
+
};
|
|
12
|
+
var SYMBOLOGIES = ["code128", "ean13", "upca"];
|
|
13
|
+
function assertSymbology(symbology) {
|
|
14
|
+
if (!SYMBOLOGIES.includes(symbology)) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Unknown barcode symbology "${String(symbology)}". Expected one of ${SYMBOLOGIES.map((s) => `"${s}"`).join(", ")}.`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return symbology;
|
|
20
|
+
}
|
|
21
|
+
function gs1CheckDigit(payloadDigits) {
|
|
22
|
+
let sum = 0;
|
|
23
|
+
let weight = 3;
|
|
24
|
+
for (let i = payloadDigits.length - 1; i >= 0; i -= 1) {
|
|
25
|
+
sum += (payloadDigits.charCodeAt(i) - 48) * weight;
|
|
26
|
+
weight = weight === 3 ? 1 : 3;
|
|
27
|
+
}
|
|
28
|
+
return (10 - sum % 10) % 10;
|
|
29
|
+
}
|
|
30
|
+
function normalizeCode128(value) {
|
|
31
|
+
if (value.length === 0) {
|
|
32
|
+
throw new Error("Code 128 barcode value must be a non-empty string.");
|
|
33
|
+
}
|
|
34
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
35
|
+
const code = value.charCodeAt(i);
|
|
36
|
+
if (code < 32 || code > 126) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Code 128 barcode value contains a non-printable-ASCII character ${JSON.stringify(value.charAt(i))} at index ${i} (char code ${code}). Only ASCII 0x20-0x7E can be printed and read back by a human.`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function normalizeGs1(value, symbology) {
|
|
45
|
+
const raw = value.trim();
|
|
46
|
+
if (raw.length === 0) {
|
|
47
|
+
throw new Error(`${symbology === "ean13" ? "EAN-13" : "UPC-A"} barcode value must be a non-empty string.`);
|
|
48
|
+
}
|
|
49
|
+
if (!/^[0-9]+$/.test(raw)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`${symbology === "ean13" ? "EAN-13" : "UPC-A"} barcode value "${raw}" must contain digits only (no spaces, dashes, or letters).`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const lengths = GS1_DIGIT_LENGTHS[symbology];
|
|
55
|
+
const [withoutCheck, withCheck] = lengths;
|
|
56
|
+
const label = symbology === "ean13" ? "EAN-13" : "UPC-A";
|
|
57
|
+
if (raw.length === withoutCheck) {
|
|
58
|
+
return `${raw}${gs1CheckDigit(raw)}`;
|
|
59
|
+
}
|
|
60
|
+
if (raw.length === withCheck) {
|
|
61
|
+
const expected = gs1CheckDigit(raw.slice(0, -1));
|
|
62
|
+
const actual = raw.charCodeAt(raw.length - 1) - 48;
|
|
63
|
+
if (expected !== actual) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`${label} barcode value "${raw}" has an invalid GS1 check digit: expected ${expected}, got ${actual}. Pass the first ${withoutCheck} digits instead and the check digit will be computed for you.`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return raw;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(
|
|
71
|
+
`${label} barcode value "${raw}" must be ${withoutCheck} digits (check digit appended) or ${withCheck} digits (check digit verified), got ${raw.length}.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
function normalizeBarcodeValue(value, symbology) {
|
|
75
|
+
assertSymbology(symbology);
|
|
76
|
+
if (typeof value !== "string") {
|
|
77
|
+
throw new Error(`Barcode value must be a string, got ${String(value)}.`);
|
|
78
|
+
}
|
|
79
|
+
if (symbology === "code128") {
|
|
80
|
+
return normalizeCode128(value);
|
|
81
|
+
}
|
|
82
|
+
return normalizeGs1(value, symbology);
|
|
83
|
+
}
|
|
84
|
+
function resolveScale(scale) {
|
|
85
|
+
if (scale === void 0) return DEFAULT_BARCODE_SCALE;
|
|
86
|
+
if (typeof scale !== "number" || !Number.isFinite(scale) || scale <= 0) {
|
|
87
|
+
throw new Error(`Barcode scale must be a positive number, got ${String(scale)}.`);
|
|
88
|
+
}
|
|
89
|
+
return scale;
|
|
90
|
+
}
|
|
91
|
+
function resolveHeight(heightModules, symbology) {
|
|
92
|
+
if (heightModules === void 0) return DEFAULT_BARCODE_HEIGHT[symbology];
|
|
93
|
+
if (typeof heightModules !== "number" || !Number.isFinite(heightModules) || heightModules <= 0) {
|
|
94
|
+
throw new Error(`Barcode heightModules must be a positive number, got ${String(heightModules)}.`);
|
|
95
|
+
}
|
|
96
|
+
return heightModules;
|
|
97
|
+
}
|
|
98
|
+
function buildRenderOptions(value, symbology, opts) {
|
|
99
|
+
const normalized = normalizeBarcodeValue(value, symbology);
|
|
100
|
+
return {
|
|
101
|
+
bcid: symbology,
|
|
102
|
+
text: normalized,
|
|
103
|
+
scale: resolveScale(opts?.scale),
|
|
104
|
+
height: resolveHeight(opts?.heightModules, symbology),
|
|
105
|
+
includetext: opts?.includeText === void 0 ? true : opts.includeText === true,
|
|
106
|
+
textxalign: "center"
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
async function generateBarcodeSvg(value, symbology, opts) {
|
|
110
|
+
const options = buildRenderOptions(value, symbology, opts);
|
|
111
|
+
const bwipjs = await import("bwip-js/browser");
|
|
112
|
+
return bwipjs.toSVG(options);
|
|
113
|
+
}
|
|
114
|
+
var USE_SVG_INSTEAD = "Use generateBarcodeSvg() instead \u2014 SVG renders identically, works in Node and the browser, and prints at the printer's resolution rather than the canvas's.";
|
|
115
|
+
async function generateBarcodeDataUrl(value, symbology, opts) {
|
|
116
|
+
const options = buildRenderOptions(value, symbology, opts);
|
|
117
|
+
if (typeof document === "undefined") {
|
|
118
|
+
throw new Error(`generateBarcodeDataUrl() needs a DOM canvas and cannot run in Node. ${USE_SVG_INSTEAD}`);
|
|
119
|
+
}
|
|
120
|
+
const bwipjs = await import("bwip-js/browser");
|
|
121
|
+
const toCanvas = bwipjs.toCanvas;
|
|
122
|
+
if (typeof toCanvas !== "function") {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`bwip-js resolved to its non-browser build, which has no toCanvas() \u2014 barcode PNG rendering is unavailable in this environment. ${USE_SVG_INSTEAD}`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
let canvas;
|
|
128
|
+
try {
|
|
129
|
+
canvas = toCanvas(document.createElement("canvas"), options);
|
|
130
|
+
} catch (cause) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`This environment's canvas could not render the barcode (${cause instanceof Error ? cause.message : String(cause)}). ${USE_SVG_INSTEAD}`,
|
|
133
|
+
{ cause }
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
const dataUrl = canvas.toDataURL("image/png");
|
|
137
|
+
if (typeof dataUrl !== "string" || !dataUrl.startsWith("data:image/png")) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`This environment's canvas produced no PNG data URL (got ${String(dataUrl)}) \u2014 barcode PNG rendering is unavailable here. ${USE_SVG_INSTEAD}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return dataUrl;
|
|
143
|
+
}
|
|
144
|
+
export {
|
|
145
|
+
DEFAULT_BARCODE_HEIGHT,
|
|
146
|
+
DEFAULT_BARCODE_SCALE,
|
|
147
|
+
generateBarcodeDataUrl,
|
|
148
|
+
generateBarcodeSvg,
|
|
149
|
+
normalizeBarcodeValue
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=barcode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/barcode.ts"],"sourcesContent":["/**\n * @ai-matrx/print/barcode — 1-D BARCODE GENERATION for retail and warehouse labels.\n *\n * New code (no port source). Scanning/decoding is NOT here. This module only\n * ever turns a value into a linear symbol, in the three symbologies that\n * actually show up on the commerce lane's labels:\n *\n * - `code128` — the general-purpose workhorse. The `manufacturer_serial`\n * identifier kind is usually printed as Code 128: arbitrary printable ASCII,\n * dense, and every warehouse scanner reads it.\n * - `ean13` / `upca` — retail GTINs. These are NOT free-form strings: they\n * carry a GS1 mod-10 check digit, and a label printed with the wrong one is\n * a label that gets rejected at the point of sale.\n *\n * Two rules that are NOT style choices:\n * - EVERY generate call routes through `normalizeBarcodeValue` first. A caller\n * that hands us 12 digits gets the check digit APPENDED; a caller that hands\n * us 13 gets it VERIFIED and throws on a mismatch. There is no path that\n * prints an unvalidated retail symbol.\n * - The human-readable line defaults ON (`includeText`). On a retail or\n * warehouse label the printed digits are the fallback when the scanner\n * fails, and a keyer at a register cannot read bars.\n *\n * The module is import-inert: `bwip-js` is only ever pulled in via\n * `await import(\"bwip-js\")` inside a function body, so importing this file from\n * a React Server Component (or any bundle that never draws a barcode) costs\n * nothing. No module-level mutable state, no React, no DOM at import time.\n *\n * SVG works EVERYWHERE — bwip-js `toSVG` is pure JS with no canvas dependency,\n * so it is the lane for Node (server-side PDF/label composition) as much as for\n * the browser. The PNG data-URL lane is browser-only by design: it uses\n * bwip-js's canvas rendering rather than dragging `node-canvas` into a package\n * that mostly runs in a print window. In Node it throws and names the fix.\n */\n\n/**\n * We import the \"bwip-js/browser\" SUBPATH rather than the bare \"bwip-js\"\n * specifier, deliberately and for two reasons:\n * 1. bwip-js's export map has no `default` condition — only `browser`, `node`,\n * `electron` and `react-native` — so a bundler-style TypeScript resolver\n * cannot resolve the bare specifier at all. The subpath resolves cleanly.\n * 2. The browser build is the SUPERSET for our purposes: its `toSVG` is pure\n * JS and imports/runs fine under Node (verified — nothing touches `document`\n * at module scope), and it is the only build that carries `toCanvas`, which\n * the PNG lane needs. The node build would give us `toBuffer` instead and\n * no canvas lane at all.\n */\nimport type { RenderOptions } from \"bwip-js/browser\";\n\n// ─── Contract ────────────────────────────────────────────────────────────────\n\n/** The three 1-D symbologies the platform prints. Values are bwip-js `bcid`s verbatim. */\nexport type BarcodeSymbology = \"code128\" | \"ean13\" | \"upca\";\n\nexport interface BarcodeOptions {\n /**\n * Bar height. This is bwip-js's `height`, whose unit is MILLIMETRES (it is\n * not a module count — the name follows the package's option contract).\n * Defaults per symbology: 12 for Code 128, 22.85 for EAN-13/UPC-A, the GS1\n * nominal symbol height at 100% magnification.\n */\n heightModules?: number;\n /** Human-readable line under the bars. Default TRUE — it is the fallback when a scan fails. */\n includeText?: boolean;\n /** Module width multiplier (bwip-js `scale`). Default 2. */\n scale?: number;\n}\n\n/** Default module-width multiplier for every Matrx-printed barcode. */\nexport const DEFAULT_BARCODE_SCALE = 2;\n\n/**\n * Default bar height in millimetres, per symbology.\n * 22.85mm is the GS1 nominal EAN-13/UPC-A symbol height at 100% magnification;\n * Code 128 has no spec height, and 12mm is a comfortable warehouse-label bar.\n */\nexport const DEFAULT_BARCODE_HEIGHT: Readonly<Record<BarcodeSymbology, number>> = {\n code128: 12,\n ean13: 22.85,\n upca: 22.85,\n};\n\n/** Digit counts each GS1 symbology accepts: [without check digit, with check digit]. */\nconst GS1_DIGIT_LENGTHS: Readonly<Record<\"ean13\" | \"upca\", readonly [number, number]>> = {\n ean13: [12, 13],\n upca: [11, 12],\n};\n\nconst SYMBOLOGIES: readonly BarcodeSymbology[] = [\"code128\", \"ean13\", \"upca\"];\n\nfunction assertSymbology(symbology: BarcodeSymbology): BarcodeSymbology {\n if (!SYMBOLOGIES.includes(symbology)) {\n throw new Error(\n `Unknown barcode symbology \"${String(symbology)}\". Expected one of ${SYMBOLOGIES.map((s) => `\"${s}\"`).join(\", \")}.`,\n );\n }\n return symbology;\n}\n\n// ─── Validation ──────────────────────────────────────────────────────────────\n\n/**\n * GS1 mod-10 check digit over the payload digits (i.e. WITHOUT the check\n * digit): weights 3 and 1 alternating from the right. Length-independent, so\n * UPC-A's 11-digit payload and EAN-13's 12-digit payload use the same routine.\n */\nfunction gs1CheckDigit(payloadDigits: string): number {\n let sum = 0;\n let weight = 3;\n for (let i = payloadDigits.length - 1; i >= 0; i -= 1) {\n sum += (payloadDigits.charCodeAt(i) - 48) * weight;\n weight = weight === 3 ? 1 : 3;\n }\n return (10 - (sum % 10)) % 10;\n}\n\n/**\n * Code 128 encodes ASCII 0–127, but a label only prints what a human can read\n * back, so we accept the printable range 0x20 (space) through 0x7E (tilde).\n * Control characters and anything non-ASCII (smart quotes pasted from a\n * spreadsheet are the usual culprit) are rejected loudly rather than silently\n * mangled by the encoder.\n */\nfunction normalizeCode128(value: string): string {\n if (value.length === 0) {\n throw new Error(\"Code 128 barcode value must be a non-empty string.\");\n }\n for (let i = 0; i < value.length; i += 1) {\n const code = value.charCodeAt(i);\n if (code < 0x20 || code > 0x7e) {\n throw new Error(\n `Code 128 barcode value contains a non-printable-ASCII character ${JSON.stringify(value.charAt(i))} ` +\n `at index ${i} (char code ${code}). Only ASCII 0x20-0x7E can be printed and read back by a human.`,\n );\n }\n }\n return value;\n}\n\nfunction normalizeGs1(value: string, symbology: \"ean13\" | \"upca\"): string {\n const raw = value.trim();\n if (raw.length === 0) {\n throw new Error(`${symbology === \"ean13\" ? \"EAN-13\" : \"UPC-A\"} barcode value must be a non-empty string.`);\n }\n if (!/^[0-9]+$/.test(raw)) {\n throw new Error(\n `${symbology === \"ean13\" ? \"EAN-13\" : \"UPC-A\"} barcode value \"${raw}\" must contain digits only ` +\n `(no spaces, dashes, or letters).`,\n );\n }\n\n const lengths = GS1_DIGIT_LENGTHS[symbology];\n const [withoutCheck, withCheck] = lengths;\n const label = symbology === \"ean13\" ? \"EAN-13\" : \"UPC-A\";\n\n if (raw.length === withoutCheck) {\n // Caller gave us the payload — APPEND the check digit we compute.\n return `${raw}${gs1CheckDigit(raw)}`;\n }\n if (raw.length === withCheck) {\n // Caller gave us a full code — VERIFY the check digit they already have.\n const expected = gs1CheckDigit(raw.slice(0, -1));\n const actual = raw.charCodeAt(raw.length - 1) - 48;\n if (expected !== actual) {\n throw new Error(\n `${label} barcode value \"${raw}\" has an invalid GS1 check digit: expected ${expected}, got ${actual}. ` +\n `Pass the first ${withoutCheck} digits instead and the check digit will be computed for you.`,\n );\n }\n return raw;\n }\n throw new Error(\n `${label} barcode value \"${raw}\" must be ${withoutCheck} digits (check digit appended) or ` +\n `${withCheck} digits (check digit verified), got ${raw.length}.`,\n );\n}\n\n/**\n * Validate and normalize a value for a symbology, returning the FULL value that\n * will actually be encoded.\n *\n * - `ean13` / `upca`: digits only. 12 (EAN-13) or 11 (UPC-A) digits get the\n * GS1 mod-10 check digit APPENDED; 13 or 12 digits get theirs VERIFIED and\n * throw on a mismatch. Either way the return value is the complete code.\n * - `code128`: printable ASCII (0x20-0x7E) passes through untouched —\n * including leading/trailing spaces, which are legal Code 128 data.\n *\n * Throws an Error with a message naming the exact problem. Every generator in\n * this module routes through here first; call it directly when you need to show\n * the normalized value (e.g. the digits under the bars) before printing.\n */\nexport function normalizeBarcodeValue(value: string, symbology: BarcodeSymbology): string {\n assertSymbology(symbology);\n if (typeof value !== \"string\") {\n throw new Error(`Barcode value must be a string, got ${String(value)}.`);\n }\n if (symbology === \"code128\") {\n return normalizeCode128(value);\n }\n return normalizeGs1(value, symbology);\n}\n\n// ─── Option resolution ───────────────────────────────────────────────────────\n\nfunction resolveScale(scale: number | undefined): number {\n if (scale === undefined) return DEFAULT_BARCODE_SCALE;\n if (typeof scale !== \"number\" || !Number.isFinite(scale) || scale <= 0) {\n throw new Error(`Barcode scale must be a positive number, got ${String(scale)}.`);\n }\n return scale;\n}\n\nfunction resolveHeight(heightModules: number | undefined, symbology: BarcodeSymbology): number {\n if (heightModules === undefined) return DEFAULT_BARCODE_HEIGHT[symbology];\n if (typeof heightModules !== \"number\" || !Number.isFinite(heightModules) || heightModules <= 0) {\n throw new Error(`Barcode heightModules must be a positive number, got ${String(heightModules)}.`);\n }\n return heightModules;\n}\n\n/**\n * Build the bwip-js render options. Note the object is fully populated here —\n * under `exactOptionalPropertyTypes` we never assign `undefined` to an optional\n * key, so every field is either a real value or absent.\n */\nfunction buildRenderOptions(value: string, symbology: BarcodeSymbology, opts: BarcodeOptions | undefined): RenderOptions {\n const normalized = normalizeBarcodeValue(value, symbology);\n return {\n bcid: symbology,\n text: normalized,\n scale: resolveScale(opts?.scale),\n height: resolveHeight(opts?.heightModules, symbology),\n includetext: opts?.includeText === undefined ? true : opts.includeText === true,\n textxalign: \"center\",\n };\n}\n\n// ─── Rendering ───────────────────────────────────────────────────────────────\n\n/**\n * SVG markup string — the lane that works EVERYWHERE (Node and browser), since\n * bwip-js `toSVG` is pure JS with no canvas dependency. Lazy-imports `bwip-js`\n * at call time, so this module stays import-inert.\n *\n * The value is validated and normalized first; a bad GS1 check digit throws\n * here rather than printing a label nobody can sell.\n */\nexport async function generateBarcodeSvg(\n value: string,\n symbology: BarcodeSymbology,\n opts?: BarcodeOptions,\n): Promise<string> {\n const options = buildRenderOptions(value, symbology, opts);\n const bwipjs = await import(\"bwip-js/browser\");\n return bwipjs.toSVG(options);\n}\n\n/**\n * `toCanvas` only exists in bwip-js's browser build (the node build ships\n * `toBuffer` instead), so even on the browser subpath we PROBE for it rather\n * than assume which condition a host bundler ultimately resolved.\n */\ninterface BwipCanvasLane {\n toCanvas?: (canvas: HTMLCanvasElement, opts: RenderOptions) => HTMLCanvasElement;\n}\n\nconst USE_SVG_INSTEAD =\n \"Use generateBarcodeSvg() instead — SVG renders identically, works in Node and the browser, \" +\n \"and prints at the printer's resolution rather than the canvas's.\";\n\n/**\n * PNG data URL, for embedding straight into a print window's `<img src>`.\n *\n * BROWSER ONLY. In Node this throws immediately and names the fix rather than\n * dragging `node-canvas` (a native build) into a package that mostly runs in a\n * print window. The same clear error is thrown in a DOM environment whose\n * canvas is a stub (jsdom without the optional `canvas` package returns `null`\n * from `toDataURL`) — we would rather fail loudly than hand back a broken\n * `src` that silently renders as a missing image.\n */\nexport async function generateBarcodeDataUrl(\n value: string,\n symbology: BarcodeSymbology,\n opts?: BarcodeOptions,\n): Promise<string> {\n const options = buildRenderOptions(value, symbology, opts);\n\n if (typeof document === \"undefined\") {\n throw new Error(`generateBarcodeDataUrl() needs a DOM canvas and cannot run in Node. ${USE_SVG_INSTEAD}`);\n }\n\n const bwipjs = (await import(\"bwip-js/browser\")) as unknown as BwipCanvasLane;\n const toCanvas = bwipjs.toCanvas;\n if (typeof toCanvas !== \"function\") {\n throw new Error(\n `bwip-js resolved to its non-browser build, which has no toCanvas() — barcode PNG rendering is ` +\n `unavailable in this environment. ${USE_SVG_INSTEAD}`,\n );\n }\n\n let canvas: HTMLCanvasElement;\n try {\n canvas = toCanvas(document.createElement(\"canvas\"), options);\n } catch (cause) {\n // A DOM without a real 2-D context (jsdom without the optional `canvas`\n // package is the common one) fails somewhere inside bwip-js's drawing\n // code. Re-throw with the fix instead of a stray TypeError.\n throw new Error(\n `This environment's canvas could not render the barcode (${cause instanceof Error ? cause.message : String(cause)}). ` +\n `${USE_SVG_INSTEAD}`,\n { cause },\n );\n }\n const dataUrl: unknown = canvas.toDataURL(\"image/png\");\n if (typeof dataUrl !== \"string\" || !dataUrl.startsWith(\"data:image/png\")) {\n throw new Error(\n `This environment's canvas produced no PNG data URL (got ${String(dataUrl)}) — barcode PNG rendering ` +\n `is unavailable here. ${USE_SVG_INSTEAD}`,\n );\n }\n return dataUrl;\n}\n"],"mappings":"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","names":[]}
|