isahaq-barcode 1.8.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,10 @@
1
1
  /**
2
2
  * Validator - Data validation for different barcode types
3
+ *
4
+ * Validation is intentionally a superset check: it catches the mistakes that
5
+ * produce confusing encoder errors (wrong length, wrong charset, bad check
6
+ * digit). Symbology specific rules that only the encoder knows are surfaced by
7
+ * the encoder itself.
3
8
  */
4
9
 
5
10
  const { BarcodeTypes } = require('../types/BarcodeTypes');
@@ -7,24 +12,21 @@ const { BarcodeTypes } = require('../types/BarcodeTypes');
7
12
  class Validator {
8
13
  constructor() {
9
14
  this.patterns = {
10
- // Numeric patterns
11
15
  numeric: /^[0-9]+$/,
12
- // Alphanumeric patterns
13
16
  alphanumeric: /^[A-Za-z0-9]+$/,
14
- // Code 39 pattern
15
- code39: /^[A-Z0-9\s\-\.\$\/\+\%]+$/,
16
- // Code 39 extended pattern
17
+ code39: /^[A-Z0-9\s\-.$/+%]+$/,
17
18
  code39extended: /^[\x00-\x7F]+$/,
18
- // EAN/UPC patterns
19
+ codabar: /^[A-Da-d][0-9\-$:/.+]*[A-Da-d]$/,
20
+ code11: /^[0-9-]+$/,
19
21
  ean13: /^[0-9]{12,13}$/,
20
22
  ean8: /^[0-9]{7,8}$/,
23
+ ean2: /^[0-9]{2}$/,
24
+ ean5: /^[0-9]{5}$/,
21
25
  upca: /^[0-9]{11,12}$/,
22
- upce: /^[0-9]{6,8}$/,
23
- // QR Code pattern (supports Unicode)
26
+ upce: /^[0-9]{7,8}$/,
27
+ itf14: /^[0-9]{13,14}$/,
24
28
  qrcode: /^[\s\S]+$/,
25
- // Data Matrix pattern
26
29
  datamatrix: /^[\s\S]+$/,
27
- // PDF417 pattern
28
30
  pdf417: /^[\s\S]+$/,
29
31
  };
30
32
  }
@@ -50,10 +52,8 @@ class Validator {
50
52
  };
51
53
  }
52
54
 
53
- // Get configuration for the barcode type
54
55
  const config = BarcodeTypes.getConfig(type);
55
56
 
56
- // Check length constraints
57
57
  if (data.length < config.minLength) {
58
58
  return {
59
59
  valid: false,
@@ -68,7 +68,6 @@ class Validator {
68
68
  };
69
69
  }
70
70
 
71
- // Type-specific validation
72
71
  const validationResult = this.validateByType(data, type);
73
72
  if (!validationResult.valid) {
74
73
  return validationResult;
@@ -94,42 +93,91 @@ class Validator {
94
93
  case 'code128':
95
94
  case 'code128a':
96
95
  case 'code128b':
97
- case 'code128c':
98
96
  case 'code128auto':
99
- return this.validateCode128(data, type);
97
+ return this.validateCode128(data);
98
+
99
+ case 'code128c':
100
+ return this.requireNumeric(data, 'Code 128 Subset C');
100
101
 
101
102
  case 'code39':
102
- case 'code39extended':
103
103
  case 'code39checksum':
104
104
  case 'code39auto':
105
105
  return this.validateCode39(data, type);
106
106
 
107
+ case 'code39extended':
108
+ return this.validateCode39(data, type);
109
+
107
110
  case 'code93':
108
111
  return this.validateCode93(data);
109
112
 
113
+ case 'code25':
114
+ case 'code25auto':
115
+ case 'standard25':
116
+ case 'standard25checksum':
117
+ case 'interleaved25':
118
+ case 'interleaved25checksum':
119
+ case 'interleaved25auto':
120
+ case 'msi':
121
+ case 'msichecksum':
122
+ case 'msiauto':
123
+ case 'code32':
124
+ case 'postnet':
125
+ case 'planet':
126
+ case 'imb':
127
+ case 'pharmacode':
128
+ case 'pharmacodetwotracks':
129
+ return this.requireNumeric(data, BarcodeTypes.getTypeInfo(type).name);
130
+
110
131
  case 'ean13':
111
- return this.validateEAN13(data);
132
+ return this.validateEAN(data, 13, 'EAN-13');
112
133
 
113
134
  case 'ean8':
114
- return this.validateEAN8(data);
135
+ return this.validateEAN(data, 8, 'EAN-8');
136
+
137
+ case 'ean2':
138
+ return this.validatePattern(data, 'ean2', 'EAN-2 must be 2 digits');
139
+
140
+ case 'ean5':
141
+ return this.validatePattern(data, 'ean5', 'EAN-5 must be 5 digits');
115
142
 
116
143
  case 'upca':
117
- return this.validateUPCA(data);
144
+ return this.validateEAN(data, 12, 'UPC-A');
118
145
 
119
146
  case 'upce':
120
- return this.validateUPCE(data);
147
+ return this.validatePattern(
148
+ data,
149
+ 'upce',
150
+ 'UPC-E must be 7 or 8 digits'
151
+ );
152
+
153
+ case 'itf14':
154
+ return this.validateEAN(data, 14, 'ITF-14');
155
+
156
+ case 'codabar':
157
+ return this.validatePattern(
158
+ data,
159
+ 'codabar',
160
+ 'Codabar must start and end with A, B, C or D and contain 0-9 -$:/.+'
161
+ );
162
+
163
+ case 'code11':
164
+ return this.validatePattern(
165
+ data,
166
+ 'code11',
167
+ 'Code 11 supports digits and the - character only'
168
+ );
121
169
 
122
170
  case 'qrcode':
123
- return this.validateQRCode(data);
124
-
125
171
  case 'datamatrix':
126
- return this.validateDataMatrix(data);
127
-
172
+ case 'aztec':
128
173
  case 'pdf417':
129
- return this.validatePDF417(data);
174
+ case 'microqr':
175
+ case 'maxicode':
176
+ case 'code16k':
177
+ case 'code49':
178
+ return { valid: true };
130
179
 
131
180
  default:
132
- // For other types, just check if data is not empty
133
181
  return {
134
182
  valid: data.length > 0,
135
183
  error: data.length === 0 ? 'Data cannot be empty' : null,
@@ -140,11 +188,9 @@ class Validator {
140
188
  /**
141
189
  * Validate Code 128 data
142
190
  * @param {string} data - The data to validate
143
- * @param {string} type - The Code 128 variant
144
191
  * @returns {Object} Validation result
145
192
  */
146
193
  validateCode128(data) {
147
- // Code 128 supports ASCII characters
148
194
  if (!/^[\x00-\x7F]+$/.test(data)) {
149
195
  return {
150
196
  valid: false,
@@ -163,21 +209,21 @@ class Validator {
163
209
  */
164
210
  validateCode39(data, type) {
165
211
  if (type === 'code39extended') {
166
- // Extended Code 39 supports full ASCII
167
- if (!/^[\x00-\x7F]+$/.test(data)) {
212
+ if (!this.patterns.code39extended.test(data)) {
168
213
  return {
169
214
  valid: false,
170
215
  error: 'Extended Code 39 supports ASCII characters only',
171
216
  };
172
217
  }
173
- } else {
174
- // Standard Code 39 supports limited character set
175
- if (!this.patterns.code39.test(data)) {
176
- return {
177
- valid: false,
178
- error: 'Code 39 supports characters: A-Z, 0-9, space, -.$/+%',
179
- };
180
- }
218
+
219
+ return { valid: true };
220
+ }
221
+
222
+ if (!this.patterns.code39.test(data)) {
223
+ return {
224
+ valid: false,
225
+ error: 'Code 39 supports characters: A-Z, 0-9, space, -.$/+%',
226
+ };
181
227
  }
182
228
 
183
229
  return { valid: true };
@@ -189,10 +235,10 @@ class Validator {
189
235
  * @returns {Object} Validation result
190
236
  */
191
237
  validateCode93(data) {
192
- if (!this.patterns.alphanumeric.test(data)) {
238
+ if (!/^[A-Z0-9\s\-.$/+%]+$/.test(data)) {
193
239
  return {
194
240
  valid: false,
195
- error: 'Code 93 supports alphanumeric characters only',
241
+ error: 'Code 93 supports characters: A-Z, 0-9, space, -.$/+%',
196
242
  };
197
243
  }
198
244
 
@@ -200,52 +246,33 @@ class Validator {
200
246
  }
201
247
 
202
248
  /**
203
- * Validate EAN-13 data
249
+ * Validate an EAN/UPC style code, including its check digit when supplied
204
250
  * @param {string} data - The data to validate
251
+ * @param {number} fullLength - Length of the code including the check digit
252
+ * @param {string} label - Human readable symbology name
205
253
  * @returns {Object} Validation result
206
254
  */
207
- validateEAN13(data) {
208
- if (!this.patterns.ean13.test(data)) {
255
+ validateEAN(data, fullLength, label) {
256
+ if (!this.patterns.numeric.test(data)) {
209
257
  return {
210
258
  valid: false,
211
- error: 'EAN-13 must be 12 or 13 digits',
259
+ error: `${label} supports digits only`,
212
260
  };
213
261
  }
214
262
 
215
- // Validate check digit if 13 digits provided
216
- if (data.length === 13) {
217
- const checkDigit = this.calculateEANCheckDigit(data.substring(0, 12));
218
- if (parseInt(data[12]) !== checkDigit) {
219
- return {
220
- valid: false,
221
- error: 'Invalid EAN-13 check digit',
222
- };
223
- }
224
- }
225
-
226
- return { valid: true };
227
- }
228
-
229
- /**
230
- * Validate EAN-8 data
231
- * @param {string} data - The data to validate
232
- * @returns {Object} Validation result
233
- */
234
- validateEAN8(data) {
235
- if (!this.patterns.ean8.test(data)) {
263
+ if (data.length !== fullLength && data.length !== fullLength - 1) {
236
264
  return {
237
265
  valid: false,
238
- error: 'EAN-8 must be 7 or 8 digits',
266
+ error: `${label} must be ${fullLength - 1} or ${fullLength} digits`,
239
267
  };
240
268
  }
241
269
 
242
- // Validate check digit if 8 digits provided
243
- if (data.length === 8) {
244
- const checkDigit = this.calculateEANCheckDigit(data.substring(0, 7));
245
- if (parseInt(data[7]) !== checkDigit) {
270
+ if (data.length === fullLength) {
271
+ const expected = this.calculateEANCheckDigit(data.slice(0, -1));
272
+ if (Number(data[data.length - 1]) !== expected) {
246
273
  return {
247
274
  valid: false,
248
- error: 'Invalid EAN-8 check digit',
275
+ error: `Invalid ${label} check digit (expected ${expected})`,
249
276
  };
250
277
  }
251
278
  }
@@ -254,31 +281,31 @@ class Validator {
254
281
  }
255
282
 
256
283
  /**
257
- * Validate UPC-A data
284
+ * Validate data against one of the configured patterns
258
285
  * @param {string} data - The data to validate
286
+ * @param {string} pattern - Pattern name
287
+ * @param {string} error - Error message used when the pattern does not match
259
288
  * @returns {Object} Validation result
260
289
  */
261
- validateUPCA(data) {
262
- if (!this.patterns.upca.test(data)) {
263
- return {
264
- valid: false,
265
- error: 'UPC-A must be 11 or 12 digits',
266
- };
290
+ validatePattern(data, pattern, error) {
291
+ if (!this.patterns[pattern].test(data)) {
292
+ return { valid: false, error };
267
293
  }
268
294
 
269
295
  return { valid: true };
270
296
  }
271
297
 
272
298
  /**
273
- * Validate UPC-E data
299
+ * Require numeric data
274
300
  * @param {string} data - The data to validate
301
+ * @param {string} label - Human readable symbology name
275
302
  * @returns {Object} Validation result
276
303
  */
277
- validateUPCE(data) {
278
- if (!this.patterns.upce.test(data)) {
304
+ requireNumeric(data, label) {
305
+ if (!this.patterns.numeric.test(data)) {
279
306
  return {
280
307
  valid: false,
281
- error: 'UPC-E must be 6 to 8 digits',
308
+ error: `${label} supports digits only`,
282
309
  };
283
310
  }
284
311
 
@@ -286,66 +313,57 @@ class Validator {
286
313
  }
287
314
 
288
315
  /**
289
- * Validate QR Code data
290
- * @param {string} data - The data to validate
291
- * @returns {Object} Validation result
316
+ * Calculate the mod-10 check digit used by EAN, UPC and ITF codes.
317
+ * Weights alternate 3 and 1 starting from the rightmost payload digit.
318
+ * @param {string} data - The payload without its check digit
319
+ * @returns {number} Check digit
292
320
  */
293
- validateQRCode(data) {
294
- // QR Code supports Unicode, so we just check if it's not empty
295
- if (data.length === 0) {
296
- return {
297
- valid: false,
298
- error: 'QR Code data cannot be empty',
299
- };
321
+ calculateEANCheckDigit(data) {
322
+ let sum = 0;
323
+
324
+ for (let i = 0; i < data.length; i++) {
325
+ const digit = parseInt(data[i], 10);
326
+ const weight = (data.length - 1 - i) % 2 === 0 ? 3 : 1;
327
+ sum += digit * weight;
300
328
  }
301
329
 
302
- return { valid: true };
330
+ return (10 - (sum % 10)) % 10;
303
331
  }
304
332
 
305
333
  /**
306
- * Validate Data Matrix data
334
+ * Validate EAN-13 data
307
335
  * @param {string} data - The data to validate
308
336
  * @returns {Object} Validation result
309
337
  */
310
- validateDataMatrix(data) {
311
- if (data.length === 0) {
312
- return {
313
- valid: false,
314
- error: 'Data Matrix data cannot be empty',
315
- };
316
- }
317
-
318
- return { valid: true };
338
+ validateEAN13(data) {
339
+ return this.validateEAN(data, 13, 'EAN-13');
319
340
  }
320
341
 
321
342
  /**
322
- * Validate PDF417 data
343
+ * Validate EAN-8 data
323
344
  * @param {string} data - The data to validate
324
345
  * @returns {Object} Validation result
325
346
  */
326
- validatePDF417(data) {
327
- if (data.length === 0) {
328
- return {
329
- valid: false,
330
- error: 'PDF417 data cannot be empty',
331
- };
332
- }
347
+ validateEAN8(data) {
348
+ return this.validateEAN(data, 8, 'EAN-8');
349
+ }
333
350
 
334
- return { valid: true };
351
+ /**
352
+ * Validate UPC-A data
353
+ * @param {string} data - The data to validate
354
+ * @returns {Object} Validation result
355
+ */
356
+ validateUPCA(data) {
357
+ return this.validateEAN(data, 12, 'UPC-A');
335
358
  }
336
359
 
337
360
  /**
338
- * Calculate EAN check digit
339
- * @param {string} data - The data without check digit
340
- * @returns {number} Check digit
361
+ * Validate UPC-E data
362
+ * @param {string} data - The data to validate
363
+ * @returns {Object} Validation result
341
364
  */
342
- calculateEANCheckDigit(data) {
343
- let sum = 0;
344
- for (let i = 0; i < data.length; i++) {
345
- const digit = parseInt(data[i]);
346
- sum += i % 2 === 0 ? digit : digit * 3;
347
- }
348
- return (10 - (sum % 10)) % 10;
365
+ validateUPCE(data) {
366
+ return this.validatePattern(data, 'upce', 'UPC-E must be 7 or 8 digits');
349
367
  }
350
368
  }
351
369
 
package/.eslintrc.js DELETED
@@ -1,29 +0,0 @@
1
- module.exports = {
2
- env: {
3
- node: true,
4
- es2021: true,
5
- jest: true,
6
- },
7
- extends: ['standard'],
8
- parserOptions: {
9
- ecmaVersion: 12,
10
- sourceType: 'module',
11
- },
12
- rules: {
13
- 'no-console': 'warn',
14
- 'no-unused-vars': 'error',
15
- 'prefer-const': 'error',
16
- 'no-var': 'error',
17
- 'object-shorthand': 'error',
18
- 'prefer-template': 'error',
19
- },
20
- globals: {
21
- Buffer: 'readonly',
22
- process: 'readonly',
23
- __dirname: 'readonly',
24
- __filename: 'readonly',
25
- module: 'readonly',
26
- require: 'readonly',
27
- exports: 'readonly',
28
- },
29
- };
package/.prettierignore DELETED
@@ -1,11 +0,0 @@
1
- node_modules/
2
- dist/
3
- build/
4
- coverage/
5
- *.log
6
- .DS_Store
7
- .env
8
- .env.local
9
- .env.development.local
10
- .env.test.local
11
- .env.production.local
package/.prettierrc DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "semi": true,
3
- "trailingComma": "es5",
4
- "singleQuote": true,
5
- "printWidth": 80,
6
- "tabWidth": 2,
7
- "useTabs": false,
8
- "bracketSpacing": true,
9
- "arrowParens": "avoid",
10
- "endOfLine": "lf"
11
- }
package/eslint.config.js DELETED
@@ -1,67 +0,0 @@
1
- module.exports = [
2
- {
3
- files: ['**/*.js'],
4
- languageOptions: {
5
- ecmaVersion: 2022,
6
- sourceType: 'module',
7
- globals: {
8
- console: 'readonly',
9
- process: 'readonly',
10
- Buffer: 'readonly',
11
- __dirname: 'readonly',
12
- __filename: 'readonly',
13
- global: 'readonly',
14
- module: 'readonly',
15
- require: 'readonly',
16
- exports: 'readonly',
17
- document: 'readonly',
18
- FileReader: 'readonly',
19
- },
20
- },
21
- plugins: {
22
- prettier: require('eslint-plugin-prettier'),
23
- },
24
- rules: {
25
- 'prettier/prettier': 'error',
26
- 'no-unused-vars': 'warn',
27
- 'no-console': 'off',
28
- 'no-undef': 'error',
29
- },
30
- },
31
- {
32
- files: ['tests/**/*.js', '**/*.test.js'],
33
- languageOptions: {
34
- ecmaVersion: 2022,
35
- sourceType: 'module',
36
- globals: {
37
- console: 'readonly',
38
- process: 'readonly',
39
- Buffer: 'readonly',
40
- __dirname: 'readonly',
41
- __filename: 'readonly',
42
- global: 'readonly',
43
- module: 'readonly',
44
- require: 'readonly',
45
- exports: 'readonly',
46
- describe: 'readonly',
47
- test: 'readonly',
48
- it: 'readonly',
49
- expect: 'readonly',
50
- beforeEach: 'readonly',
51
- afterEach: 'readonly',
52
- beforeAll: 'readonly',
53
- afterAll: 'readonly',
54
- jest: 'readonly',
55
- },
56
- },
57
- plugins: {
58
- prettier: require('eslint-plugin-prettier'),
59
- },
60
- rules: {
61
- 'prettier/prettier': 'error',
62
- 'no-unused-vars': 'warn',
63
- 'no-console': 'off',
64
- 'no-undef': 'error',
65
- },
66
- },
67
- ];
@@ -1,58 +0,0 @@
1
- /**
2
- * Basic usage examples for Isahaq Barcode Generator
3
- */
4
-
5
- const BarcodeGenerator = require('../index');
6
-
7
- async function basicExamples() {
8
- console.log('šŸš€ Basic Barcode Generation Examples\n');
9
-
10
- // 1. Generate PNG barcode
11
- console.log('1. Generating PNG barcode...');
12
- const pngBuffer = BarcodeGenerator.png('1234567890', 'code128');
13
- console.log(` PNG buffer size: ${pngBuffer.length} bytes`);
14
-
15
- // 2. Generate SVG barcode
16
- console.log('2. Generating SVG barcode...');
17
- const svgString = BarcodeGenerator.svg('1234567890', 'ean13');
18
- console.log(` SVG length: ${svgString.length} characters`);
19
-
20
- // 3. Generate HTML barcode
21
- console.log('3. Generating HTML barcode...');
22
- const htmlString = BarcodeGenerator.html('1234567890', 'code39');
23
- console.log(` HTML length: ${htmlString.length} characters`);
24
-
25
- // 4. Generate PDF barcode
26
- console.log('4. Generating PDF barcode...');
27
- const pdfBuffer = await BarcodeGenerator.pdf('1234567890', 'code128');
28
- console.log(` PDF buffer size: ${pdfBuffer.length} bytes`);
29
-
30
- // 5. Generate QR code
31
- console.log('5. Generating QR code...');
32
- const qrCode = BarcodeGenerator.modernQr({
33
- data: 'https://example.com',
34
- size: 300,
35
- margin: 10,
36
- });
37
- const qrBuffer = await qrCode.generate();
38
- console.log(` QR code buffer size: ${qrBuffer.length} bytes`);
39
-
40
- // 6. Generate QR code with logo
41
- console.log('6. Generating QR code with logo...');
42
- const qrWithLogo = BarcodeGenerator.modernQr({
43
- data: 'https://example.com',
44
- size: 300,
45
- logoPath: 'path/to/logo.png',
46
- logoSize: 60,
47
- label: 'Scan me!',
48
- });
49
- const qrWithLogoBuffer = await qrWithLogo.generate();
50
- console.log(
51
- ` QR code with logo buffer size: ${qrWithLogoBuffer.length} bytes`
52
- );
53
-
54
- console.log('\nāœ… All examples completed successfully!');
55
- }
56
-
57
- // Run examples
58
- basicExamples().catch(console.error);
@@ -1,56 +0,0 @@
1
- [
2
- {
3
- "data": "1234567890",
4
- "type": "code128",
5
- "format": "png",
6
- "options": {
7
- "width": 3,
8
- "height": 150,
9
- "displayValue": true
10
- }
11
- },
12
- {
13
- "data": "9876543210",
14
- "type": "code39",
15
- "format": "svg",
16
- "options": {
17
- "width": 2,
18
- "height": 100,
19
- "displayValue": false
20
- }
21
- },
22
- {
23
- "data": "1234567890123",
24
- "type": "ean13",
25
- "format": "png",
26
- "options": {
27
- "width": 2,
28
- "height": 120,
29
- "displayValue": true
30
- }
31
- },
32
- {
33
- "data": "https://example.com",
34
- "type": "qrcode",
35
- "format": "png",
36
- "options": {
37
- "size": 300,
38
- "margin": 10,
39
- "logoPath": "path/to/logo.png",
40
- "logoSize": 60,
41
- "label": "Scan me!"
42
- }
43
- },
44
- {
45
- "data": "https://github.com",
46
- "type": "qrcode",
47
- "format": "png",
48
- "options": {
49
- "size": 400,
50
- "margin": 15,
51
- "watermark": "GitHub",
52
- "watermarkPosition": "center",
53
- "errorCorrectionLevel": "H"
54
- }
55
- }
56
- ]