@sythos/js_barcode_universal 1.0.0 → 1.2.5

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 (57) hide show
  1. package/LICENSE +16 -17
  2. package/NOTICE.md +24 -22
  3. package/README.md +137 -60
  4. package/bundle/sythos-barcode.esm.js +3380 -216
  5. package/bundle/sythos-barcode.js +3368 -216
  6. package/examples/create.html +2 -0
  7. package/examples/read.html +341 -341
  8. package/licenses/README.md +58 -29
  9. package/licenses/aztec-code.license +74 -0
  10. package/licenses/codabar.license +9 -9
  11. package/licenses/code-11.license +9 -9
  12. package/licenses/code-128.license +6 -6
  13. package/licenses/code-39.license +6 -6
  14. package/licenses/code-93.license +7 -7
  15. package/licenses/data-matrix.license +11 -11
  16. package/licenses/ean-13.license +6 -6
  17. package/licenses/ean-8.license +6 -6
  18. package/licenses/gs1-128.license +6 -6
  19. package/licenses/isbn.license +7 -7
  20. package/licenses/itf-14.license +6 -6
  21. package/licenses/itf.license +6 -6
  22. package/licenses/micropdf417.license +96 -0
  23. package/licenses/msi-plessey.license +7 -7
  24. package/licenses/pdf417.license +37 -0
  25. package/licenses/pharmacode.license +7 -7
  26. package/licenses/qr-code.license +6 -6
  27. package/licenses/upc-a.license +7 -7
  28. package/licenses/upc-e.license +6 -6
  29. package/package.json +13 -3
  30. package/src/aztec/decoder.js +317 -0
  31. package/src/aztec/detector.js +224 -0
  32. package/src/aztec/encoder.js +257 -0
  33. package/src/aztec/high-level.js +211 -0
  34. package/src/aztec/index.js +45 -0
  35. package/src/aztec/tables.js +210 -0
  36. package/src/core/galois-field.js +3 -0
  37. package/src/core/reed-solomon.js +64 -50
  38. package/src/datamatrix/decoder.js +262 -262
  39. package/src/datamatrix/detector.js +225 -225
  40. package/src/datamatrix/encoder.js +191 -191
  41. package/src/datamatrix/index.js +42 -42
  42. package/src/datamatrix/tables.js +123 -123
  43. package/src/index.js +113 -3
  44. package/src/micropdf417/compaction.js +116 -0
  45. package/src/micropdf417/decoder.js +183 -0
  46. package/src/micropdf417/detector.js +149 -0
  47. package/src/micropdf417/encoder.js +209 -0
  48. package/src/micropdf417/error-correction.js +55 -0
  49. package/src/micropdf417/index.js +49 -0
  50. package/src/micropdf417/tables.js +184 -0
  51. package/src/pdf417/compaction.js +298 -0
  52. package/src/pdf417/decoder.js +75 -0
  53. package/src/pdf417/detector.js +468 -0
  54. package/src/pdf417/encoder.js +91 -0
  55. package/src/pdf417/error-correction.js +47 -0
  56. package/src/pdf417/index.js +6 -0
  57. package/src/pdf417/tables.js +317 -0
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Sythos Barcode Suite v1.0.0
2
+ * Sythos Barcode Suite v1.2.5
3
3
  *
4
4
  * MIT License
5
5
  *
@@ -2918,6 +2918,9 @@ const GF256_QR = new GaloisField({ size: 256, primitive: 0x011d, name: 'GF(256)/
2918
2918
  /** Data Matrix ECC200. x^8 + x^5 + x^3 + x^2 + 1 */
2919
2919
  const GF256_DM = new GaloisField({ size: 256, primitive: 0x012d, name: 'GF(256)/DataMatrix' });
2920
2920
 
2921
+ /** Aztec's eight-bit data field is algebraically identical to Data Matrix's. */
2922
+ const GF256_AZTEC = GF256_DM;
2923
+
2921
2924
  /** PDF417. Prime field; 3 is a primitive root modulo 929. */
2922
2925
  const GF929 = new GaloisField({ size: 929, prime: true, generator: 3, name: 'GF(929)' });
2923
2926
 
@@ -2930,6 +2933,7 @@ const GF4096 = new GaloisField({ size: 4096, primitive: 0x1069, name: 'GF(4096)'
2930
2933
  __exports.GaloisField = GaloisField;
2931
2934
  __exports.GF256_QR = GF256_QR;
2932
2935
  __exports.GF256_DM = GF256_DM;
2936
+ __exports.GF256_AZTEC = GF256_AZTEC;
2933
2937
  __exports.GF929 = GF929;
2934
2938
  __exports.GF16 = GF16;
2935
2939
  __exports.GF64 = GF64;
@@ -2965,7 +2969,7 @@ const { ChecksumError } = __require("core/errors.js");
2965
2969
  *
2966
2970
  * g(x) = product over i of (x - a^(base + i)), i = 0 .. eccLen-1
2967
2971
  *
2968
- * `base` is 0 for QR and Aztec; 1 for Data Matrix and PDF417.
2972
+ * `base` is 0 for QR; 1 for Aztec, Data Matrix and PDF417.
2969
2973
  *
2970
2974
  * @param {number} eccLen
2971
2975
  * @param {import('./galois-field.js').GaloisField} field
@@ -3062,6 +3066,44 @@ function evalPoly(poly, x, field) {
3062
3066
  return acc;
3063
3067
  }
3064
3068
 
3069
+ function multiplyAscending(left, right, field, limit) {
3070
+ const out = new Array(Math.min(limit, left.length + right.length - 1)).fill(0);
3071
+ for (let i = 0; i < left.length; i++) for (let j = 0; j < right.length && i + j < out.length; j++) {
3072
+ out[i + j] = field.add(out[i + j], field.mul(left[i], right[j]));
3073
+ }
3074
+ return out;
3075
+ }
3076
+
3077
+ function berlekampMassey(syndromes, field) {
3078
+ const limit = syndromes.length;
3079
+ const lambda = new Array(limit + 1).fill(0);
3080
+ const previous = new Array(limit + 1).fill(0);
3081
+ const temporary = new Array(limit + 1).fill(0);
3082
+ lambda[0] = 1;
3083
+ previous[0] = 1;
3084
+ let errorCount = 0;
3085
+ let shift = 1;
3086
+ let lastDiscrepancy = 1;
3087
+
3088
+ for (let step = 0; step < limit; step++) {
3089
+ let discrepancy = syndromes[step];
3090
+ for (let i = 1; i <= errorCount; i++) discrepancy = field.add(discrepancy, field.mul(lambda[i], syndromes[step - i]));
3091
+ if (discrepancy === 0) { shift++; continue; }
3092
+ const scale = field.div(discrepancy, lastDiscrepancy);
3093
+ for (let i = 0; i <= limit; i++) temporary[i] = lambda[i];
3094
+ for (let i = 0; i + shift <= limit; i++) if (previous[i] !== 0) {
3095
+ lambda[i + shift] = field.sub(lambda[i + shift], field.mul(scale, previous[i]));
3096
+ }
3097
+ if (2 * errorCount <= step) {
3098
+ errorCount = step + 1 - errorCount;
3099
+ for (let i = 0; i <= limit; i++) previous[i] = temporary[i];
3100
+ lastDiscrepancy = discrepancy;
3101
+ shift = 1;
3102
+ } else shift++;
3103
+ }
3104
+ return { locator: lambda.slice(0, errorCount + 1), errorCount };
3105
+ }
3106
+
3065
3107
  /**
3066
3108
  * Correct errors in a received codeword, in place.
3067
3109
  *
@@ -3069,11 +3111,16 @@ function evalPoly(poly, x, field) {
3069
3111
  * @param {number} eccLen
3070
3112
  * @param {import('./galois-field.js').GaloisField} field
3071
3113
  * @param {number} [base]
3114
+ * @param {number[]} [erasures] Known damaged indexes, counted from wire order.
3072
3115
  * @returns {number} Number of symbols corrected.
3073
3116
  * @throws {ChecksumError} If the damage exceeds the correction capacity.
3074
3117
  */
3075
- function rsDecode(received, eccLen, field, base = 0) {
3118
+ function rsDecode(received, eccLen, field, base = 0, erasures = []) {
3076
3119
  const n = received.length;
3120
+ if (!Array.isArray(erasures) || new Set(erasures).size !== erasures.length || erasures.some((index) => !Number.isInteger(index) || index < 0 || index >= n)) {
3121
+ throw new ChecksumError('Reed-Solomon: erasure positions must be unique codeword indexes');
3122
+ }
3123
+ if (erasures.length > eccLen) throw new ChecksumError(`Reed-Solomon: ${erasures.length} erasures exceeds correction capacity ${eccLen} (${field.name})`);
3077
3124
 
3078
3125
  // --- Syndromes. S[i] = R(a^(base+i)); all zero means an intact codeword.
3079
3126
  const syn = new Array(eccLen).fill(0);
@@ -3085,53 +3132,24 @@ function rsDecode(received, eccLen, field, base = 0) {
3085
3132
  }
3086
3133
  if (!damaged) return 0;
3087
3134
 
3088
- // --- Berlekamp-Massey. Degree-ascending here: lambda[k] is the coefficient
3089
- // of x^k, which is how the recurrence is naturally stated.
3090
- const lambda = new Array(eccLen + 1).fill(0);
3091
- const prev = new Array(eccLen + 1).fill(0);
3092
- const tmp = new Array(eccLen + 1).fill(0);
3093
- lambda[0] = 1;
3094
- prev[0] = 1;
3095
- let errCount = 0; // current LFSR length
3096
- let shift = 1; // steps since `prev` was last updated
3097
- let lastDisc = 1; // discrepancy at that update
3098
-
3099
- for (let step = 0; step < eccLen; step++) {
3100
- let disc = syn[step];
3101
- for (let i = 1; i <= errCount; i++) {
3102
- disc = field.add(disc, field.mul(lambda[i], syn[step - i]));
3103
- }
3104
-
3105
- if (disc === 0) {
3106
- shift++;
3107
- continue;
3108
- }
3109
-
3110
- const scale = field.div(disc, lastDisc);
3111
- tmp.fill(0);
3112
- for (let i = 0; i <= eccLen; i++) tmp[i] = lambda[i];
3113
-
3114
- for (let i = 0; i + shift <= eccLen; i++) {
3115
- if (prev[i] === 0) continue;
3116
- lambda[i + shift] = field.sub(lambda[i + shift], field.mul(scale, prev[i]));
3117
- }
3118
-
3119
- if (2 * errCount <= step) {
3120
- errCount = step + 1 - errCount;
3121
- for (let i = 0; i <= eccLen; i++) prev[i] = tmp[i];
3122
- lastDisc = disc;
3123
- shift = 1;
3124
- } else {
3125
- shift++;
3126
- }
3127
- }
3128
-
3129
- if (errCount === 0 || errCount > eccLen / 2) {
3135
+ // Remove the known roots before locating unknown errors. The leading
3136
+ // erasureCount terms contain only the known-location transient and are not
3137
+ // part of the error-only recurrence.
3138
+ let erasureLocator = [1];
3139
+ for (const index of erasures) {
3140
+ const location = field.exp(n - 1 - index);
3141
+ erasureLocator = multiplyAscending(erasureLocator, [1, field.neg(location)], field, eccLen + 1);
3142
+ }
3143
+ const modified = multiplyAscending(syn, erasureLocator, field, eccLen).slice(erasures.length);
3144
+ const { locator: errorLocator, errorCount } = berlekampMassey(modified, field);
3145
+ if (2 * errorCount + erasures.length > eccLen) {
3130
3146
  throw new ChecksumError(
3131
- `Reed-Solomon: ${errCount} errors exceeds correction capacity ` +
3132
- `${Math.floor(eccLen / 2)} (${field.name})`
3147
+ `Reed-Solomon: ${errorCount} errors and ${erasures.length} erasures exceed correction capacity ` +
3148
+ `${eccLen} (${field.name})`
3133
3149
  );
3134
3150
  }
3151
+ const lambda = multiplyAscending(erasureLocator, errorLocator, field, eccLen + 1);
3152
+ const totalCount = errorCount + erasures.length;
3135
3153
 
3136
3154
  // --- Chien search. Position p (counted from the low-order end) is in error
3137
3155
  // when lambda(a^-p) == 0.
@@ -3140,16 +3158,16 @@ function rsDecode(received, eccLen, field, base = 0) {
3140
3158
  const xInv = field.exp(-p);
3141
3159
  let acc = 0;
3142
3160
  let term = 1;
3143
- for (let i = 0; i <= errCount; i++) {
3161
+ for (let i = 0; i <= totalCount; i++) {
3144
3162
  acc = field.add(acc, field.mul(lambda[i], term));
3145
3163
  term = field.mul(term, xInv);
3146
3164
  }
3147
3165
  if (acc === 0) positions.push(p);
3148
3166
  }
3149
3167
 
3150
- if (positions.length !== errCount) {
3168
+ if (positions.length !== totalCount) {
3151
3169
  throw new ChecksumError(
3152
- `Reed-Solomon: located ${positions.length} of ${errCount} error positions`
3170
+ `Reed-Solomon: located ${positions.length} of ${totalCount} error positions`
3153
3171
  );
3154
3172
  }
3155
3173
 
@@ -3158,7 +3176,7 @@ function rsDecode(received, eccLen, field, base = 0) {
3158
3176
  const omega = new Array(eccLen).fill(0);
3159
3177
  for (let i = 0; i < eccLen; i++) {
3160
3178
  let acc = 0;
3161
- for (let j = 0; j <= i && j <= errCount; j++) {
3179
+ for (let j = 0; j <= i && j <= totalCount; j++) {
3162
3180
  acc = field.add(acc, field.mul(lambda[j], syn[i - j]));
3163
3181
  }
3164
3182
  omega[i] = acc;
@@ -3182,7 +3200,7 @@ function rsDecode(received, eccLen, field, base = 0) {
3182
3200
  // in a prime field every term contributes with an integer multiplier.
3183
3201
  let den = 0;
3184
3202
  term = 1;
3185
- for (let i = 1; i <= errCount; i++) {
3203
+ for (let i = 1; i <= totalCount; i++) {
3186
3204
  if (field.prime) {
3187
3205
  // i * lambda[i] * x^(i-1), where `i` is repeated addition.
3188
3206
  let mult = 0;
@@ -7211,218 +7229,3244 @@ const __reexport3 = __require("qr/tables.js"); __exports.validateTables = __reex
7211
7229
 
7212
7230
  };
7213
7231
 
7214
- __modules["render/options.js"] = function (__require, __exports) {
7232
+ __modules["aztec/high-level.js"] = function (__require, __exports) {
7215
7233
  /**
7216
- * Shared render options, normalised once so every backend agrees.
7234
+ * Aztec high-level stream writer.
7217
7235
  *
7218
- * @module render/options
7236
+ * The output is deliberately a `BitWriter`, rather than a byte array: Aztec's
7237
+ * text controls and binary-shift lengths are not byte aligned. This module is
7238
+ * also the boundary where JavaScript strings become UTF-8. Passing a byte
7239
+ * view bypasses that conversion and preserves every octet unchanged.
7240
+ *
7241
+ * The initial state mandated by the symbology is UPPER. The greedy text pass
7242
+ * uses UPPER, LOWER, DIGIT and PUNCT tables, selecting the shortest available
7243
+ * latch at each byte. Bytes without a text-table representation are emitted
7244
+ * through the standard B/S (binary shift) escape. B/S is available from
7245
+ * UPPER and makes this a complete, lossless representation of UTF-8 payloads.
7246
+ *
7247
+ * @module aztec/high-level
7219
7248
  */
7220
- const { BitMatrix } = __require("core/bit-matrix.js");
7249
+ const { BitWriter } = __require("core/bit-buffer.js");
7250
+ const { EncodeError } = __require("core/errors.js");
7221
7251
 
7222
- /**
7223
- * @typedef {object} RenderOptions
7224
- * @property {number} [scale] Pixels per module. Default 8.
7225
- * @property {number} [margin] Quiet-zone modules on every side. Default 4.
7226
- * @property {string} [dark] Colour of set modules. Default '#000000'.
7227
- * @property {string} [light] Colour of clear modules, or 'none' for transparent.
7228
- * @property {number} [barHeight] For 1D symbols: total bar height in pixels.
7229
- */
7252
+ /** Aztec high-level table identifiers, exposed for decoder/API symmetry. */
7253
+ const HIGH_LEVEL_MODE = Object.freeze({
7254
+ UPPER: 0,
7255
+ LOWER: 1,
7256
+ DIGIT: 2,
7257
+ MIXED: 3,
7258
+ PUNCT: 4,
7259
+ });
7260
+
7261
+ /** Maximum number of bytes represented by one B/S escape. */
7262
+ const MAX_BINARY_SHIFT = 2078;
7230
7263
 
7231
7264
  /**
7232
- * Expand and pad the matrix, and resolve every dimension.
7233
- *
7234
- * Linear symbols arrive one module tall. They are stretched to `barHeight`
7235
- * *before* the quiet zone is applied, so the margin ends up uniform on all
7236
- * four sides — padding first would leave a quiet zone one module tall against
7237
- * bars a hundred pixels tall, which no scanner would accept.
7265
+ * Convert accepted public input to its encoded octets.
7238
7266
  *
7239
- * @param {BitMatrix} matrix
7240
- * @param {RenderOptions} options
7267
+ * @param {string|ArrayBuffer|ArrayBufferView} value
7268
+ * @param {'utf-8'} [charset]
7269
+ * @returns {Uint8Array}
7241
7270
  */
7242
- function normalizeOptions(matrix, options = {}) {
7243
- const scale = Math.max(1, Math.floor(options.scale ?? 8));
7244
- const margin = Math.max(0, Math.floor(options.margin ?? 4));
7245
- const dark = options.dark ?? '#000000';
7246
- const light = options.light ?? '#ffffff';
7247
- const barHeight = options.barHeight ?? null;
7271
+ function aztecBytes(value, charset = 'utf-8') {
7272
+ if (charset !== 'utf-8') throw new EncodeError(`Aztec: unsupported charset "${charset}"`);
7273
+ if (typeof value === 'string') return new TextEncoder().encode(value);
7274
+ if (value instanceof ArrayBuffer) return new Uint8Array(value);
7275
+ if (ArrayBuffer.isView(value)) {
7276
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
7277
+ }
7278
+ throw new EncodeError('Aztec: value must be a string, ArrayBuffer, or byte view');
7279
+ }
7248
7280
 
7249
- let base = matrix;
7250
- const is1D = matrix.height === 1;
7281
+ /** @param {number} byte @returns {number} UPPER-table value, or -1. */
7282
+ function upperValue(byte) {
7283
+ if (byte === 0x20) return 1;
7284
+ if (byte >= 0x41 && byte <= 0x5a) return byte - 0x41 + 2;
7285
+ return -1;
7286
+ }
7251
7287
 
7252
- if (is1D) {
7253
- // Default to a bar height that stays scannable: tall enough that a laser
7254
- // crossing at a slight angle still passes through the whole symbol.
7255
- const targetPixels = barHeight ?? Math.max(40, Math.round(matrix.width * scale * 0.15));
7256
- const rows = Math.max(1, Math.round(targetPixels / scale));
7257
- base = new BitMatrix(matrix.width, rows);
7258
- for (let x = 0; x < matrix.width; x++) {
7259
- if (!matrix.get(x, 0)) continue;
7260
- for (let y = 0; y < rows; y++) base.set(x, y);
7261
- }
7288
+ /** Aztec's latch table, packed as `(bitCount << 16) | bits`. */
7289
+ const LATCH = Object.freeze([
7290
+ [0, 327708, 327710, 327709, 656318],
7291
+ [590318, 0, 327710, 327709, 656318],
7292
+ [262158, 590300, 0, 590301, 932798],
7293
+ [327709, 327708, 656322, 0, 327710],
7294
+ [327711, 656380, 656382, 656381, 0],
7295
+ ]);
7296
+
7297
+ /** @param {number} byte @returns {number} */
7298
+ function lowerValue(byte) {
7299
+ if (byte === 0x20) return 1;
7300
+ if (byte >= 0x61 && byte <= 0x7a) return byte - 0x61 + 2;
7301
+ return -1;
7302
+ }
7303
+
7304
+ /** @param {number} byte @returns {number} */
7305
+ function digitValue(byte) {
7306
+ if (byte === 0x20) return 1;
7307
+ if (byte >= 0x30 && byte <= 0x39) return byte - 0x30 + 2;
7308
+ if (byte === 0x2c) return 12;
7309
+ if (byte === 0x2e) return 13;
7310
+ return -1;
7311
+ }
7312
+
7313
+ const PUNCT = new Map([
7314
+ [0x0d, 1], [0x21, 6], [0x22, 7], [0x23, 8], [0x24, 9], [0x25, 10],
7315
+ [0x26, 11], [0x27, 12], [0x28, 13], [0x29, 14], [0x2a, 15], [0x2b, 16],
7316
+ [0x2c, 17], [0x2d, 18], [0x2e, 19], [0x2f, 20], [0x3a, 21], [0x3b, 22],
7317
+ [0x3c, 23], [0x3d, 24], [0x3e, 25], [0x3f, 26], [0x5b, 27], [0x5d, 28],
7318
+ [0x7b, 29], [0x7d, 30],
7319
+ ]);
7320
+
7321
+ /** @param {number} byte @param {number} mode @returns {number} */
7322
+ function textValue(byte, mode) {
7323
+ switch (mode) {
7324
+ case HIGH_LEVEL_MODE.UPPER: return upperValue(byte);
7325
+ case HIGH_LEVEL_MODE.LOWER: return lowerValue(byte);
7326
+ case HIGH_LEVEL_MODE.DIGIT: return digitValue(byte);
7327
+ case HIGH_LEVEL_MODE.PUNCT: return PUNCT.get(byte) ?? -1;
7328
+ default: return -1;
7262
7329
  }
7330
+ }
7263
7331
 
7264
- const source = margin > 0 ? base.withMargin(margin) : base;
7332
+ /** @param {BitWriter} writer @param {number} from @param {number} to */
7333
+ function latch(writer, from, to) {
7334
+ if (from === to) return;
7335
+ const packed = LATCH[from][to];
7336
+ writer.put(packed & 0xffff, packed >>> 16);
7337
+ }
7265
7338
 
7266
- return {
7267
- scale,
7268
- margin,
7269
- dark,
7270
- light,
7271
- is1D,
7272
- source,
7273
- rowHeight: scale,
7274
- pixelWidth: source.width * scale,
7275
- pixelHeight: source.height * scale,
7276
- };
7339
+ /** @param {number} mode @returns {number} */
7340
+ function characterWidth(mode) {
7341
+ return mode === HIGH_LEVEL_MODE.DIGIT ? 4 : 5;
7277
7342
  }
7278
7343
 
7279
7344
  /**
7280
- * Parse a CSS colour into RGBA bytes.
7345
+ * Write an Aztec binary-shift segment while in UPPER mode.
7281
7346
  *
7282
- * Supports the forms a barcode actually needs: #rgb, #rgba, #rrggbb,
7283
- * #rrggbbaa, rgb(), rgba(), plus 'none' and 'transparent'.
7347
+ * B/S is `11111`; its five-bit length directly covers 1..31 bytes. A zero
7348
+ * length selects the extended eleven-bit form, whose stored value is n - 31.
7349
+ * Splitting at 2078 keeps each control representable and makes arbitrarily
7350
+ * long byte input well-defined.
7284
7351
  *
7285
- * @param {string} colour
7286
- * @returns {[number, number, number, number]}
7352
+ * @param {BitWriter} writer
7353
+ * @param {Uint8Array} bytes
7354
+ * @param {number} start
7355
+ * @param {number} length
7287
7356
  */
7288
- function parseColor(colour) {
7289
- const value = String(colour).trim().toLowerCase();
7290
-
7291
- if (value === 'none' || value === 'transparent') return [0, 0, 0, 0];
7292
- if (value === 'white') return [255, 255, 255, 255];
7293
- if (value === 'black') return [0, 0, 0, 255];
7294
-
7295
- if (value[0] === '#') {
7296
- const hex = value.slice(1);
7297
- const expand = (c) => parseInt(c + c, 16);
7298
- if (hex.length === 3) {
7299
- return [expand(hex[0]), expand(hex[1]), expand(hex[2]), 255];
7300
- }
7301
- if (hex.length === 4) {
7302
- return [expand(hex[0]), expand(hex[1]), expand(hex[2]), expand(hex[3])];
7303
- }
7304
- if (hex.length === 6) {
7305
- return [
7306
- parseInt(hex.slice(0, 2), 16),
7307
- parseInt(hex.slice(2, 4), 16),
7308
- parseInt(hex.slice(4, 6), 16),
7309
- 255,
7310
- ];
7311
- }
7312
- if (hex.length === 8) {
7313
- return [
7314
- parseInt(hex.slice(0, 2), 16),
7315
- parseInt(hex.slice(2, 4), 16),
7316
- parseInt(hex.slice(4, 6), 16),
7317
- parseInt(hex.slice(6, 8), 16),
7318
- ];
7357
+ function writeBinaryShift(writer, bytes, start, length) {
7358
+ let at = start;
7359
+ let left = length;
7360
+ while (left > 0) {
7361
+ const count = Math.min(left, MAX_BINARY_SHIFT);
7362
+ writer.put(31, 5); // UPPER B/S
7363
+ if (count <= 31) writer.put(count, 5);
7364
+ else {
7365
+ writer.put(0, 5);
7366
+ writer.put(count - 31, 11);
7319
7367
  }
7368
+ for (let i = 0; i < count; i++) writer.put(bytes[at + i], 8);
7369
+ at += count;
7370
+ left -= count;
7320
7371
  }
7372
+ }
7321
7373
 
7322
- const fn = value.match(/^rgba?\(([^)]+)\)$/);
7323
- if (fn) {
7324
- const parts = fn[1].split(/[,/\s]+/).filter(Boolean);
7325
- const channel = (s) => (s.endsWith('%')
7326
- ? Math.round((parseFloat(s) / 100) * 255)
7327
- : Math.round(parseFloat(s)));
7328
- const r = channel(parts[0]);
7329
- const g = channel(parts[1]);
7330
- const b = channel(parts[2]);
7331
- let a = 255;
7332
- if (parts.length > 3) {
7333
- a = parts[3].endsWith('%')
7334
- ? Math.round((parseFloat(parts[3]) / 100) * 255)
7335
- : Math.round(parseFloat(parts[3]) * 255);
7374
+ /**
7375
+ * Build a valid Aztec high-level bitstream.
7376
+ *
7377
+ * @param {string|ArrayBuffer|ArrayBufferView} value
7378
+ * @param {{charset?: 'utf-8'}} [options]
7379
+ * @returns {BitWriter}
7380
+ */
7381
+ function encodeHighLevel(value, options = {}) {
7382
+ const bytes = aztecBytes(value, options.charset ?? 'utf-8');
7383
+ const writer = new BitWriter();
7384
+ let mode = HIGH_LEVEL_MODE.UPPER;
7385
+
7386
+ for (let at = 0; at < bytes.length;) {
7387
+ let bestMode = -1;
7388
+ let bestValue = -1;
7389
+ let bestCost = Number.POSITIVE_INFINITY;
7390
+ for (const candidate of [HIGH_LEVEL_MODE.UPPER, HIGH_LEVEL_MODE.LOWER, HIGH_LEVEL_MODE.DIGIT, HIGH_LEVEL_MODE.PUNCT]) {
7391
+ const value = textValue(bytes[at], candidate);
7392
+ if (value < 0) continue;
7393
+ const latchCost = candidate === mode ? 0 : LATCH[mode][candidate] >>> 16;
7394
+ const cost = latchCost + characterWidth(candidate);
7395
+ if (cost < bestCost) { bestCost = cost; bestMode = candidate; bestValue = value; }
7396
+ }
7397
+ if (bestMode >= 0) {
7398
+ latch(writer, mode, bestMode);
7399
+ writer.put(bestValue, characterWidth(bestMode));
7400
+ mode = bestMode;
7401
+ at++;
7402
+ } else {
7403
+ // B/S is defined from UPPER; the latch is retained after the shift.
7404
+ latch(writer, mode, HIGH_LEVEL_MODE.UPPER);
7405
+ mode = HIGH_LEVEL_MODE.UPPER;
7406
+ const start = at;
7407
+ while (at < bytes.length && ![HIGH_LEVEL_MODE.UPPER, HIGH_LEVEL_MODE.LOWER, HIGH_LEVEL_MODE.DIGIT, HIGH_LEVEL_MODE.PUNCT].some((m) => textValue(bytes[at], m) >= 0)) at++;
7408
+ writeBinaryShift(writer, bytes, start, at - start);
7336
7409
  }
7337
- return [r, g, b, a];
7338
7410
  }
7339
-
7340
- // Unrecognised: fall back to opaque black rather than throwing, so an
7341
- // unusual colour never costs someone a barcode.
7342
- return [0, 0, 0, 255];
7411
+ return writer;
7343
7412
  }
7344
7413
 
7345
- __exports.normalizeOptions = normalizeOptions;
7346
- __exports.parseColor = parseColor;
7414
+ __exports.HIGH_LEVEL_MODE = HIGH_LEVEL_MODE;
7415
+ __exports.MAX_BINARY_SHIFT = MAX_BINARY_SHIFT;
7416
+ __exports.aztecBytes = aztecBytes;
7417
+ __exports.writeBinaryShift = writeBinaryShift;
7418
+ __exports.encodeHighLevel = encodeHighLevel;
7347
7419
  };
7348
7420
 
7349
- __modules["render/svg.js"] = function (__require, __exports) {
7421
+ __modules["aztec/tables.js"] = function (__require, __exports) {
7350
7422
  /**
7351
- * SVG output.
7423
+ * Aztec Code layer geometry and Reed-Solomon parameters.
7352
7424
  *
7353
- * Dark modules are emitted as a single `<path>` with horizontal runs merged,
7354
- * not as one `<rect>` per module. A version 40 QR symbol has 31329 modules; the
7355
- * naive rendering is a megabyte of XML that browsers choke on, while the merged
7356
- * path is a few kilobytes and draws identically.
7425
+ * `totalBits` counts the payload ring before its leading pad bits are added;
7426
+ * consequently only `usableBits` can be partitioned into codewords. Compact
7427
+ * symbols have no reference grid. Full symbols insert alternating reference
7428
+ * rows and columns every 16 modules around the centre.
7357
7429
  *
7358
- * @module render/svg
7430
+ * The five data fields use generator base 1. GF(256)/DataMatrix is also the
7431
+ * Aztec 8-bit field: both use primitive polynomial 0x12d.
7432
+ *
7433
+ * @module aztec/tables
7359
7434
  */
7360
- const { normalizeOptions } = __require("render/options.js");
7435
+ const { GF16, GF64, GF256_AZTEC, GF1024, GF4096 } = __require("core/galois-field.js");
7436
+
7437
+ /** Reed-Solomon generator base defined for Aztec parameter and data fields. */
7438
+ const AZTEC_RS_GENERATOR_BASE = 1;
7439
+
7440
+ /** Minimum recommended error correction: 23 percent plus three codewords. */
7441
+ const AZTEC_DEFAULT_ECC_PERCENT = 23;
7442
+ const AZTEC_MIN_ECC_WORDS = 3;
7443
+
7444
+ /** Word size selected solely by the number of layers. */
7445
+ function wordSizeForLayers(layers) {
7446
+ if (!Number.isInteger(layers) || layers < 1 || layers > 32) {
7447
+ throw new RangeError(`Aztec: layers must be an integer from 1 to 32 (got ${layers})`);
7448
+ }
7449
+ if (layers <= 2) return 6;
7450
+ if (layers <= 8) return 8;
7451
+ if (layers <= 22) return 10;
7452
+ return 12;
7453
+ }
7454
+
7455
+ /** Return the field used by Aztec codewords of `wordSize` bits. */
7456
+ function fieldForWordSize(wordSize) {
7457
+ switch (wordSize) {
7458
+ case 4: return GF16; // Mode message only.
7459
+ case 6: return GF64;
7460
+ case 8: return GF256_AZTEC;
7461
+ case 10: return GF1024;
7462
+ case 12: return GF4096;
7463
+ default: throw new RangeError(`Aztec: unsupported codeword size ${wordSize}`);
7464
+ }
7465
+ }
7466
+
7467
+ /** Return the data field selected for a symbol with `layers` layers. */
7468
+ function fieldForLayers(layers) {
7469
+ return fieldForWordSize(wordSizeForLayers(layers));
7470
+ }
7471
+
7472
+ /** Matrix side length, including Full-mode reference grid lines. */
7473
+ function aztecSymbolSize(layers, compact = false) {
7474
+ if (!Number.isInteger(layers) || layers < 1 || layers > (compact ? 4 : 32)) {
7475
+ throw new RangeError(`Aztec: ${compact ? 'Compact' : 'Full'} layers out of range: ${layers}`);
7476
+ }
7477
+ if (compact) return 11 + 4 * layers;
7478
+ const baseMatrixSize = 14 + 4 * layers;
7479
+ return baseMatrixSize + 1 + 2 * Math.floor((baseMatrixSize / 2 - 1) / 15);
7480
+ }
7481
+
7482
+ function layer(layers, compact) {
7483
+ const wordSize = wordSizeForLayers(layers);
7484
+ const totalBits = ((compact ? 88 : 112) + 16 * layers) * layers;
7485
+ const usableBits = totalBits - totalBits % wordSize;
7486
+ const totalCodewords = usableBits / wordSize;
7487
+ const baseMatrixSize = (compact ? 11 : 14) + 4 * layers;
7488
+ return Object.freeze({
7489
+ compact,
7490
+ layers,
7491
+ wordSize,
7492
+ totalBits,
7493
+ usableBits,
7494
+ totalCodewords,
7495
+ // Compact mode encodes the count in six bits and can therefore hold no
7496
+ // more than 64 data codewords even where the ring itself is larger.
7497
+ maxDataCodewords: compact ? Math.min(totalCodewords, 64) : totalCodewords,
7498
+ baseMatrixSize,
7499
+ symbolSize: aztecSymbolSize(layers, compact),
7500
+ modeMessageDataWords: compact ? 2 : 4,
7501
+ modeMessageWords: compact ? 7 : 10,
7502
+ modeMessageBits: compact ? 28 : 40,
7503
+ rsGeneratorBase: AZTEC_RS_GENERATOR_BASE,
7504
+ });
7505
+ }
7506
+
7507
+ /** Compact Aztec layers 1 through 4, in encoding preference order. */
7508
+ const AZTEC_COMPACT_LAYERS = Object.freeze(
7509
+ Array.from({ length: 4 }, (_, i) => layer(i + 1, true)),
7510
+ );
7511
+
7512
+ /** Full Aztec layers 1 through 32, in ascending layer order. */
7513
+ const AZTEC_FULL_LAYERS = Object.freeze(
7514
+ Array.from({ length: 32 }, (_, i) => layer(i + 1, false)),
7515
+ );
7516
+
7517
+ /** All allowed symbols. Compact entries precede Full entries for automatic selection. */
7518
+ const AZTEC_LAYERS = Object.freeze([
7519
+ ...AZTEC_COMPACT_LAYERS,
7520
+ ...AZTEC_FULL_LAYERS,
7521
+ ]);
7522
+
7523
+ /** Return one immutable layer record. */
7524
+ function aztecLayer(layers, compact = false) {
7525
+ if (!Number.isInteger(layers) || layers < 1 || layers > (compact ? 4 : 32)) {
7526
+ throw new RangeError(`Aztec: ${compact ? 'Compact' : 'Full'} layers out of range: ${layers}`);
7527
+ }
7528
+ return (compact ? AZTEC_COMPACT_LAYERS : AZTEC_FULL_LAYERS)[layers - 1];
7529
+ }
7361
7530
 
7362
7531
  /**
7363
- * @param {string} value
7364
- * @returns {string}
7532
+ * Calculate the minimum parity count for a data word count.
7533
+ *
7534
+ * The percentage is rounded up because a fractional codeword cannot be
7535
+ * emitted. The mandatory three words protect short payloads, where a bare
7536
+ * percentage would otherwise round to zero.
7365
7537
  */
7366
- function escapeAttr(value) {
7367
- return String(value)
7368
- .replace(/&/g, '&amp;')
7369
- .replace(/</g, '&lt;')
7370
- .replace(/>/g, '&gt;')
7371
- .replace(/"/g, '&quot;');
7538
+ function eccCodewordsFor(dataCodewords, eccPercent = AZTEC_DEFAULT_ECC_PERCENT) {
7539
+ if (!Number.isInteger(dataCodewords) || dataCodewords < 0) {
7540
+ throw new RangeError(`Aztec: data codewords must be a non-negative integer (got ${dataCodewords})`);
7541
+ }
7542
+ if (!Number.isFinite(eccPercent) || eccPercent < 0 || eccPercent > 100) {
7543
+ throw new RangeError(`Aztec: ECC percent must be between 0 and 100 (got ${eccPercent})`);
7544
+ }
7545
+ return Math.ceil(dataCodewords * eccPercent / 100) + AZTEC_MIN_ECC_WORDS;
7372
7546
  }
7373
7547
 
7374
7548
  /**
7375
- * Render to an SVG document.
7549
+ * Choose the first symbol which holds an already stuffed payload.
7376
7550
  *
7377
- * @param {import('../core/bit-matrix.js').BitMatrix} matrix
7378
- * @param {import('./options.js').RenderOptions} [options]
7379
- * @returns {string}
7551
+ * `dataBits` must be a multiple of the candidate word size; callers which
7552
+ * start from high-level bits must stuff separately per candidate word size.
7380
7553
  */
7381
- function toSVG(matrix, options = {}) {
7382
- const opts = normalizeOptions(matrix, options);
7383
- const { scale, source, pixelWidth, pixelHeight, rowHeight } = opts;
7554
+ function selectAztecLayer(dataBits, {
7555
+ eccPercent = AZTEC_DEFAULT_ECC_PERCENT,
7556
+ layers = null,
7557
+ compact = null,
7558
+ } = {}) {
7559
+ if (!Number.isInteger(dataBits) || dataBits < 0) {
7560
+ throw new RangeError(`Aztec: data bits must be a non-negative integer (got ${dataBits})`);
7561
+ }
7562
+ if (compact !== null && typeof compact !== 'boolean') {
7563
+ throw new TypeError('Aztec: compact must be true, false or null');
7564
+ }
7384
7565
 
7385
- let path = '';
7386
- for (let y = 0; y < source.height; y++) {
7387
- let x = 0;
7388
- while (x < source.width) {
7389
- if (!source.get(x, y)) { x++; continue; }
7390
- let run = 1;
7391
- while (x + run < source.width && source.get(x + run, y)) run++;
7392
- // Relative horizontal-vertical path commands: shorter than rects and
7393
- // free of the seams that appear between adjacent rects at some zooms.
7394
- path += `M${x * scale} ${y * rowHeight}h${run * scale}v${rowHeight}h${-run * scale}z`;
7395
- x += run;
7566
+ let candidates;
7567
+ if (layers !== null) {
7568
+ if (compact === null) throw new TypeError('Aztec: compact must be specified when layers is specified');
7569
+ candidates = [aztecLayer(layers, compact)];
7570
+ } else if (compact === null) {
7571
+ candidates = AZTEC_LAYERS;
7572
+ } else {
7573
+ candidates = compact ? AZTEC_COMPACT_LAYERS : AZTEC_FULL_LAYERS;
7574
+ }
7575
+
7576
+ for (const candidate of candidates) {
7577
+ if (dataBits % candidate.wordSize !== 0) continue;
7578
+ const dataCodewords = dataBits / candidate.wordSize;
7579
+ const eccCodewords = eccCodewordsFor(dataCodewords, eccPercent);
7580
+ if (dataCodewords <= candidate.maxDataCodewords &&
7581
+ dataCodewords + eccCodewords <= candidate.totalCodewords) {
7582
+ return Object.freeze({ ...candidate, dataCodewords, eccCodewords });
7396
7583
  }
7397
7584
  }
7398
7585
 
7399
- const bg = opts.light === 'none'
7400
- ? ''
7401
- : `<rect width="${pixelWidth}" height="${pixelHeight}" fill="${escapeAttr(opts.light)}"/>`;
7586
+ throw new RangeError('Aztec: payload and requested error correction do not fit an available symbol');
7587
+ }
7402
7588
 
7403
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${pixelWidth}" height="${pixelHeight}" ` +
7404
- `viewBox="0 0 ${pixelWidth} ${pixelHeight}" shape-rendering="crispEdges">` +
7405
- bg +
7406
- `<path d="${path}" fill="${escapeAttr(opts.dark)}"/>` +
7407
- '</svg>';
7589
+ /** Check static identities so table corruption fails explicitly in tests. */
7590
+ function validateAztecTables() {
7591
+ const issues = [];
7592
+ for (const entry of AZTEC_LAYERS) {
7593
+ if (entry.usableBits % entry.wordSize !== 0) issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: unaligned usable bits`);
7594
+ if (entry.totalCodewords !== entry.usableBits / entry.wordSize) issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: codeword mismatch`);
7595
+ if (entry.symbolSize !== aztecSymbolSize(entry.layers, entry.compact)) issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: matrix size mismatch`);
7596
+ if (entry.rsGeneratorBase !== AZTEC_RS_GENERATOR_BASE) issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: generator base mismatch`);
7597
+ if (entry.compact && entry.maxDataCodewords > 64) issues.push(`C${entry.layers}: Compact data-word limit exceeded`);
7598
+ }
7599
+ return issues;
7408
7600
  }
7409
7601
 
7602
+ __exports.AZTEC_RS_GENERATOR_BASE = AZTEC_RS_GENERATOR_BASE;
7603
+ __exports.AZTEC_DEFAULT_ECC_PERCENT = AZTEC_DEFAULT_ECC_PERCENT;
7604
+ __exports.AZTEC_MIN_ECC_WORDS = AZTEC_MIN_ECC_WORDS;
7605
+ __exports.wordSizeForLayers = wordSizeForLayers;
7606
+ __exports.fieldForWordSize = fieldForWordSize;
7607
+ __exports.fieldForLayers = fieldForLayers;
7608
+ __exports.aztecSymbolSize = aztecSymbolSize;
7609
+ __exports.AZTEC_COMPACT_LAYERS = AZTEC_COMPACT_LAYERS;
7610
+ __exports.AZTEC_FULL_LAYERS = AZTEC_FULL_LAYERS;
7611
+ __exports.AZTEC_LAYERS = AZTEC_LAYERS;
7612
+ __exports.aztecLayer = aztecLayer;
7613
+ __exports.eccCodewordsFor = eccCodewordsFor;
7614
+ __exports.selectAztecLayer = selectAztecLayer;
7615
+ __exports.validateAztecTables = validateAztecTables;
7616
+ };
7617
+
7618
+ __modules["aztec/encoder.js"] = function (__require, __exports) {
7410
7619
  /**
7411
- * Base64 that works identically in Node and the browser.
7620
+ * Aztec encoder: high-level bits, bit stuffing, Reed-Solomon and matrix layout.
7412
7621
  *
7413
- * `btoa` is byte-oriented, so the UTF-8 encoding has to happen first — passing
7414
- * it a string with any character above U+00FF throws.
7622
+ * `tables.js` is intentionally the source of geometry and field selection.
7623
+ * Its `aztecLayer(layers, compact)` entries must expose `totalBits`,
7624
+ * `totalCodewords`, `baseMatrixSize` and `symbolSize`; `fieldForLayers()` must
7625
+ * return the matching binary field. All Aztec Reed-Solomon generators start
7626
+ * at alpha^1, hence the explicit base `1` in both data and mode messages.
7415
7627
  *
7416
- * @param {string} text
7417
- * @returns {string}
7628
+ * @module aztec/encoder
7418
7629
  */
7419
- function toBase64(text) {
7420
- const bytes = new TextEncoder().encode(text);
7421
- let binary = '';
7422
- for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
7423
- if (typeof btoa === 'function') return btoa(binary);
7424
- // Node before btoa was global, and non-browser embedders.
7425
- /* eslint-disable-next-line no-undef */
7630
+ const { BitWriter } = __require("core/bit-buffer.js");
7631
+ const { BitMatrix } = __require("core/bit-matrix.js");
7632
+ const { EncodeError } = __require("core/errors.js");
7633
+ const { rsEncode } = __require("core/reed-solomon.js");
7634
+ const { encodeHighLevel } = __require("aztec/high-level.js");
7635
+ const { AZTEC_COMPACT_LAYERS, AZTEC_FULL_LAYERS, aztecLayer, eccCodewordsFor, fieldForLayers, fieldForWordSize, wordSizeForLayers } = __require("aztec/tables.js");
7636
+
7637
+ /** @param {BitWriter} bits @param {number} at @returns {boolean} */
7638
+ function bitAt(bits, at) {
7639
+ return at >= 0 && at < bits.length && ((bits.bytes[at >>> 3] >>> (7 - (at & 7))) & 1) !== 0;
7640
+ }
7641
+
7642
+ /** @param {BitWriter} bits @param {number} from @param {number} count @returns {number} */
7643
+ function readBits(bits, from, count) {
7644
+ let value = 0;
7645
+ for (let i = 0; i < count; i++) value = (value << 1) | (bitAt(bits, from + i) ? 1 : 0);
7646
+ return value;
7647
+ }
7648
+
7649
+ /**
7650
+ * Prevent all-zero and all-one codewords except their final bit. The final
7651
+ * bit is intentionally re-consumed after a stuffed word; it is the mechanism
7652
+ * that makes the transform injective and reversible.
7653
+ *
7654
+ * @param {BitWriter} bits @param {number} wordSize @returns {BitWriter}
7655
+ */
7656
+ function stuffBits(bits, wordSize) {
7657
+ const out = new BitWriter();
7658
+ const reserved = (1 << wordSize) - 2;
7659
+ for (let at = 0; at < bits.length; at += wordSize) {
7660
+ const word = readBits(bits, at, wordSize);
7661
+ if ((word & reserved) === reserved) {
7662
+ out.put(word & reserved, wordSize);
7663
+ at--;
7664
+ } else if ((word & reserved) === 0) {
7665
+ out.put(word | 1, wordSize);
7666
+ at--;
7667
+ } else {
7668
+ out.put(word, wordSize);
7669
+ }
7670
+ }
7671
+ return out;
7672
+ }
7673
+
7674
+ /**
7675
+ * Add systematic Aztec Reed-Solomon parity and the leading alignment bits.
7676
+ * @param {BitWriter} data @param {number} totalBits @param {number} wordSize
7677
+ * @param {import('../core/galois-field.js').GaloisField} field
7678
+ * @returns {{bits: BitWriter, dataWords: number, eccWords: number}}
7679
+ */
7680
+ function addCheckWords(data, totalBits, wordSize, field) {
7681
+ const totalWords = Math.floor(totalBits / wordSize);
7682
+ const dataWords = Math.ceil(data.length / wordSize);
7683
+ if (dataWords > totalWords) throw new EncodeError('Aztec: data codewords exceed layer capacity');
7684
+ const eccWords = totalWords - dataWords;
7685
+ const words = new Array(dataWords);
7686
+ for (let i = 0; i < dataWords; i++) words[i] = readBits(data, i * wordSize, wordSize);
7687
+ const ecc = rsEncode(words, eccWords, field, 1);
7688
+ const out = new BitWriter();
7689
+ out.put(0, totalBits % wordSize);
7690
+ for (const word of words) out.put(word, wordSize);
7691
+ for (const word of ecc) out.put(word, wordSize);
7692
+ return { bits: out, dataWords, eccWords };
7693
+ }
7694
+
7695
+ /** @param {number} layers @param {number} dataWords @param {boolean} compact @returns {BitWriter} */
7696
+ function modeMessage(layers, dataWords, compact) {
7697
+ const raw = new BitWriter();
7698
+ if (compact) {
7699
+ raw.put(layers - 1, 2);
7700
+ raw.put(dataWords - 1, 6);
7701
+ return addCheckWords(raw, 28, 4, fieldForWordSize(4)).bits;
7702
+ }
7703
+ raw.put(layers - 1, 5);
7704
+ raw.put(dataWords - 1, 11);
7705
+ return addCheckWords(raw, 40, 4, fieldForWordSize(4)).bits;
7706
+ }
7707
+
7708
+ /** @param {BitMatrix} matrix @param {number} center @param {number} size */
7709
+ function drawBullsEye(matrix, center, size) {
7710
+ for (let ring = 0; ring < size; ring += 2) {
7711
+ for (let p = center - ring; p <= center + ring; p++) {
7712
+ matrix.set(p, center - ring); matrix.set(p, center + ring);
7713
+ matrix.set(center - ring, p); matrix.set(center + ring, p);
7714
+ }
7715
+ }
7716
+ matrix.set(center - size, center - size);
7717
+ matrix.set(center - size + 1, center - size);
7718
+ matrix.set(center - size, center - size + 1);
7719
+ matrix.set(center + size, center - size);
7720
+ matrix.set(center + size, center - size + 1);
7721
+ matrix.set(center + size, center + size - 1);
7722
+ }
7723
+
7724
+ /** @param {BitMatrix} matrix @param {BitWriter} message @param {boolean} compact @param {number} center */
7725
+ function drawModeMessage(matrix, message, compact, center) {
7726
+ if (compact) {
7727
+ for (let i = 0; i < 7; i++) {
7728
+ const offset = center - 3 + i;
7729
+ if (bitAt(message, i)) matrix.set(offset, center - 5);
7730
+ if (bitAt(message, i + 7)) matrix.set(center + 5, offset);
7731
+ if (bitAt(message, 20 - i)) matrix.set(offset, center + 5);
7732
+ if (bitAt(message, 27 - i)) matrix.set(center - 5, offset);
7733
+ }
7734
+ } else {
7735
+ for (let i = 0; i < 10; i++) {
7736
+ const offset = center - 5 + i + Math.floor(i / 5);
7737
+ if (bitAt(message, i)) matrix.set(offset, center - 7);
7738
+ if (bitAt(message, i + 10)) matrix.set(center + 7, offset);
7739
+ if (bitAt(message, 29 - i)) matrix.set(offset, center + 7);
7740
+ if (bitAt(message, 39 - i)) matrix.set(center - 7, offset);
7741
+ }
7742
+ }
7743
+ }
7744
+
7745
+ /**
7746
+ * Lay low-level bits in the four-sided, inward Aztec spiral.
7747
+ * @param {BitWriter} bits @param {{layers:number,compact:boolean,baseMatrixSize:number,symbolSize:number}} symbol
7748
+ * @returns {BitMatrix}
7749
+ */
7750
+ function buildAztecMatrix(bits, symbol) {
7751
+ const { layers, compact, baseMatrixSize, symbolSize } = symbol;
7752
+ const matrix = new BitMatrix(symbolSize);
7753
+ const alignment = new Int32Array(baseMatrixSize);
7754
+ const center = Math.floor(symbolSize / 2);
7755
+
7756
+ if (compact) {
7757
+ for (let i = 0; i < baseMatrixSize; i++) alignment[i] = i;
7758
+ } else {
7759
+ const originalCenter = Math.floor(baseMatrixSize / 2);
7760
+ for (let i = 0; i < originalCenter; i++) {
7761
+ const offset = i + Math.floor(i / 15);
7762
+ alignment[originalCenter - i - 1] = center - offset - 1;
7763
+ alignment[originalCenter + i] = center + offset + 1;
7764
+ }
7765
+ }
7766
+
7767
+ let bit = 0;
7768
+ for (let layer = 0; layer < layers; layer++) {
7769
+ const rowSize = (layers - layer) * 4 + (compact ? 9 : 12);
7770
+ const low = layer * 2;
7771
+ const high = baseMatrixSize - 1 - low;
7772
+ for (let j = 0; j < rowSize; j++) {
7773
+ const offset = j * 2;
7774
+ for (let k = 0; k < 2; k++) {
7775
+ if (bitAt(bits, bit + offset + k)) matrix.set(alignment[low + k], alignment[low + j]);
7776
+ if (bitAt(bits, bit + rowSize * 2 + offset + k)) matrix.set(alignment[low + j], alignment[high - k]);
7777
+ if (bitAt(bits, bit + rowSize * 4 + offset + k)) matrix.set(alignment[high - k], alignment[high - j]);
7778
+ if (bitAt(bits, bit + rowSize * 6 + offset + k)) matrix.set(alignment[high - j], alignment[low + k]);
7779
+ }
7780
+ }
7781
+ bit += rowSize * 8;
7782
+ }
7783
+ if (bit !== bits.length) throw new EncodeError(`Aztec: layout consumed ${bit} of ${bits.length} bits`);
7784
+
7785
+ const mode = modeMessage(layers, symbol.dataWords, compact);
7786
+ drawModeMessage(matrix, mode, compact, center);
7787
+ drawBullsEye(matrix, center, compact ? 5 : 7);
7788
+
7789
+ if (!compact) {
7790
+ for (let i = 0, offset = 0; i < Math.floor(baseMatrixSize / 2) - 1; i += 15, offset += 16) {
7791
+ for (let p = center & 1; p < symbolSize; p += 2) {
7792
+ matrix.set(center - offset, p); matrix.set(center + offset, p);
7793
+ matrix.set(p, center - offset); matrix.set(p, center + offset);
7794
+ }
7795
+ }
7796
+ }
7797
+ return matrix;
7798
+ }
7799
+
7800
+ /** @param {number | undefined} layers @param {boolean | undefined} compact */
7801
+ function candidates(layers, compact) {
7802
+ if (layers !== undefined) {
7803
+ if (!Number.isInteger(layers) || layers < 1 || layers > 32) throw new EncodeError('Aztec: layers must be an integer 1..32');
7804
+ if (compact === true && layers > 4) throw new EncodeError('Aztec: compact symbols support layers 1..4');
7805
+ return [aztecLayer(layers, compact === true)];
7806
+ }
7807
+ if (compact === true) return AZTEC_COMPACT_LAYERS;
7808
+ if (compact === false) return AZTEC_FULL_LAYERS;
7809
+ return [...AZTEC_COMPACT_LAYERS, ...AZTEC_FULL_LAYERS];
7810
+ }
7811
+
7812
+ /**
7813
+ * Encode a UTF-8 string or bytes into an Aztec Code matrix.
7814
+ *
7815
+ * @param {string|ArrayBuffer|ArrayBufferView} value
7816
+ * @param {{layers?:number,compact?:boolean,eccPercent?:number,charset?:'utf-8'}} [options]
7817
+ * @returns {BitMatrix & {format?:string,layers?:number,compact?:boolean,eccPercent?:number,dataCodewords?:number}}
7818
+ */
7819
+ function encodeAztec(value, options = {}) {
7820
+ const eccPercent = options.eccPercent ?? 23;
7821
+ if (!Number.isFinite(eccPercent) || eccPercent < 5 || eccPercent > 95) {
7822
+ throw new EncodeError('Aztec: eccPercent must be between 5 and 95');
7823
+ }
7824
+ const high = encodeHighLevel(value, { charset: options.charset ?? 'utf-8' });
7825
+ for (const candidate of candidates(options.layers, options.compact)) {
7826
+ if (!candidate) continue;
7827
+ const wordSize = wordSizeForLayers(candidate.layers);
7828
+ const stuffed = stuffBits(high, wordSize);
7829
+ const dataWords = Math.ceil(stuffed.length / wordSize);
7830
+ const eccWords = eccCodewordsFor(dataWords, eccPercent);
7831
+ if (dataWords > candidate.maxDataCodewords || dataWords + eccWords > candidate.totalCodewords) continue;
7832
+ const checked = addCheckWords(stuffed, candidate.totalBits, wordSize, fieldForLayers(candidate.layers));
7833
+ // `addCheckWords` uses every remaining word as parity. This is stronger
7834
+ // than the requested percentage, never weaker, and canonical for a chosen
7835
+ // layer/data-word combination.
7836
+ const symbol = { ...candidate, dataWords: checked.dataWords };
7837
+ const matrix = buildAztecMatrix(checked.bits, symbol);
7838
+ matrix.format = 'aztec'; matrix.layers = candidate.layers; matrix.compact = candidate.compact;
7839
+ matrix.eccPercent = Math.round(checked.eccWords * wordSize * 100 / Math.max(1, stuffed.length));
7840
+ matrix.dataCodewords = checked.dataWords;
7841
+ return matrix;
7842
+ }
7843
+ throw new EncodeError('Aztec: payload does not fit the requested layers and error correction');
7844
+ }
7845
+
7846
+ __exports.stuffBits = stuffBits;
7847
+ __exports.addCheckWords = addCheckWords;
7848
+ __exports.modeMessage = modeMessage;
7849
+ __exports.buildAztecMatrix = buildAztecMatrix;
7850
+ __exports.encodeAztec = encodeAztec;
7851
+ };
7852
+
7853
+ __modules["aztec/decoder.js"] = function (__require, __exports) {
7854
+ /**
7855
+ * Decoder for a sampled Aztec symbol.
7856
+ *
7857
+ * This module deliberately accepts only a square, module-aligned BitMatrix.
7858
+ * Locating a bull's-eye in a photograph and perspective sampling are detector
7859
+ * concerns. Keeping the two stages apart makes all bit order and ECC rules
7860
+ * testable without image-processing noise.
7861
+ *
7862
+ * Contract with tables.js:
7863
+ * - aztecSymbolForLayers(compact, layers) returns the nominal symbol data;
7864
+ * - aztecWordSizeForLayers(layers) returns 6, 8, 10 or 12;
7865
+ * - aztecFieldForLayers(layers) returns the matching binary Galois field;
7866
+ * - aztecMatrixSize(compact, layers) returns the rendered square size.
7867
+ *
7868
+ * @module aztec/decoder
7869
+ */
7870
+ const { FormatError } = __require("core/errors.js");
7871
+ const { rsDecode } = __require("core/reed-solomon.js");
7872
+ const { aztecLayer: aztecSymbolForLayers, wordSizeForLayers: aztecWordSizeForLayers, fieldForLayers: aztecFieldForLayers, fieldForWordSize, aztecSymbolSize: aztecMatrixSize } = __require("aztec/tables.js");
7873
+
7874
+ const UPPER = ['CTRL_PS', ' ', ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'CTRL_LL', 'CTRL_ML', 'CTRL_DL', 'CTRL_BS'];
7875
+ const LOWER = ['CTRL_PS', ' ', ...'abcdefghijklmnopqrstuvwxyz', 'CTRL_US', 'CTRL_ML', 'CTRL_DL', 'CTRL_BS'];
7876
+ const MIXED = [
7877
+ 'CTRL_PS', ' ', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\b', '\t', '\n', '\x0b', '\f', '\r', '\x1b',
7878
+ '\x1c', '\x1d', '\x1e', '\x1f', '@', '\\', '^', '_', '`', '|', '~', '\x7f', 'CTRL_LL', 'CTRL_UL', 'CTRL_PL', 'CTRL_BS',
7879
+ ];
7880
+ const PUNCT = ['FLG(n)', '\r', '\r\n', '. ', ', ', ': ', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', ']', '{', '}', 'CTRL_UL'];
7881
+ const DIGIT = ['CTRL_PS', ' ', ...'0123456789', ',', '.', 'CTRL_UL'];
7882
+ const TABLES = { UPPER, LOWER, MIXED, PUNCT, DIGIT };
7883
+
7884
+ /** @param {boolean[]} bits @param {number} offset @param {number} count */
7885
+ function readBits(bits, offset, count) {
7886
+ if (offset + count > bits.length) throw new FormatError('Aztec: truncated high-level stream');
7887
+ let value = 0;
7888
+ for (let i = 0; i < count; i++) value = (value << 1) | (bits[offset + i] ? 1 : 0);
7889
+ return value;
7890
+ }
7891
+
7892
+ /** @param {number} value @param {number} count @param {boolean[]} out */
7893
+ function appendBits(value, count, out) {
7894
+ for (let i = count - 1; i >= 0; i--) out.push(((value >>> i) & 1) !== 0);
7895
+ }
7896
+
7897
+ /**
7898
+ * Decode an Aztec high-level bit stream to its exact byte payload.
7899
+ *
7900
+ * Text tables contribute their ISO-8859-1 byte values; Binary Shift appends
7901
+ * raw bytes. ECI markers are consumed but intentionally not emitted: callers
7902
+ * receive the transported byte payload and may select their own charset.
7903
+ *
7904
+ * @param {boolean[]} bits
7905
+ * @returns {Uint8Array}
7906
+ */
7907
+ function decodeHighLevelBits(bits) {
7908
+ const output = [];
7909
+ let latch = 'UPPER';
7910
+ let shift = 'UPPER';
7911
+ let offset = 0;
7912
+
7913
+ while (offset < bits.length) {
7914
+ if (shift === 'BINARY') {
7915
+ if (offset + 5 > bits.length) break; // legal trailing pad
7916
+ let length = readBits(bits, offset, 5);
7917
+ offset += 5;
7918
+ if (length === 0) {
7919
+ if (offset + 11 > bits.length) throw new FormatError('Aztec: truncated Binary Shift length');
7920
+ length = readBits(bits, offset, 11) + 31;
7921
+ offset += 11;
7922
+ }
7923
+ if (offset + length * 8 > bits.length) throw new FormatError('Aztec: truncated Binary Shift data');
7924
+ for (let i = 0; i < length; i++) {
7925
+ output.push(readBits(bits, offset, 8));
7926
+ offset += 8;
7927
+ }
7928
+ shift = latch;
7929
+ continue;
7930
+ }
7931
+
7932
+ const size = shift === 'DIGIT' ? 4 : 5;
7933
+ if (offset + size > bits.length) break; // trailing pad after unstuffing
7934
+ const code = readBits(bits, offset, size);
7935
+ offset += size;
7936
+ const table = TABLES[shift];
7937
+ const token = table[code];
7938
+ if (token === undefined) throw new FormatError(`Aztec: invalid ${shift} code ${code}`);
7939
+
7940
+ if (token === 'FLG(n)') {
7941
+ if (offset + 3 > bits.length) throw new FormatError('Aztec: truncated FLG(n)');
7942
+ const count = readBits(bits, offset, 3);
7943
+ offset += 3;
7944
+ if (count === 0) output.push(0x1d); // FNC1 / GS
7945
+ else if (count <= 6) {
7946
+ // ECI assignment number, encoded as count decimal digits. It changes
7947
+ // interpretation, not the wire bytes, so consume it without output.
7948
+ for (let i = 0; i < count; i++) {
7949
+ if (offset + 4 > bits.length) throw new FormatError('Aztec: truncated ECI');
7950
+ const digit = readBits(bits, offset, 4);
7951
+ offset += 4;
7952
+ if (digit < 2 || digit > 11) throw new FormatError('Aztec: invalid ECI digit');
7953
+ }
7954
+ } else {
7955
+ throw new FormatError(`Aztec: unsupported FLG(${count})`);
7956
+ }
7957
+ shift = latch;
7958
+ continue;
7959
+ }
7960
+
7961
+ if (token.startsWith('CTRL_')) {
7962
+ const targetCode = token.slice(5, -1);
7963
+ const latchMode = token.endsWith('L');
7964
+ const target = ({ P: 'PUNCT', L: 'LOWER', M: 'MIXED', D: 'DIGIT', U: 'UPPER', B: 'BINARY' })[targetCode];
7965
+ if (!target) throw new FormatError(`Aztec: invalid control ${token}`);
7966
+ shift = target;
7967
+ if (latchMode) latch = shift;
7968
+ continue;
7969
+ }
7970
+
7971
+ for (let i = 0; i < token.length; i++) output.push(token.charCodeAt(i));
7972
+ shift = latch;
7973
+ }
7974
+ return Uint8Array.from(output);
7975
+ }
7976
+
7977
+ /** @param {boolean} compact @param {number} layers */
7978
+ function alignmentMap(compact, layers) {
7979
+ const baseSize = (compact ? 11 : 14) + layers * 4;
7980
+ if (compact) return Array.from({ length: baseSize }, (_, i) => i);
7981
+ const size = aztecMatrixSize(layers, false);
7982
+ const map = new Array(baseSize);
7983
+ const baseCenter = baseSize >> 1;
7984
+ const center = size >> 1;
7985
+ for (let i = 0; i < baseCenter; i++) {
7986
+ const offset = i + Math.floor(i / 15);
7987
+ map[baseCenter - i - 1] = center - offset - 1;
7988
+ map[baseCenter + i] = center + offset + 1;
7989
+ }
7990
+ return map;
7991
+ }
7992
+
7993
+ /**
7994
+ * Read the four sides of the parameter message. The order mirrors the
7995
+ * clockwise write order and is independent of the data spiral.
7996
+ *
7997
+ * @param {import('../core/bit-matrix.js').BitMatrix} matrix
7998
+ * @param {boolean} compact
7999
+ * @returns {boolean[]}
8000
+ */
8001
+ function readModeBits(matrix, compact) {
8002
+ const center = matrix.width >> 1;
8003
+ const side = compact ? 7 : 10;
8004
+ const offset = compact ? 5 : 7;
8005
+ // Full symbols skip the reference grid line through the bull's-eye. This
8006
+ // exact sequence is also used by drawModeMessage() in encoder.js.
8007
+ const positions = Array.from(
8008
+ { length: side },
8009
+ (_, i) => compact ? center - 3 + i : center - 5 + i + Math.floor(i / 5),
8010
+ );
8011
+ const bits = [];
8012
+ for (let i = 0; i < side; i++) bits.push(matrix.get(positions[i], center - offset));
8013
+ for (let i = 0; i < side; i++) bits.push(matrix.get(center + offset, positions[i]));
8014
+ for (let i = 0; i < side; i++) bits.push(matrix.get(positions[side - 1 - i], center + offset));
8015
+ for (let i = 0; i < side; i++) bits.push(matrix.get(center - offset, positions[side - 1 - i]));
8016
+ return bits;
8017
+ }
8018
+
8019
+ /** @param {boolean[]} bits @param {boolean} compact */
8020
+ function decodeModeMessage(bits, compact) {
8021
+ const total = compact ? 7 : 10;
8022
+ const dataWords = compact ? 2 : 4;
8023
+ const words = new Array(total);
8024
+ for (let i = 0; i < total; i++) words[i] = readBits(bits, i * 4, 4);
8025
+ const corrections = rsDecode(words, total - dataWords, fieldForWordSize(4), 1);
8026
+ let data = 0;
8027
+ for (let i = 0; i < dataWords; i++) data = (data << 4) | words[i];
8028
+ const layers = compact ? (data >>> 6) + 1 : (data >>> 11) + 1;
8029
+ const dataCodewords = compact ? (data & 0x3f) + 1 : (data & 0x7ff) + 1;
8030
+ return { layers, dataCodewords, corrections };
8031
+ }
8032
+
8033
+ /** @param {boolean} compact @param {number} layers */
8034
+ function totalBitsInLayers(compact, layers) {
8035
+ return ((compact ? 88 : 112) + 16 * layers) * layers;
8036
+ }
8037
+
8038
+ /**
8039
+ * Extract raw, stuffed codeword bits in logical ring order.
8040
+ * @param {import('../core/bit-matrix.js').BitMatrix} matrix
8041
+ * @param {boolean} compact @param {number} layers
8042
+ * @returns {boolean[]}
8043
+ */
8044
+ function extractBits(matrix, compact, layers) {
8045
+ const baseSize = (compact ? 11 : 14) + layers * 4;
8046
+ const map = alignmentMap(compact, layers);
8047
+ const raw = new Array(totalBitsInLayers(compact, layers));
8048
+ let offset = 0;
8049
+ for (let layer = 0; layer < layers; layer++) {
8050
+ const rowSize = (layers - layer) * 4 + (compact ? 9 : 12);
8051
+ for (let j = 0; j < rowSize; j++) {
8052
+ const col = j * 2;
8053
+ for (let k = 0; k < 2; k++) {
8054
+ raw[offset + col + k] = matrix.get(map[layer * 2 + k], map[layer * 2 + j]);
8055
+ raw[offset + rowSize * 2 + col + k] = matrix.get(map[layer * 2 + j], map[baseSize - 1 - layer * 2 - k]);
8056
+ raw[offset + rowSize * 4 + col + k] = matrix.get(map[baseSize - 1 - layer * 2 - k], map[baseSize - 1 - layer * 2 - j]);
8057
+ raw[offset + rowSize * 6 + col + k] = matrix.get(map[baseSize - 1 - layer * 2 - j], map[layer * 2 + k]);
8058
+ }
8059
+ }
8060
+ offset += rowSize * 8;
8061
+ }
8062
+ return raw;
8063
+ }
8064
+
8065
+ /** @param {boolean[]} raw @param {number} layers @param {number} dataCodewords */
8066
+ function correctAndUnstuff(raw, layers, dataCodewords) {
8067
+ const wordSize = aztecWordSizeForLayers(layers);
8068
+ const totalWords = Math.floor(raw.length / wordSize);
8069
+ if (dataCodewords <= 0 || dataCodewords > totalWords) throw new FormatError('Aztec: invalid data word count');
8070
+ const start = raw.length % wordSize;
8071
+ const words = new Array(totalWords);
8072
+ for (let i = 0; i < totalWords; i++) words[i] = readBits(raw, start + i * wordSize, wordSize);
8073
+ const corrections = rsDecode(words, totalWords - dataCodewords, aztecFieldForLayers(layers), 1);
8074
+ const mask = (1 << wordSize) - 1;
8075
+ const corrected = [];
8076
+ for (let i = 0; i < dataCodewords; i++) {
8077
+ const word = words[i];
8078
+ if (word === 0 || word === mask) throw new FormatError('Aztec: invalid stuffed codeword');
8079
+ if (word === 1 || word === mask - 1) {
8080
+ for (let j = 0; j < wordSize - 1; j++) corrected.push(word === mask - 1);
8081
+ } else {
8082
+ appendBits(word, wordSize, corrected);
8083
+ }
8084
+ }
8085
+ return { bits: corrected, corrections };
8086
+ }
8087
+
8088
+ /** @param {Uint8Array} bytes */
8089
+ function bytesToText(bytes) {
8090
+ try { return new TextDecoder('utf-8', { fatal: true }).decode(bytes); }
8091
+ catch { return new TextDecoder('latin1').decode(bytes); }
8092
+ }
8093
+
8094
+ /**
8095
+ * Decode a square Aztec symbol with one bit per module and no quiet zone.
8096
+ * The matrix must already be oriented with the mode message at the top.
8097
+ *
8098
+ * @param {import('../core/bit-matrix.js').BitMatrix} matrix
8099
+ * @returns {{text: string, bytes: Uint8Array, compact: boolean, layers: number, corrections: number, eccPercent: number}}
8100
+ */
8101
+ function decodeAztec(matrix) {
8102
+ if (!matrix || matrix.width !== matrix.height) throw new FormatError('Aztec: expected a square BitMatrix');
8103
+ let compact;
8104
+ let mode;
8105
+ // Compact and full dimensions are disjoint; trying both also makes malformed
8106
+ // candidate handling deterministic for the future image detector.
8107
+ for (const candidate of [true, false]) {
8108
+ try {
8109
+ const value = decodeModeMessage(readModeBits(matrix, candidate), candidate);
8110
+ if (value.layers < 1 || value.layers > (candidate ? 4 : 32)) continue;
8111
+ if (aztecMatrixSize(value.layers, candidate) !== matrix.width) continue;
8112
+ compact = candidate;
8113
+ mode = value;
8114
+ break;
8115
+ } catch { /* Try the other family. */ }
8116
+ }
8117
+ if (compact === undefined || !mode) throw new FormatError('Aztec: invalid mode message or dimensions');
8118
+ // Ensure the declared layer data agrees with the table module, so a future
8119
+ // tables refactor cannot silently make decoder capacity calculations stale.
8120
+ aztecSymbolForLayers(mode.layers, compact);
8121
+ const raw = extractBits(matrix, compact, mode.layers);
8122
+ const payload = correctAndUnstuff(raw, mode.layers, mode.dataCodewords);
8123
+ const bytes = decodeHighLevelBits(payload.bits);
8124
+ const totalWords = Math.floor(raw.length / aztecWordSizeForLayers(mode.layers));
8125
+ return {
8126
+ text: bytesToText(bytes),
8127
+ bytes,
8128
+ compact,
8129
+ layers: mode.layers,
8130
+ corrections: mode.corrections + payload.corrections,
8131
+ eccPercent: Math.round(((totalWords - mode.dataCodewords) * 100) / totalWords),
8132
+ };
8133
+ }
8134
+
8135
+ __exports.decodeHighLevelBits = decodeHighLevelBits;
8136
+ __exports.decodeAztec = decodeAztec;
8137
+ };
8138
+
8139
+ __modules["aztec/detector.js"] = function (__require, __exports) {
8140
+ /**
8141
+ * Aztec image detection.
8142
+ *
8143
+ * Aztec has no finder pattern at its outer border. Its reliable geometric
8144
+ * anchor is instead the alternating square bull's-eye in the centre: five
8145
+ * rings in Compact symbols, seven rings in Full symbols. The detector finds
8146
+ * isolated central modules, verifies those rings at module centres, then
8147
+ * samples each legal symbol dimension. The decoder is deliberately the final
8148
+ * arbiter: its mode-message Reed--Solomon check rejects accidental concentric
8149
+ * artwork and tells us which of the compact/full dimensions is real.
8150
+ *
8151
+ * Sampling uses a quadrilateral, not a cropped bitmap, so the detected
8152
+ * rotation is corrected before decoding. The ring search covers arbitrary
8153
+ * in-plane rotations (four-degree coarse search; at normal camera scales its
8154
+ * positional error remains well inside a module). The optional inverse pass
8155
+ * supports light modules on a dark field.
8156
+ *
8157
+ * @module aztec/detector
8158
+ */
8159
+ const { NotFoundError } = __require("core/errors.js");
8160
+ const { sampleQuad } = __require("image/grid-sampler.js");
8161
+ const { decodeAztec } = __require("aztec/decoder.js");
8162
+
8163
+ /** @typedef {{x:number, y:number}} Point */
8164
+ /** @typedef {{corners: Point[], dimension: number, compact: boolean, moduleSize: number, matrix: import('../core/bit-matrix.js').BitMatrix}} Detection */
8165
+
8166
+ // Compact: 11 + 4 layers. Full symbols add reference-grid rows/columns every
8167
+ // 15 modules measured from their central 14-module base, not every 15 layers.
8168
+ const DIMENSIONS = [
8169
+ ...[1, 2, 3, 4].map((layers) => ({ compact: true, dimension: 11 + 4 * layers })),
8170
+ ...Array.from({ length: 32 }, (_, index) => {
8171
+ const layers = index + 1;
8172
+ return { compact: false, dimension: 15 + 4 * layers + 2 * Math.floor((2 * layers + 6) / 15) };
8173
+ }),
8174
+ ];
8175
+
8176
+ function pixel(image, x, y) {
8177
+ const ix = Math.round(x);
8178
+ const iy = Math.round(y);
8179
+ return ix >= 0 && iy >= 0 && ix < image.width && iy < image.height && image.get(ix, iy);
8180
+ }
8181
+
8182
+ /** Connected components of either polarity, retaining only plausible modules. */
8183
+ function components(image, value) {
8184
+ const seen = new Uint8Array(image.width * image.height);
8185
+ const out = [];
8186
+ const maximumArea = Math.max(4, Math.floor(image.width * image.height * 0.08));
8187
+ for (let y = 0; y < image.height; y++) for (let x = 0; x < image.width; x++) {
8188
+ const start = y * image.width + x;
8189
+ if (seen[start] || image.get(x, y) !== value) continue;
8190
+ const xs = [x];
8191
+ const ys = [y];
8192
+ seen[start] = 1;
8193
+ let head = 0;
8194
+ let minX = x; let maxX = x; let minY = y; let maxY = y;
8195
+ while (head < xs.length) {
8196
+ const px = xs[head]; const py = ys[head++];
8197
+ if (px < minX) minX = px; if (px > maxX) maxX = px;
8198
+ if (py < minY) minY = py; if (py > maxY) maxY = py;
8199
+ for (const [nx, ny] of [[px - 1, py], [px + 1, py], [px, py - 1], [px, py + 1]]) {
8200
+ if (nx < 0 || ny < 0 || nx >= image.width || ny >= image.height) continue;
8201
+ const at = ny * image.width + nx;
8202
+ if (!seen[at] && image.get(nx, ny) === value) {
8203
+ seen[at] = 1; xs.push(nx); ys.push(ny);
8204
+ }
8205
+ }
8206
+ }
8207
+ const width = maxX - minX + 1;
8208
+ const height = maxY - minY + 1;
8209
+ const area = width * height;
8210
+ // The central module is solid and approximately square. This filter is
8211
+ // intentionally permissive because a rotated raster module is diamond-ish.
8212
+ if (xs.length <= maximumArea && Math.abs(width - height) <= Math.max(1, Math.ceil(Math.max(width, height) * 0.35)) &&
8213
+ xs.length >= area * 0.45) {
8214
+ out.push({ x: (minX + maxX) / 2, y: (minY + maxY) / 2, width, height, pixels: xs.length });
8215
+ }
8216
+ }
8217
+ return out.sort((a, b) => b.pixels - a.pixels).slice(0, 2000);
8218
+ }
8219
+
8220
+ function expectedDark(ring, inverted) {
8221
+ return inverted ? (ring & 1) === 1 : (ring & 1) === 0;
8222
+ }
8223
+
8224
+ /** Score one square bull's-eye at an angle and a candidate module pitch. */
8225
+ function ringScore(image, centre, pitch, angle, inverted, rings) {
8226
+ const cos = Math.cos(angle);
8227
+ const sin = Math.sin(angle);
8228
+ let correct = 0;
8229
+ let total = 0;
8230
+ for (let ring = 0; ring < rings; ring++) {
8231
+ const wanted = expectedDark(ring, inverted);
8232
+ for (let j = -ring; j <= ring; j++) for (let i = -ring; i <= ring; i++) {
8233
+ if (ring && Math.abs(i) !== ring && Math.abs(j) !== ring) continue;
8234
+ const x = centre.x + (i * cos - j * sin) * pitch;
8235
+ const y = centre.y + (i * sin + j * cos) * pitch;
8236
+ if (pixel(image, x, y) === wanted) correct++;
8237
+ total++;
8238
+ }
8239
+ }
8240
+ return correct / total;
8241
+ }
8242
+
8243
+ function rotateCorners(corners, turn) {
8244
+ return corners.slice(turn).concat(corners.slice(0, turn));
8245
+ }
8246
+
8247
+ function invert(matrix) {
8248
+ const out = matrix.clone();
8249
+ for (let y = 0; y < out.height; y++) for (let x = 0; x < out.width; x++) out.flip(x, y);
8250
+ return out;
8251
+ }
8252
+
8253
+ function cornersFor(centre, pitch, angle, dimension) {
8254
+ const half = dimension * pitch / 2;
8255
+ const cos = Math.cos(angle);
8256
+ const sin = Math.sin(angle);
8257
+ const point = (x, y) => ({ x: centre.x + x * cos - y * sin, y: centre.y + x * sin + y * cos });
8258
+ return [point(-half, -half), point(half, -half), point(half, half), point(-half, half)];
8259
+ }
8260
+
8261
+ /**
8262
+ * Find an Aztec symbol in a binarized image.
8263
+ *
8264
+ * The returned matrix is in the orientation accepted by the Aztec decoder.
8265
+ * A valid mode message is required before a geometric candidate is returned,
8266
+ * making false positives from decorative concentric squares very unlikely.
8267
+ *
8268
+ * @param {import('../core/bit-matrix.js').BitMatrix} binaryImage Set bit = dark.
8269
+ * @returns {Detection | null}
8270
+ */
8271
+ function detectAztec(binaryImage) {
8272
+ if (!binaryImage || !binaryImage.width || !binaryImage.height) {
8273
+ throw new NotFoundError('detectAztec: no image supplied');
8274
+ }
8275
+ const candidates = [];
8276
+ for (const inverted of [false, true]) {
8277
+ for (const core of components(binaryImage, !inverted)) {
8278
+ // A non-rotated one-module component directly gives its pitch. For
8279
+ // rotated modules its bounding box grows by |sin| + |cos|, compensated
8280
+ // below for every tested angle.
8281
+ for (let degrees = 0; degrees < 180; degrees += 4) {
8282
+ const angle = degrees * Math.PI / 180;
8283
+ const scale = Math.abs(Math.cos(angle)) + Math.abs(Math.sin(angle));
8284
+ const pitch = ((core.width + core.height) / 2) / scale;
8285
+ if (pitch < 0.8) continue;
8286
+ // Test Full first: its seven rings also exclude Compact candidates.
8287
+ const fullScore = ringScore(binaryImage, core, pitch, angle, inverted, 7);
8288
+ const rings = fullScore >= 0.88 ? 7 : 5;
8289
+ const score = rings === 7 ? fullScore : ringScore(binaryImage, core, pitch, angle, inverted, 5);
8290
+ if (score < 0.91) continue;
8291
+ const symbolKinds = rings === 7 ? DIMENSIONS.filter((item) => !item.compact) : DIMENSIONS.filter((item) => item.compact);
8292
+ for (const kind of symbolKinds) {
8293
+ const baseCorners = cornersFor(core, pitch, angle, kind.dimension);
8294
+ for (let turn = 0; turn < 4; turn++) {
8295
+ const corners = rotateCorners(baseCorners, turn);
8296
+ let matrix;
8297
+ try { matrix = sampleQuad(binaryImage, kind.dimension, corners); } catch (e) { continue; }
8298
+ if (inverted) matrix = invert(matrix);
8299
+ try {
8300
+ // The decoder verifies the mode-message ECC and exact geometry.
8301
+ // We do not expose its result here so callers can use pure
8302
+ // detection without treating payload decoding as an API contract.
8303
+ decodeAztec(matrix);
8304
+ candidates.push({ corners, dimension: kind.dimension, compact: kind.compact,
8305
+ moduleSize: pitch, matrix, score });
8306
+ } catch (e) { /* Not an Aztec mode message at this dimension. */ }
8307
+ }
8308
+ }
8309
+ }
8310
+ }
8311
+ }
8312
+ candidates.sort((a, b) => b.score - a.score || b.moduleSize - a.moduleSize);
8313
+ const best = candidates[0];
8314
+ if (!best) return null;
8315
+ delete best.score;
8316
+ return best;
8317
+ }
8318
+
8319
+ /**
8320
+ * Detect then decode an Aztec symbol. Detection failure is a normal result for
8321
+ * images without an Aztec code, therefore invalid candidates return null.
8322
+ *
8323
+ * @param {import('../core/bit-matrix.js').BitMatrix} binaryImage
8324
+ * @returns {(import('./decoder.js').DecodeResult & {corners: Point[]}) | null}
8325
+ */
8326
+ function detectAndDecodeAztec(binaryImage) {
8327
+ let detection;
8328
+ try { detection = detectAztec(binaryImage); } catch (e) { return null; }
8329
+ if (!detection) return null;
8330
+ try { return Object.assign({ corners: detection.corners }, decodeAztec(detection.matrix)); }
8331
+ catch (e) { return null; }
8332
+ }
8333
+
8334
+ __exports.detectAztec = detectAztec;
8335
+ __exports.detectAndDecodeAztec = detectAndDecodeAztec;
8336
+ };
8337
+
8338
+ __modules["aztec/index.js"] = function (__require, __exports) {
8339
+ /** Aztec Code entry points. @module aztec */
8340
+ const __reexport0 = __require("aztec/encoder.js"); __exports.encodeAztec = __reexport0.encodeAztec;
8341
+ const __reexport1 = __require("aztec/decoder.js"); __exports.decodeAztec = __reexport1.decodeAztec;
8342
+ const __reexport2 = __require("aztec/detector.js"); __exports.detectAztec = __reexport2.detectAztec; __exports.detectAndDecodeAztec = __reexport2.detectAndDecodeAztec;
8343
+ const __reexport3 = __require("aztec/tables.js"); __exports.AZTEC_COMPACT_LAYERS = __reexport3.AZTEC_COMPACT_LAYERS; __exports.AZTEC_FULL_LAYERS = __reexport3.AZTEC_FULL_LAYERS; __exports.AZTEC_LAYERS = __reexport3.AZTEC_LAYERS; __exports.AZTEC_DEFAULT_ECC_PERCENT = __reexport3.AZTEC_DEFAULT_ECC_PERCENT; __exports.AZTEC_RS_GENERATOR_BASE = __reexport3.AZTEC_RS_GENERATOR_BASE; __exports.aztecLayer = __reexport3.aztecLayer; __exports.aztecSymbolSize = __reexport3.aztecSymbolSize; __exports.validateAztecTables = __reexport3.validateAztecTables;
8344
+
8345
+
8346
+ };
8347
+
8348
+ __modules["pdf417/compaction.js"] = function (__require, __exports) {
8349
+ /** PDF417 high-level text, byte and numeric compaction. @module pdf417/compaction */
8350
+ const { EncodeError, FormatError } = __require("core/errors.js");
8351
+
8352
+ const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ';
8353
+ const LOWER = 'abcdefghijklmnopqrstuvwxyz ';
8354
+ const MIXED = '0123456789&\r\t,:#-.$/+%*=^';
8355
+ const PUNCT = ';<>@[\\]_`~!\r\t,:\n-.$/"|*()?{}\'';
8356
+
8357
+ function packBase30(values) {
8358
+ const out = [];
8359
+ for (let i = 0; i < values.length; i += 2) out.push(values[i] * 30 + (i + 1 < values.length ? values[i + 1] : 29));
8360
+ return out;
8361
+ }
8362
+
8363
+ /** Compact a value using the PDF417 Text Compaction alphabet. */
8364
+ function compactPdf417Text(value) {
8365
+ if (typeof value !== 'string') throw new EncodeError('PDF417 text: value must be a string');
8366
+ const values = [];
8367
+ let submode = 'alpha';
8368
+ for (const character of value) {
8369
+ const inAlpha = ALPHA.indexOf(character), inLower = LOWER.indexOf(character);
8370
+ const inMixed = MIXED.indexOf(character), inPunct = PUNCT.indexOf(character);
8371
+ if (submode === 'alpha') {
8372
+ if (inAlpha >= 0) values.push(inAlpha);
8373
+ else if (inLower >= 0) { values.push(27, inLower); submode = 'lower'; }
8374
+ else if (inMixed >= 0 || character === ' ') { values.push(28, character === ' ' ? 26 : inMixed); submode = 'mixed'; }
8375
+ else if (inPunct >= 0) values.push(29, inPunct);
8376
+ else throw new EncodeError(`PDF417 text: unsupported character ${JSON.stringify(character)}`);
8377
+ } else if (submode === 'lower') {
8378
+ if (inLower >= 0) values.push(inLower);
8379
+ else if (inAlpha >= 0) values.push(27, inAlpha);
8380
+ else if (inMixed >= 0 || character === ' ') { values.push(28, character === ' ' ? 26 : inMixed); submode = 'mixed'; }
8381
+ else if (inPunct >= 0) values.push(29, inPunct);
8382
+ else throw new EncodeError(`PDF417 text: unsupported character ${JSON.stringify(character)}`);
8383
+ } else {
8384
+ if (inMixed >= 0) values.push(inMixed);
8385
+ else if (character === ' ') values.push(26);
8386
+ else if (inAlpha >= 0) { values.push(28); submode = 'alpha'; values.push(inAlpha); }
8387
+ else if (inLower >= 0) { values.push(27); submode = 'lower'; values.push(inLower); }
8388
+ else if (inPunct >= 0) values.push(29, inPunct);
8389
+ else throw new EncodeError(`PDF417 text: unsupported character ${JSON.stringify(character)}`);
8390
+ }
8391
+ }
8392
+ return packBase30(values);
8393
+ }
8394
+
8395
+ function asBytes(value) {
8396
+ if (value instanceof Uint8Array) return value;
8397
+ if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
8398
+ if (typeof value === 'string') return new TextEncoder().encode(value);
8399
+ throw new EncodeError('PDF417 byte: value must be text or a byte array');
8400
+ }
8401
+
8402
+ /** Compact bytes using latch 924 for exact six-byte blocks and 901 otherwise. */
8403
+ function compactPdf417Bytes(value) {
8404
+ const bytes = asBytes(value);
8405
+ const utf8 = typeof value === 'string' && /[^\x00-\x7f]/.test(value);
8406
+ const out = utf8 ? [927, 26] : [];
8407
+ out.push(bytes.length > 0 && bytes.length % 6 === 0 ? 924 : 901);
8408
+ let at = 0;
8409
+ while (at + 6 <= bytes.length) {
8410
+ let number = 0n;
8411
+ for (let i = 0; i < 6; i++) number = (number << 8n) | BigInt(bytes[at++]);
8412
+ const group = new Array(5);
8413
+ for (let i = 4; i >= 0; i--) { group[i] = Number(number % 900n); number /= 900n; }
8414
+ out.push(...group);
8415
+ }
8416
+ while (at < bytes.length) out.push(bytes[at++]);
8417
+ return out;
8418
+ }
8419
+
8420
+ /** Compact decimal digits using latch 902 and groups of at most 44 digits. */
8421
+ function compactPdf417Numeric(value) {
8422
+ if (typeof value !== 'string' || !/^\d+$/.test(value)) throw new EncodeError('PDF417 numeric: value must contain decimal digits only');
8423
+ const out = [902];
8424
+ for (let at = 0; at < value.length; at += 44) {
8425
+ let number = BigInt(`1${value.slice(at, at + 44)}`);
8426
+ const group = [];
8427
+ do { group.unshift(Number(number % 900n)); number /= 900n; } while (number > 0n);
8428
+ out.push(...group);
8429
+ }
8430
+ return out;
8431
+ }
8432
+
8433
+ /** Compact a single value, selecting text, numeric or byte mode. */
8434
+ function compactPdf417(value, options = {}) {
8435
+ const mode = options.compaction ?? 'auto';
8436
+ if (mode === 'text') return compactPdf417Text(value);
8437
+ if (mode === 'byte') return compactPdf417Bytes(value);
8438
+ if (mode === 'numeric') return compactPdf417Numeric(value);
8439
+ if (mode !== 'auto') throw new EncodeError(`PDF417: unsupported compaction mode ${JSON.stringify(mode)}`);
8440
+ if (typeof value === 'string' && /^\d{13,}$/.test(value)) return compactPdf417Numeric(value);
8441
+ if (typeof value === 'string') {
8442
+ try { return compactPdf417Text(value); } catch (error) { if (!(error instanceof EncodeError)) throw error; }
8443
+ }
8444
+ return compactPdf417Bytes(value);
8445
+ }
8446
+
8447
+ function assertCodeword(codeword) {
8448
+ if (!Number.isInteger(codeword) || codeword < 0 || codeword > 928) throw new FormatError('PDF417: codeword is outside 0..928');
8449
+ }
8450
+
8451
+ function decodeUtf8(bytes, eci) {
8452
+ if (eci === 3) return Array.from(bytes, (byte) => String.fromCharCode(byte)).join('');
8453
+ if (eci !== 26) throw new FormatError(`PDF417 ECI: unsupported assignment number ${eci}`);
8454
+ try { return new TextDecoder('utf-8', { fatal: true }).decode(new Uint8Array(bytes)); }
8455
+ catch { throw new FormatError('PDF417 byte: invalid UTF-8 sequence'); }
8456
+ }
8457
+
8458
+ function decodeByteSegment(codewords, at, eci, sixOnly = false) {
8459
+ const values = [];
8460
+ while (at < codewords.length && codewords[at] < 900) values.push(codewords[at++]);
8461
+ if (sixOnly && values.length % 5) throw new FormatError('PDF417 byte: 924 segment must contain complete six-byte groups');
8462
+ const bytes = [];
8463
+ // In 901 mode an encoder can use five terminal literal codewords. The
8464
+ // unambiguous groups are therefore the ones followed by another codeword;
8465
+ // 924 is available whenever a segment consists exclusively of six-byte groups.
8466
+ const groupCount = sixOnly ? values.length / 5 : Math.max(0, Math.floor((values.length - 1) / 5));
8467
+ for (let groupAt = 0; groupAt < groupCount * 5; groupAt += 5) {
8468
+ let number = 0n;
8469
+ for (let i = 0; i < 5; i++) number = number * 900n + BigInt(values[groupAt + i]);
8470
+ const group = new Uint8Array(6);
8471
+ for (let i = 5; i >= 0; i--) { group[i] = Number(number & 255n); number >>= 8n; }
8472
+ if (number !== 0n) throw new FormatError('PDF417 byte: base-900 group exceeds six bytes');
8473
+ bytes.push(...group);
8474
+ }
8475
+ for (let i = groupCount * 5; i < values.length; i++) {
8476
+ if (values[i] > 255) throw new FormatError('PDF417 byte: literal tail is outside 0..255');
8477
+ bytes.push(values[i]);
8478
+ }
8479
+ return { at, text: decodeUtf8(bytes, eci), bytes: Uint8Array.from(bytes) };
8480
+ }
8481
+
8482
+ function decodeTextSegment(codewords, at, eci) {
8483
+ let mode = 'alpha';
8484
+ let output = '';
8485
+ let shift = null;
8486
+ let shiftedBytes = [];
8487
+ const bytes = [];
8488
+ const flushShiftedBytes = () => {
8489
+ if (shiftedBytes.length) {
8490
+ output += decodeUtf8(shiftedBytes, eci);
8491
+ bytes.push(...shiftedBytes);
8492
+ shiftedBytes = [];
8493
+ }
8494
+ };
8495
+ const emit = (alphabet, value) => {
8496
+ if (value < 0 || value >= alphabet.length) throw new FormatError('PDF417 text: invalid submode value');
8497
+ output += alphabet[value];
8498
+ };
8499
+ const process = (value) => {
8500
+ if (shift) { emit(shift === 'alpha' ? ALPHA : PUNCT, value); shift = null; return; }
8501
+ if (mode === 'alpha') {
8502
+ if (value < 26) emit(ALPHA, value);
8503
+ else if (value === 26) output += ' ';
8504
+ else if (value === 27) mode = 'lower';
8505
+ else if (value === 28) mode = 'mixed';
8506
+ else if (value === 29) shift = 'punct';
8507
+ } else if (mode === 'lower') {
8508
+ if (value < 26) emit(LOWER, value);
8509
+ else if (value === 26) output += ' ';
8510
+ else if (value === 27) shift = 'alpha';
8511
+ else if (value === 28) mode = 'mixed';
8512
+ else if (value === 29) shift = 'punct';
8513
+ } else if (mode === 'mixed') {
8514
+ if (value < 25) emit(MIXED, value);
8515
+ else if (value === 25) mode = 'punct';
8516
+ else if (value === 26) output += ' ';
8517
+ else if (value === 27) mode = 'lower';
8518
+ else if (value === 28) mode = 'alpha';
8519
+ else if (value === 29) shift = 'punct';
8520
+ } else {
8521
+ if (value < 29) emit(PUNCT, value);
8522
+ else if (value === 29) mode = 'alpha';
8523
+ }
8524
+ };
8525
+ while (at < codewords.length) {
8526
+ const codeword = codewords[at];
8527
+ if (codeword >= 900 && codeword !== 913) break;
8528
+ at++;
8529
+ if (codeword === 913) {
8530
+ if (at >= codewords.length || codewords[at] > 255) throw new FormatError('PDF417 text: invalid byte shift');
8531
+ shiftedBytes.push(codewords[at++]);
8532
+ continue;
8533
+ }
8534
+ flushShiftedBytes();
8535
+ process(Math.floor(codeword / 30));
8536
+ process(codeword % 30);
8537
+ }
8538
+ flushShiftedBytes();
8539
+ return { at, text: output, bytes: Uint8Array.from(bytes) };
8540
+ }
8541
+
8542
+ function decodeNumericSegment(codewords, at) {
8543
+ let output = '';
8544
+ while (at < codewords.length && codewords[at] < 900) {
8545
+ const end = Math.min(at + 15, codewords.length);
8546
+ let number = 0n;
8547
+ for (; at < end && codewords[at] < 900; at++) number = number * 900n + BigInt(codewords[at]);
8548
+ const decimal = number.toString();
8549
+ if (!decimal.startsWith('1')) throw new FormatError('PDF417 numeric: missing leading sentinel');
8550
+ output += decimal.slice(1);
8551
+ }
8552
+ return { at, text: output, bytes: new Uint8Array(0) };
8553
+ }
8554
+
8555
+ /**
8556
+ * Decode PDF417 compaction while preserving raw Byte Compaction and byte-shift
8557
+ * payloads. Text and Numeric Compaction do not manufacture bytes: their text
8558
+ * is available on each segment, while `bytes` contains only octets carried by
8559
+ * modes that encode octets explicitly.
8560
+ */
8561
+ function decodePdf417CompactionDetailed(codewords) {
8562
+ if (!Array.isArray(codewords) && !ArrayBuffer.isView(codewords)) throw new FormatError('PDF417: codewords must be an array');
8563
+ for (const codeword of codewords) assertCodeword(codeword);
8564
+ let at = 0;
8565
+ // ISO/IEC 8859-1 is the PDF417 default; UTF-8 is selected explicitly with ECI 26.
8566
+ let eci = 3;
8567
+ let output = '';
8568
+ const bytes = [];
8569
+ const segments = [];
8570
+ while (at < codewords.length) {
8571
+ const codeword = codewords[at];
8572
+ if (codeword < 900 || codeword === 900 || codeword === 913) {
8573
+ const start = at;
8574
+ const latch = codeword === 900 ? codeword : null;
8575
+ if (latch !== null) at++;
8576
+ const segment = decodeTextSegment(codewords, at, eci);
8577
+ at = segment.at;
8578
+ output += segment.text;
8579
+ bytes.push(...segment.bytes);
8580
+ if (segment.text.length || segment.bytes.length) segments.push({ mode: 'text', text: segment.text, bytes: segment.bytes, eci, latch, codewordStart: start, codewordEnd: at });
8581
+ continue;
8582
+ }
8583
+ const start = at;
8584
+ at++;
8585
+ if (codeword === 901) {
8586
+ const segment = decodeByteSegment(codewords, at, eci);
8587
+ at = segment.at;
8588
+ output += segment.text;
8589
+ bytes.push(...segment.bytes);
8590
+ segments.push({ mode: 'byte', text: segment.text, bytes: segment.bytes, eci, latch: codeword, codewordStart: start, codewordEnd: at });
8591
+ } else if (codeword === 924) {
8592
+ const segment = decodeByteSegment(codewords, at, eci, true);
8593
+ at = segment.at;
8594
+ output += segment.text;
8595
+ bytes.push(...segment.bytes);
8596
+ segments.push({ mode: 'byte', text: segment.text, bytes: segment.bytes, eci, latch: codeword, codewordStart: start, codewordEnd: at });
8597
+ } else if (codeword === 902) {
8598
+ const segment = decodeNumericSegment(codewords, at);
8599
+ at = segment.at;
8600
+ output += segment.text;
8601
+ segments.push({ mode: 'numeric', text: segment.text, bytes: segment.bytes, eci, latch: codeword, codewordStart: start, codewordEnd: at });
8602
+ } else if (codeword === 927) {
8603
+ if (at >= codewords.length || codewords[at] > 899) throw new FormatError('PDF417 ECI: missing assignment number');
8604
+ eci = codewords[at++];
8605
+ } else {
8606
+ throw new FormatError(`PDF417: unsupported compaction codeword ${codeword}`);
8607
+ }
8608
+ }
8609
+ return { text: output, bytes: Uint8Array.from(bytes), segments };
8610
+ }
8611
+
8612
+ /** Decode PDF417 Text, Byte, Numeric and UTF-8 ECI compaction segments in source order. */
8613
+ function decodePdf417Compaction(codewords) {
8614
+ return decodePdf417CompactionDetailed(codewords).text;
8615
+ }
8616
+
8617
+ __exports.compactPdf417Text = compactPdf417Text;
8618
+ __exports.compactPdf417Bytes = compactPdf417Bytes;
8619
+ __exports.compactPdf417Numeric = compactPdf417Numeric;
8620
+ __exports.compactPdf417 = compactPdf417;
8621
+ __exports.decodePdf417CompactionDetailed = decodePdf417CompactionDetailed;
8622
+ __exports.decodePdf417Compaction = decodePdf417Compaction;
8623
+ };
8624
+
8625
+ __modules["pdf417/error-correction.js"] = function (__require, __exports) {
8626
+ const { EncodeError } = __require("core/errors.js");
8627
+ const { GF929 } = __require("core/galois-field.js");
8628
+ const { rsDecode, rsEncode } = __require("core/reed-solomon.js");
8629
+ function pdf417EccLength(level) {
8630
+ if (!Number.isInteger(level) || level < 0 || level > 8) throw new EncodeError('PDF417: error correction level must be in 0..8');
8631
+ return 1 << (level + 1);
8632
+ }
8633
+ function pdf417ErrorCorrection(data, level) {
8634
+ return rsEncode(data, pdf417EccLength(level), GF929, 1);
8635
+ }
8636
+
8637
+ /** Correct PDF417 codewords, optionally marking unreadable codewords as erasures. */
8638
+ function pdf417CorrectErrors(codewords, level, erasures = []) {
8639
+ return rsDecode(codewords, pdf417EccLength(level), GF929, 1, erasures);
8640
+ }
8641
+
8642
+ __exports.pdf417EccLength = pdf417EccLength;
8643
+ __exports.pdf417ErrorCorrection = pdf417ErrorCorrection;
8644
+ __exports.pdf417CorrectErrors = pdf417CorrectErrors;
8645
+ };
8646
+
8647
+ __modules["pdf417/tables.js"] = function (__require, __exports) {
8648
+ /** PDF417 symbol-character pattern table. @module pdf417/tables */
8649
+ const PDF417_CLUSTER_NUMBERS = Object.freeze([0, 3, 6]);
8650
+ const PDF417_CODEWORDS_PER_CLUSTER = 929;
8651
+
8652
+ // Transcribed from the normative AIM USS PDF417 specification, Appendix H,
8653
+ // Table H1 (Bar-Space Sequence Table), from a publicly accessible copy hosted
8654
+ // at https://expresscorp.com/wp-content/uploads/2023/02/USS-PDF-417.pdf.
8655
+ // ISO/IEC 15438:2015 is the current ISO specification for PDF417.
8656
+ // Each eight-digit sequence is ordered bar, space, bar, space, bar, space,
8657
+ // bar, space and is indexed by its codeword value from 0 through 928.
8658
+ const WIDTH_SEQUENCES = Object.freeze([
8659
+ '3111113641111144511111523111123541111243511112512111132631111334211114251111151621111524111116152111213631112144' +
8660
+ '4111215221112235311122434111225111112326211123341111242511113136211131443111315211113235211132433111325111113334' +
8661
+ '2111334211114144211141521111424321114251111151525111611131121135411211435112115121121226311212344112124221121325' +
8662
+ '3112133311121416211214243112143211121515211215231112161421122135311221434112215111122226211222343112224211122325' +
8663
+ '2112233331122341111224242112243211123135211231433112315111123234211232421112333321123341111241432112415111124242' +
8664
+ '1112434121131126311311344113114221131225311312334113124111131316211313243113133211131415211314231113151411131613' +
8665
+ '1113212621132134311321421113222521132233311322411113232421132332111324231113252211133134211331421113323321133241' +
8666
+ '1113333211134142211411253114113341141141111412162114122431141232111413152114132331141331111414142114142211141513' +
8667
+ '2114152111142125211421333114214111142224211422321114232321142331111424221114252121143141111433311115111621151124' +
8668
+ '3115113211151215211512233115123111151314211513221115141321151421111515121115212411152223111523221116111531161131' +
8669
+ '2116122221161321111615113211113542111143521111512211122632111234421112422211132532111333421113411211141622111424' +
8670
+ '1211151522112135321121434211215112112226221122343211224212112325221123331211242412112523121131352211314332113151' +
8671
+ '1211323422113242121133331211343212114143221141511211424212115151312111264121113451211142312112254121123351211241' +
8672
+ '2121131631211324412113322121141531211423412114312121151431211522221211263212113442121142212121262212122532121233' +
8673
+ '4212124121212225312122334121224111212316121214152212142332121431112124152121242311212514121221262212213432122142' +
8674
+ '1121312612122225221222333212224111213225212132333121324111213324121224231121342312123134221231421121413412123233' +
8675
+ '2212324111214233212142411121433212124142112151421212424111215241312211254122113351221141212212163122122441221232' +
8676
+ '2122131531221323412213312122141431221422212215132122161222131125321311334213114121222125221312243213123211222216' +
8677
+ '1213131531222232321313311122231512131414221314221122241421222422221315211213161212132125221321333213214111223125' +
8678
+ '1213222422132232112232242122323222132331112233231213242212132521121331332213314111224133121332321122423212133331' +
8679
+ '1122433111225141212311163123112441231132212312153123122341231231212313143123132221231413312314212123151221231611' +
8680
+ '1214111622141124321411321123211612141215221412233214123111232215212322233123223111232314121414132214142111232413' +
8681
+ '2123242111232512121421242214213211233124121422232214223111233223212332311123332212142421112334211123413211234231' +
8682
+ '2124111531241123412411312124121431241222212413133124132121241412212415111215111522151123321511311124211512151214' +
8683
+ '2215122211242214212422222215132111242313121514121124241212151511121521231124312311243222112433213125112231251221' +
8684
+ '2125141122161122121612131125221311252312112524112311112633111134431111422311122533111233131113162311132433111332' +
8685
+ '1311141523111423131115141311161313112126231121343311214213112225231122333311224113112324231123321311242313112522' +
8686
+ '1311313423113142131132332311324113113332131141421311424132211125422111335221114122211216322112244221123222211315' +
8687
+ '3221132342211331222114143221142222211513322115212312112533121133431211412221212523121224331212321221221613121315' +
8688
+ '3221223233121331122123152221232323121422122124141312151312212513131221252312213333122141122131251312222432213141' +
8689
+ '1221322422213232231223311221332313122422122134221312313323123141122141331312323212214232131233311312414112215141' +
8690
+ '3131111641311124513111323131121541311223513112313131131441311322313114134131142131311512222211163222112442221132' +
8691
+ '2131211622221215413121324222123121312215313122234131223121312314222214133222142121312413313124212222161113131116' +
8692
+ '2313112433131132122221161313121523131223331312311131311612222215222222233222223111313215213132233131323123131421' +
8693
+ '1131331412222413222224211131341313131611131321242313213212223124131322232313223111314124122232232222323111314223' +
8694
+ '2131423113132421122234211313313212224132131332311131513212224231313211154132112351321131313212144132122231321313' +
8695
+ '4132132131321412313215112223111532231123422311312132211522231214413221312132221431322222322313212132231322231412' +
8696
+ '2132241222231511213225111314111523141123331411311223211513141214231412221132311512232214222322222314132111323214' +
8697
+ '2132322213141412113233131223241213141511122325111314212323142131122331231314222211324123122332221314232111324222' +
8698
+ '1223332113143131113251313133111441331122313312134133122131331312313314112224111432241122213321142224121332241221' +
8699
+ '2133221331332221213323122224141121332411131511142315112212242114131512132315122111333114122422132224222111333213' +
8700
+ '2133322113151411113333121224241111333411122431221133412211334221413411213134131132251121222512122225131113161113' +
8701
+ '1225211311343113131613111225231124111125141112162411122414111315241113233411133114111414241114221411151324111521' +
8702
+ '1411212524112133341121411411222424112232141123232411233114112422141125211411313324113141141132321411333114114141' +
8703
+ '2321111633211124432111322321121533211223232113143321132223211413332114212321151214121116241211243412113213212116' +
8704
+ '1412121533212132341212311321221523212223332122311321231414121413241214211321241323212421141216111412212424122132' +
8705
+ '1321312414122223241222311321322323213231132133221412242114123132132141321412323113214231323111154231112352311131' +
8706
+ '3231121442311222323113134231132132311412323115112322111533221123223121152322121433221222223122143231222233221321' +
8707
+ '2231231323221412223124122322151122312511141311152413112313222115141312143322213112313115132222142322222224131321' +
8708
+ '1231321422313222141314121231331313222412141315111322251114132123241321311322312314132222123141231322322214132321' +
8709
+ '1231422213223321141331311322413112315131414111145141112241411213514112214141131241411411323211144232112231412114' +
8710
+ '4141212242321221314122134141222131412312323214113141241123231114332311222232211423231213332312212141311422322213' +
8711
+ '3232222121413213314132212323141121413312223224112141341114141114241411221323211414141213241412211232311413232213' +
8712
+ '2323222111414114123232132232322114141411114142132141422113232411114143121414212213233122141422211232412213233221' +
8713
+ '1141512212324221114152214142111351421121414212124142131132331113423311213142211341422121314222123233131131422311' +
8714
+ '2324111333241121223321132324121221423113223322122324131121423212223323112142331114151113241511211324211323242121' +
8715
+ '1233311313242212141513111142411312333212132423111142421212333311114243111324312111425121414312113143211231432211' +
8716
+ '2234211221433112214332111325211212343112114341121143421115111116151112152511122315111314151114131511151215112124' +
8717
+ '1511222315112322151124211511313215113231242111152421121434211222242113133421132124211412242115111512111525121123' +
8718
+ '1421211524212123251212221421221424212222142123132421232114212412151215111421251115122123251221311421312324213131' +
8719
+ '1421322215122321142133211512313114214131333111143331121333311312333114112422111423312114333121223422122123312213' +
8720
+ '3331222123312312242214112331241115131114142221141513121325131221133131141422221315131312133132131422231215131411' +
8721
+ '1331331214222411151321221422312215132221133141221422322113314221424111134241121242411311333211133241211342412121' +
8722
+ '3241221233321311324123112423111334231121233221133332212122413113233222122423131122413212233223112241331115141113' +
8723
+ '2514112114232113242321211332311314232212151413111241411313323212142323111241421213323311151421211423312113324121' +
8724
+ '1241512151511112515112114242111241512112424212114151221133331112324221123333121131513112324222113151321124241112' +
8725
+ '2333211224241211224231122333221121514112',
8726
+ '5111112561111133411112165111122461111232411113155111132361111331411114145111142241111513511115214111161241112125' +
8727
+ '5111213361112141311122164111222451112232311123154111232351112331311124144111242231112513411125213111261231113125' +
8728
+ '4111313351113141211132163111322441113232211133153111332341113331211134143111342221113513311135212111361221114125' +
8729
+ '3111413341114141111142162111422431114232111143152111432331114331111144142111442211114513211145211111512521115133' +
8730
+ '3111514111115224211152321111532321115331111154221111613321116141111162321111633141121116511211246112113241121215' +
8731
+ '5112122361121231411213145112132241121413511214214112151241121611311221164112212451122132311222154112222351122231' +
8732
+ '3112231441122322311224134112242131122512311226112112311631123124411231322112321531123223411232312112331431123322' +
8733
+ '2112341331123421211235122112361111124116211241243112413211124215211242233112423111124314211243221112441321124421' +
8734
+ '1112451211125124211251321112522321125231111253221112542111126132111262314113111551131123611311314113121451131222' +
8735
+ '4113131351131321411314124113151131132115411321235113213131132214411322223113231341132321311324123113251121133115' +
8736
+ '3113312341133131211332143113322221133313311333212113341221133511111341152113412331134131111342142113422211134313' +
8737
+ '2113432111134412111345111113512321135131111352221113532111136131411411145114112241141213511412214114131241141411' +
8738
+ '3114211441142122311422134114222131142312311424112114311431143122211432133114322121143312211434111114411421144122' +
8739
+ '1114421321144221111443121114441111145122111452214115111351151121411512124115131131152113411521213115221231152311' +
8740
+ '2115311331153121211532122115331111154113211541211115421211154311411611124116121131162112311622112116311221163211' +
8741
+ '4211111652111124621111324211121552111223621112314211131452111322421114135211142142111512421116113211211642112124' +
8742
+ '5211213232112215421122235211223132112314421123223211241342112421321125123211261122113116321131244211313222113215' +
8743
+ '3211322342113231221133143211332222113413321134212211351222113611121141162211412432114132121142152211422332114231' +
8744
+ '1211431422114322121144132211442112114512121151242211513212115223221152311211532212115421121161321211623151211115' +
8745
+ '6121112311211164512112146121122211211263512113136121132111211362512114125121151142121115521211236212113141212115' +
8746
+ '4212121461212131412122145121222252121321412123134212141241212412421215114121251132122115421221235212213131213115' +
8747
+ '3212221442122222312132144121322242122321312133133212241231213412321225113121351122123115321231234212313121214115' +
8748
+ '2212321432123222212142143121422232123321212143132212341221214412221235112121451112124115221241233212413111215115' +
8749
+ '1212421422124222112152142121522222124321112153131212441211215412121245111212512322125131112161231212522211216222' +
8750
+ '1212532111216321121261315122111461221122112211635122121361221221112212625122131211221361512214114213111452131122' +
8751
+ '4122211442131213521312214122221351222221412223124213141141222411321321144213212231223114321322134213222131223213' +
8752
+ '4122322131223312321324113122341122133114321331222122411422133213321332212122421331224221212243122213341121224411' +
8753
+ '1213411422134122112251141213421322134221112252132122522111225312121344111122541112135122112261221213522111226221' +
8754
+ '5123111361231121112311625123121211231261512313114214111352141121412321135123212141232212421413114123231132142113' +
8755
+ '4214212131233113321422123123321232142311312333112214311332143121212341133123412121234212221433112123431112144113' +
8756
+ '2214412111235113121442121123521212144311112353111214512111236121512411121124116151241211421511124124211242151211' +
8757
+ '4124221132152112312431123215221131243211221531122124411222153211212442111215411211245112121542111124521151251111' +
8758
+ '4216111141252111321621113125311122163111212541114311111553111123631111314311121453111222431113135311132143111412' +
8759
+ '4311151133112115431121235311213133112214431122223311231343112321331124123311251123113115331131234311313123113214' +
8760
+ '3311322223113313331133212311341223113511131141152311412333114131131142142311422213114313231143211311441213114511' +
8761
+ '1311512323115131131152221311532113116131522111146221112212211163522112136221122112211262522113121221136152211411' +
8762
+ '4312111453121122422121144312121353121221422122135221222142212312431214114221241133122114431221223221311433122213' +
8763
+ '4312222132213213422132213221331233122411322134112312311433123122222141142312321333123221222142133221422122214312' +
8764
+ '2312341122214411131241142312412212215114131242132312422112215213222152211221531213124411122154111312512212216122' +
8765
+ '1312522112216221613111131131115421311162613112121131125321311261613113111131135211311451522211136222112112221162' +
8766
+ '5131211361312121113121621222126151312212522213111131226151312311431311135313112142222113431312124131311351313121' +
8767
+ '4313131141313212422223114131331133132113431321213222311333132212313141133222321233132311313142123222331131314311' +
8768
+ '2313311333133121222241132313321221315113222242122313331121315212222243112131531113134113231341211222511313134212' +
8769
+ '1131611312225212131343111131621212225311113163111313512112226121613211121132115321321161613212111132125211321351' +
8770
+ '5223111212231161513221125223121111322161513222114314111242232112431412114132311242232211413232113314211232233112' +
8771
+ '3314221131324112322332113132421123143112222341122314321121325112222342112132521113144112122351121314421111326112' +
8772
+ '1223521111326211613311111133115211331251522411115133211143151111422421114133311133152111322431113133411123153111' +
8773
+ '2224411121335111131541111224511111336111113411514411111454111122441112135411122144111312441114113411211444112122' +
8774
+ '3411221344112221341123123411241124113114341131222411321334113221241133122411341114114114241141221411421324114221' +
8775
+ '1411431214114411141151221411522153211113632111211321116253211212132112615321131144121113541211214321211344121212' +
8776
+ '4321221244121311432123113412211344122121332131133412221233213212341223113321331124123113341231212321411324123212' +
8777
+ '2321421224123311232143111412411324124121132151131412421213215212141243111321531114125121132161216231111212311153' +
8778
+ '2231116162311211123112521231135153221112132211615231211253221211123121615231221144131112432221124413121142313112' +
8779
+ '4322221142313211341321123322311234132211323141123322321132314211241331122322411224133211223151122322421122315211' +
8780
+ '1413411213225112141342111231611213225211123162111141114421411152114112432141125111411342114114416232111112321152' +
8781
+ '6141211111412152123212511141225153231111523221115141311144141111432321114232311141414111341421113323311132324111' +
8782
+ '3141511124143111232341112232511121416111141441111323511112326111114211432142115111421242114213411233115111422151' +
8783
+ '1143114211431241114411414511111345111212451113113511211345112121351122123511231125113113351131212511321225113311' +
8784
+ '1511411325114121151142121511431115115121542111121421116154211211451211124421211245121211442122113512211234213112' +
8785
+ '3512221134213211251231122421411225123211242142111512411214215112151242111421521163311111133111521331125154221111' +
8786
+ '5331211145131111442221114331311135132111342231113331411125133111242241112331511115134111142251111331611112411143' +
8787
+ '2241115112411242124113411332115112412151115111342151114211511233215112411151133211511431124211421151214212421241' +
8788
+ '1151224111521133215211411152123211521331124311411152214111531132115312311154113136112112361122112611311226113211' +
8789
+ '1611411216114211452121113612211135213111261231112521411116124111152151111431115113411142134112411251113322511141' +
8790
+ '1251123212511331134211411251214111611124216111321161122321611231116113221161142112521132116121321252123111612231' +
8791
+ '1162112321621131116212221162132112531131116221311163112211631221144111411351113213511231126111232261113112611222' +
8792
+ '1261132113521131126121311262112212621221',
8793
+ '2111115531111163111112462111125431111262111113452111135331111361111114442111145211111543611121141111215521112163' +
8794
+ '6111221311112254211122626111231211112353211123616111241111112452511131146111312211113163511132136111322111113262' +
8795
+ '5111331211113361511134114111411451114122411142135111422141114312411144113111511441115122311152134111522131115312' +
8796
+ '3111541121116114311161222111621331116221211163121112114621121154311211621112124521121253311212611112134421121352' +
8797
+ '1112144321121451111215426112211311122154211221626112221211122253211222616112231111122352111224515112311361123121' +
8798
+ '1112316251123212111232615112331141124113511241214112421241124311311251134112512131125212311253112112611331126121' +
8799
+ '2112621221126311111311452113115331131161111312442113125211131343211313511113144211131541611321121113215321132161' +
8800
+ '6113221111132252111323515113311211133161511332114113411241134211311351123113521121136112211362111114114421141152' +
8801
+ '1114124321141251111413421114144161142111111421521114225151143111411441113114511111151143211511511115124211151341' +
8802
+ '1115215111161142111612411211114622111154321111621211124522111253321112611211134422111352121114432211145112111542' +
8803
+ '6211211312112154221121626211221212112253221122616211231112112352121124515211311362113121121131625211321212113261' +
8804
+ '5211331142114113521141214211421242114311321151134211512132115212321153112211611332116121221162122211631121211145' +
8805
+ '3121115341211161112112362121124431211252112113352121134331211351112114342121144211211533212115411121163212121145' +
8806
+ '2212115332121161112121451212124422121252112122442121225222121351112123431212144211212442121215411121254162122112' +
8807
+ '1212215322122161612131126212221111213153121222526121321111213252121223511121335152123112121231615121411252123211' +
8808
+ '1121416151214211421241124121511242124211412152113212511231216112321252113121621122126112221262111122113621221144' +
8809
+ '3122115211221235212212433122125111221334212213421122143321221441112215321122163112131144221311521122214412131243' +
8810
+ '2213125111222243212222511122234212131441112224416213211112132152612231111122315212132251112232515213311151224111' +
8811
+ '4213411141225111321351113122611122136111112311352123114331231151112312342123124211231333212313411123143211231531' +
8812
+ '1214114322141151112321431214124211232242121413411123234112142151112331511124113421241142112412332124124111241332' +
8813
+ '1124143112151142112421421215124111242241112511332125114111251232112513311216114111252141112611321126123113111145' +
8814
+ '2311115333111161131112442311125213111343231113511311144213111541631121121311215323112161631122111311225213112351' +
8815
+ '5311311213113161531132114311411243114211331151123311521123116112231162111221113622211144322111521221123522211243' +
8816
+ '3221125112211334222113421221143322211441122115321221163113121144231211521221214413121243231212511221224322212251' +
8817
+ '1221234213121441122124416312211113122152622131111221315213122251122132515312311152214111431241114221511133125111' +
8818
+ '3221611123126111213111353131114341311151113112262131123431311242113113252131133331311341113114242131143211311523' +
8819
+ '2131153111311622122211352222114332221151113121351222123422221242113122342131224222221341113123331222143211312432' +
8820
+ '1222153111312531131311432313115112222143131312421131314312222242131313411131324212222341113133411313215112223151' +
8821
+ '1131415111321126213211343132114211321225213212333132124111321324213213321132142321321431113215221132162112231134' +
8822
+ '2223114211322134122312332223124111322233213222411132233212231431113224311314114212232142131412411132314212232241' +
8823
+ '1132324111331125213311333133114111331224213312321133132321331331113314221133152112241133222411411133213312241232' +
8824
+ '1133223212241331113323311315114112242141113331411134112421341132113412232134123111341322113414211225113211342132' +
8825
+ '1225123111342231113511232135113111351222113513211226113111352131113611221136122114111144241111521411124324111251' +
8826
+ '1411134214111441141121521411225154113111441141113411511124116111132111352321114333211151132112342321124213211333' +
8827
+ '2321134113211432132115311412114324121151132121431412124213212242141213411321234114122151132131511231112622311134' +
8828
+ '3231114212311225223112333231124112311324223113321231142322311431123115221231162113221134232211421231213413221233' +
8829
+ '2322124112312233132213321231233213221431123124311413114213222142141312411231314213222241123132412141112531411133' +
8830
+ '4141114111411216214112243141123211411315214113233141133111411414214114221141151321411521114116121232112522321133' +
8831
+ '3232114111412125123212242232123211412224214122322232133111412323123214221141242212321521114125211323113323231141' +
8832
+ '1232213313231232114131331232223213231331114132321232233111413331141411411323214112323141114141411142111621421124' +
8833
+ '3142113211421215214212233142123111421314214213221142141321421421114215121142161112331124223311321142212412331223' +
8834
+ '2233123111422223214222311142232212331421114224211324113212332132132412311142313212332231114232311143111521431123' +
8835
+ '3143113111431214214312221143131321431321114314121143151112341123223411311143212312341222114322221234132111432321' +
8836
+ '1325113112342131114331311144111421441122114412132144122111441312114414111235112211442122123512211144222111451113' +
8837
+ '2145112111451212114513111236112111452121151111432511115115111242151113411511215114211134242111421421123324211241' +
8838
+ '1421133214211431151211421421214215121241142122411331112523311133333111411331122423311232133113232331133113311422' +
8839
+ '1331152114221133242211411331213314221232133122321422133113312331151311411422214113313141124111162241112432411132' +
8840
+ '1241121522411223324112311241131422411322124114132241142112411512124116111332112423321132124121241332122323321231' +
8841
+ '1241222322412231124123221332142112412421142311321332213214231231124131321332223112413231215111153151112341511131' +
8842
+ '2151121431511222215113133151132121511412215115111242111522421123324211311151211512421214224212221151221421512222' +
8843
+ '2242132111512313124214121151241212421511115125111333112323331131124221231333122211513123124222221333132111513222' +
8844
+ '1242232111513321142411311333213112423131115141312152111431521122215212133152122121521312215214111243111422431122' +
8845
+ '1152211412431213224312211152221321522221115223121243141111522411133411221243212213341221115231221243222111523221' +
8846
+ '2153111331531121215312122153131112441113224411211153211312441212115322121244131111532311133511211244212111533121' +
8847
+ '2154111221541211124511121154211212451211115422111611114216111241152111332521114115211232152113311612114115212141' +
8848
+ '1431112424311132143112232431123114311322143114211522113214312132152212311431223113411115234111233341113113411214' +
8849
+ '2341122213411313234113211341141213411511143211232432113113412123234121311341222214321321134123211523113114322131' +
8850
+ '1341313122511114325111222251121332511221225113122251141113421114234211221251211422512122234212211251221313421312' +
8851
+ '1251231213421411125124111433112213422122143312211251312213422221125132213161111341611121316112123161131122521113' +
8852
+ '3252112121612113225212122161221222521311216123111343111323431121125221131343121211613113125222121343131111613212' +
8853
+ '1252231111613311143411211343212112523121116141213162111231621211225311122162211222531211216222111344111212532112' +
8854
+ '1344121111623112125322111162321131631111225411112163211113451111125421111163311116211132162112311531112325311131' +
8855
+ '1531122215311321162211311531213114411114244111221441121324411221144113121441141115321122144121221532122114412221' +
8856
+ '2351111333511121235112122351131114421113244211211351211323512121135122121442131113512311153311211442212113513121' +
8857
+ '3261111232611211235211122261211223521211226122111443111213522112144312111261311213522211126132113262111123531111' +
8858
+ '2262211114441111135321111262311116311122163112211541111325411121154112121541131116321121154121212451111224511211' +
8859
+ '1542111214512112154212111451221133611111',
8860
+ ]);
8861
+
8862
+ function patternFromWidths(sequence) {
8863
+ let pattern = 0;
8864
+ for (let element = 0; element < 8; element++) {
8865
+ const width = sequence.charCodeAt(element) - 48;
8866
+ const dark = (element & 1) === 0;
8867
+ for (let i = 0; i < width; i++) pattern = (pattern << 1) | (dark ? 1 : 0);
8868
+ }
8869
+ return pattern;
8870
+ }
8871
+
8872
+ /** Return the cluster discriminator for an eight-element bar/space sequence. */
8873
+ function pdf417ClusterForWidths(widths) {
8874
+ if (!widths || widths.length !== 8) throw new Error('PDF417: a character requires eight element widths');
8875
+ return ((widths[0] - widths[2] + widths[4] - widths[6]) % 9 + 9) % 9;
8876
+ }
8877
+
8878
+ function buildPatternTable() {
8879
+ const tables = WIDTH_SEQUENCES.map((source, tableIndex) => {
8880
+ if (source.length !== PDF417_CODEWORDS_PER_CLUSTER * 8) throw new Error('PDF417: corrupt pattern-table length');
8881
+ const expectedCluster = PDF417_CLUSTER_NUMBERS[tableIndex];
8882
+ const table = new Int32Array(PDF417_CODEWORDS_PER_CLUSTER);
8883
+ const seen = new Set();
8884
+ for (let codeword = 0; codeword < PDF417_CODEWORDS_PER_CLUSTER; codeword++) {
8885
+ const sequence = source.slice(codeword * 8, codeword * 8 + 8);
8886
+ const widths = Array.from(sequence, (digit) => digit.charCodeAt(0) - 48);
8887
+ if (widths.some((width) => width < 1 || width > 6) || widths.reduce((sum, width) => sum + width, 0) !== 17) {
8888
+ throw new Error('PDF417: corrupt pattern-table width');
8889
+ }
8890
+ if (pdf417ClusterForWidths(widths) !== expectedCluster) throw new Error('PDF417: corrupt pattern-table cluster');
8891
+ const pattern = patternFromWidths(sequence);
8892
+ if (seen.has(pattern)) throw new Error('PDF417: duplicate pattern in cluster');
8893
+ seen.add(pattern);
8894
+ table[codeword] = pattern;
8895
+ }
8896
+ return Object.freeze(Array.from(table));
8897
+ });
8898
+ return Object.freeze(tables);
8899
+ }
8900
+
8901
+ /** Pattern table indexed by cluster index (0, 1, 2) and codeword value. */
8902
+ const PDF417_PATTERN_TABLE = buildPatternTable();
8903
+ const PATTERN_TO_CODEWORD = new Map();
8904
+ for (let index = 0; index < PDF417_PATTERN_TABLE.length; index++) {
8905
+ for (let codeword = 0; codeword < PDF417_CODEWORDS_PER_CLUSTER; codeword++) {
8906
+ PATTERN_TO_CODEWORD.set(PDF417_PATTERN_TABLE[index][codeword], Object.freeze({
8907
+ codeword, cluster: PDF417_CLUSTER_NUMBERS[index],
8908
+ }));
8909
+ }
8910
+ }
8911
+
8912
+ /**
8913
+ * Return the 17-bit bar/space pattern for a codeword in a row cluster.
8914
+ * @param {number} codeword
8915
+ * @param {number} cluster Cluster number 0, 3 or 6.
8916
+ * @returns {number}
8917
+ */
8918
+ function pdf417PatternForCodeword(codeword, cluster) {
8919
+ if (!Number.isInteger(codeword) || codeword < 0 || codeword >= PDF417_CODEWORDS_PER_CLUSTER) throw new Error('PDF417: codeword must be in 0..928');
8920
+ const index = PDF417_CLUSTER_NUMBERS.indexOf(cluster);
8921
+ if (index < 0) throw new Error('PDF417: cluster must be 0, 3 or 6');
8922
+ return PDF417_PATTERN_TABLE[index][codeword];
8923
+ }
8924
+
8925
+ /**
8926
+ * Decode an exact 17-bit symbol-character pattern to its codeword and cluster.
8927
+ * @param {number} pattern
8928
+ * @returns {{codeword: number, cluster: number} | null}
8929
+ */
8930
+ function pdf417CodewordForPattern(pattern) {
8931
+ const result = PATTERN_TO_CODEWORD.get(pattern);
8932
+ return result ? { ...result } : null;
8933
+ }
8934
+
8935
+ __exports.PDF417_CLUSTER_NUMBERS = PDF417_CLUSTER_NUMBERS;
8936
+ __exports.PDF417_CODEWORDS_PER_CLUSTER = PDF417_CODEWORDS_PER_CLUSTER;
8937
+ __exports.pdf417ClusterForWidths = pdf417ClusterForWidths;
8938
+ __exports.PDF417_PATTERN_TABLE = PDF417_PATTERN_TABLE;
8939
+ __exports.pdf417PatternForCodeword = pdf417PatternForCodeword;
8940
+ __exports.pdf417CodewordForPattern = pdf417CodewordForPattern;
8941
+ };
8942
+
8943
+ __modules["pdf417/encoder.js"] = function (__require, __exports) {
8944
+ const { BitMatrix } = __require("core/bit-matrix.js");
8945
+ const { EncodeError } = __require("core/errors.js");
8946
+ const { compactPdf417 } = __require("pdf417/compaction.js");
8947
+ const { pdf417ErrorCorrection, pdf417EccLength } = __require("pdf417/error-correction.js");
8948
+ const { pdf417PatternForCodeword } = __require("pdf417/tables.js");
8949
+
8950
+ const START = '81111113';
8951
+ const STOP = '711311121';
8952
+
8953
+ function append(matrix, y, x, sequence, height) {
8954
+ let dark = true;
8955
+ for (const digit of sequence) {
8956
+ const width = digit.charCodeAt(0) - 48;
8957
+ if (dark) matrix.setRegion(x, y, width, height);
8958
+ x += width; dark = !dark;
8959
+ }
8960
+ return x;
8961
+ }
8962
+ function patternSequence(pattern) { return pattern.toString(2).padStart(17, '0').replace(/0+|1+/g, (run) => String(run.length)); }
8963
+ function indicators(row, rows, cols, level) {
8964
+ const group = Math.floor(row / 3), y = Math.floor((rows - 1) / 3), z = level * 3 + (rows - 1) % 3, v = cols - 1;
8965
+ if (row % 3 === 0) return [30 * group + y, 30 * group + v];
8966
+ if (row % 3 === 1) return [30 * group + z, 30 * group + y];
8967
+ return [30 * group + v, 30 * group + z];
8968
+ }
8969
+ function dimensions(needed, level, options) {
8970
+ for (const [name, value, min, max] of [['rows', options.rows, 3, 90], ['columns', options.columns, 1, 30]]) {
8971
+ if (value !== undefined && (!Number.isInteger(value) || value < min || value > max)) throw new EncodeError(`PDF417: ${name} must be an integer in ${min}..${max}`);
8972
+ }
8973
+ if (options.aspectRatio !== undefined && (!Number.isFinite(options.aspectRatio) || options.aspectRatio <= 0)) throw new EncodeError('PDF417: aspectRatio must be positive');
8974
+ const ecc = pdf417EccLength(level); let best = null;
8975
+ for (let rows = options.rows ?? 3; rows <= (options.rows ?? 90); rows++) for (let cols = options.columns ?? 1; cols <= (options.columns ?? 30); cols++) {
8976
+ if (rows < 3 || rows * cols > 928 || rows * cols - ecc < needed) continue;
8977
+ const ratio = (69 + cols * 17) / (rows * (options.rowHeight ?? 3));
8978
+ const score = (rows * cols - ecc - needed) * 10 + Math.abs(ratio - (options.aspectRatio ?? 3));
8979
+ if (!best || score < best.score) best = { rows, cols, score };
8980
+ }
8981
+ if (!best) throw new EncodeError('PDF417: payload does not fit the requested dimensions and error correction level');
8982
+ return best;
8983
+ }
8984
+ function encodePDF417(value, options = {}) {
8985
+ const level = options.eccLevel ?? 2, rowHeight = options.rowHeight ?? 3;
8986
+ if (!Number.isInteger(rowHeight) || rowHeight < 3) throw new EncodeError('PDF417: rowHeight must be an integer of at least 3');
8987
+ const payload = compactPdf417(value, { compaction: options.compaction });
8988
+ const { rows, cols } = dimensions(payload.length + 1, level, { ...options, rowHeight });
8989
+ const eccLength = pdf417EccLength(level), dataLength = rows * cols - eccLength;
8990
+ const data = [dataLength, ...payload]; while (data.length < dataLength) data.push(900);
8991
+ const codewords = data.concat(pdf417ErrorCorrection(data, level));
8992
+ const matrix = new BitMatrix(69 + cols * 17, rows * rowHeight);
8993
+ for (let row = 0; row < rows; row++) {
8994
+ const y = row * rowHeight, cluster = (row % 3) * 3, [left, right] = indicators(row, rows, cols, level);
8995
+ let x = append(matrix, y, 0, START, rowHeight);
8996
+ x = append(matrix, y, x, patternSequence(pdf417PatternForCodeword(left, cluster)), rowHeight);
8997
+ for (let col = 0; col < cols; col++) x = append(matrix, y, x, patternSequence(pdf417PatternForCodeword(codewords[row * cols + col], cluster)), rowHeight);
8998
+ x = append(matrix, y, x, patternSequence(pdf417PatternForCodeword(right, cluster)), rowHeight);
8999
+ append(matrix, y, x, STOP, rowHeight);
9000
+ }
9001
+ matrix.pdf417 = { rows, columns: cols, eccLevel: level, rowHeight, codewords };
9002
+ return matrix;
9003
+ }
9004
+
9005
+ __exports.encodePDF417 = encodePDF417;
9006
+ };
9007
+
9008
+ __modules["pdf417/decoder.js"] = function (__require, __exports) {
9009
+ const { FormatError } = __require("core/errors.js");
9010
+ const { decodePdf417CompactionDetailed } = __require("pdf417/compaction.js");
9011
+ const { pdf417CorrectErrors, pdf417EccLength } = __require("pdf417/error-correction.js");
9012
+ const { pdf417CodewordForPattern } = __require("pdf417/tables.js");
9013
+
9014
+ const START = '11111111010101000';
9015
+ const STOP = '111111101000101001';
9016
+ function bits(matrix, y, x, width) { let value = 0; for (let i = 0; i < width; i++) value = (value << 1) | (matrix.get(x + i, y) ? 1 : 0); return value; }
9017
+ function indicators(row, rows, cols, level) { const group = Math.floor(row / 3), y = Math.floor((rows - 1) / 3), z = level * 3 + (rows - 1) % 3, v = cols - 1; return row % 3 === 0 ? [30 * group + y, 30 * group + v] : row % 3 === 1 ? [30 * group + z, 30 * group + y] : [30 * group + v, 30 * group + z]; }
9018
+ function decodePDF417(matrix, options = {}) {
9019
+ if (!matrix?.width || !matrix?.height || (matrix.width - 69) % 17) throw new FormatError('PDF417: invalid matrix dimensions');
9020
+ const cols = (matrix.width - 69) / 17, rowHeight = options.rowHeight ?? matrix.pdf417?.rowHeight ?? 3;
9021
+ if (!Number.isInteger(rowHeight) || matrix.height % rowHeight) throw new FormatError('PDF417: invalid row height');
9022
+ const rows = matrix.height / rowHeight;
9023
+ if (rows < 3 || rows > 90 || cols < 1 || cols > 30) throw new FormatError('PDF417: dimensions outside the standard range');
9024
+ const all = [];
9025
+ const erasures = [];
9026
+ for (let row = 0; row < rows; row++) {
9027
+ const y = row * rowHeight, cluster = (row % 3) * 3;
9028
+ if (bits(matrix, y, 0, 17).toString(2).padStart(17, '0') !== START || bits(matrix, y, matrix.width - 18, 18).toString(2).padStart(18, '0') !== STOP) throw new FormatError('PDF417: missing start or stop pattern');
9029
+ const read = (x) => { const result = pdf417CodewordForPattern(bits(matrix, y, x, 17)); if (!result || result.cluster !== cluster) throw new FormatError('PDF417: invalid codeword pattern'); return result.codeword; };
9030
+ const left = read(17), right = read(34 + cols * 17);
9031
+ let matched = false; for (let level = 0; level <= 8; level++) { const expected = indicators(row, rows, cols, level); if (left === expected[0] && right === expected[1]) { matched = true; break; } }
9032
+ if (!matched) throw new FormatError('PDF417: row indicator mismatch');
9033
+ for (let col = 0; col < cols; col++) {
9034
+ try { all.push(read(34 + col * 17)); }
9035
+ catch {
9036
+ erasures.push(all.length);
9037
+ all.push(0);
9038
+ }
9039
+ }
9040
+ }
9041
+ let level = -1; for (let candidate = 0; candidate <= 8; candidate++) if (all.length > pdf417EccLength(candidate)) { level = candidate; break; }
9042
+ // The row indicators determine the level uniquely across the symbol.
9043
+ for (let candidate = 0; candidate <= 8; candidate++) {
9044
+ let ok = true; for (let row = 0; row < rows; row++) { const y = row * rowHeight, cluster = (row % 3) * 3, expected = indicators(row, rows, cols, candidate); const left = pdf417CodewordForPattern(bits(matrix, y, 17, 17)); const right = pdf417CodewordForPattern(bits(matrix, y, 34 + cols * 17, 17)); if (!left || !right || left.cluster !== cluster || right.cluster !== cluster || left.codeword !== expected[0] || right.codeword !== expected[1]) { ok = false; break; } } if (ok) { level = candidate; break; }
9045
+ }
9046
+ if (level < 0) throw new FormatError('PDF417: could not determine error correction level');
9047
+ const corrected = all.slice(); const corrections = pdf417CorrectErrors(corrected, level, erasures);
9048
+ const length = corrected[0]; if (length < 1 || length > corrected.length - pdf417EccLength(level)) throw new FormatError('PDF417: invalid symbol length descriptor');
9049
+ const payload = corrected.slice(1, length);
9050
+ const decoded = decodePdf417CompactionDetailed(payload);
9051
+ return { ...decoded, codewords: corrected, rows, columns: cols, eccLevel: level, corrections };
9052
+ }
9053
+
9054
+ __exports.decodePDF417 = decodePDF417;
9055
+ };
9056
+
9057
+ __modules["pdf417/detector.js"] = function (__require, __exports) {
9058
+ const { BitMatrix } = __require("core/bit-matrix.js");
9059
+ const { sampleGrid, sampleGridVoting } = __require("image/grid-sampler.js");
9060
+ const { PerspectiveTransform } = __require("image/perspective.js");
9061
+ const { decodePDF417 } = __require("pdf417/decoder.js");
9062
+
9063
+ const START_PATTERN = [8, 1, 1, 1, 1, 1, 1, 3];
9064
+ const STOP_PATTERN = [7, 1, 1, 3, 1, 1, 1, 2, 1];
9065
+ const SCAN_ANGLES = [0, -4, 4, -8, 8, -14, 14, -22, 22, -32, 32]
9066
+ .map((degrees) => degrees * Math.PI / 180);
9067
+
9068
+ function rotateClockwise(source) {
9069
+ const rotated = new BitMatrix(source.height, source.width);
9070
+ for (let y = 0; y < source.height; y++) for (let x = 0; x < source.width; x++) {
9071
+ if (source.get(x, y)) rotated.set(source.height - 1 - y, x);
9072
+ }
9073
+ return rotated;
9074
+ }
9075
+
9076
+ function scanGeometry(angle) {
9077
+ return {
9078
+ along: { x: Math.cos(angle), y: Math.sin(angle) },
9079
+ across: { x: -Math.sin(angle), y: Math.cos(angle) },
9080
+ };
9081
+ }
9082
+
9083
+ function projectionRange(image, vector) {
9084
+ const points = [
9085
+ { x: 0, y: 0 }, { x: image.width - 1, y: 0 },
9086
+ { x: image.width - 1, y: image.height - 1 }, { x: 0, y: image.height - 1 },
9087
+ ];
9088
+ const values = points.map((point) => point.x * vector.x + point.y * vector.y);
9089
+ return { min: Math.min(...values), max: Math.max(...values) };
9090
+ }
9091
+
9092
+ function lineRange(image, geometry, across) {
9093
+ const { along, across: normal } = geometry;
9094
+ let min = -Infinity, max = Infinity;
9095
+ const constrain = (low, high, step, offset) => {
9096
+ if (Math.abs(step) < 1e-9) return offset >= low && offset <= high;
9097
+ const first = (low - offset) / step, second = (high - offset) / step;
9098
+ min = Math.max(min, Math.min(first, second));
9099
+ max = Math.min(max, Math.max(first, second));
9100
+ return min <= max;
9101
+ };
9102
+ if (!constrain(0, image.width - 1, along.x, normal.x * across) ||
9103
+ !constrain(0, image.height - 1, along.y, normal.y * across)) return null;
9104
+ return { min: Math.ceil(min), max: Math.floor(max) };
9105
+ }
9106
+
9107
+ function runsInLine(image, geometry, across) {
9108
+ const range = lineRange(image, geometry, across);
9109
+ if (!range || range.max < range.min) return [];
9110
+ const { along, across: normal } = geometry;
9111
+ const runs = [];
9112
+ const valueAt = (position) => image.get(
9113
+ Math.round(along.x * position + normal.x * across),
9114
+ Math.round(along.y * position + normal.y * across),
9115
+ );
9116
+ let dark = valueAt(range.min), start = range.min;
9117
+ for (let position = range.min + 1; position <= range.max + 1; position++) {
9118
+ const value = position <= range.max ? valueAt(position) : !dark;
9119
+ if (value !== dark) {
9120
+ runs.push({ dark, start, end: position, length: position - start });
9121
+ start = position; dark = value;
9122
+ }
9123
+ }
9124
+ return runs;
9125
+ }
9126
+
9127
+ function removeSinglePixelSpecks(runs) {
9128
+ const clean = runs.map((run) => ({ ...run }));
9129
+ for (let index = 1; index < clean.length - 1;) {
9130
+ if (clean[index].length > 1) { index++; continue; }
9131
+ const merged = {
9132
+ dark: clean[index - 1].dark,
9133
+ start: clean[index - 1].start,
9134
+ end: clean[index + 1].end,
9135
+ length: clean[index - 1].length + clean[index].length + clean[index + 1].length,
9136
+ };
9137
+ clean.splice(index - 1, 3, merged);
9138
+ index = Math.max(1, index - 2);
9139
+ }
9140
+ return clean;
9141
+ }
9142
+
9143
+ function matchPattern(runs, at, expected) {
9144
+ if (at + expected.length > runs.length || !runs[at].dark) return null;
9145
+ let observed = 0, modules = 0;
9146
+ for (let i = 0; i < expected.length; i++) { observed += runs[at + i].length; modules += expected[i]; }
9147
+ const scale = observed / modules;
9148
+ if (scale < 0.65) return null;
9149
+ let error = 0;
9150
+ for (let i = 0; i < expected.length; i++) {
9151
+ const delta = Math.abs(runs[at + i].length - expected[i] * scale);
9152
+ if (delta > Math.max(1.15, scale * 0.62)) return null;
9153
+ error += delta / scale;
9154
+ }
9155
+ if (error / expected.length > 0.48) return null;
9156
+ return { start: runs[at].start, end: runs[at + expected.length - 1].end, scale, error, at };
9157
+ }
9158
+
9159
+ function quietPenalty(runs, start, stop) {
9160
+ const before = start.at > 0 ? runs[start.at - 1].length / start.scale : 0;
9161
+ const afterIndex = stop.at + STOP_PATTERN.length;
9162
+ const after = afterIndex < runs.length ? runs[afterIndex].length / stop.scale : 0;
9163
+ return Math.max(0, 2 - before) + Math.max(0, 2 - after);
9164
+ }
9165
+
9166
+ function patternPairs(runs) {
9167
+ const starts = [], stops = [];
9168
+ for (let at = 0; at < runs.length; at++) {
9169
+ const start = matchPattern(runs, at, START_PATTERN); if (start) starts.push(start);
9170
+ const stop = matchPattern(runs, at, STOP_PATTERN); if (stop) stops.push(stop);
9171
+ }
9172
+ const pairs = [];
9173
+ for (const start of starts) for (const stop of stops) {
9174
+ if (stop.start <= start.end) continue;
9175
+ const measured = stop.end - start.start;
9176
+ const localScale = Math.sqrt(start.scale * stop.scale);
9177
+ const roughColumns = Math.round((measured / localScale - 69) / 17);
9178
+ for (let columns = Math.max(1, roughColumns - 2); columns <= Math.min(30, roughColumns + 2); columns++) {
9179
+ const width = 69 + columns * 17;
9180
+ const globalScale = measured / width;
9181
+ const ratio = Math.max(start.scale, stop.scale, globalScale) /
9182
+ Math.min(start.scale, stop.scale, globalScale);
9183
+ if (ratio > 1.85) continue;
9184
+ const scaleError = Math.abs(Math.log(start.scale / globalScale)) +
9185
+ Math.abs(Math.log(stop.scale / globalScale));
9186
+ pairs.push({ start, stop, columns, width, scale: globalScale,
9187
+ startScale: start.scale, stopScale: stop.scale,
9188
+ error: start.error + stop.error + scaleError * 4 + quietPenalty(runs, start, stop) * 0.15 });
9189
+ }
9190
+ }
9191
+ pairs.sort((a, b) => a.error - b.error);
9192
+ const used = new Set();
9193
+ return pairs.filter((pair) => {
9194
+ if (used.has(pair.columns)) return false;
9195
+ used.add(pair.columns);
9196
+ return used.size <= 3;
9197
+ });
9198
+ }
9199
+
9200
+ function pointAt(geometry, along, across) {
9201
+ return {
9202
+ x: geometry.along.x * along + geometry.across.x * across,
9203
+ y: geometry.along.y * along + geometry.across.y * across,
9204
+ };
9205
+ }
9206
+
9207
+ function scanHits(image, angle) {
9208
+ const geometry = scanGeometry(angle);
9209
+ const range = projectionRange(image, geometry.across);
9210
+ const hits = [];
9211
+ for (let across = Math.ceil(range.min); across <= Math.floor(range.max); across++) {
9212
+ const raw = runsInLine(image, geometry, across);
9213
+ let pairs = patternPairs(raw);
9214
+ if (!pairs.length) pairs = patternPairs(removeSinglePixelSpecks(raw));
9215
+ for (const pair of pairs) {
9216
+ hits.push({ across, left: pair.start.start, right: pair.stop.end,
9217
+ leftPoint: pointAt(geometry, pair.start.start, across),
9218
+ rightPoint: pointAt(geometry, pair.stop.end, across),
9219
+ scale: pair.scale, startScale: pair.startScale, stopScale: pair.stopScale,
9220
+ columns: pair.columns, width: pair.width, error: pair.error,
9221
+ geometry });
9222
+ }
9223
+ }
9224
+ return hits;
9225
+ }
9226
+
9227
+ function fittedLine(hits, key) {
9228
+ const meanX = hits.reduce((sum, hit) => sum + hit.across, 0) / hits.length;
9229
+ const meanY = hits.reduce((sum, hit) => sum + hit[key], 0) / hits.length;
9230
+ let covariance = 0, variance = 0;
9231
+ for (const hit of hits) {
9232
+ const delta = hit.across - meanX;
9233
+ covariance += delta * (hit[key] - meanY);
9234
+ variance += delta * delta;
9235
+ }
9236
+ const slope = variance ? covariance / variance : 0;
9237
+ return { slope, intercept: meanY - slope * meanX };
9238
+ }
9239
+
9240
+ function lineValue(line, at) {
9241
+ return line.intercept + line.slope * at;
9242
+ }
9243
+
9244
+ function finderExtent(image, hits, edgeKey, scaleKey, centreModules, fallback) {
9245
+ const geometry = hits[0].geometry;
9246
+ const edge = fittedLine(hits, edgeKey), scale = fittedLine(hits, scaleKey);
9247
+ const span = fallback.bottom - fallback.top;
9248
+ const projection = projectionRange(image, geometry.across);
9249
+ const margin = Math.max(6, span * 0.35, hits[0].scale * 4);
9250
+ const start = Math.max(Math.ceil(projection.min), Math.floor(fallback.top - margin));
9251
+ const end = Math.min(Math.floor(projection.max), Math.ceil(fallback.bottom + margin));
9252
+ const values = [];
9253
+ for (let across = start; across <= end; across++) {
9254
+ const localScale = Math.max(0.5, lineValue(scale, across));
9255
+ const centre = lineValue(edge, across) + localScale * centreModules;
9256
+ let dark = 0;
9257
+ for (const offset of [-0.75, 0, 0.75]) {
9258
+ const point = pointAt(geometry, centre + localScale * offset, across);
9259
+ if (image.get(Math.round(point.x), Math.round(point.y))) dark++;
9260
+ }
9261
+ values.push({ across, dark: dark >= 2 });
9262
+ }
9263
+ const segments = [];
9264
+ let first = null, lastDark = null, gap = 0;
9265
+ for (const value of values) {
9266
+ if (value.dark) {
9267
+ if (first === null) first = value.across;
9268
+ lastDark = value.across; gap = 0;
9269
+ } else if (first !== null && ++gap > 2) {
9270
+ segments.push({ top: first, bottom: lastDark + 1 });
9271
+ first = null; lastDark = null; gap = 0;
9272
+ }
9273
+ }
9274
+ if (first !== null) segments.push({ top: first, bottom: lastDark + 1 });
9275
+ let best = null, bestScore = -Infinity;
9276
+ for (const segment of segments) {
9277
+ const overlap = Math.max(0, Math.min(segment.bottom, fallback.bottom) - Math.max(segment.top, fallback.top));
9278
+ const score = overlap * 4 + (segment.bottom - segment.top) -
9279
+ Math.abs((segment.top + segment.bottom) / 2 - (fallback.top + fallback.bottom) / 2);
9280
+ if (overlap >= span * 0.55 && score > bestScore) { best = segment; bestScore = score; }
9281
+ }
9282
+ return best ?? fallback;
9283
+ }
9284
+
9285
+ function intersectBoundary(leftAcross, rightAcross, leftCentre, rightCentre, leftEdge, rightEdge) {
9286
+ const delta = rightCentre - leftCentre;
9287
+ if (Math.abs(delta) < 1e-6) return { left: leftAcross, right: rightAcross };
9288
+ const slope = (rightAcross - leftAcross) / delta;
9289
+ const intercept = leftAcross - slope * leftCentre;
9290
+ const atEdge = (edge, fallback) => {
9291
+ const denominator = 1 - slope * edge.slope;
9292
+ return Math.abs(denominator) < 1e-6 ? fallback :
9293
+ (slope * edge.intercept + intercept) / denominator;
9294
+ };
9295
+ return { left: atEdge(leftEdge, leftAcross), right: atEdge(rightEdge, rightAcross) };
9296
+ }
9297
+
9298
+ function groupHits(hits, image) {
9299
+ const groups = [];
9300
+ for (const hit of hits.sort((a, b) => a.across - b.across || a.error - b.error)) {
9301
+ let best = null, bestDistance = Infinity;
9302
+ for (const group of groups) {
9303
+ const last = group.hits[group.hits.length - 1];
9304
+ const gap = hit.across - last.across;
9305
+ if (hit.columns !== last.columns || gap <= 0 || gap > Math.max(5, hit.scale * 3)) continue;
9306
+ const scaleRatio = Math.max(hit.scale, last.scale) / Math.min(hit.scale, last.scale);
9307
+ if (scaleRatio > 1.45) continue;
9308
+ const tolerance = Math.max(5, hit.scale * 4 + gap);
9309
+ const distance = Math.abs(hit.left - last.left) + Math.abs(hit.right - last.right);
9310
+ if (distance > tolerance * 2 || distance >= bestDistance) continue;
9311
+ best = group; bestDistance = distance;
9312
+ }
9313
+ if (best) best.hits.push(hit); else groups.push({ hits: [hit] });
9314
+ }
9315
+ const candidates = [];
9316
+ for (const group of groups) {
9317
+ const rows = group.hits;
9318
+ const scale = rows.reduce((sum, hit) => sum + hit.scale, 0) / rows.length;
9319
+ const top = rows[0].across, bottom = rows[rows.length - 1].across + 1;
9320
+ const span = bottom - top;
9321
+ if (rows.length < 4 || span < Math.max(6, scale * 4) || rows.length / span < 0.28) continue;
9322
+ const geometry = rows[0].geometry;
9323
+ const left = fittedLine(rows, 'left'), right = fittedLine(rows, 'right');
9324
+ const startScale = fittedLine(rows, 'startScale'), stopScale = fittedLine(rows, 'stopScale');
9325
+ const fallback = { top, bottom };
9326
+ const leftExtent = image ? finderExtent(image, rows, 'left', 'startScale', 4, fallback) : fallback;
9327
+ const rightExtent = image ? finderExtent(image, rows, 'right', 'stopScale', -14.5, fallback) : fallback;
9328
+ const boundary = (leftAcross, rightAcross) => intersectBoundary(
9329
+ leftAcross,
9330
+ rightAcross,
9331
+ lineValue(left, leftAcross) + lineValue(startScale, leftAcross) * 4,
9332
+ lineValue(right, rightAcross) - lineValue(stopScale, rightAcross) * 14.5,
9333
+ left,
9334
+ right,
9335
+ );
9336
+ const topBoundary = boundary(leftExtent.top, rightExtent.top);
9337
+ const bottomBoundary = boundary(leftExtent.bottom, rightExtent.bottom);
9338
+ candidates.push({
9339
+ width: rows[0].width, scale,
9340
+ corners: [
9341
+ pointAt(geometry, lineValue(left, topBoundary.left), topBoundary.left),
9342
+ pointAt(geometry, lineValue(right, topBoundary.right), topBoundary.right),
9343
+ pointAt(geometry, lineValue(right, bottomBoundary.right), bottomBoundary.right),
9344
+ pointAt(geometry, lineValue(left, bottomBoundary.left), bottomBoundary.left),
9345
+ ],
9346
+ score: rows.length / span * 8 + Math.log2(rows.length + 1) * 2 -
9347
+ rows.reduce((sum, hit) => sum + hit.error, 0) / rows.length,
9348
+ });
9349
+ }
9350
+ return candidates.sort((a, b) => b.score - a.score);
9351
+ }
9352
+
9353
+ function validQuadrilateral(value) {
9354
+ return Array.isArray(value) && value.length === 4 &&
9355
+ value.every((point) => Number.isFinite(point?.x) && Number.isFinite(point?.y));
9356
+ }
9357
+
9358
+ function manualCandidate(corners) {
9359
+ const horizontal = (Math.hypot(corners[1].x - corners[0].x, corners[1].y - corners[0].y) +
9360
+ Math.hypot(corners[2].x - corners[3].x, corners[2].y - corners[3].y)) / 2;
9361
+ const vertical = (Math.hypot(corners[3].x - corners[0].x, corners[3].y - corners[0].y) +
9362
+ Math.hypot(corners[2].x - corners[1].x, corners[2].y - corners[1].y)) / 2;
9363
+ const out = [];
9364
+ for (let columns = 1; columns <= 30; columns++) {
9365
+ const width = 69 + columns * 17, scale = horizontal / width;
9366
+ if (scale < 0.65) continue;
9367
+ const moduleHeight = vertical / scale;
9368
+ let plausibility = Infinity;
9369
+ for (let rowHeight = 3; rowHeight <= 12; rowHeight++) {
9370
+ const rows = Math.round(moduleHeight / rowHeight);
9371
+ if (rows >= 3 && rows <= 90) plausibility = Math.min(plausibility,
9372
+ Math.abs(moduleHeight - rows * rowHeight) / rowHeight);
9373
+ }
9374
+ out.push({ width, scale, corners, score: Number.isFinite(plausibility) ? 2 - plausibility : 0 });
9375
+ }
9376
+ return out.sort((a, b) => b.score - a.score);
9377
+ }
9378
+
9379
+ function edgeLength(first, second) {
9380
+ return Math.hypot(second.x - first.x, second.y - first.y);
9381
+ }
9382
+
9383
+ function rowCandidates(candidate, options) {
9384
+ const vertical = (edgeLength(candidate.corners[0], candidate.corners[3]) +
9385
+ edgeLength(candidate.corners[1], candidate.corners[2])) / 2;
9386
+ const allowedHeights = Number.isInteger(options.rowHeight) ? [options.rowHeight] :
9387
+ Array.from({ length: 10 }, (_, index) => index + 3);
9388
+ const rows = [];
9389
+ for (let value = 3; value <= 90; value++) {
9390
+ let score = Infinity;
9391
+ for (const rowHeight of allowedHeights) {
9392
+ score = Math.min(score, Math.abs(Math.log(vertical / (candidate.scale * value * rowHeight))));
9393
+ }
9394
+ rows.push({ value, score });
9395
+ }
9396
+ return rows.sort((a, b) => a.score - b.score).map((entry) => entry.value);
9397
+ }
9398
+
9399
+ function shiftedCandidate(candidate, amount) {
9400
+ if (!amount) return candidate;
9401
+ const topDx = candidate.corners[3].x - candidate.corners[0].x;
9402
+ const topDy = candidate.corners[3].y - candidate.corners[0].y;
9403
+ const bottomDx = candidate.corners[2].x - candidate.corners[1].x;
9404
+ const bottomDy = candidate.corners[2].y - candidate.corners[1].y;
9405
+ const topLength = Math.hypot(topDx, topDy) || 1;
9406
+ const bottomLength = Math.hypot(bottomDx, bottomDy) || 1;
9407
+ return { ...candidate, corners: [
9408
+ { x: candidate.corners[0].x - topDx / topLength * amount, y: candidate.corners[0].y - topDy / topLength * amount },
9409
+ { x: candidate.corners[1].x - bottomDx / bottomLength * amount, y: candidate.corners[1].y - bottomDy / bottomLength * amount },
9410
+ { x: candidate.corners[2].x + bottomDx / bottomLength * amount, y: candidate.corners[2].y + bottomDy / bottomLength * amount },
9411
+ { x: candidate.corners[3].x + topDx / topLength * amount, y: candidate.corners[3].y + topDy / topLength * amount },
9412
+ ] };
9413
+ }
9414
+
9415
+ function canonicalRows(matrix) {
9416
+ const rowHeight = 3;
9417
+ const out = new BitMatrix(matrix.width, matrix.height * rowHeight);
9418
+ for (let y = 0; y < matrix.height; y++) for (let x = 0; x < matrix.width; x++) {
9419
+ if (matrix.get(x, y)) out.setRegion(x, y * rowHeight, 1, rowHeight);
9420
+ }
9421
+ return out;
9422
+ }
9423
+
9424
+ function sampleCandidate(image, candidate, options) {
9425
+ for (const edgeShift of [0, candidate.scale * 0.35, -candidate.scale * 0.25]) {
9426
+ const geometry = shiftedCandidate(candidate, edgeShift);
9427
+ for (const rows of rowCandidates(geometry, options)) {
9428
+ const transform = PerspectiveTransform.quadToQuad(0, 0, geometry.width, 0, geometry.width, rows, 0, rows,
9429
+ geometry.corners[0].x, geometry.corners[0].y, geometry.corners[1].x, geometry.corners[1].y,
9430
+ geometry.corners[2].x, geometry.corners[2].y, geometry.corners[3].x, geometry.corners[3].y);
9431
+ for (const voting of [false, true]) {
9432
+ let matrix;
9433
+ try { matrix = voting ? sampleGridVoting(image, geometry.width, rows, transform) : sampleGrid(image, geometry.width, rows, transform); }
9434
+ catch { continue; }
9435
+ try {
9436
+ const result = decodePDF417(matrix, { ...options, rowHeight: 1 });
9437
+ return { matrix: canonicalRows(matrix), result, corners: geometry.corners };
9438
+ }
9439
+ catch { /* Try the next geometry. */ }
9440
+ }
9441
+ }
9442
+ }
9443
+ return null;
9444
+ }
9445
+
9446
+ function automaticCandidates(image) {
9447
+ const out = [];
9448
+ for (const angle of SCAN_ANGLES) {
9449
+ out.push(...groupHits(scanHits(image, angle), image));
9450
+ if (out.length) break;
9451
+ }
9452
+ return out;
9453
+ }
9454
+
9455
+ function detectInOrientation(image, options, supplied) {
9456
+ for (const candidate of [...supplied, ...automaticCandidates(image)]) {
9457
+ const decoded = sampleCandidate(image, candidate, options);
9458
+ if (decoded) return { candidate, decoded };
9459
+ }
9460
+ if (supplied.length) return null;
9461
+ for (const angle of SCAN_ANGLES.slice(1)) {
9462
+ const candidates = groupHits(scanHits(image, angle), image);
9463
+ for (const candidate of candidates) {
9464
+ const decoded = sampleCandidate(image, candidate, options);
9465
+ if (decoded) return { candidate, decoded };
9466
+ }
9467
+ }
9468
+ return null;
9469
+ }
9470
+
9471
+ /*
9472
+ * Locate a binarized raster symbol from its repeated start and stop patterns.
9473
+ * The detector estimates a projective quadrilateral; grayscale binarization
9474
+ * remains the caller's responsibility.
9475
+ */
9476
+ function detectPDF417(binaryImage, options = {}) {
9477
+ if (!binaryImage?.width || !binaryImage?.height || typeof binaryImage.get !== 'function') return null;
9478
+ let oriented = binaryImage;
9479
+ let toOriginal = (point) => ({ x: point.x, y: point.y });
9480
+ for (let turns = 0; turns < 4; turns++) {
9481
+ const supplied = turns === 0 && validQuadrilateral(options.quadrilateral)
9482
+ ? manualCandidate(options.quadrilateral.map(({ x, y }) => ({ x, y }))) : [];
9483
+ const found = detectInOrientation(oriented, options, supplied);
9484
+ if (found) {
9485
+ return { matrix: found.decoded.matrix, rotation: turns * 90,
9486
+ corners: found.decoded.corners.map(toOriginal), ...found.decoded.result };
9487
+ }
9488
+ const previous = oriented, previousToOriginal = toOriginal;
9489
+ oriented = rotateClockwise(previous);
9490
+ toOriginal = (point) => previousToOriginal({ x: point.y, y: previous.height - point.x });
9491
+ }
9492
+ return null;
9493
+ }
9494
+ function detectAndDecodePDF417(binaryImage, options = {}) { return detectPDF417(binaryImage, options); }
9495
+
9496
+ __exports.detectPDF417 = detectPDF417;
9497
+ __exports.detectAndDecodePDF417 = detectAndDecodePDF417;
9498
+ };
9499
+
9500
+ __modules["pdf417/index.js"] = function (__require, __exports) {
9501
+ const __reexport0 = __require("pdf417/compaction.js"); __exports.compactPdf417 = __reexport0.compactPdf417; __exports.compactPdf417Text = __reexport0.compactPdf417Text; __exports.compactPdf417Bytes = __reexport0.compactPdf417Bytes; __exports.compactPdf417Numeric = __reexport0.compactPdf417Numeric; __exports.decodePdf417Compaction = __reexport0.decodePdf417Compaction; __exports.decodePdf417CompactionDetailed = __reexport0.decodePdf417CompactionDetailed;
9502
+ const __reexport1 = __require("pdf417/encoder.js"); __exports.encodePDF417 = __reexport1.encodePDF417;
9503
+ const __reexport2 = __require("pdf417/decoder.js"); __exports.decodePDF417 = __reexport2.decodePDF417;
9504
+ const __reexport3 = __require("pdf417/detector.js"); __exports.detectPDF417 = __reexport3.detectPDF417; __exports.detectAndDecodePDF417 = __reexport3.detectAndDecodePDF417;
9505
+ const __reexport4 = __require("pdf417/error-correction.js"); __exports.pdf417EccLength = __reexport4.pdf417EccLength; __exports.pdf417ErrorCorrection = __reexport4.pdf417ErrorCorrection; __exports.pdf417CorrectErrors = __reexport4.pdf417CorrectErrors;
9506
+ const __reexport5 = __require("pdf417/tables.js"); __exports.PDF417_PATTERN_TABLE = __reexport5.PDF417_PATTERN_TABLE; __exports.PDF417_CLUSTER_NUMBERS = __reexport5.PDF417_CLUSTER_NUMBERS; __exports.pdf417PatternForCodeword = __reexport5.pdf417PatternForCodeword; __exports.pdf417CodewordForPattern = __reexport5.pdf417CodewordForPattern;
9507
+
9508
+
9509
+ };
9510
+
9511
+ __modules["micropdf417/tables.js"] = function (__require, __exports) {
9512
+ /**
9513
+ * MicroPDF417 format facts and Row Address Pattern (RAP) helpers.
9514
+ *
9515
+ * The tables are represented as compact, immutable data and are guarded by
9516
+ * {@link validateMicroPdf417Tables}. They are deliberately separate from the
9517
+ * PDF417 symbol-character table: MicroPDF417 has a fixed family of symbols and
9518
+ * its own row-address system.
9519
+ *
9520
+ * Values are derived from publicly available symbology documentation and
9521
+ * independently checked against black-box reference output. This module makes
9522
+ * no certification or conformance claim.
9523
+ *
9524
+ * @module micropdf417/tables
9525
+ */
9526
+
9527
+ const variant = (id, columns, rows, eccCodewords, rapStart, rapRotation) => Object.freeze({
9528
+ id,
9529
+ columns,
9530
+ rows,
9531
+ totalCodewords: columns * rows,
9532
+ dataCodewords: columns * rows - eccCodewords,
9533
+ eccCodewords,
9534
+ rapStart,
9535
+ rapRotation,
9536
+ });
9537
+
9538
+ /** All 34 predefined MicroPDF417 symbol variants, in format-table order. */
9539
+ const MICROPDF417_VARIANTS = Object.freeze([
9540
+ variant(1, 1, 11, 7, 1, 8), variant(2, 1, 14, 7, 8, 0),
9541
+ variant(3, 1, 17, 7, 36, 0), variant(4, 1, 20, 8, 19, 0),
9542
+ variant(5, 1, 24, 8, 9, 8), variant(6, 1, 28, 8, 25, 8),
9543
+ variant(7, 2, 8, 8, 1, 0), variant(8, 2, 11, 9, 1, 8),
9544
+ variant(9, 2, 14, 9, 8, 0), variant(10, 2, 17, 10, 36, 0),
9545
+ variant(11, 2, 20, 11, 19, 0), variant(12, 2, 23, 13, 9, 8),
9546
+ variant(13, 2, 26, 15, 27, 8),
9547
+ variant(14, 3, 6, 12, 1, 0), variant(15, 3, 8, 14, 7, 0),
9548
+ variant(16, 3, 10, 16, 15, 0), variant(17, 3, 12, 18, 25, 0),
9549
+ variant(18, 3, 15, 21, 37, 0), variant(19, 3, 20, 26, 1, 16),
9550
+ variant(20, 3, 26, 32, 1, 8), variant(21, 3, 32, 38, 21, 8),
9551
+ variant(22, 3, 38, 44, 15, 16), variant(23, 3, 44, 50, 1, 24),
9552
+ variant(24, 4, 4, 8, 47, 24), variant(25, 4, 6, 12, 1, 0),
9553
+ variant(26, 4, 8, 14, 7, 0), variant(27, 4, 10, 16, 15, 0),
9554
+ variant(28, 4, 12, 18, 25, 0), variant(29, 4, 15, 21, 37, 0),
9555
+ variant(30, 4, 20, 26, 1, 16), variant(31, 4, 26, 32, 1, 8),
9556
+ variant(32, 4, 32, 38, 21, 8), variant(33, 4, 38, 44, 15, 16),
9557
+ variant(34, 4, 44, 50, 1, 24),
9558
+ ]);
9559
+
9560
+ const byId = new Map(MICROPDF417_VARIANTS.map((entry) => [entry.id, entry]));
9561
+
9562
+ // Six run widths, ordered bar-space-bar-space-bar-space. A RAP is ten
9563
+ // modules wide; the right RAP has one additional one-module stop bar when
9564
+ // rendered. Keeping runs rather than bitmap literals makes each invariant
9565
+ // inspectable and avoids a rendering-specific representation here.
9566
+ const SIDE_RAP_RUNS = Object.freeze([
9567
+ '221311', '311311', '312211', '222211', '213211', '214111', '223111', '313111',
9568
+ '322111', '412111', '421111', '331111', '241111', '232111', '231211', '321211',
9569
+ '411211', '411121', '411112', '321112', '312112', '311212', '311221', '311131',
9570
+ '311122', '311113', '221113', '221122', '221131', '221221', '222121', '312121',
9571
+ '321121', '231121', '231112', '222112', '213112', '212212', '212221', '212131',
9572
+ '212122', '212113', '211213', '211123', '211132', '211141', '211231', '211222',
9573
+ '211312', '211321', '211411', '212311',
9574
+ ]);
9575
+
9576
+ const CENTER_RAP_RUNS = Object.freeze([
9577
+ '112231', '121231', '122131', '131131', '131221', '132121', '141121', '141211',
9578
+ '142111', '133111', '132211', '131311', '122311', '123211', '124111', '115111',
9579
+ '114211', '114121', '123121', '123112', '122212', '122221', '121321', '121411',
9580
+ '112411', '113311', '113221', '113212', '113122', '122122', '131122', '131113',
9581
+ '122113', '113113', '112213', '112222', '112312', '112321', '111421', '111331',
9582
+ '111322', '111232', '111223', '111133', '111124', '111214', '112114', '121114',
9583
+ '121123', '121132', '112132', '112141',
9584
+ ]);
9585
+
9586
+ /** @param {number} value @param {number} offset @returns {number} */
9587
+ function microPdf417NextRap(value, offset = 1) {
9588
+ if (!Number.isInteger(value) || value < 1 || value > 52) throw new RangeError('MicroPDF417: RAP number must be in 1..52');
9589
+ if (!Number.isInteger(offset)) throw new RangeError('MicroPDF417: RAP offset must be an integer');
9590
+ return ((value - 1 + offset) % 52 + 52) % 52 + 1;
9591
+ }
9592
+
9593
+ /** @param {number} id @returns {Readonly<typeof MICROPDF417_VARIANTS[number]>} */
9594
+ function microPdf417VariantByNumber(id) {
9595
+ const entry = byId.get(id);
9596
+ if (!entry) throw new RangeError('MicroPDF417: variant must be an integer in 1..34');
9597
+ return entry;
9598
+ }
9599
+
9600
+ /**
9601
+ * Return the smallest data-region candidate that fits `codewords`.
9602
+ * Ties are resolved by width, then height, so selection is deterministic.
9603
+ */
9604
+ function microPdf417VariantForCapacity(codewords) {
9605
+ if (!Number.isInteger(codewords) || codewords < 1) throw new RangeError('MicroPDF417: codeword capacity must be a positive integer');
9606
+ const candidates = MICROPDF417_VARIANTS.filter((entry) => entry.dataCodewords >= codewords);
9607
+ if (!candidates.length) throw new RangeError('MicroPDF417: payload exceeds the largest symbol data region');
9608
+ return candidates.slice().sort((a, b) => a.totalCodewords - b.totalCodewords || a.columns - b.columns || a.rows - b.rows)[0];
9609
+ }
9610
+
9611
+ /** Return the six bar/space run widths for a numbered side or center RAP. */
9612
+ function microPdf417RapSequence(number, kind = 'side') {
9613
+ if (!Number.isInteger(number) || number < 1 || number > 52) throw new RangeError('MicroPDF417: RAP number must be in 1..52');
9614
+ if (kind === 'side') return SIDE_RAP_RUNS[number - 1];
9615
+ if (kind === 'center') return CENTER_RAP_RUNS[number - 1];
9616
+ throw new RangeError('MicroPDF417: RAP kind must be side or center');
9617
+ }
9618
+
9619
+ /**
9620
+ * Resolve all row-address data for a zero-based row within a variant.
9621
+ * @returns {{left: number, center: number|null, right: number, cluster: 0|3|6}}
9622
+ */
9623
+ function microPdf417RowAddress(entry, row) {
9624
+ if (!entry || !Number.isInteger(entry.columns) || !Number.isInteger(entry.rows)) throw new TypeError('MicroPDF417: a variant entry is required');
9625
+ if (!Number.isInteger(row) || row < 0 || row >= entry.rows) throw new RangeError(`MicroPDF417: row must be in 0..${entry.rows - 1}`);
9626
+ const left = microPdf417NextRap(entry.rapStart, row);
9627
+ const cluster = /** @type {0|3|6} */ (((left - 1) % 3) * 3);
9628
+ if (entry.columns < 3) return { left, center: null, right: microPdf417NextRap(left, entry.rapRotation), cluster };
9629
+ const center = microPdf417NextRap(left, entry.rapRotation);
9630
+ return { left, center, right: microPdf417NextRap(center, entry.rapRotation), cluster };
9631
+ }
9632
+
9633
+ const validRuns = (runs) => runs.length === 6 && /^[1-9]{6}$/.test(runs) && [...runs].reduce((sum, digit) => sum + Number(digit), 0) === 10;
9634
+ const oneEdgeShift = (from, to) => [...from].reduce((sum, digit, index) => sum + Math.abs(Number(digit) - Number(to[index])), 0) === 2;
9635
+
9636
+ /** Return any table-invariant failures; an empty result means the table is coherent. */
9637
+ function validateMicroPdf417Tables() {
9638
+ const issues = [];
9639
+ if (MICROPDF417_VARIANTS.length !== 34) issues.push('expected 34 variants');
9640
+ const ids = new Set();
9641
+ const formats = new Set();
9642
+ for (const entry of MICROPDF417_VARIANTS) {
9643
+ if (ids.has(entry.id)) issues.push(`duplicate variant ${entry.id}`); ids.add(entry.id);
9644
+ const format = `${entry.columns}x${entry.rows}`;
9645
+ if (formats.has(format)) issues.push(`duplicate format ${format}`); formats.add(format);
9646
+ if (entry.totalCodewords !== entry.columns * entry.rows) issues.push(`${format}: total codeword geometry mismatch`);
9647
+ if (entry.dataCodewords + entry.eccCodewords !== entry.totalCodewords) issues.push(`${format}: data/ECC capacity mismatch`);
9648
+ if (entry.eccCodewords < 7 || entry.eccCodewords > 50) issues.push(`${format}: invalid ECC length`);
9649
+ if (entry.rapStart < 1 || entry.rapStart > 52 || entry.rapRotation < 0 || entry.rapRotation > 51) issues.push(`${format}: invalid RAP assignment`);
9650
+ for (let row = 0; row < entry.rows; row++) {
9651
+ const address = microPdf417RowAddress(entry, row);
9652
+ if (address.cluster !== ((address.left - 1) % 3) * 3) issues.push(`${format}: cluster mismatch at row ${row}`);
9653
+ if ((entry.columns < 3) !== (address.center === null)) issues.push(`${format}: center RAP layout mismatch`);
9654
+ }
9655
+ }
9656
+ for (const [kind, runs] of [['side', SIDE_RAP_RUNS], ['center', CENTER_RAP_RUNS]]) {
9657
+ if (runs.length !== 52) issues.push(`${kind}: expected 52 RAPs`);
9658
+ if (new Set(runs).size !== runs.length) issues.push(`${kind}: duplicate RAP`);
9659
+ for (let i = 0; i < runs.length; i++) {
9660
+ if (!validRuns(runs[i])) issues.push(`${kind}: invalid RAP ${i + 1}`);
9661
+ if (runs.length && !oneEdgeShift(runs[i], runs[(i + 1) % runs.length])) issues.push(`${kind}: RAP ${i + 1} is not adjacent to its successor`);
9662
+ }
9663
+ }
9664
+ return issues;
9665
+ }
9666
+
9667
+ __exports.MICROPDF417_VARIANTS = MICROPDF417_VARIANTS;
9668
+ __exports.microPdf417NextRap = microPdf417NextRap;
9669
+ __exports.microPdf417VariantByNumber = microPdf417VariantByNumber;
9670
+ __exports.microPdf417VariantForCapacity = microPdf417VariantForCapacity;
9671
+ __exports.microPdf417RapSequence = microPdf417RapSequence;
9672
+ __exports.microPdf417RowAddress = microPdf417RowAddress;
9673
+ __exports.validateMicroPdf417Tables = validateMicroPdf417Tables;
9674
+ };
9675
+
9676
+ __modules["micropdf417/error-correction.js"] = function (__require, __exports) {
9677
+ /** MicroPDF417 error correction over the existing GF(929) core. @module micropdf417/error-correction */
9678
+ const { EncodeError } = __require("core/errors.js");
9679
+ const { GF929 } = __require("core/galois-field.js");
9680
+ const { generatorPoly, rsDecode, rsEncode } = __require("core/reed-solomon.js");
9681
+
9682
+ function eccLength(entry) {
9683
+ if (!entry || !Number.isInteger(entry.eccCodewords)) throw new EncodeError('MicroPDF417: a variant with an ECC length is required');
9684
+ if (entry.eccCodewords < 1 || entry.eccCodewords >= GF929.size) throw new EncodeError('MicroPDF417: ECC length is outside GF(929) bounds');
9685
+ return entry.eccCodewords;
9686
+ }
9687
+
9688
+ /** Return the fixed number of parity codewords for a MicroPDF417 variant. */
9689
+ function microPdf417EccLength(entry) { return eccLength(entry); }
9690
+
9691
+ /** Build the MicroPDF417 generator polynomial for a variant's fixed ECC length. */
9692
+ function microPdf417Generator(entry) { return generatorPoly(eccLength(entry), GF929, 1); }
9693
+
9694
+ /** Compute systematic MicroPDF417 parity codewords. `data` must already include padding. */
9695
+ function microPdf417ErrorCorrection(data, entry) { return rsEncode(data, eccLength(entry), GF929, 1); }
9696
+
9697
+ /** Correct a complete MicroPDF417 codeword stream, optionally marking erasures. */
9698
+ function microPdf417CorrectErrors(codewords, entry, erasures = []) {
9699
+ return rsDecode(codewords, eccLength(entry), GF929, 1, erasures);
9700
+ }
9701
+
9702
+ __exports.microPdf417EccLength = microPdf417EccLength;
9703
+ __exports.microPdf417Generator = microPdf417Generator;
9704
+ __exports.microPdf417ErrorCorrection = microPdf417ErrorCorrection;
9705
+ __exports.microPdf417CorrectErrors = microPdf417CorrectErrors;
9706
+ };
9707
+
9708
+ __modules["micropdf417/compaction.js"] = function (__require, __exports) {
9709
+ /** MicroPDF417 high-level compaction adapter. @module micropdf417/compaction */
9710
+ const { EncodeError } = __require("core/errors.js");
9711
+ const { compactPdf417Bytes, compactPdf417Numeric, compactPdf417Text } = __require("pdf417/compaction.js");
9712
+
9713
+ function byteLength(value) {
9714
+ if (value instanceof Uint8Array) return value.byteLength;
9715
+ if (ArrayBuffer.isView(value)) return value.byteLength;
9716
+ return -1;
9717
+ }
9718
+
9719
+ function assertNotEmpty(value) {
9720
+ if ((typeof value === 'string' && value.length === 0) || byteLength(value) === 0) {
9721
+ throw new EncodeError('MicroPDF417: value must not be empty');
9722
+ }
9723
+ }
9724
+
9725
+ function latin1Bytes(value) {
9726
+ if (typeof value !== 'string') return value;
9727
+ const bytes = [];
9728
+ for (const character of value) {
9729
+ const codePoint = character.codePointAt(0);
9730
+ if (codePoint > 255) {
9731
+ throw new EncodeError('MicroPDF417 ECI 3: string contains a character outside ISO-8859-1');
9732
+ }
9733
+ bytes.push(codePoint);
9734
+ }
9735
+ return Uint8Array.from(bytes);
9736
+ }
9737
+
9738
+ function compactByte(value, eci) {
9739
+ if (eci === undefined) return compactPdf417Bytes(value);
9740
+ if (eci === 3) return compactPdf417Bytes(latin1Bytes(value));
9741
+ if (eci === 26) {
9742
+ if (typeof value !== 'string') {
9743
+ throw new EncodeError('MicroPDF417 ECI 26: value must be a string so UTF-8 validity is known');
9744
+ }
9745
+ const encoded = compactPdf417Bytes(value);
9746
+ return encoded[0] === 927 && encoded[1] === 26 ? encoded : [927, 26, ...encoded];
9747
+ }
9748
+ throw new EncodeError('MicroPDF417: supported ECI assignment numbers are 3 and 26');
9749
+ }
9750
+
9751
+ /**
9752
+ * Compact one MicroPDF417 value.
9753
+ *
9754
+ * Unlike PDF417, MicroPDF417 starts in Byte Compaction. Text therefore needs
9755
+ * an explicit 900 latch. Byte compaction always emits 901 or 924 so its start
9756
+ * state is unambiguous, including when an ECI designator precedes it.
9757
+ */
9758
+ function compactMicroPDF417(value, options = {}) {
9759
+ assertNotEmpty(value);
9760
+ const mode = options.compaction ?? 'auto';
9761
+ const eci = options.eci;
9762
+ if (eci !== undefined && eci !== 3 && eci !== 26) {
9763
+ throw new EncodeError('MicroPDF417: supported ECI assignment numbers are 3 and 26');
9764
+ }
9765
+
9766
+ if (mode === 'text') {
9767
+ if (eci !== undefined) throw new EncodeError('MicroPDF417: explicit ECI is supported only with byte compaction');
9768
+ return [900, ...compactPdf417Text(value)];
9769
+ }
9770
+ if (mode === 'numeric') {
9771
+ if (eci !== undefined) throw new EncodeError('MicroPDF417: explicit ECI is supported only with byte compaction');
9772
+ return compactPdf417Numeric(value);
9773
+ }
9774
+ if (mode === 'byte') return compactByte(value, eci);
9775
+ if (mode !== 'auto') {
9776
+ throw new EncodeError(`MicroPDF417: unsupported compaction mode ${JSON.stringify(mode)}`);
9777
+ }
9778
+
9779
+ if (eci !== undefined) return compactByte(value, eci);
9780
+ if (typeof value === 'string' && /^\d{13,}$/.test(value)) return compactPdf417Numeric(value);
9781
+ if (typeof value === 'string') {
9782
+ try {
9783
+ return [900, ...compactPdf417Text(value)];
9784
+ } catch (error) {
9785
+ if (!(error instanceof EncodeError)) throw error;
9786
+ }
9787
+ }
9788
+ return compactPdf417Bytes(value);
9789
+ }
9790
+
9791
+ __exports.compactMicroPDF417 = compactMicroPDF417;
9792
+ };
9793
+
9794
+ __modules["micropdf417/encoder.js"] = function (__require, __exports) {
9795
+ /** MicroPDF417 encoder. @module micropdf417/encoder */
9796
+ const { BitMatrix } = __require("core/bit-matrix.js");
9797
+ const { EncodeError } = __require("core/errors.js");
9798
+ const { pdf417PatternForCodeword } = __require("pdf417/tables.js");
9799
+ const { compactMicroPDF417 } = __require("micropdf417/compaction.js");
9800
+ const { microPdf417ErrorCorrection } = __require("micropdf417/error-correction.js");
9801
+ const { MICROPDF417_VARIANTS, microPdf417RapSequence, microPdf417RowAddress, microPdf417VariantByNumber, microPdf417VariantForCapacity } = __require("micropdf417/tables.js");
9802
+
9803
+ function appendWidths(matrix, y, x, sequence, height) {
9804
+ let dark = true;
9805
+ for (const digit of sequence) {
9806
+ const width = digit.charCodeAt(0) - 48;
9807
+ if (!Number.isInteger(width) || width < 1 || width > 6) {
9808
+ throw new EncodeError('MicroPDF417: invalid module-width sequence');
9809
+ }
9810
+ if (dark) matrix.setRegion(x, y, width, height);
9811
+ x += width;
9812
+ dark = !dark;
9813
+ }
9814
+ return x;
9815
+ }
9816
+
9817
+ function codewordSequence(codeword, cluster) {
9818
+ return pdf417PatternForCodeword(codeword, cluster)
9819
+ .toString(2)
9820
+ .padStart(17, '0')
9821
+ .replace(/0+|1+/g, (run) => String(run.length));
9822
+ }
9823
+
9824
+ function symbolWidth(columns) {
9825
+ return 21 + columns * 17 + (columns > 2 ? 10 : 0);
9826
+ }
9827
+
9828
+ function validateOptions(options) {
9829
+ const rowHeight = options.rowHeight ?? 2;
9830
+ if (!Number.isInteger(rowHeight) || rowHeight < 2) {
9831
+ throw new EncodeError('MicroPDF417: rowHeight must be an integer of at least 2');
9832
+ }
9833
+ if (options.columns !== undefined &&
9834
+ (!Number.isInteger(options.columns) || options.columns < 1 || options.columns > 4)) {
9835
+ throw new EncodeError('MicroPDF417: columns must be an integer in 1..4');
9836
+ }
9837
+ if (options.variant !== undefined &&
9838
+ (!Number.isInteger(options.variant) || options.variant < 1 || options.variant > 34)) {
9839
+ throw new EncodeError('MicroPDF417: variant must be an integer in 1..34');
9840
+ }
9841
+ if (options.aspectRatio !== undefined &&
9842
+ (!Number.isFinite(options.aspectRatio) || options.aspectRatio <= 0)) {
9843
+ throw new EncodeError('MicroPDF417: aspectRatio must be positive');
9844
+ }
9845
+ if (options.rows !== undefined) {
9846
+ throw new EncodeError('MicroPDF417: rows are fixed by the selected variant');
9847
+ }
9848
+ if (options.eccLevel !== undefined) {
9849
+ throw new EncodeError('MicroPDF417: error correction is fixed by the selected variant');
9850
+ }
9851
+ for (const feature of [
9852
+ 'structuredAppend', 'macro', 'macroPdf417', 'macroControlBlock',
9853
+ 'readerInit', 'gs1', 'hibc', 'linkage',
9854
+ ]) {
9855
+ if (options[feature] !== undefined) {
9856
+ throw new EncodeError(`MicroPDF417: ${feature} is not implemented`);
9857
+ }
9858
+ }
9859
+ return rowHeight;
9860
+ }
9861
+
9862
+ function chooseVariant(codewordCount, rowHeight, options) {
9863
+ if (options.variant !== undefined) {
9864
+ const variant = microPdf417VariantByNumber(options.variant);
9865
+ if (!variant) throw new EncodeError(`MicroPDF417: unknown variant ${options.variant}`);
9866
+ if (options.columns !== undefined && variant.columns !== options.columns) {
9867
+ throw new EncodeError(`MicroPDF417: variant ${variant.id} has ${variant.columns} columns`);
9868
+ }
9869
+ if (codewordCount > variant.dataCodewords) {
9870
+ throw new EncodeError(
9871
+ `MicroPDF417: payload requires ${codewordCount} data codewords, variant ${variant.id} holds ${variant.dataCodewords}`
9872
+ );
9873
+ }
9874
+ return variant;
9875
+ }
9876
+
9877
+ if (options.columns === undefined && options.aspectRatio === undefined) {
9878
+ try {
9879
+ return microPdf417VariantForCapacity(codewordCount);
9880
+ } catch (error) {
9881
+ if (!(error instanceof RangeError)) throw error;
9882
+ throw new EncodeError(`MicroPDF417: payload requires ${codewordCount} data codewords and exceeds every variant`);
9883
+ }
9884
+ }
9885
+
9886
+ const candidates = MICROPDF417_VARIANTS.filter((variant) =>
9887
+ variant.dataCodewords >= codewordCount &&
9888
+ (options.columns === undefined || variant.columns === options.columns)
9889
+ );
9890
+ if (!candidates.length) {
9891
+ const columnText = options.columns === undefined ? '' : ` with ${options.columns} columns`;
9892
+ throw new EncodeError(`MicroPDF417: payload does not fit any supported variant${columnText}`);
9893
+ }
9894
+ if (options.aspectRatio === undefined) {
9895
+ return candidates.reduce((best, variant) =>
9896
+ variant.dataCodewords < best.dataCodewords ||
9897
+ (variant.dataCodewords === best.dataCodewords && variant.totalCodewords < best.totalCodewords)
9898
+ ? variant : best
9899
+ );
9900
+ }
9901
+
9902
+ const target = options.aspectRatio;
9903
+ return candidates.reduce((best, variant) => {
9904
+ const ratio = symbolWidth(variant.columns) / (variant.rows * rowHeight);
9905
+ const score = Math.abs(Math.log(ratio / target)) +
9906
+ (variant.dataCodewords - codewordCount) / 10000;
9907
+ return !best || score < best.score ? { variant, score } : best;
9908
+ }, null).variant;
9909
+ }
9910
+
9911
+ /** Encode a value as one of the 34 fixed MicroPDF417 variants. */
9912
+ function encodeMicroPDF417(value, options = {}) {
9913
+ const rowHeight = validateOptions(options);
9914
+ const payload = compactMicroPDF417(value, options);
9915
+ const variant = chooseVariant(payload.length, rowHeight, options);
9916
+ const data = payload.slice();
9917
+ while (data.length < variant.dataCodewords) data.push(900);
9918
+ const ecc = microPdf417ErrorCorrection(data, variant);
9919
+ if (ecc.length !== variant.eccCodewords) {
9920
+ throw new EncodeError('MicroPDF417: error-correction length does not match the selected variant');
9921
+ }
9922
+ const codewords = data.concat(ecc);
9923
+ if (codewords.length !== variant.totalCodewords || codewords.length !== variant.rows * variant.columns) {
9924
+ throw new EncodeError('MicroPDF417: selected variant has inconsistent codeword dimensions');
9925
+ }
9926
+
9927
+ const matrix = new BitMatrix(symbolWidth(variant.columns), variant.rows * rowHeight);
9928
+ for (let row = 0; row < variant.rows; row++) {
9929
+ const y = row * rowHeight;
9930
+ const address = microPdf417RowAddress(variant, row);
9931
+ let x = appendWidths(matrix, y, 0, microPdf417RapSequence(address.left, 'side'), rowHeight);
9932
+ for (let column = 0; column < variant.columns; column++) {
9933
+ x = appendWidths(
9934
+ matrix,
9935
+ y,
9936
+ x,
9937
+ codewordSequence(codewords[row * variant.columns + column], address.cluster),
9938
+ rowHeight
9939
+ );
9940
+ const hasCentralRap = (variant.columns === 3 && column === 0) ||
9941
+ (variant.columns === 4 && column === 1);
9942
+ if (hasCentralRap) {
9943
+ if (address.center === null) {
9944
+ throw new EncodeError('MicroPDF417: selected variant is missing its centre row address');
9945
+ }
9946
+ x = appendWidths(matrix, y, x, microPdf417RapSequence(address.center, 'center'), rowHeight);
9947
+ }
9948
+ }
9949
+ x = appendWidths(matrix, y, x, microPdf417RapSequence(address.right, 'side'), rowHeight);
9950
+ matrix.setRegion(x, y, 1, rowHeight);
9951
+ x++;
9952
+ if (x !== matrix.width) throw new EncodeError('MicroPDF417: row width does not match the selected variant');
9953
+ }
9954
+
9955
+ matrix.micropdf417 = {
9956
+ variant: variant.id,
9957
+ rows: variant.rows,
9958
+ columns: variant.columns,
9959
+ eccCodewords: variant.eccCodewords,
9960
+ rowHeight,
9961
+ payloadCodewords: payload.length,
9962
+ dataCodewords: data,
9963
+ codewords,
9964
+ };
9965
+ return matrix;
9966
+ }
9967
+
9968
+ __exports.encodeMicroPDF417 = encodeMicroPDF417;
9969
+ };
9970
+
9971
+ __modules["micropdf417/decoder.js"] = function (__require, __exports) {
9972
+ /**
9973
+ * Direct-module MicroPDF417 decoder.
9974
+ *
9975
+ * This module reads an already sampled, axis-aligned `BitMatrix`. Image finding,
9976
+ * perspective correction, and recognition from photographic pixels are deliberately
9977
+ * outside this first decoder boundary. The RAP sequence is authoritative for format
9978
+ * detection; matrix metadata is used only as an optional row-height hint.
9979
+ *
9980
+ * @module micropdf417/decoder
9981
+ */
9982
+ const { FormatError } = __require("core/errors.js");
9983
+ const { decodePdf417CompactionDetailed } = __require("pdf417/compaction.js");
9984
+ const { pdf417CodewordForPattern } = __require("pdf417/tables.js");
9985
+ const { microPdf417CorrectErrors } = __require("micropdf417/error-correction.js");
9986
+ const { MICROPDF417_VARIANTS, microPdf417RapSequence, microPdf417RowAddress, microPdf417VariantByNumber } = __require("micropdf417/tables.js");
9987
+
9988
+ function symbolWidth(columns) {
9989
+ return 21 + columns * 17 + (columns > 2 ? 10 : 0);
9990
+ }
9991
+
9992
+ function bits(matrix, y, x, width) {
9993
+ let value = 0;
9994
+ for (let i = 0; i < width; i++) value = (value << 1) | (matrix.get(x + i, y) ? 1 : 0);
9995
+ return value;
9996
+ }
9997
+
9998
+ function widthsToBits(widths) {
9999
+ let dark = true;
10000
+ let out = '';
10001
+ for (const digit of widths) {
10002
+ out += (dark ? '1' : '0').repeat(digit.charCodeAt(0) - 48);
10003
+ dark = !dark;
10004
+ }
10005
+ return out;
10006
+ }
10007
+
10008
+ function hasExpectedBits(matrix, y, x, sequence) {
10009
+ const expected = widthsToBits(sequence);
10010
+ for (let i = 0; i < expected.length; i++) {
10011
+ if ((matrix.get(x + i, y) ? '1' : '0') !== expected[i]) return false;
10012
+ }
10013
+ return true;
10014
+ }
10015
+
10016
+ function centralRapAfter(entry, column) {
10017
+ return (entry.columns === 3 && column === 0) ||
10018
+ (entry.columns === 4 && column === 1);
10019
+ }
10020
+
10021
+ /** Return true when all address patterns for this format candidate agree. */
10022
+ function hasValidRowAddresses(matrix, entry, rowHeight) {
10023
+ for (let row = 0; row < entry.rows; row++) {
10024
+ const y = row * rowHeight;
10025
+ const address = microPdf417RowAddress(entry, row);
10026
+ let x = 0;
10027
+ if (!hasExpectedBits(matrix, y, x, microPdf417RapSequence(address.left, 'side'))) return false;
10028
+ x += 10;
10029
+ for (let column = 0; column < entry.columns; column++) {
10030
+ x += 17;
10031
+ if (centralRapAfter(entry, column)) {
10032
+ if (address.center === null ||
10033
+ !hasExpectedBits(matrix, y, x, microPdf417RapSequence(address.center, 'center'))) return false;
10034
+ x += 10;
10035
+ }
10036
+ }
10037
+ if (!hasExpectedBits(matrix, y, x, microPdf417RapSequence(address.right, 'side'))) return false;
10038
+ x += 10;
10039
+ if (!matrix.get(x, y) || x + 1 !== matrix.width) return false;
10040
+ }
10041
+ return true;
10042
+ }
10043
+
10044
+ function candidateFormats(matrix, options) {
10045
+ const metadataHeight = matrix.micropdf417?.rowHeight;
10046
+ const requestedHeight = options.rowHeight ?? metadataHeight;
10047
+ if (requestedHeight !== undefined && (!Number.isInteger(requestedHeight) || requestedHeight < 1)) {
10048
+ throw new FormatError('MicroPDF417: rowHeight must be a positive integer');
10049
+ }
10050
+ const requestedVariant = options.variant === undefined ? null : microPdf417VariantByNumber(options.variant);
10051
+ const pool = requestedVariant ? [requestedVariant] : MICROPDF417_VARIANTS;
10052
+ const candidates = [];
10053
+ for (const entry of pool) {
10054
+ if (matrix.width !== symbolWidth(entry.columns)) continue;
10055
+ if (matrix.height % entry.rows) continue;
10056
+ const rowHeight = matrix.height / entry.rows;
10057
+ if (requestedHeight !== undefined && rowHeight !== requestedHeight) continue;
10058
+ if (hasValidRowAddresses(matrix, entry, rowHeight)) candidates.push({ entry, rowHeight });
10059
+ }
10060
+ return candidates;
10061
+ }
10062
+
10063
+ function resolveFormat(matrix, options) {
10064
+ if (!matrix?.width || !matrix?.height || typeof matrix.get !== 'function') {
10065
+ throw new FormatError('MicroPDF417: matrix with width, height and get() is required');
10066
+ }
10067
+ const candidates = candidateFormats(matrix, options);
10068
+ if (!candidates.length) throw new FormatError('MicroPDF417: no variant matches matrix geometry and row-address patterns');
10069
+ if (candidates.length > 1) throw new FormatError('MicroPDF417: row-address patterns do not identify a unique variant');
10070
+ return candidates[0];
10071
+ }
10072
+
10073
+ function readCodewords(matrix, entry, rowHeight) {
10074
+ const codewords = [];
10075
+ const erasures = [];
10076
+ for (let row = 0; row < entry.rows; row++) {
10077
+ const y = row * rowHeight;
10078
+ const address = microPdf417RowAddress(entry, row);
10079
+ let x = 10;
10080
+ for (let column = 0; column < entry.columns; column++) {
10081
+ const decoded = pdf417CodewordForPattern(bits(matrix, y, x, 17));
10082
+ if (!decoded || decoded.cluster !== address.cluster) {
10083
+ erasures.push(codewords.length);
10084
+ codewords.push(0);
10085
+ } else {
10086
+ codewords.push(decoded.codeword);
10087
+ }
10088
+ x += 17;
10089
+ if (centralRapAfter(entry, column)) x += 10;
10090
+ }
10091
+ }
10092
+ return { codewords, erasures };
10093
+ }
10094
+
10095
+ /**
10096
+ * Decode a sampled MicroPDF417 matrix.
10097
+ *
10098
+ * The complete fixed data region is compacted after correction. Encoder padding
10099
+ * is PDF417 Text latch 900, which contributes no characters at the end of the
10100
+ * payload. This avoids relying on non-symbol metadata for payload length.
10101
+ */
10102
+ function decodeMicroPDF417(matrix, options = {}) {
10103
+ const { entry, rowHeight } = resolveFormat(matrix, options);
10104
+ const { codewords, erasures } = readCodewords(matrix, entry, rowHeight);
10105
+ const corrections = microPdf417CorrectErrors(codewords, entry, erasures);
10106
+ const data = codewords.slice(0, entry.dataCodewords);
10107
+ const decoded = decodePdf417CompactionDetailed(data);
10108
+ return {
10109
+ ...decoded,
10110
+ codewords,
10111
+ rows: entry.rows,
10112
+ columns: entry.columns,
10113
+ variant: entry.id,
10114
+ eccCodewords: entry.eccCodewords,
10115
+ rowHeight,
10116
+ corrections,
10117
+ };
10118
+ }
10119
+
10120
+ __exports.decodeMicroPDF417 = decodeMicroPDF417;
10121
+ };
10122
+
10123
+ __modules["micropdf417/detector.js"] = function (__require, __exports) {
10124
+ /**
10125
+ * Axis-aligned MicroPDF417 raster detection.
10126
+ *
10127
+ * A MicroPDF417 symbol has a fixed module width for each column count and a
10128
+ * dark leading and trailing module in every row. Consequently the bounding
10129
+ * rectangle of dark pixels identifies the complete symbol even when a light
10130
+ * quiet zone surrounds it. Its width determines the integer raster scale;
10131
+ * the existing direct-module decoder then verifies every row-address pattern
10132
+ * and selects the exact variant. This is intentionally narrower than the
10133
+ * PDF417 photo detector: it handles clean binarized rasters only, not skew or
10134
+ * projective camera images.
10135
+ *
10136
+ * @module micropdf417/detector
10137
+ */
10138
+ const { BitMatrix } = __require("core/bit-matrix.js");
10139
+ const { decodeMicroPDF417 } = __require("micropdf417/decoder.js");
10140
+
10141
+ /** @typedef {{x:number, y:number}} Point */
10142
+
10143
+ // Each row has two 10-module side RAPs, a final separator, and 17 modules for
10144
+ // each data column. Three and four columns also contain one central RAP.
10145
+ function symbolWidth(columns) { return 21 + columns * 17 + (columns > 2 ? 10 : 0); }
10146
+ const WIDTHS = [1, 2, 3, 4].map(symbolWidth);
10147
+
10148
+ function rotateClockwise(source) {
10149
+ const rotated = new BitMatrix(source.height, source.width);
10150
+ for (let y = 0; y < source.height; y++) for (let x = 0; x < source.width; x++) {
10151
+ if (source.get(x, y)) rotated.set(source.height - 1 - y, x);
10152
+ }
10153
+ return rotated;
10154
+ }
10155
+
10156
+ function integerScale(width, modules) {
10157
+ if (width % modules) return 0;
10158
+ const scale = width / modules;
10159
+ return Number.isInteger(scale) && scale > 0 ? scale : 0;
10160
+ }
10161
+
10162
+ /** Collapse exact integer scale blocks using a majority vote. */
10163
+ function sampleRaster(image, bounds, modulesWide, scale) {
10164
+ const modulesHigh = bounds.height / scale;
10165
+ if (!Number.isInteger(modulesHigh) || modulesHigh < 1) return null;
10166
+ const matrix = new BitMatrix(modulesWide, modulesHigh);
10167
+ for (let y = 0; y < modulesHigh; y++) for (let x = 0; x < modulesWide; x++) {
10168
+ let dark = 0;
10169
+ for (let py = 0; py < scale; py++) for (let px = 0; px < scale; px++) {
10170
+ if (image.get(bounds.x + x * scale + px, bounds.y + y * scale + py)) dark++;
10171
+ }
10172
+ if (dark * 2 >= scale * scale) matrix.set(x, y);
10173
+ }
10174
+ return matrix;
10175
+ }
10176
+
10177
+ function rectangle(bounds) {
10178
+ return [
10179
+ { x: bounds.x, y: bounds.y },
10180
+ { x: bounds.x + bounds.width, y: bounds.y },
10181
+ { x: bounds.x + bounds.width, y: bounds.y + bounds.height },
10182
+ { x: bounds.x, y: bounds.y + bounds.height },
10183
+ ];
10184
+ }
10185
+
10186
+ function detectAxisAligned(image, options) {
10187
+ const bounds = image.getBounds();
10188
+ if (!bounds) return null;
10189
+ for (const width of WIDTHS) {
10190
+ const scale = integerScale(bounds.width, width);
10191
+ if (!scale || bounds.height % scale) continue;
10192
+ const matrix = sampleRaster(image, bounds, width, scale);
10193
+ if (!matrix) continue;
10194
+ try {
10195
+ const decoded = decodeMicroPDF417(matrix, options);
10196
+ return { matrix, corners: rectangle(bounds), moduleSize: scale, ...decoded };
10197
+ } catch { /* The RAP sequence is not a MicroPDF417 symbol of this width. */ }
10198
+ }
10199
+ return null;
10200
+ }
10201
+
10202
+ /**
10203
+ * Detect and decode one clean, binarized MicroPDF417 raster.
10204
+ *
10205
+ * Integer upscaling and quiet zones are accepted. The image is retried at all
10206
+ * quarter-turns, but arbitrary angles and perspective require caller-side
10207
+ * rectification before this function is used. `rotation` reports the clockwise
10208
+ * orientation of the supplied input relative to a normally oriented symbol;
10209
+ * it is not the inverse correction applied internally while searching.
10210
+ *
10211
+ * @param {import('../core/bit-matrix.js').BitMatrix} binaryImage Set bit = dark.
10212
+ * @param {object} [options] Passed to {@link decodeMicroPDF417}.
10213
+ * @returns {(ReturnType<typeof decodeMicroPDF417> & {matrix: BitMatrix, corners: Point[], moduleSize: number, rotation: number}) | null}
10214
+ */
10215
+ function detectMicroPDF417(binaryImage, options = {}) {
10216
+ if (!binaryImage?.width || !binaryImage?.height || typeof binaryImage.get !== 'function') return null;
10217
+ let oriented = binaryImage;
10218
+ let toOriginal = (point) => ({ x: point.x, y: point.y });
10219
+ for (let turns = 0; turns < 4; turns++) {
10220
+ const found = detectAxisAligned(oriented, options);
10221
+ // Search rotates clockwise to normalize the input. Public rotation has the
10222
+ // opposite meaning: it describes how the input itself was rotated.
10223
+ if (found) return {
10224
+ ...found,
10225
+ rotation: (360 - turns * 90) % 360,
10226
+ corners: found.corners.map(toOriginal),
10227
+ };
10228
+ const previous = oriented;
10229
+ const previousToOriginal = toOriginal;
10230
+ oriented = rotateClockwise(previous);
10231
+ // Boundary coordinates (rather than just pixel centres) are transformed
10232
+ // here, so callers can draw the returned rectangle directly on the input.
10233
+ toOriginal = (point) => previousToOriginal({ x: point.y, y: previous.height - point.x });
10234
+ }
10235
+ return null;
10236
+ }
10237
+
10238
+ /** Alias kept symmetric with the other 2D readers. */
10239
+ function detectAndDecodeMicroPDF417(binaryImage, options = {}) {
10240
+ return detectMicroPDF417(binaryImage, options);
10241
+ }
10242
+
10243
+ __exports.detectMicroPDF417 = detectMicroPDF417;
10244
+ __exports.detectAndDecodeMicroPDF417 = detectAndDecodeMicroPDF417;
10245
+ };
10246
+
10247
+ __modules["micropdf417/index.js"] = function (__require, __exports) {
10248
+ const __reexport0 = __require("micropdf417/tables.js"); __exports.MICROPDF417_VARIANTS = __reexport0.MICROPDF417_VARIANTS; __exports.microPdf417NextRap = __reexport0.microPdf417NextRap; __exports.microPdf417VariantByNumber = __reexport0.microPdf417VariantByNumber; __exports.microPdf417VariantForCapacity = __reexport0.microPdf417VariantForCapacity; __exports.microPdf417RapSequence = __reexport0.microPdf417RapSequence; __exports.microPdf417RowAddress = __reexport0.microPdf417RowAddress; __exports.validateMicroPdf417Tables = __reexport0.validateMicroPdf417Tables;
10249
+ const __reexport1 = __require("micropdf417/error-correction.js"); __exports.microPdf417EccLength = __reexport1.microPdf417EccLength; __exports.microPdf417Generator = __reexport1.microPdf417Generator; __exports.microPdf417ErrorCorrection = __reexport1.microPdf417ErrorCorrection; __exports.microPdf417CorrectErrors = __reexport1.microPdf417CorrectErrors;
10250
+ const __reexport2 = __require("micropdf417/compaction.js"); __exports.compactMicroPDF417 = __reexport2.compactMicroPDF417;
10251
+ const __reexport3 = __require("micropdf417/encoder.js"); __exports.encodeMicroPDF417 = __reexport3.encodeMicroPDF417;
10252
+ const __reexport4 = __require("micropdf417/decoder.js"); __exports.decodeMicroPDF417 = __reexport4.decodeMicroPDF417;
10253
+ const __reexport5 = __require("micropdf417/detector.js"); __exports.detectMicroPDF417 = __reexport5.detectMicroPDF417; __exports.detectAndDecodeMicroPDF417 = __reexport5.detectAndDecodeMicroPDF417;
10254
+
10255
+
10256
+ };
10257
+
10258
+ __modules["render/options.js"] = function (__require, __exports) {
10259
+ /**
10260
+ * Shared render options, normalised once so every backend agrees.
10261
+ *
10262
+ * @module render/options
10263
+ */
10264
+ const { BitMatrix } = __require("core/bit-matrix.js");
10265
+
10266
+ /**
10267
+ * @typedef {object} RenderOptions
10268
+ * @property {number} [scale] Pixels per module. Default 8.
10269
+ * @property {number} [margin] Quiet-zone modules on every side. Default 4.
10270
+ * @property {string} [dark] Colour of set modules. Default '#000000'.
10271
+ * @property {string} [light] Colour of clear modules, or 'none' for transparent.
10272
+ * @property {number} [barHeight] For 1D symbols: total bar height in pixels.
10273
+ */
10274
+
10275
+ /**
10276
+ * Expand and pad the matrix, and resolve every dimension.
10277
+ *
10278
+ * Linear symbols arrive one module tall. They are stretched to `barHeight`
10279
+ * *before* the quiet zone is applied, so the margin ends up uniform on all
10280
+ * four sides — padding first would leave a quiet zone one module tall against
10281
+ * bars a hundred pixels tall, which no scanner would accept.
10282
+ *
10283
+ * @param {BitMatrix} matrix
10284
+ * @param {RenderOptions} options
10285
+ */
10286
+ function normalizeOptions(matrix, options = {}) {
10287
+ const scale = Math.max(1, Math.floor(options.scale ?? 8));
10288
+ const margin = Math.max(0, Math.floor(options.margin ?? 4));
10289
+ const dark = options.dark ?? '#000000';
10290
+ const light = options.light ?? '#ffffff';
10291
+ const barHeight = options.barHeight ?? null;
10292
+
10293
+ let base = matrix;
10294
+ const is1D = matrix.height === 1;
10295
+
10296
+ if (is1D) {
10297
+ // Default to a bar height that stays scannable: tall enough that a laser
10298
+ // crossing at a slight angle still passes through the whole symbol.
10299
+ const targetPixels = barHeight ?? Math.max(40, Math.round(matrix.width * scale * 0.15));
10300
+ const rows = Math.max(1, Math.round(targetPixels / scale));
10301
+ base = new BitMatrix(matrix.width, rows);
10302
+ for (let x = 0; x < matrix.width; x++) {
10303
+ if (!matrix.get(x, 0)) continue;
10304
+ for (let y = 0; y < rows; y++) base.set(x, y);
10305
+ }
10306
+ }
10307
+
10308
+ const source = margin > 0 ? base.withMargin(margin) : base;
10309
+
10310
+ return {
10311
+ scale,
10312
+ margin,
10313
+ dark,
10314
+ light,
10315
+ is1D,
10316
+ source,
10317
+ rowHeight: scale,
10318
+ pixelWidth: source.width * scale,
10319
+ pixelHeight: source.height * scale,
10320
+ };
10321
+ }
10322
+
10323
+ /**
10324
+ * Parse a CSS colour into RGBA bytes.
10325
+ *
10326
+ * Supports the forms a barcode actually needs: #rgb, #rgba, #rrggbb,
10327
+ * #rrggbbaa, rgb(), rgba(), plus 'none' and 'transparent'.
10328
+ *
10329
+ * @param {string} colour
10330
+ * @returns {[number, number, number, number]}
10331
+ */
10332
+ function parseColor(colour) {
10333
+ const value = String(colour).trim().toLowerCase();
10334
+
10335
+ if (value === 'none' || value === 'transparent') return [0, 0, 0, 0];
10336
+ if (value === 'white') return [255, 255, 255, 255];
10337
+ if (value === 'black') return [0, 0, 0, 255];
10338
+
10339
+ if (value[0] === '#') {
10340
+ const hex = value.slice(1);
10341
+ const expand = (c) => parseInt(c + c, 16);
10342
+ if (hex.length === 3) {
10343
+ return [expand(hex[0]), expand(hex[1]), expand(hex[2]), 255];
10344
+ }
10345
+ if (hex.length === 4) {
10346
+ return [expand(hex[0]), expand(hex[1]), expand(hex[2]), expand(hex[3])];
10347
+ }
10348
+ if (hex.length === 6) {
10349
+ return [
10350
+ parseInt(hex.slice(0, 2), 16),
10351
+ parseInt(hex.slice(2, 4), 16),
10352
+ parseInt(hex.slice(4, 6), 16),
10353
+ 255,
10354
+ ];
10355
+ }
10356
+ if (hex.length === 8) {
10357
+ return [
10358
+ parseInt(hex.slice(0, 2), 16),
10359
+ parseInt(hex.slice(2, 4), 16),
10360
+ parseInt(hex.slice(4, 6), 16),
10361
+ parseInt(hex.slice(6, 8), 16),
10362
+ ];
10363
+ }
10364
+ }
10365
+
10366
+ const fn = value.match(/^rgba?\(([^)]+)\)$/);
10367
+ if (fn) {
10368
+ const parts = fn[1].split(/[,/\s]+/).filter(Boolean);
10369
+ const channel = (s) => (s.endsWith('%')
10370
+ ? Math.round((parseFloat(s) / 100) * 255)
10371
+ : Math.round(parseFloat(s)));
10372
+ const r = channel(parts[0]);
10373
+ const g = channel(parts[1]);
10374
+ const b = channel(parts[2]);
10375
+ let a = 255;
10376
+ if (parts.length > 3) {
10377
+ a = parts[3].endsWith('%')
10378
+ ? Math.round((parseFloat(parts[3]) / 100) * 255)
10379
+ : Math.round(parseFloat(parts[3]) * 255);
10380
+ }
10381
+ return [r, g, b, a];
10382
+ }
10383
+
10384
+ // Unrecognised: fall back to opaque black rather than throwing, so an
10385
+ // unusual colour never costs someone a barcode.
10386
+ return [0, 0, 0, 255];
10387
+ }
10388
+
10389
+ __exports.normalizeOptions = normalizeOptions;
10390
+ __exports.parseColor = parseColor;
10391
+ };
10392
+
10393
+ __modules["render/svg.js"] = function (__require, __exports) {
10394
+ /**
10395
+ * SVG output.
10396
+ *
10397
+ * Dark modules are emitted as a single `<path>` with horizontal runs merged,
10398
+ * not as one `<rect>` per module. A version 40 QR symbol has 31329 modules; the
10399
+ * naive rendering is a megabyte of XML that browsers choke on, while the merged
10400
+ * path is a few kilobytes and draws identically.
10401
+ *
10402
+ * @module render/svg
10403
+ */
10404
+ const { normalizeOptions } = __require("render/options.js");
10405
+
10406
+ /**
10407
+ * @param {string} value
10408
+ * @returns {string}
10409
+ */
10410
+ function escapeAttr(value) {
10411
+ return String(value)
10412
+ .replace(/&/g, '&amp;')
10413
+ .replace(/</g, '&lt;')
10414
+ .replace(/>/g, '&gt;')
10415
+ .replace(/"/g, '&quot;');
10416
+ }
10417
+
10418
+ /**
10419
+ * Render to an SVG document.
10420
+ *
10421
+ * @param {import('../core/bit-matrix.js').BitMatrix} matrix
10422
+ * @param {import('./options.js').RenderOptions} [options]
10423
+ * @returns {string}
10424
+ */
10425
+ function toSVG(matrix, options = {}) {
10426
+ const opts = normalizeOptions(matrix, options);
10427
+ const { scale, source, pixelWidth, pixelHeight, rowHeight } = opts;
10428
+
10429
+ let path = '';
10430
+ for (let y = 0; y < source.height; y++) {
10431
+ let x = 0;
10432
+ while (x < source.width) {
10433
+ if (!source.get(x, y)) { x++; continue; }
10434
+ let run = 1;
10435
+ while (x + run < source.width && source.get(x + run, y)) run++;
10436
+ // Relative horizontal-vertical path commands: shorter than rects and
10437
+ // free of the seams that appear between adjacent rects at some zooms.
10438
+ path += `M${x * scale} ${y * rowHeight}h${run * scale}v${rowHeight}h${-run * scale}z`;
10439
+ x += run;
10440
+ }
10441
+ }
10442
+
10443
+ const bg = opts.light === 'none'
10444
+ ? ''
10445
+ : `<rect width="${pixelWidth}" height="${pixelHeight}" fill="${escapeAttr(opts.light)}"/>`;
10446
+
10447
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${pixelWidth}" height="${pixelHeight}" ` +
10448
+ `viewBox="0 0 ${pixelWidth} ${pixelHeight}" shape-rendering="crispEdges">` +
10449
+ bg +
10450
+ `<path d="${path}" fill="${escapeAttr(opts.dark)}"/>` +
10451
+ '</svg>';
10452
+ }
10453
+
10454
+ /**
10455
+ * Base64 that works identically in Node and the browser.
10456
+ *
10457
+ * `btoa` is byte-oriented, so the UTF-8 encoding has to happen first — passing
10458
+ * it a string with any character above U+00FF throws.
10459
+ *
10460
+ * @param {string} text
10461
+ * @returns {string}
10462
+ */
10463
+ function toBase64(text) {
10464
+ const bytes = new TextEncoder().encode(text);
10465
+ let binary = '';
10466
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
10467
+ if (typeof btoa === 'function') return btoa(binary);
10468
+ // Node before btoa was global, and non-browser embedders.
10469
+ /* eslint-disable-next-line no-undef */
7426
10470
  return Buffer.from(bytes).toString('base64');
7427
10471
  }
7428
10472
 
@@ -8466,6 +11510,9 @@ const { ONED_FORMATS } = __require("oned/index.js");
8466
11510
  const { decodeOneD } = __require("oned/reader.js");
8467
11511
  const datamatrix = __require("datamatrix/index.js");
8468
11512
  const qr = __require("qr/index.js");
11513
+ const aztec = __require("aztec/index.js");
11514
+ const pdf417 = __require("pdf417/index.js");
11515
+ const micropdf417 = __require("micropdf417/index.js");
8469
11516
  __exports.BitMatrix = BitMatrix;
8470
11517
  const __reexport0 = __require("core/errors.js"); __exports.BarcodeError = __reexport0.BarcodeError; __exports.EncodeError = __reexport0.EncodeError; __exports.NotFoundError = __reexport0.NotFoundError; __exports.FormatError = __reexport0.FormatError; __exports.ChecksumError = __reexport0.ChecksumError;
8471
11518
  const __reexport1 = __require("image/luminance.js"); __exports.LuminanceSource = __reexport1.LuminanceSource;
@@ -8478,6 +11525,9 @@ const __reexport6 = __require("render/index.js"); __exports.renderToCanvasAuto =
8478
11525
  const __reexport7 = __require("render/index.js"); __exports.renderToCanvasAutoAsync = __reexport7.renderToCanvasAutoAsync; __exports.isWebGPUAvailable = __reexport7.isWebGPUAvailable;
8479
11526
  const __reexport8 = __require("qr/index.js"); __exports.encodeQR = __reexport8.encodeQR; __exports.decodeQR = __reexport8.decodeQR; __exports.detectQR = __reexport8.detectQR; __exports.detectAndDecodeQR = __reexport8.detectAndDecodeQR;
8480
11527
  const __reexport9 = __require("datamatrix/index.js"); __exports.encodeDataMatrix = __reexport9.encodeDataMatrix; __exports.decodeDataMatrix = __reexport9.decodeDataMatrix; __exports.detectDataMatrix = __reexport9.detectDataMatrix; __exports.detectAndDecodeDataMatrix = __reexport9.detectAndDecodeDataMatrix;
11528
+ const __reexport10 = __require("aztec/index.js"); __exports.encodeAztec = __reexport10.encodeAztec; __exports.decodeAztec = __reexport10.decodeAztec; __exports.detectAztec = __reexport10.detectAztec; __exports.detectAndDecodeAztec = __reexport10.detectAndDecodeAztec;
11529
+ const __reexport11 = __require("pdf417/index.js"); __exports.encodePDF417 = __reexport11.encodePDF417; __exports.decodePDF417 = __reexport11.decodePDF417; __exports.detectPDF417 = __reexport11.detectPDF417; __exports.detectAndDecodePDF417 = __reexport11.detectAndDecodePDF417;
11530
+ const __reexport12 = __require("micropdf417/index.js"); __exports.encodeMicroPDF417 = __reexport12.encodeMicroPDF417; __exports.decodeMicroPDF417 = __reexport12.decodeMicroPDF417; __exports.detectMicroPDF417 = __reexport12.detectMicroPDF417; __exports.detectAndDecodeMicroPDF417 = __reexport12.detectAndDecodeMicroPDF417;
8481
11531
 
8482
11532
  /**
8483
11533
  * @typedef {object} FormatInfo
@@ -8503,6 +11553,16 @@ const qrCanDecode = qrPresent &&
8503
11553
  typeof qr.detectAndDecodeQR === 'function' && qr.QR_CAN_DECODE !== false;
8504
11554
  const dataMatrixCanEncode = typeof datamatrix.encodeDataMatrix === 'function';
8505
11555
  const dataMatrixCanDecode = typeof datamatrix.detectAndDecodeDataMatrix === 'function';
11556
+ const aztecCanEncode = typeof aztec.encodeAztec === 'function';
11557
+ const aztecCanDecode = typeof aztec.detectAndDecodeAztec === 'function';
11558
+ const pdf417CanEncode = typeof pdf417.encodePDF417 === 'function';
11559
+ // The matrix decoder is complete, but automatic image localization is still
11560
+ // limited to clean module-aligned symbols or an application-supplied
11561
+ // quadrilateral. Keep the generic scanner capability opt-in until a
11562
+ // perspective/noise corpus is passed.
11563
+ const pdf417CanDecode = typeof pdf417.detectAndDecodePDF417 === 'function';
11564
+ const microPdf417CanEncode = typeof micropdf417.encodeMicroPDF417 === 'function';
11565
+ const microPdf417CanDecode = typeof micropdf417.detectAndDecodeMicroPDF417 === 'function';
8506
11566
 
8507
11567
  /**
8508
11568
  * Every format this build supports.
@@ -8537,6 +11597,27 @@ function listFormats() {
8537
11597
  canRead: dataMatrixCanDecode,
8538
11598
  kind: /** @type {'2D'} */ ('2D'),
8539
11599
  });
11600
+ formats.push({
11601
+ id: 'aztec',
11602
+ label: 'Aztec Code',
11603
+ canWrite: aztecCanEncode,
11604
+ canRead: aztecCanDecode,
11605
+ kind: /** @type {'2D'} */ ('2D'),
11606
+ });
11607
+ formats.push({
11608
+ id: 'pdf417',
11609
+ label: 'PDF417',
11610
+ canWrite: pdf417CanEncode,
11611
+ canRead: pdf417CanDecode,
11612
+ kind: /** @type {'2D'} */ ('2D'),
11613
+ });
11614
+ formats.push({
11615
+ id: 'micropdf417',
11616
+ label: 'MicroPDF417',
11617
+ canWrite: microPdf417CanEncode,
11618
+ canRead: microPdf417CanDecode,
11619
+ kind: /** @type {'2D'} */ ('2D'),
11620
+ });
8540
11621
 
8541
11622
  return formats;
8542
11623
  }
@@ -8557,6 +11638,16 @@ function listFormats() {
8557
11638
  * @param {boolean} [options.checkDigit] Append a check digit, where optional.
8558
11639
  * @param {boolean} [options.fullAscii] Code 39 extended encoding.
8559
11640
  * @param {boolean} [options.gs1] Emit a leading FNC1.
11641
+ * @param {number} [options.layers] Aztec layer count; automatic if omitted.
11642
+ * @param {boolean} [options.compact] Force an Aztec Compact or Full symbol.
11643
+ * @param {number} [options.eccPercent] Requested Aztec error-correction percentage.
11644
+ * @param {number} [options.eccLevel] PDF417 error-correction level, 0-8.
11645
+ * @param {number} [options.columns] PDF417 columns, 1-30.
11646
+ * @param {number} [options.rows] PDF417 rows, 3-90.
11647
+ * @param {number} [options.rowHeight] PDF417 row height in modules.
11648
+ * @param {'auto'|'text'|'byte'|'numeric'} [options.compaction] PDF417 compaction mode.
11649
+ * @param {number} [options.eci] MicroPDF417 byte-compaction ECI assignment (3 or 26).
11650
+ * @param {number} [options.aspectRatio] Preferred MicroPDF417 symbol aspect ratio.
8560
11651
  * @returns {BitMatrix}
8561
11652
  */
8562
11653
  function encode(text, options = {}) {
@@ -8569,10 +11660,19 @@ function encode(text, options = {}) {
8569
11660
  if (format === 'datamatrix' || format === 'data-matrix') {
8570
11661
  return datamatrix.encodeDataMatrix(value, options);
8571
11662
  }
11663
+ if (format === 'aztec' || format === 'aztec-code') {
11664
+ return aztec.encodeAztec(value, options);
11665
+ }
11666
+ if (format === 'pdf417' || format === 'pdf-417') {
11667
+ return pdf417.encodePDF417(value, options);
11668
+ }
11669
+ if (format === 'micropdf417' || format === 'micro-pdf417' || format === 'micro-pdf-417') {
11670
+ return micropdf417.encodeMicroPDF417(value, options);
11671
+ }
8572
11672
 
8573
11673
  const entry = ONED_FORMATS[format];
8574
11674
  if (!entry) {
8575
- const known = [...Object.keys(ONED_FORMATS), 'qr', 'datamatrix'].join(', ');
11675
+ const known = [...Object.keys(ONED_FORMATS), 'qr', 'datamatrix', 'aztec', 'pdf417', 'micropdf417'].join(', ');
8576
11676
  throw new EncodeError(`Unknown format "${format}". Known formats: ${known}`);
8577
11677
  }
8578
11678
  return entry.encode(value, options);
@@ -8582,9 +11682,19 @@ function encode(text, options = {}) {
8582
11682
  * @typedef {object} DecodeResult
8583
11683
  * @property {string} text
8584
11684
  * @property {string} format
8585
- * @property {Uint8Array} [bytes] Raw payload, before text decoding.
11685
+ * @property {Uint8Array} [bytes] Raw octets exposed by byte-oriented payload modes, before text decoding.
11686
+ * @property {{mode: 'text'|'byte'|'numeric', text: string, bytes: Uint8Array, eci: number, latch: number|null, codewordStart: number, codewordEnd: number}[]} [segments] PDF417 compaction segments in source order.
8586
11687
  * @property {number} [version] QR version.
8587
11688
  * @property {string} [ecc] QR error-correction level.
11689
+ * @property {number} [layers] Aztec layer count.
11690
+ * @property {boolean} [compact] Whether an Aztec symbol is Compact.
11691
+ * @property {number} [corrections] Reed–Solomon corrections applied by an Aztec decode.
11692
+ * @property {number} [rows] PDF417 row count.
11693
+ * @property {number} [columns] PDF417 column count.
11694
+ * @property {number} [eccLevel] PDF417 error-correction level.
11695
+ * @property {number} [rowHeight] PDF417 row height in modules.
11696
+ * @property {number} [variant] MicroPDF417 predefined variant number.
11697
+ * @property {number} [eccCodewords] MicroPDF417 fixed error-correction codewords.
8588
11698
  */
8589
11699
 
8590
11700
  /**
@@ -8606,6 +11716,9 @@ function decode(image, options = {}) {
8606
11716
  const want = formats ? new Set(formats.map((f) => f.toLowerCase())) : null;
8607
11717
  const wantQR = !want || want.has('qr') || want.has('qrcode');
8608
11718
  const wantDataMatrix = !want || want.has('datamatrix') || want.has('data-matrix');
11719
+ const wantAztec = !want || want.has('aztec') || want.has('aztec-code');
11720
+ const wantPDF417 = !want || want.has('pdf417') || want.has('pdf-417');
11721
+ const wantMicroPDF417 = !want || want.has('micropdf417') || want.has('micro-pdf417') || want.has('micro-pdf-417');
8609
11722
  const wantOneD = !want || [...want].some((f) => f in ONED_FORMATS);
8610
11723
 
8611
11724
  const source = LuminanceSource.fromImageData(image);
@@ -8643,6 +11756,45 @@ function decode(image, options = {}) {
8643
11756
  }
8644
11757
  }
8645
11758
 
11759
+ if (wantAztec && aztecCanDecode) {
11760
+ // The central bull's-eye is a small, high-contrast target. Hybrid
11761
+ // thresholding can flatten it on clean rendered symbols, so mirror the
11762
+ // Data Matrix global fallback in auto mode.
11763
+ const aztecBits = binarizer === 'auto' ? [bits, binarize(pass, 'global')] : [bits];
11764
+ for (const candidateBits of aztecBits) {
11765
+ try {
11766
+ const found = aztec.detectAndDecodeAztec(candidateBits);
11767
+ if (found) { results.push({ ...found, format: 'aztec' }); break; }
11768
+ } catch {
11769
+ /* no Aztec code with this threshold */
11770
+ }
11771
+ }
11772
+ }
11773
+
11774
+ if (wantPDF417 && pdf417CanDecode) {
11775
+ try {
11776
+ const found = pdf417.detectAndDecodePDF417(bits);
11777
+ if (found) results.push({ ...found, format: 'pdf417' });
11778
+ } catch {
11779
+ /* no PDF417 in this pass */
11780
+ }
11781
+ }
11782
+
11783
+ if (wantMicroPDF417 && microPdf417CanDecode) {
11784
+ // MicroPDF417 detection measures runs across the whole raster. Hybrid
11785
+ // thresholding can alter uniform modules near local-window boundaries,
11786
+ // so retry the global threshold in auto mode as for the other 2D codes.
11787
+ const microPdf417Bits = binarizer === 'auto' ? [bits, binarize(pass, 'global')] : [bits];
11788
+ for (const candidateBits of microPdf417Bits) {
11789
+ try {
11790
+ const found = micropdf417.detectAndDecodeMicroPDF417(candidateBits);
11791
+ if (found) { results.push({ ...found, format: 'micropdf417' }); break; }
11792
+ } catch {
11793
+ /* no MicroPDF417 with this threshold */
11794
+ }
11795
+ }
11796
+ }
11797
+
8646
11798
  if (wantOneD) {
8647
11799
  const oneDFormats = want ? [...want].filter((f) => f in ONED_FORMATS) : null;
8648
11800
  for (const found of decodeOneD(bits, { formats: oneDFormats, tryHarder })) {
@@ -8677,7 +11829,7 @@ function decodeStrict(image, options) {
8677
11829
  }
8678
11830
 
8679
11831
  /** Library version, matching package.json. */
8680
- const VERSION = '1.0.0';
11832
+ const VERSION = '1.2.5';
8681
11833
 
8682
11834
  __exports.listFormats = listFormats;
8683
11835
  __exports.encode = encode;