@sythos/js_barcode_universal 1.5.3 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -118,8 +118,8 @@ The `unpkg` and `jsdelivr` fields point at the IIFE bundle, so a CDN needs no in
118
118
 
119
119
  ```html
120
120
  <script src="https://unpkg.com/@sythos/js_barcode_universal"></script>
121
- <script src="https://unpkg.com/@sythos/js_barcode_universal@1.5.3"></script>
122
- <script src="https://cdn.jsdelivr.net/npm/@sythos/js_barcode_universal@1.5.3"></script>
121
+ <script src="https://unpkg.com/@sythos/js_barcode_universal@1.5.4"></script>
122
+ <script src="https://cdn.jsdelivr.net/npm/@sythos/js_barcode_universal@1.5.4"></script>
123
123
  ```
124
124
 
125
125
  Pin the version for anything you ship; the unpinned form resolves to `latest` and will move under
@@ -407,6 +407,12 @@ the generic `ean2` and `ean5` format IDs. The image reader recognizes them only
407
407
  when attached to a validated EAN/UPC parent; use the composition helpers with
408
408
  an EAN/UPC base symbol.
409
409
 
410
+ EAN-2 and EAN-5 are parent-bound supplements, never standalone image results. They may be
411
+ listed with EAN-13, EAN-8, UPC-A, UPC-E or Bookland ISBN in `formats`: the parent remains valid
412
+ without a supplement, and a valid requested supplement is exposed only through `result.addon`.
413
+ If only `ean2` or `ean5` is requested, a validated EAN/UPC parent is still required and remains
414
+ the returned `format`; an absent, malformed or unrequested supplement never rejects the parent.
415
+
410
416
  ### GS1 DataBar
411
417
 
412
418
  The `databar` subpath exposes original GS1 GTIN/AI codecs plus physical
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Sythos Barcode Suite v1.5.3
2
+ * Sythos Barcode Suite v1.5.4
3
3
  *
4
4
  * MIT License
5
5
  *
@@ -3907,6 +3907,34 @@ const DECODERS = [
3907
3907
  ['codabar', decodeCodabar],
3908
3908
  ];
3909
3909
 
3910
+ const EAN_PARENT_FORMATS = new Set(['ean13', 'ean8', 'upca', 'upce', 'isbn']);
3911
+ const EAN_SUPPLEMENT_FORMATS = new Set(['ean2', 'ean5']);
3912
+
3913
+ /** @param {string} format @returns {boolean} */
3914
+ function isEANParentFormat(format) {
3915
+ return format === 'ean13' || format === 'ean8' || format === 'upca' || format === 'upce';
3916
+ }
3917
+
3918
+ /**
3919
+ * An ISBN is printed as a Bookland EAN-13, so its decoded parent remains
3920
+ * `ean13` while an ISBN filter accepts only the Bookland prefixes.
3921
+ *
3922
+ * @param {{format:string, text:string}} result
3923
+ * @param {Set<string>} enabled
3924
+ * @returns {boolean}
3925
+ */
3926
+ function isRequestedEANParent(result, enabled) {
3927
+ if (enabled.has(result.format)) return true;
3928
+ return result.format === 'ean13' && enabled.has('isbn') && /^97[89]/.test(result.text);
3929
+ }
3930
+
3931
+ /** @param {object} result @returns {object} */
3932
+ function withoutEANAddon(result) {
3933
+ const { addon, ...parent } = result;
3934
+ void addon;
3935
+ return parent;
3936
+ }
3937
+
3910
3938
  /**
3911
3939
  * Read every linear symbol found in a binarized image.
3912
3940
  *
@@ -3924,7 +3952,8 @@ function decodeOneD(image, options = {}) {
3924
3952
  if (!enabled) return true;
3925
3953
  if (enabled.has(id)) return true;
3926
3954
  if (id === 'ean13' || id === 'ean8' || id === 'upca' || id === 'upce') {
3927
- return enabled.has('ean2') || enabled.has('ean5');
3955
+ return enabled.has('ean2') || enabled.has('ean5') ||
3956
+ (id === 'ean13' && enabled.has('isbn'));
3928
3957
  }
3929
3958
  if (id === 'code128') return enabled.has('gs1128');
3930
3959
  if (id === 'gs1databar14') return enabled.has('databar') || enabled.has('gs1-databar14');
@@ -3961,11 +3990,21 @@ function decodeOneD(image, options = {}) {
3961
3990
  }
3962
3991
  if (!result) continue;
3963
3992
  if (enabled) {
3964
- const addonRequested = enabled.has('ean2') || enabled.has('ean5');
3965
- const isEANBase = result.format === 'ean13' || result.format === 'ean8' ||
3966
- result.format === 'upca' || result.format === 'upce';
3967
- if (isEANBase && addonRequested) {
3968
- if (!result.addon || !enabled.has(result.addon.format)) continue;
3993
+ if (isEANParentFormat(result.format)) {
3994
+ const baseRequested = [...EAN_PARENT_FORMATS].some((format) => enabled.has(format));
3995
+ const parentRequested = isRequestedEANParent(result, enabled);
3996
+ if (baseRequested && !parentRequested) {
3997
+ continue;
3998
+ }
3999
+ if (!baseRequested) {
4000
+ // A supplement is never an independent barcode. When it is the
4001
+ // only requested format, return its validated EAN/UPC parent.
4002
+ if (!result.addon || !EAN_SUPPLEMENT_FORMATS.has(result.addon.format) ||
4003
+ !enabled.has(result.addon.format)) continue;
4004
+ } else if (result.addon && !enabled.has(result.addon.format)) {
4005
+ // Supplements are optional whenever a requested parent exists.
4006
+ result = withoutEANAddon(result);
4007
+ }
3969
4008
  } else if (result.format === 'gs1128') {
3970
4009
  if (!enabled.has('gs1128') && !enabled.has('code128')) continue;
3971
4010
  } else if (result.format === 'gs1databar14') {
@@ -3985,7 +4024,15 @@ function decodeOneD(image, options = {}) {
3985
4024
  }
3986
4025
  }
3987
4026
 
3988
- return results;
4027
+ // A valid EAN/UPC parent is substantially more constrained than a generic
4028
+ // narrow/wide candidate. Suppress competing interpretations of the same
4029
+ // scanline, while retaining symbols detected on other rows.
4030
+ const eanRows = new Set(results
4031
+ .filter((result) => isEANParentFormat(result.format))
4032
+ .map((result) => result.row));
4033
+ return eanRows.size === 0
4034
+ ? results
4035
+ : results.filter((result) => !eanRows.has(result.row) || isEANParentFormat(result.format));
3989
4036
  }
3990
4037
 
3991
4038
  /**
@@ -16813,7 +16860,7 @@ function decodeStrict(image, options) {
16813
16860
  }
16814
16861
 
16815
16862
  /** Library version, matching package.json. */
16816
- const VERSION = '1.5.3';
16863
+ const VERSION = '1.5.4';
16817
16864
 
16818
16865
  __exports.listFormats = listFormats;
16819
16866
  __exports.encode = encode;
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Sythos Barcode Suite v1.5.3
2
+ * Sythos Barcode Suite v1.5.4
3
3
  *
4
4
  * MIT License
5
5
  *
@@ -3908,6 +3908,34 @@ const DECODERS = [
3908
3908
  ['codabar', decodeCodabar],
3909
3909
  ];
3910
3910
 
3911
+ const EAN_PARENT_FORMATS = new Set(['ean13', 'ean8', 'upca', 'upce', 'isbn']);
3912
+ const EAN_SUPPLEMENT_FORMATS = new Set(['ean2', 'ean5']);
3913
+
3914
+ /** @param {string} format @returns {boolean} */
3915
+ function isEANParentFormat(format) {
3916
+ return format === 'ean13' || format === 'ean8' || format === 'upca' || format === 'upce';
3917
+ }
3918
+
3919
+ /**
3920
+ * An ISBN is printed as a Bookland EAN-13, so its decoded parent remains
3921
+ * `ean13` while an ISBN filter accepts only the Bookland prefixes.
3922
+ *
3923
+ * @param {{format:string, text:string}} result
3924
+ * @param {Set<string>} enabled
3925
+ * @returns {boolean}
3926
+ */
3927
+ function isRequestedEANParent(result, enabled) {
3928
+ if (enabled.has(result.format)) return true;
3929
+ return result.format === 'ean13' && enabled.has('isbn') && /^97[89]/.test(result.text);
3930
+ }
3931
+
3932
+ /** @param {object} result @returns {object} */
3933
+ function withoutEANAddon(result) {
3934
+ const { addon, ...parent } = result;
3935
+ void addon;
3936
+ return parent;
3937
+ }
3938
+
3911
3939
  /**
3912
3940
  * Read every linear symbol found in a binarized image.
3913
3941
  *
@@ -3925,7 +3953,8 @@ function decodeOneD(image, options = {}) {
3925
3953
  if (!enabled) return true;
3926
3954
  if (enabled.has(id)) return true;
3927
3955
  if (id === 'ean13' || id === 'ean8' || id === 'upca' || id === 'upce') {
3928
- return enabled.has('ean2') || enabled.has('ean5');
3956
+ return enabled.has('ean2') || enabled.has('ean5') ||
3957
+ (id === 'ean13' && enabled.has('isbn'));
3929
3958
  }
3930
3959
  if (id === 'code128') return enabled.has('gs1128');
3931
3960
  if (id === 'gs1databar14') return enabled.has('databar') || enabled.has('gs1-databar14');
@@ -3962,11 +3991,21 @@ function decodeOneD(image, options = {}) {
3962
3991
  }
3963
3992
  if (!result) continue;
3964
3993
  if (enabled) {
3965
- const addonRequested = enabled.has('ean2') || enabled.has('ean5');
3966
- const isEANBase = result.format === 'ean13' || result.format === 'ean8' ||
3967
- result.format === 'upca' || result.format === 'upce';
3968
- if (isEANBase && addonRequested) {
3969
- if (!result.addon || !enabled.has(result.addon.format)) continue;
3994
+ if (isEANParentFormat(result.format)) {
3995
+ const baseRequested = [...EAN_PARENT_FORMATS].some((format) => enabled.has(format));
3996
+ const parentRequested = isRequestedEANParent(result, enabled);
3997
+ if (baseRequested && !parentRequested) {
3998
+ continue;
3999
+ }
4000
+ if (!baseRequested) {
4001
+ // A supplement is never an independent barcode. When it is the
4002
+ // only requested format, return its validated EAN/UPC parent.
4003
+ if (!result.addon || !EAN_SUPPLEMENT_FORMATS.has(result.addon.format) ||
4004
+ !enabled.has(result.addon.format)) continue;
4005
+ } else if (result.addon && !enabled.has(result.addon.format)) {
4006
+ // Supplements are optional whenever a requested parent exists.
4007
+ result = withoutEANAddon(result);
4008
+ }
3970
4009
  } else if (result.format === 'gs1128') {
3971
4010
  if (!enabled.has('gs1128') && !enabled.has('code128')) continue;
3972
4011
  } else if (result.format === 'gs1databar14') {
@@ -3986,7 +4025,15 @@ function decodeOneD(image, options = {}) {
3986
4025
  }
3987
4026
  }
3988
4027
 
3989
- return results;
4028
+ // A valid EAN/UPC parent is substantially more constrained than a generic
4029
+ // narrow/wide candidate. Suppress competing interpretations of the same
4030
+ // scanline, while retaining symbols detected on other rows.
4031
+ const eanRows = new Set(results
4032
+ .filter((result) => isEANParentFormat(result.format))
4033
+ .map((result) => result.row));
4034
+ return eanRows.size === 0
4035
+ ? results
4036
+ : results.filter((result) => !eanRows.has(result.row) || isEANParentFormat(result.format));
3990
4037
  }
3991
4038
 
3992
4039
  /**
@@ -16814,7 +16861,7 @@ function decodeStrict(image, options) {
16814
16861
  }
16815
16862
 
16816
16863
  /** Library version, matching package.json. */
16817
- const VERSION = '1.5.3';
16864
+ const VERSION = '1.5.4';
16818
16865
 
16819
16866
  __exports.listFormats = listFormats;
16820
16867
  __exports.encode = encode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sythos/js_barcode_universal",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "description": "Read and write barcodes in JavaScript with zero runtime dependencies. QR Code, Micro QR, rMQR, FrameQR Code, Aztec Code and Rune, PDF417 variants, GS1 DataBar Omnidirectional/Truncated, EAN supplements and one-dimensional formats.",
5
5
  "author": {
6
6
  "name": "Sythos",
package/src/index.js CHANGED
@@ -588,4 +588,4 @@ export function decodeStrict(image, options) {
588
588
  }
589
589
 
590
590
  /** Library version, matching package.json. */
591
- export const VERSION = '1.5.3';
591
+ export const VERSION = '1.5.4';
@@ -1142,6 +1142,34 @@ const DECODERS = [
1142
1142
  ['codabar', decodeCodabar],
1143
1143
  ];
1144
1144
 
1145
+ const EAN_PARENT_FORMATS = new Set(['ean13', 'ean8', 'upca', 'upce', 'isbn']);
1146
+ const EAN_SUPPLEMENT_FORMATS = new Set(['ean2', 'ean5']);
1147
+
1148
+ /** @param {string} format @returns {boolean} */
1149
+ function isEANParentFormat(format) {
1150
+ return format === 'ean13' || format === 'ean8' || format === 'upca' || format === 'upce';
1151
+ }
1152
+
1153
+ /**
1154
+ * An ISBN is printed as a Bookland EAN-13, so its decoded parent remains
1155
+ * `ean13` while an ISBN filter accepts only the Bookland prefixes.
1156
+ *
1157
+ * @param {{format:string, text:string}} result
1158
+ * @param {Set<string>} enabled
1159
+ * @returns {boolean}
1160
+ */
1161
+ function isRequestedEANParent(result, enabled) {
1162
+ if (enabled.has(result.format)) return true;
1163
+ return result.format === 'ean13' && enabled.has('isbn') && /^97[89]/.test(result.text);
1164
+ }
1165
+
1166
+ /** @param {object} result @returns {object} */
1167
+ function withoutEANAddon(result) {
1168
+ const { addon, ...parent } = result;
1169
+ void addon;
1170
+ return parent;
1171
+ }
1172
+
1145
1173
  /**
1146
1174
  * Read every linear symbol found in a binarized image.
1147
1175
  *
@@ -1159,7 +1187,8 @@ export function decodeOneD(image, options = {}) {
1159
1187
  if (!enabled) return true;
1160
1188
  if (enabled.has(id)) return true;
1161
1189
  if (id === 'ean13' || id === 'ean8' || id === 'upca' || id === 'upce') {
1162
- return enabled.has('ean2') || enabled.has('ean5');
1190
+ return enabled.has('ean2') || enabled.has('ean5') ||
1191
+ (id === 'ean13' && enabled.has('isbn'));
1163
1192
  }
1164
1193
  if (id === 'code128') return enabled.has('gs1128');
1165
1194
  if (id === 'gs1databar14') return enabled.has('databar') || enabled.has('gs1-databar14');
@@ -1196,11 +1225,21 @@ export function decodeOneD(image, options = {}) {
1196
1225
  }
1197
1226
  if (!result) continue;
1198
1227
  if (enabled) {
1199
- const addonRequested = enabled.has('ean2') || enabled.has('ean5');
1200
- const isEANBase = result.format === 'ean13' || result.format === 'ean8' ||
1201
- result.format === 'upca' || result.format === 'upce';
1202
- if (isEANBase && addonRequested) {
1203
- if (!result.addon || !enabled.has(result.addon.format)) continue;
1228
+ if (isEANParentFormat(result.format)) {
1229
+ const baseRequested = [...EAN_PARENT_FORMATS].some((format) => enabled.has(format));
1230
+ const parentRequested = isRequestedEANParent(result, enabled);
1231
+ if (baseRequested && !parentRequested) {
1232
+ continue;
1233
+ }
1234
+ if (!baseRequested) {
1235
+ // A supplement is never an independent barcode. When it is the
1236
+ // only requested format, return its validated EAN/UPC parent.
1237
+ if (!result.addon || !EAN_SUPPLEMENT_FORMATS.has(result.addon.format) ||
1238
+ !enabled.has(result.addon.format)) continue;
1239
+ } else if (result.addon && !enabled.has(result.addon.format)) {
1240
+ // Supplements are optional whenever a requested parent exists.
1241
+ result = withoutEANAddon(result);
1242
+ }
1204
1243
  } else if (result.format === 'gs1128') {
1205
1244
  if (!enabled.has('gs1128') && !enabled.has('code128')) continue;
1206
1245
  } else if (result.format === 'gs1databar14') {
@@ -1220,7 +1259,15 @@ export function decodeOneD(image, options = {}) {
1220
1259
  }
1221
1260
  }
1222
1261
 
1223
- return results;
1262
+ // A valid EAN/UPC parent is substantially more constrained than a generic
1263
+ // narrow/wide candidate. Suppress competing interpretations of the same
1264
+ // scanline, while retaining symbols detected on other rows.
1265
+ const eanRows = new Set(results
1266
+ .filter((result) => isEANParentFormat(result.format))
1267
+ .map((result) => result.row));
1268
+ return eanRows.size === 0
1269
+ ? results
1270
+ : results.filter((result) => !eanRows.has(result.row) || isEANParentFormat(result.format));
1224
1271
  }
1225
1272
 
1226
1273
  /**