btrz-liquid-templates 1.11.0 → 2.0.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.
Files changed (85) hide show
  1. package/docs/index.md +6 -3
  2. package/package.json +8 -4
  3. package/src/barcode-generator.js +20 -0
  4. package/src/barcode-types.js +133 -0
  5. package/src/barcode.js +8 -13
  6. package/test/barcode-equivalence-test.js +63 -0
  7. package/test/barcode-test.js +44 -0
  8. package/test/barcode-types-test.js +61 -0
  9. package/test/fixtures/barcode-sample-content.js +100 -0
  10. package/test/fixtures/generate-legacy-barcodes.js +158 -0
  11. package/test/fixtures/legacy-barcodes/azrune-1.png +0 -0
  12. package/test/fixtures/legacy-barcodes/aztec-AZTEC.png +0 -0
  13. package/test/fixtures/legacy-barcodes/c25iata-1234567.png +0 -0
  14. package/test/fixtures/legacy-barcodes/c25ind-1234567.png +0 -0
  15. package/test/fixtures/legacy-barcodes/c25inter-1234567890.png +0 -0
  16. package/test/fixtures/legacy-barcodes/c25logic-1234567.png +0 -0
  17. package/test/fixtures/legacy-barcodes/c25matrix-1234567.png +0 -0
  18. package/test/fixtures/legacy-barcodes/channel-01.png +0 -0
  19. package/test/fixtures/legacy-barcodes/codabar-A123456A.png +0 -0
  20. package/test/fixtures/legacy-barcodes/codablockf-CODABLOCK.png +0 -0
  21. package/test/fixtures/legacy-barcodes/code11-1234.png +0 -0
  22. package/test/fixtures/legacy-barcodes/code128-1234.png +0 -0
  23. package/test/fixtures/legacy-barcodes/code128b-1234.png +0 -0
  24. package/test/fixtures/legacy-barcodes/code16k-1234.png +0 -0
  25. package/test/fixtures/legacy-barcodes/code32-12345678.png +0 -0
  26. package/test/fixtures/legacy-barcodes/code39-ABC123.png +0 -0
  27. package/test/fixtures/legacy-barcodes/code49-1234.png +0 -0
  28. package/test/fixtures/legacy-barcodes/code93-1234.png +0 -0
  29. package/test/fixtures/legacy-barcodes/codeone-1234.png +0 -0
  30. package/test/fixtures/legacy-barcodes/daft-DAFT.png +0 -0
  31. package/test/fixtures/legacy-barcodes/datamatrix-DATAMATRIX.png +0 -0
  32. package/test/fixtures/legacy-barcodes/dotcode-DOTCODE.png +0 -0
  33. package/test/fixtures/legacy-barcodes/dpident-12345678901.png +0 -0
  34. package/test/fixtures/legacy-barcodes/dpleit-1234567890123.png +0 -0
  35. package/test/fixtures/legacy-barcodes/ean128-_01_09501101530003.png +0 -0
  36. package/test/fixtures/legacy-barcodes/ean14-1234567890123.png +0 -0
  37. package/test/fixtures/legacy-barcodes/eanx-5901234123457.png +0 -0
  38. package/test/fixtures/legacy-barcodes/eanx_chk-5901234123457.png +0 -0
  39. package/test/fixtures/legacy-barcodes/excode39-ABC_123.png +0 -0
  40. package/test/fixtures/legacy-barcodes/flat-1.png +0 -0
  41. package/test/fixtures/legacy-barcodes/generation-failures.json +1 -0
  42. package/test/fixtures/legacy-barcodes/hanxin-HANXIN.png +0 -0
  43. package/test/fixtures/legacy-barcodes/hibc_128-_ABC1234ABC1234_0123456789012.png +0 -0
  44. package/test/fixtures/legacy-barcodes/hibc_39-_ABC1234ABC1234_0123456789012.png +0 -0
  45. package/test/fixtures/legacy-barcodes/hibc_aztec-_ABC1234ABC1234_0123456789012.png +0 -0
  46. package/test/fixtures/legacy-barcodes/hibc_dm-_ABC1234ABC1234_0123456789012.png +0 -0
  47. package/test/fixtures/legacy-barcodes/hibc_micpdf-_ABC1234ABC1234_0123456789012.png +0 -0
  48. package/test/fixtures/legacy-barcodes/hibc_pdf-_ABC1234ABC1234_0123456789012.png +0 -0
  49. package/test/fixtures/legacy-barcodes/hibc_qr-_ABC1234ABC1234_0123456789012.png +0 -0
  50. package/test/fixtures/legacy-barcodes/isbnx-9780306406157.png +0 -0
  51. package/test/fixtures/legacy-barcodes/itf14-1234567890123.png +0 -0
  52. package/test/fixtures/legacy-barcodes/japanpost-12345678901.png +0 -0
  53. package/test/fixtures/legacy-barcodes/kix-15400171583802.png +0 -0
  54. package/test/fixtures/legacy-barcodes/logmars-ABC123.png +0 -0
  55. package/test/fixtures/legacy-barcodes/manifest.json +1142 -0
  56. package/test/fixtures/legacy-barcodes/maxicode-1234567890123456789012345678901234.png +0 -0
  57. package/test/fixtures/legacy-barcodes/micropdf417-MICROPDF.png +0 -0
  58. package/test/fixtures/legacy-barcodes/microqr-MICROQR.png +0 -0
  59. package/test/fixtures/legacy-barcodes/msi_plessey-123456.png +0 -0
  60. package/test/fixtures/legacy-barcodes/nve18-12345678901234567.png +0 -0
  61. package/test/fixtures/legacy-barcodes/pdf417-PDF417.png +0 -0
  62. package/test/fixtures/legacy-barcodes/pdf417trunc-PDF417.png +0 -0
  63. package/test/fixtures/legacy-barcodes/pharma-123.png +0 -0
  64. package/test/fixtures/legacy-barcodes/pharma_two-123456.png +0 -0
  65. package/test/fixtures/legacy-barcodes/planet-01234567890.png +0 -0
  66. package/test/fixtures/legacy-barcodes/plessey-1234567890.png +0 -0
  67. package/test/fixtures/legacy-barcodes/postnet-01234.png +0 -0
  68. package/test/fixtures/legacy-barcodes/pzn-123456.png +0 -0
  69. package/test/fixtures/legacy-barcodes/qrcode-hello_world.png +0 -0
  70. package/test/fixtures/legacy-barcodes/rm4scc-W1J0TRQ.png +0 -0
  71. package/test/fixtures/legacy-barcodes/rss14-0123456789051.png +0 -0
  72. package/test/fixtures/legacy-barcodes/rss14stack-0123456789051.png +0 -0
  73. package/test/fixtures/legacy-barcodes/rss14stack_omni-0123456789051.png +0 -0
  74. package/test/fixtures/legacy-barcodes/rss_exp-_01_09501101530003.png +0 -0
  75. package/test/fixtures/legacy-barcodes/rss_ltd-0123456789051.png +0 -0
  76. package/test/fixtures/legacy-barcodes/telepen-Telepen.png +0 -0
  77. package/test/fixtures/legacy-barcodes/telepen_num-123456.png +0 -0
  78. package/test/fixtures/legacy-barcodes/ultra-ULTRA.png +0 -0
  79. package/test/fixtures/legacy-barcodes/upca-012345678905.png +0 -0
  80. package/test/fixtures/legacy-barcodes/upca_chk-012345678905.png +0 -0
  81. package/test/fixtures/legacy-barcodes/upce-0123456.png +0 -0
  82. package/test/fixtures/symbology-type-names.js +95 -0
  83. package/test/helpers/barcode-decode.js +30 -0
  84. package/test/helpers/generate-bwip-test-png.js +21 -0
  85. package/test/index-test.js +59 -41
package/docs/index.md CHANGED
@@ -5,7 +5,7 @@ layout: default
5
5
 
6
6
  Create pdf documents from liquid templates.
7
7
 
8
- This library uses [LiquidJS](https://liquidjs.com/), [Symbology](https://symbology.dev/), [Bz-Date](https://www.npmjs.com/package/bz-date), [btrz-formatter](https://www.npmjs.com/package/btrz-formatter) and link them together while adding some helpers to easily generate PDFs from templates.
8
+ This library uses [LiquidJS](https://liquidjs.com/), [bwip-js](https://github.com/metafloor/bwip-js), [Bz-Date](https://www.npmjs.com/package/bz-date), [btrz-formatter](https://www.npmjs.com/package/btrz-formatter) and link them together while adding some helpers to easily generate PDFs from templates.
9
9
 
10
10
  The vesion of PDFMake has been inlined and modified to allow for text rotation.
11
11
 
@@ -286,8 +286,11 @@ It will use the value given and use generate a 'code11' barcode with a height of
286
286
 
287
287
  ### Supported types
288
288
 
289
- It supports all types supported by [https://symbology.dev/](Symbology).
290
- While symbology supports QRCODE PDFMake also had support for QR natively and we recommend it.
289
+ The `{% barcode %}` tag accepts legacy Symbology-style type names and maps them to [bwip-js](https://github.com/metafloor/bwip-js) barcode identifiers. Common examples include `code128`, `code11`, `code39`, `qrcode`, `pdf417`, and `datamatrix`.
290
+
291
+ The following legacy type names are not supported and will throw an explicit error: `gridmatrix`, `koreapost`, `upnqr`, and `vin`.
292
+
293
+ While barcode generation supports QR codes, PDFMake also had support for QR natively and we recommend it for QR-specific use cases.
291
294
 
292
295
  * ## httpImg
293
296
 
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "btrz-liquid-templates",
3
- "version": "1.11.0",
3
+ "version": "2.0.0",
4
4
  "description": "Extension and helpers for liquid templates to deal with our data and other things we need to encapsulate",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
7
- "tdd": "node --test --watch test/index-test.js",
8
- "test": "node --test test/index-test.js",
7
+ "tdd": "node --test --watch test/*-test.js",
8
+ "test": "node --test test/*-test.js",
9
9
  "preversion": "npm test",
10
10
  "postversion": "git push origin main && git push --tags",
11
11
  "version": "git tag $1"
@@ -23,11 +23,15 @@
23
23
  "dependencies": {
24
24
  "axios": "1.18.1",
25
25
  "btrz-formatter": "1.7.0",
26
+ "bwip-js": "^4.11.2",
26
27
  "bz-date": "2.2.0",
27
28
  "liquidjs": "10.27.1",
28
29
  "moment": "2.30.1",
29
30
  "moment-timezone": "0.6.2",
30
- "symbology": "4.0.2",
31
31
  "written-number": "0.11.1"
32
+ },
33
+ "devDependencies": {
34
+ "@undecaf/zbar-wasm": "^0.11.0",
35
+ "pngjs": "^7.0.0"
32
36
  }
33
37
  }
@@ -0,0 +1,20 @@
1
+ const bwipjs = require("bwip-js");
2
+ const {resolveBarcodeType} = require("./barcode-types.js");
3
+
4
+ async function generateBarcodePng({type, content, height}) {
5
+ const bcid = resolveBarcodeType(type);
6
+ const pngBuffer = await bwipjs.toBuffer({
7
+ bcid,
8
+ text: String(content),
9
+ scale: 2,
10
+ height: Number(height) || 30,
11
+ includetext: false,
12
+ backgroundcolor: "FFFFFF"
13
+ });
14
+
15
+ return `data:image/png;base64,${pngBuffer.toString("base64")}`;
16
+ }
17
+
18
+ module.exports = {
19
+ generateBarcodePng
20
+ };
@@ -0,0 +1,133 @@
1
+ const UNSUPPORTED_TYPES = new Set([
2
+ "GRIDMATRIX",
3
+ "KOREAPOST",
4
+ "UPNQR",
5
+ "VIN"
6
+ ]);
7
+
8
+ const SYMBLOGY_TO_BWIP = {
9
+ AUSPOST: "auspost",
10
+ AUSREDIRECT: "auspost",
11
+ AUSREPLY: "auspost",
12
+ AUSROUTE: "auspost",
13
+ AZRUNE: "aztecrune",
14
+ AZTEC: "azteccode",
15
+ C25IATA: "iata2of5",
16
+ C25IND: "industrial2of5",
17
+ C25INTER: "interleaved2of5",
18
+ C25LOGIC: "datalogic2of5",
19
+ C25MATRIX: "matrix2of5",
20
+ CHANNEL: "channelcode",
21
+ CODABAR: "rationalizedCodabar",
22
+ CODABLOCKF: "codablockf",
23
+ CODE11: "code11",
24
+ CODE128: "code128",
25
+ CODE128B: "code128",
26
+ CODE16K: "code16k",
27
+ CODE32: "code32",
28
+ CODE39: "code39",
29
+ CODE49: "code49",
30
+ CODE93: "code93",
31
+ CODEONE: "codeone",
32
+ DAFT: "daft",
33
+ DATAMATRIX: "datamatrix",
34
+ DOTCODE: "dotcode",
35
+ DPIDENT: "identcode",
36
+ DPLEIT: "leitcode",
37
+ EAN128: "gs1-128",
38
+ EAN128_CC: "gs1-128composite",
39
+ EAN14: "ean14",
40
+ EANX: "ean13",
41
+ EANX_CC: "ean13composite",
42
+ EANX_CHK: "ean13",
43
+ EXCODE39: "code39ext",
44
+ FIM: "flattermarken",
45
+ FLAT: "flattermarken",
46
+ HANXIN: "hanxin",
47
+ HIBC_128: "hibccode128",
48
+ HIBC_39: "hibccode39",
49
+ HIBC_AZTEC: "hibcazteccode",
50
+ HIBC_DM: "hibcdatamatrix",
51
+ HIBC_MICPDF: "hibcmicropdf417",
52
+ HIBC_PDF: "hibcpdf417",
53
+ HIBC_QR: "hibcqrcode",
54
+ ISBNX: "isbn",
55
+ ITF14: "itf14",
56
+ JAPANPOST: "japanpost",
57
+ KIX: "kix",
58
+ LOGMARS: "code39",
59
+ MAILMARK: "mailmark",
60
+ MAXICODE: "maxicode",
61
+ MICROPDF417: "micropdf417",
62
+ MICROQR: "microqrcode",
63
+ MSI_PLESSEY: "msi",
64
+ NVE18: "sscc18",
65
+ ONECODE: "onecode",
66
+ PDF417: "pdf417",
67
+ PDF417TRUNC: "pdf417compact",
68
+ PHARMA: "pharmacode",
69
+ PHARMA_TWO: "pharmacode2",
70
+ PLANET: "planet",
71
+ PLESSEY: "plessey",
72
+ POSTNET: "postnet",
73
+ PZN: "pzn",
74
+ QRCODE: "qrcode",
75
+ RM4SCC: "royalmail",
76
+ RSS14: "databaromni",
77
+ RSS14STACK: "databarstacked",
78
+ RSS14STACK_CC: "databarstackedcomposite",
79
+ RSS14STACK_OMNI: "databarstackedomni",
80
+ RSS14_CC: "databaromnicomposite",
81
+ RSS14_OMNI_CC: "databarstackedomnicomposite",
82
+ RSS_EXP: "databarexpanded",
83
+ RSS_EXPSTACK: "databarexpandedstacked",
84
+ RSS_EXPSTACK_CC: "databarexpandedstackedcomposite",
85
+ RSS_EXP_CC: "databarexpandedcomposite",
86
+ RSS_LTD: "databarlimited",
87
+ RSS_LTD_CC: "databarlimitedcomposite",
88
+ TELEPEN: "telepen",
89
+ TELEPEN_NUM: "telepennumeric",
90
+ ULTRA: "ultracode",
91
+ UPCA: "upca",
92
+ UPCA_CC: "upcacomposite",
93
+ UPCA_CHK: "upca",
94
+ UPCE: "upce",
95
+ UPCE_CC: "upcecomposite",
96
+ UPCE_CHK: "upce"
97
+ };
98
+
99
+ function normalizeBarcodeType(type) {
100
+ return String(type || "code128").toUpperCase();
101
+ }
102
+
103
+ function resolveBarcodeType(type) {
104
+ const normalized = normalizeBarcodeType(type);
105
+
106
+ if (UNSUPPORTED_TYPES.has(normalized)) {
107
+ throw new Error(`Unsupported barcode type: ${normalized}`);
108
+ }
109
+
110
+ const bcid = SYMBLOGY_TO_BWIP[normalized];
111
+ if (!bcid) {
112
+ throw new Error(`Unsupported barcode type: ${normalized}`);
113
+ }
114
+
115
+ return bcid;
116
+ }
117
+
118
+ function listSupportedSymbologyTypes() {
119
+ return Object.keys(SYMBLOGY_TO_BWIP).sort();
120
+ }
121
+
122
+ function listUnsupportedSymbologyTypes() {
123
+ return [...UNSUPPORTED_TYPES].sort();
124
+ }
125
+
126
+ module.exports = {
127
+ UNSUPPORTED_TYPES,
128
+ SYMBLOGY_TO_BWIP,
129
+ normalizeBarcodeType,
130
+ resolveBarcodeType,
131
+ listSupportedSymbologyTypes,
132
+ listUnsupportedSymbologyTypes
133
+ };
package/src/barcode.js CHANGED
@@ -1,8 +1,4 @@
1
- const {SymbologyType, createStream, OutputType} = require("symbology");
2
-
3
- function getCode(code) {
4
- return SymbologyType[code.toUpperCase()];
5
- }
1
+ const {generateBarcodePng} = require("./barcode-generator.js");
6
2
 
7
3
  function Barcode(engine) {
8
4
  this.registerTag("barcode", {
@@ -23,13 +19,12 @@ function Barcode(engine) {
23
19
  }
24
20
  },
25
21
  render: async function(ctx) {
26
- let content = await this.liquid.evalValue(this.content, ctx) || this.content;
27
- let result = await createStream({
28
- symbology: getCode(this.type),
29
- height: this.height,
30
- showHumanReadableText: false,
31
- scale: 2.0,
32
- }, String(content), OutputType.PNG);
22
+ const content = await this.liquid.evalValue(this.content, ctx) || this.content;
23
+ const image = await generateBarcodePng({
24
+ type: this.type,
25
+ content,
26
+ height: this.height
27
+ });
33
28
  let margin = [0,0,0,0];
34
29
  try {
35
30
  margin = this.margin.split(",").map((i) => {
@@ -43,7 +38,7 @@ function Barcode(engine) {
43
38
  margin = [0,0,0,0];
44
39
  }
45
40
  return `{
46
- "image": "${result.data}",
41
+ "image": "${image}",
47
42
  "width": ${this.width || 200},
48
43
  "margin": [${margin.join(",")}]
49
44
  }`;
@@ -0,0 +1,63 @@
1
+ const assert = require("node:assert/strict");
2
+ const fs = require("node:fs");
3
+ const path = require("node:path");
4
+ const {describe, it} = require("node:test");
5
+ const {listSupportedSymbologyTypes} = require("../src/barcode-types.js");
6
+ const {decodePngDataUri, isValidPngDataUri} = require("./helpers/barcode-decode.js");
7
+ const {generateBwipTestPng} = require("./helpers/generate-bwip-test-png.js");
8
+
9
+ const FIXTURE_DIR = path.join(__dirname, "fixtures/legacy-barcodes");
10
+ const manifest = JSON.parse(fs.readFileSync(path.join(FIXTURE_DIR, "manifest.json"), "utf8"));
11
+ const manifestByType = new Map(manifest.map((entry) => [entry.type, entry]));
12
+
13
+ function legacyFixtureToDataUri(filename) {
14
+ const pngBuffer = fs.readFileSync(path.join(FIXTURE_DIR, filename));
15
+ return `data:image/png;base64,${pngBuffer.toString("base64")}`;
16
+ }
17
+
18
+ describe("barcode equivalence", () => {
19
+ it("should include a manifest entry for every supported symbology type", () => {
20
+ for (const type of listSupportedSymbologyTypes()) {
21
+ assert.ok(manifestByType.has(type.toLowerCase()), `Missing manifest entry for ${type}`);
22
+ }
23
+ assert.equal(manifest.length, listSupportedSymbologyTypes().length);
24
+ });
25
+
26
+ for (const testCase of manifest) {
27
+ it(`should support ${testCase.type} with bwip-js`, async () => {
28
+ const nextImage = await generateBwipTestPng({
29
+ type: testCase.type,
30
+ content: testCase.bwipContent || testCase.content,
31
+ height: testCase.height,
32
+ bwipOptions: testCase.bwipOptions || {}
33
+ });
34
+
35
+ assert.match(nextImage, /^data:image\/png;base64,/);
36
+ assert.equal(isValidPngDataUri(nextImage), true);
37
+ assert.equal(testCase.bwipGenerated, true);
38
+ });
39
+
40
+ if (testCase.legacyGenerated) {
41
+ it(`should match legacy ${testCase.type} decoded payload when scannable`, async () => {
42
+ const legacyImage = legacyFixtureToDataUri(testCase.file);
43
+ const nextImage = await generateBwipTestPng({
44
+ type: testCase.type,
45
+ content: testCase.bwipContent || testCase.content,
46
+ height: testCase.height,
47
+ bwipOptions: testCase.bwipOptions || {}
48
+ });
49
+
50
+ if (!testCase.decodable) {
51
+ assert.equal(isValidPngDataUri(legacyImage), true);
52
+ assert.equal(isValidPngDataUri(nextImage), true);
53
+ return;
54
+ }
55
+
56
+ const legacyDecoded = await decodePngDataUri(legacyImage);
57
+ const nextDecoded = await decodePngDataUri(nextImage);
58
+
59
+ assert.equal(nextDecoded, legacyDecoded);
60
+ });
61
+ }
62
+ }
63
+ });
@@ -0,0 +1,44 @@
1
+ const assert = require("node:assert/strict");
2
+ const {describe, it} = require("node:test");
3
+ const {generateBarcodePng} = require("../src/barcode-generator.js");
4
+ const {decodePngDataUri, isValidPngDataUri} = require("./helpers/barcode-decode.js");
5
+
6
+ describe("barcode-generator.js", () => {
7
+ it("should generate a valid PNG data URI for code128", async () => {
8
+ const image = await generateBarcodePng({
9
+ type: "code128",
10
+ content: "1234",
11
+ height: 10
12
+ });
13
+
14
+ assert.match(image, /^data:image\/png;base64,/);
15
+ assert.equal(isValidPngDataUri(image), true);
16
+ });
17
+
18
+ it("should decode generated code128 barcodes to the original payload", async () => {
19
+ const image = await generateBarcodePng({
20
+ type: "code128",
21
+ content: "1234",
22
+ height: 10
23
+ });
24
+
25
+ assert.equal(await decodePngDataUri(image), "1234");
26
+ });
27
+
28
+ it("should decode generated eanx barcodes to the original payload", async () => {
29
+ const image = await generateBarcodePng({
30
+ type: "eanx",
31
+ content: "5901234123457",
32
+ height: 10
33
+ });
34
+
35
+ assert.equal(await decodePngDataUri(image), "5901234123457");
36
+ });
37
+
38
+ it("should reject unsupported symbology formats", async () => {
39
+ await assert.rejects(
40
+ () => generateBarcodePng({type: "gridmatrix", content: "1234", height: 10}),
41
+ /Unsupported barcode type: GRIDMATRIX/
42
+ );
43
+ });
44
+ });
@@ -0,0 +1,61 @@
1
+ const assert = require("node:assert/strict");
2
+ const {describe, it} = require("node:test");
3
+ const SYMBOLOGY_TYPE_NAMES = require("./fixtures/symbology-type-names.js");
4
+ const {
5
+ resolveBarcodeType,
6
+ listSupportedSymbologyTypes,
7
+ listUnsupportedSymbologyTypes,
8
+ UNSUPPORTED_TYPES,
9
+ SYMBLOGY_TO_BWIP
10
+ } = require("../src/barcode-types.js");
11
+
12
+ describe("barcode-types.js", () => {
13
+ it("should map every symbology enum name except unsupported ones", () => {
14
+ const expectedSupported = SYMBOLOGY_TYPE_NAMES
15
+ .filter((type) => !UNSUPPORTED_TYPES.has(type))
16
+ .sort();
17
+
18
+ assert.deepStrictEqual(listSupportedSymbologyTypes(), expectedSupported);
19
+ });
20
+
21
+ it("should expose unsupported symbology formats", () => {
22
+ assert.deepStrictEqual(listUnsupportedSymbologyTypes(), [
23
+ "GRIDMATRIX",
24
+ "KOREAPOST",
25
+ "UPNQR",
26
+ "VIN"
27
+ ]);
28
+ });
29
+
30
+ it("should resolve known aliases to bwip-js bcid values", () => {
31
+ assert.equal(resolveBarcodeType("code128"), "code128");
32
+ assert.equal(resolveBarcodeType("CODE11"), "code11");
33
+ assert.equal(resolveBarcodeType("qrcode"), "qrcode");
34
+ assert.equal(resolveBarcodeType("EAN128"), "gs1-128");
35
+ assert.equal(resolveBarcodeType("RM4SCC"), "royalmail");
36
+ });
37
+
38
+ it("should throw for unsupported symbology formats", () => {
39
+ for (const type of listUnsupportedSymbologyTypes()) {
40
+ assert.throws(
41
+ () => resolveBarcodeType(type),
42
+ (err) => err.message === `Unsupported barcode type: ${type}`
43
+ );
44
+ }
45
+ });
46
+
47
+ it("should throw for unknown symbology formats", () => {
48
+ assert.throws(
49
+ () => resolveBarcodeType("not-a-real-format"),
50
+ /Unsupported barcode type: NOT-A-REAL-FORMAT/
51
+ );
52
+ });
53
+
54
+ it("should map every supported symbology type to a non-empty bcid", () => {
55
+ for (const type of listSupportedSymbologyTypes()) {
56
+ const bcid = SYMBLOGY_TO_BWIP[type];
57
+ assert.ok(bcid, `Expected bcid mapping for ${type}`);
58
+ assert.equal(typeof bcid, "string");
59
+ }
60
+ });
61
+ });
@@ -0,0 +1,100 @@
1
+ // Sample payloads per legacy Symbology type. Values prefer content that works in both
2
+ // Symbology 4.0.2 and bwip-js where possible; `bwipContent` overrides bwip-only cases.
3
+ module.exports = {
4
+ AUSPOST: {content: "62123456789"},
5
+ AUSREDIRECT: {content: "62123456789"},
6
+ AUSREPLY: {content: "62123456789"},
7
+ AUSROUTE: {content: "62123456789"},
8
+ AZRUNE: {content: "1"},
9
+ AZTEC: {content: "AZTEC"},
10
+ C25IATA: {content: "1234567"},
11
+ C25IND: {content: "1234567"},
12
+ C25INTER: {content: "1234567890"},
13
+ C25LOGIC: {content: "1234567"},
14
+ C25MATRIX: {content: "1234567"},
15
+ CHANNEL: {content: "01"},
16
+ CODABAR: {content: "A123456A"},
17
+ CODABLOCKF: {content: "CODABLOCK"},
18
+ CODE11: {content: "1234"},
19
+ CODE128: {content: "1234"},
20
+ CODE128B: {content: "1234"},
21
+ CODE16K: {content: "1234"},
22
+ CODE32: {content: "12345678"},
23
+ CODE39: {content: "ABC123"},
24
+ CODE49: {content: "1234"},
25
+ CODE93: {content: "1234"},
26
+ CODEONE: {content: "1234"},
27
+ DAFT: {content: "DAFT"},
28
+ DATAMATRIX: {content: "DATAMATRIX"},
29
+ DOTCODE: {content: "DOTCODE"},
30
+ DPIDENT: {content: "12345678901"},
31
+ DPLEIT: {content: "1234567890123"},
32
+ EAN128: {content: "[01]09501101530003", bwipContent: "(01)09501101530003"},
33
+ EAN128_CC: {content: "(01)09501101530003|99TEST", bwipContent: "(01)09501101530003|(10)ABC"},
34
+ EAN14: {content: "1234567890123", bwipContent: "(01)09501101530003", bwipOptions: {parse: true}},
35
+ EANX: {content: "5901234123457"},
36
+ EANX_CC: {content: "5901234123457|99TEST", bwipContent: "5901234123457|(10)ABC"},
37
+ EANX_CHK: {content: "5901234123457"},
38
+ EXCODE39: {content: "ABC-123"},
39
+ FIM: {content: "1"},
40
+ FLAT: {content: "1"},
41
+ HANXIN: {content: "HANXIN"},
42
+ HIBC_128: {content: "+ABC1234ABC1234/$$0123456789012"},
43
+ HIBC_39: {content: "+ABC1234ABC1234/$$0123456789012"},
44
+ HIBC_AZTEC: {content: "+ABC1234ABC1234/$$0123456789012"},
45
+ HIBC_DM: {content: "+ABC1234ABC1234/$$0123456789012"},
46
+ HIBC_MICPDF: {content: "+ABC1234ABC1234/$$0123456789012"},
47
+ HIBC_PDF: {content: "+ABC1234ABC1234/$$0123456789012"},
48
+ HIBC_QR: {content: "+ABC1234ABC1234/$$0123456789012"},
49
+ ISBNX: {content: "9780306406157", bwipContent: "978-0-306-40615-7"},
50
+ ITF14: {content: "1234567890123"},
51
+ JAPANPOST: {content: "12345678901"},
52
+ KIX: {content: "15400171583802"},
53
+ LOGMARS: {content: "ABC123"},
54
+ MAILMARK: {
55
+ content: "AV11HX7TCH",
56
+ bwipContent: `JGB ${"X".repeat(41)}`,
57
+ bwipOptions: {type: 9}
58
+ },
59
+ MAXICODE: {content: "1234567890123456789012345678901234"},
60
+ MICROPDF417: {content: "MICROPDF"},
61
+ MICROQR: {content: "MICROQR"},
62
+ MSI_PLESSEY: {content: "123456"},
63
+ NVE18: {
64
+ content: "12345678901234567",
65
+ bwipContent: "(00)12345678901234567",
66
+ bwipOptions: {parse: true, includecheck: true}
67
+ },
68
+ ONECODE: {content: "01234567890501234567890501", bwipContent: "01234567890501234567890501234"},
69
+ PDF417: {content: "PDF417"},
70
+ PDF417TRUNC: {content: "PDF417"},
71
+ PHARMA: {content: "123"},
72
+ PHARMA_TWO: {content: "123456"},
73
+ PLANET: {content: "01234567890"},
74
+ PLESSEY: {content: "1234567890"},
75
+ POSTNET: {content: "01234"},
76
+ PZN: {content: "123456"},
77
+ QRCODE: {content: "hello-world"},
78
+ RM4SCC: {content: "W1J0TRQ"},
79
+ RSS14: {content: "0123456789051", bwipContent: "(01)09501101530003", bwipOptions: {parse: true}},
80
+ RSS14STACK: {content: "0123456789051", bwipContent: "(01)09501101530003", bwipOptions: {parse: true}},
81
+ RSS14STACK_CC: {content: "0123456789051|99TEST", bwipContent: "(01)09501101530003|(10)ABC"},
82
+ RSS14STACK_OMNI: {content: "0123456789051", bwipContent: "(01)09501101530003", bwipOptions: {parse: true}},
83
+ RSS14_CC: {content: "012345678905|99TEST", bwipContent: "(01)09501101530003|(10)ABC"},
84
+ RSS14_OMNI_CC: {content: "0123456789051|99TEST", bwipContent: "(01)09501101530003|(10)ABC"},
85
+ RSS_EXP: {content: "[01]09501101530003", bwipContent: "(01)09501101530003"},
86
+ RSS_EXPSTACK: {content: "(01)09501101530003"},
87
+ RSS_EXPSTACK_CC: {content: "(01)09501101530003|99TEST", bwipContent: "(01)09501101530003|(10)ABC"},
88
+ RSS_EXP_CC: {content: "(01)09501101530003|99TEST", bwipContent: "(01)09501101530003|(10)ABC"},
89
+ RSS_LTD: {content: "0123456789051", bwipContent: "(01)09501101530003", bwipOptions: {parse: true}},
90
+ RSS_LTD_CC: {content: "0123456789051|99TEST", bwipContent: "(01)09501101530003|(10)ABC"},
91
+ TELEPEN: {content: "Telepen"},
92
+ TELEPEN_NUM: {content: "123456"},
93
+ ULTRA: {content: "ULTRA"},
94
+ UPCA: {content: "012345678905"},
95
+ UPCA_CC: {content: "012345678905|99TEST", bwipContent: "012345678905|(10)ABC"},
96
+ UPCA_CHK: {content: "012345678905"},
97
+ UPCE: {content: "0123456"},
98
+ UPCE_CC: {content: "0123456|99TEST", bwipContent: "0123456|(10)ABC"},
99
+ UPCE_CHK: {content: "0123456"}
100
+ };
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * One-time maintenance script: requires symbology@4.0.2 (npm i -D symbology@4.0.2).
4
+ * Generates legacy PNG fixtures for every supported barcode type.
5
+ *
6
+ * Usage: node test/fixtures/generate-legacy-barcodes.js
7
+ */
8
+ const fs = require("node:fs");
9
+ const path = require("node:path");
10
+ const bwipjs = require("bwip-js");
11
+ const {SymbologyType, createStream, OutputType} = require("symbology");
12
+ const {scanImageData} = require("@undecaf/zbar-wasm");
13
+ const {PNG} = require("pngjs");
14
+ const {listSupportedSymbologyTypes, resolveBarcodeType} = require("../../src/barcode-types.js");
15
+ const SAMPLE_CONTENT = require("./barcode-sample-content.js");
16
+
17
+ const FIXTURE_DIR = path.join(__dirname, "legacy-barcodes");
18
+ const HEIGHT = 30;
19
+
20
+ function slug(value) {
21
+ return String(value).replace(/[^a-zA-Z0-9]+/g, "_").slice(0, 40);
22
+ }
23
+
24
+ async function tryDecodePngBuffer(pngBuffer) {
25
+ const png = PNG.sync.read(pngBuffer);
26
+ const symbols = await scanImageData({
27
+ data: png.data,
28
+ width: png.width,
29
+ height: png.height
30
+ });
31
+ return symbols.length ? symbols[0].decode() : null;
32
+ }
33
+
34
+ async function generateLegacyPng(type, content) {
35
+ const symbologyType = SymbologyType[type];
36
+ if (!symbologyType) {
37
+ throw new Error(`Symbology enum missing for ${type}`);
38
+ }
39
+
40
+ const result = await createStream({
41
+ symbology: symbologyType,
42
+ height: HEIGHT,
43
+ showHumanReadableText: false,
44
+ scale: 2.0
45
+ }, String(content), OutputType.PNG);
46
+
47
+ const base64 = result.data.replace(/^data:image\/png;base64,/, "");
48
+ return Buffer.from(base64, "base64");
49
+ }
50
+
51
+ async function generateBwipPng(type, content, bwipOptions = {}) {
52
+ const bcid = resolveBarcodeType(type);
53
+ return bwipjs.toBuffer({
54
+ bcid,
55
+ text: String(content),
56
+ scale: 2,
57
+ height: HEIGHT,
58
+ includetext: false,
59
+ backgroundcolor: "FFFFFF",
60
+ ...bwipOptions
61
+ });
62
+ }
63
+
64
+ async function main() {
65
+ fs.mkdirSync(FIXTURE_DIR, {recursive: true});
66
+
67
+ const manifest = [];
68
+ const failures = [];
69
+
70
+ for (const type of listSupportedSymbologyTypes()) {
71
+ const sample = SAMPLE_CONTENT[type];
72
+ if (!sample) {
73
+ failures.push({type, stage: "sample-content", error: "Missing sample content"});
74
+ continue;
75
+ }
76
+
77
+ const legacyContent = sample.content;
78
+ const bwipContent = sample.bwipContent || sample.content;
79
+ const bwipOptions = sample.bwipOptions || {};
80
+ const filename = `${type.toLowerCase()}-${slug(legacyContent)}.png`;
81
+ const entry = {
82
+ type: type.toLowerCase(),
83
+ content: legacyContent,
84
+ bwipContent,
85
+ bwipOptions,
86
+ height: HEIGHT
87
+ };
88
+
89
+ let legacyBuffer = null;
90
+ try {
91
+ legacyBuffer = await generateLegacyPng(type, legacyContent);
92
+ fs.writeFileSync(path.join(FIXTURE_DIR, filename), legacyBuffer);
93
+ entry.file = filename;
94
+ entry.legacyGenerated = true;
95
+ process.stdout.write("L");
96
+ } catch (error) {
97
+ entry.legacyGenerated = false;
98
+ entry.legacyError = error.message || String(error);
99
+ process.stdout.write("l");
100
+ }
101
+
102
+ try {
103
+ const bwipBuffer = await generateBwipPng(type, bwipContent, bwipOptions);
104
+ entry.bwipGenerated = true;
105
+ process.stdout.write("B");
106
+
107
+ if (legacyBuffer) {
108
+ entry.legacyDecoded = await tryDecodePngBuffer(legacyBuffer);
109
+ entry.bwipDecoded = await tryDecodePngBuffer(bwipBuffer);
110
+ entry.decodable = entry.legacyDecoded !== null
111
+ && entry.bwipDecoded !== null
112
+ && entry.legacyDecoded === entry.bwipDecoded;
113
+ } else {
114
+ entry.bwipDecoded = await tryDecodePngBuffer(bwipBuffer);
115
+ entry.decodable = entry.bwipDecoded !== null;
116
+ }
117
+ } catch (error) {
118
+ entry.bwipGenerated = false;
119
+ entry.bwipError = error.message || String(error);
120
+ failures.push({type, stage: "bwip", error: entry.bwipError});
121
+ process.stdout.write("b");
122
+ }
123
+
124
+ manifest.push(entry);
125
+ process.stdout.write(" ");
126
+ }
127
+
128
+ process.stdout.write("\n");
129
+ manifest.sort((a, b) => a.type.localeCompare(b.type));
130
+ fs.writeFileSync(
131
+ path.join(FIXTURE_DIR, "manifest.json"),
132
+ `${JSON.stringify(manifest, null, 2)}\n`
133
+ );
134
+
135
+ fs.writeFileSync(
136
+ path.join(FIXTURE_DIR, "generation-failures.json"),
137
+ `${JSON.stringify(failures, null, 2)}\n`
138
+ );
139
+
140
+ const legacyCount = manifest.filter((entry) => entry.legacyGenerated).length;
141
+ const bwipCount = manifest.filter((entry) => entry.bwipGenerated).length;
142
+ const decodableCount = manifest.filter((entry) => entry.decodable).length;
143
+
144
+ console.log(`Manifest entries: ${manifest.length}`);
145
+ console.log(`Legacy fixtures: ${legacyCount}`);
146
+ console.log(`Bwip generated: ${bwipCount}`);
147
+ console.log(`Decodable pairs: ${decodableCount}`);
148
+ console.log(`Bwip failures: ${failures.length}`);
149
+
150
+ if (failures.length) {
151
+ process.exitCode = 1;
152
+ }
153
+ }
154
+
155
+ main().catch((error) => {
156
+ console.error(error);
157
+ process.exit(1);
158
+ });