isahaq-barcode 1.9.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.
package/bin/generate.js CHANGED
@@ -5,20 +5,91 @@
5
5
  */
6
6
 
7
7
  const { Command } = require('commander');
8
- const chalk = require('chalk');
9
- const ora = require('ora');
10
8
  const fs = require('fs').promises;
11
9
  const path = require('path');
12
10
  const BarcodeGenerator = require('../index');
11
+ const { version } = require('../package.json');
12
+
13
+ /** Minimal ANSI colouring - honours NO_COLOR and non-TTY output */
14
+ const useColor =
15
+ !process.env.NO_COLOR && process.stderr.isTTY && process.env.TERM !== 'dumb';
16
+
17
+ const color = {
18
+ red: text => (useColor ? `${text}` : text),
19
+ green: text => (useColor ? `${text}` : text),
20
+ blue: text => (useColor ? `${text}` : text),
21
+ dim: text => (useColor ? `${text}` : text),
22
+ };
23
+
24
+ /**
25
+ * Report progress on stderr so stdout stays pipeable
26
+ * @param {string} message - Message to print
27
+ */
28
+ function status(message) {
29
+ process.stderr.write(`${color.dim(message)}\n`);
30
+ }
31
+
32
+ /**
33
+ * Report success on stderr
34
+ * @param {string} message - Message to print
35
+ */
36
+ function succeed(message) {
37
+ process.stderr.write(`${color.green('✔')} ${message}\n`);
38
+ }
39
+
40
+ /**
41
+ * Print an error and exit with a non-zero status
42
+ * @param {string} message - Message to print
43
+ */
44
+ function fail(message) {
45
+ process.stderr.write(`${color.red('✖')} ${message}\n`);
46
+ process.exit(1);
47
+ }
48
+
49
+ /**
50
+ * Convert a hex colour into an RGB array
51
+ * @param {string} hex - Hex colour
52
+ * @returns {number[]} RGB components
53
+ */
54
+ function hexToRgb(hex) {
55
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
56
+ return result
57
+ ? [
58
+ parseInt(result[1], 16),
59
+ parseInt(result[2], 16),
60
+ parseInt(result[3], 16),
61
+ ]
62
+ : [0, 0, 0];
63
+ }
64
+
65
+ /**
66
+ * Write output to a file, or to stdout when no path is given
67
+ * @param {Buffer|string} result - Generated barcode
68
+ * @param {string|undefined} output - Output file path
69
+ * @param {string} label - Label used in the success message
70
+ * @returns {Promise<void>}
71
+ */
72
+ async function emit(result, output, label) {
73
+ if (output) {
74
+ await fs.writeFile(output, result);
75
+ succeed(`${label} saved to ${output}`);
76
+ return;
77
+ }
78
+
79
+ if (Buffer.isBuffer(result)) {
80
+ process.stdout.write(`${result.toString('base64')}\n`);
81
+ } else {
82
+ process.stdout.write(`${result}\n`);
83
+ }
84
+ }
13
85
 
14
86
  const program = new Command();
15
87
 
16
88
  program
17
89
  .name('barcode-generate')
18
- .description('Generate barcodes and QR codes from command line')
19
- .version('1.0.0');
90
+ .description('Generate barcodes and QR codes from the command line')
91
+ .version(version);
20
92
 
21
- // Barcode generation command
22
93
  program
23
94
  .command('barcode')
24
95
  .description('Generate a barcode')
@@ -26,103 +97,83 @@ program
26
97
  .option('-t, --type <type>', 'Barcode type', 'code128')
27
98
  .option('-f, --format <format>', 'Output format (png, svg, html, pdf)', 'png')
28
99
  .option('-o, --output <file>', 'Output file path')
29
- .option('-w, --width <width>', 'Barcode width', '2')
30
- .option('-h, --height <height>', 'Barcode height', '100')
31
- .option('--no-text', 'Hide text below barcode')
32
- .option('--foreground <color>', 'Foreground color (hex)', '#000000')
33
- .option('--background <color>', 'Background color (hex)', '#ffffff')
100
+ .option('-w, --width <width>', 'Module (narrow bar) width in pixels', '2')
101
+ .option('-h, --height <height>', 'Bar height in pixels', '100')
102
+ .option('-s, --size <size>', 'Target size in pixels for 2D barcodes')
103
+ .option('-m, --margin <margin>', 'Quiet zone in pixels (default: 10 modules)')
104
+ .option('-q, --quiet-zone <modules>', 'Quiet zone in modules', '10')
105
+ .option('--no-text', 'Hide the human readable text')
106
+ .option('--foreground <color>', 'Foreground colour (hex)', '#000000')
107
+ .option('--background <color>', 'Background colour (hex)', '#ffffff')
108
+ .option('--rotate <rotation>', 'Rotation: N, R, L or I', 'N')
34
109
  .action(async options => {
35
- const spinner = ora('Generating barcode...').start();
110
+ status('Generating barcode...');
36
111
 
37
112
  try {
38
113
  const renderOptions = {
39
- width: parseInt(options.width),
40
- height: parseInt(options.height),
114
+ width: parseInt(options.width, 10),
115
+ height: parseInt(options.height, 10),
116
+ quietZone: parseInt(options.quietZone, 10),
41
117
  displayValue: options.text,
42
118
  lineColor: options.foreground,
43
119
  background: options.background,
120
+ rotate: options.rotate,
44
121
  };
45
122
 
46
- let result;
47
- switch (options.format) {
48
- case 'png':
49
- result = BarcodeGenerator.png(
50
- options.data,
51
- options.type,
52
- renderOptions
53
- );
54
- break;
55
- case 'svg':
56
- result = BarcodeGenerator.svg(
57
- options.data,
58
- options.type,
59
- renderOptions
60
- );
61
- break;
62
- case 'html':
63
- result = BarcodeGenerator.html(
64
- options.data,
65
- options.type,
66
- renderOptions
67
- );
68
- break;
69
- case 'pdf':
70
- result = BarcodeGenerator.pdf(
71
- options.data,
72
- options.type,
73
- renderOptions
74
- );
75
- break;
76
- default:
77
- throw new Error(`Unsupported format: ${options.format}`);
123
+ // Only override the module based quiet zone when a pixel margin is asked for
124
+ if (options.margin !== undefined) {
125
+ renderOptions.margin = parseInt(options.margin, 10);
78
126
  }
79
127
 
80
- if (options.output) {
81
- await fs.writeFile(options.output, result);
82
- spinner.succeed(`Barcode saved to ${options.output}`);
83
- } else {
84
- // Output to stdout (for base64 encoding)
85
- if (Buffer.isBuffer(result)) {
86
- console.log(result.toString('base64'));
87
- } else {
88
- console.log(result);
89
- }
128
+ if (options.size) {
129
+ renderOptions.size = parseInt(options.size, 10);
90
130
  }
131
+
132
+ const result = await BarcodeGenerator.generate(
133
+ options.data,
134
+ options.type,
135
+ options.format,
136
+ renderOptions
137
+ );
138
+
139
+ await emit(result, options.output, 'Barcode');
91
140
  } catch (error) {
92
- spinner.fail(`Error: ${error.message}`);
93
- process.exit(1);
141
+ fail(error.message);
94
142
  }
95
143
  });
96
144
 
97
- // QR code generation command
98
145
  program
99
146
  .command('qr')
100
147
  .description('Generate a QR code')
101
148
  .requiredOption('-d, --data <data>', 'Data to encode')
102
- .option('-s, --size <size>', 'QR code size', '300')
103
- .option('-f, --format <format>', 'Output format (png, svg, jpg)', 'png')
149
+ .option('-s, --size <size>', 'QR code size in pixels', '300')
150
+ .option('-f, --format <format>', 'Output format (png, jpeg, svg)', 'png')
104
151
  .option('-o, --output <file>', 'Output file path')
105
- .option('-m, --margin <margin>', 'Margin size', '10')
152
+ .option('-m, --margin <margin>', 'Margin in pixels', '10')
106
153
  .option(
107
154
  '-e, --error-correction <level>',
108
155
  'Error correction level (L, M, Q, H)',
109
156
  'M'
110
157
  )
111
- .option('--foreground <color>', 'Foreground color (hex)', '#000000')
112
- .option('--background <color>', 'Background color (hex)', '#ffffff')
113
- .option('--logo <path>', 'Logo image path')
114
- .option('--logo-size <size>', 'Logo size percentage', '60')
115
- .option('--label <text>', 'Label text')
116
- .option('--watermark <text>', 'Watermark text')
158
+ .option('--foreground <color>', 'Foreground colour (hex)', '#000000')
159
+ .option('--background <color>', 'Background colour (hex)', '#ffffff')
160
+ .option('--logo <path>', 'Logo image path (requires the canvas package)')
161
+ .option(
162
+ '--logo-size <size>',
163
+ 'Logo size as a percentage of the QR code',
164
+ '20'
165
+ )
166
+ .option('--label <text>', 'Label text (requires the canvas package)')
167
+ .option('--watermark <text>', 'Watermark text or image path')
117
168
  .option('--watermark-position <position>', 'Watermark position', 'center')
118
169
  .action(async options => {
119
- const spinner = ora('Generating QR code...').start();
170
+ status('Generating QR code...');
120
171
 
121
172
  try {
122
173
  const qrOptions = {
123
174
  data: options.data,
124
- size: parseInt(options.size),
125
- margin: parseInt(options.margin),
175
+ size: parseInt(options.size, 10),
176
+ margin: parseInt(options.margin, 10),
126
177
  errorCorrectionLevel: options.errorCorrection,
127
178
  foregroundColor: hexToRgb(options.foreground),
128
179
  backgroundColor: hexToRgb(options.background),
@@ -131,7 +182,7 @@ program
131
182
 
132
183
  if (options.logo) {
133
184
  qrOptions.logoPath = options.logo;
134
- qrOptions.logoSize = parseInt(options.logoSize);
185
+ qrOptions.logoSize = parseInt(options.logoSize, 10);
135
186
  }
136
187
 
137
188
  if (options.label) {
@@ -143,30 +194,20 @@ program
143
194
  qrOptions.watermarkPosition = options.watermarkPosition;
144
195
  }
145
196
 
146
- const qrCode = BarcodeGenerator.modernQr(qrOptions);
147
- const result = await qrCode.generate();
148
-
149
- if (options.output) {
150
- await fs.writeFile(options.output, result);
151
- spinner.succeed(`QR code saved to ${options.output}`);
152
- } else {
153
- // Output to stdout (for base64 encoding)
154
- console.log(result.toString('base64'));
155
- }
197
+ const result = await BarcodeGenerator.modernQr(qrOptions).generate();
198
+ await emit(result, options.output, 'QR code');
156
199
  } catch (error) {
157
- spinner.fail(`Error: ${error.message}`);
158
- process.exit(1);
200
+ fail(error.message);
159
201
  }
160
202
  });
161
203
 
162
- // Batch generation command
163
204
  program
164
205
  .command('batch')
165
206
  .description('Generate multiple barcodes from a JSON file')
166
207
  .requiredOption('-i, --input <file>', 'Input JSON file')
167
208
  .option('-o, --output-dir <dir>', 'Output directory', './output')
168
209
  .action(async options => {
169
- const spinner = ora('Processing batch generation...').start();
210
+ status('Processing batch generation...');
170
211
 
171
212
  try {
172
213
  const inputData = JSON.parse(await fs.readFile(options.input, 'utf8'));
@@ -177,10 +218,9 @@ program
177
218
  );
178
219
  }
179
220
 
180
- // Create output directory if it doesn't exist
181
221
  await fs.mkdir(options.outputDir, { recursive: true });
182
222
 
183
- const results = BarcodeGenerator.batch(inputData);
223
+ const results = await BarcodeGenerator.batch(inputData);
184
224
 
185
225
  let successCount = 0;
186
226
  let errorCount = 0;
@@ -188,87 +228,84 @@ program
188
228
  for (const result of results) {
189
229
  if (result.success) {
190
230
  const filename = `barcode_${result.index}.${result.format}`;
191
- const filepath = path.join(options.outputDir, filename);
192
- await fs.writeFile(filepath, result.result);
231
+ await fs.writeFile(
232
+ path.join(options.outputDir, filename),
233
+ result.result
234
+ );
193
235
  successCount++;
194
236
  } else {
195
237
  errorCount++;
196
- console.error(
197
- chalk.red(`Error in item ${result.index}: ${result.error}`)
238
+ process.stderr.write(
239
+ `${color.red(`Error in item ${result.index}:`)} ${result.error}\n`
198
240
  );
199
241
  }
200
242
  }
201
243
 
202
- spinner.succeed(
203
- `Batch generation completed: ${successCount} successful, ${errorCount} errors`
244
+ succeed(
245
+ `Batch generation completed: ${successCount} successful, ${errorCount} failed`
204
246
  );
247
+
248
+ if (errorCount > 0) {
249
+ process.exitCode = 1;
250
+ }
205
251
  } catch (error) {
206
- spinner.fail(`Error: ${error.message}`);
207
- process.exit(1);
252
+ fail(error.message);
208
253
  }
209
254
  });
210
255
 
211
- // List supported types command
212
256
  program
213
257
  .command('types')
214
258
  .description('List supported barcode types')
215
- .action(() => {
216
- const types = BarcodeGenerator.getBarcodeTypes();
217
- console.log(chalk.blue('Supported barcode types:'));
259
+ .option('-c, --category <category>', 'Only list types in this category')
260
+ .action(options => {
261
+ const types = options.category
262
+ ? BarcodeGenerator.getBarcodeTypesByCategory(options.category)
263
+ : BarcodeGenerator.getBarcodeTypes();
264
+
265
+ if (!types.length) {
266
+ fail(
267
+ `Unknown category: ${options.category}. Categories: ${BarcodeGenerator.getBarcodeCategories().join(
268
+ ', '
269
+ )}`
270
+ );
271
+ }
272
+
273
+ process.stderr.write(`${color.blue('Supported barcode types:')}\n`);
218
274
  types.forEach(type => {
219
- console.log(` - ${type}`);
275
+ const info = BarcodeGenerator.getBarcodeTypeInfo(type);
276
+ process.stdout.write(` ${type.padEnd(24)} ${info.description}\n`);
220
277
  });
221
278
  });
222
279
 
223
- // List supported formats command
224
280
  program
225
281
  .command('formats')
226
282
  .description('List supported output formats')
227
283
  .action(() => {
228
- const formats = BarcodeGenerator.getRenderFormats();
229
- console.log(chalk.blue('Supported output formats:'));
230
- formats.forEach(format => {
231
- console.log(` - ${format}`);
284
+ process.stderr.write(`${color.blue('Supported output formats:')}\n`);
285
+ BarcodeGenerator.getRenderFormats().forEach(format => {
286
+ const info = BarcodeGenerator.getRenderFormatInfo(format);
287
+ process.stdout.write(` ${format.padEnd(6)} ${info.description}\n`);
232
288
  });
233
289
  });
234
290
 
235
- // Validate data command
236
291
  program
237
292
  .command('validate')
238
293
  .description('Validate data for a specific barcode type')
239
294
  .requiredOption('-d, --data <data>', 'Data to validate')
240
295
  .requiredOption('-t, --type <type>', 'Barcode type')
241
296
  .action(options => {
242
- try {
243
- const validation = BarcodeGenerator.validate(options.data, options.type);
244
-
245
- if (validation.valid) {
246
- console.log(chalk.green('✓ Data is valid'));
247
- console.log(` Type: ${validation.type}`);
248
- console.log(` Length: ${validation.length}`);
249
- console.log(` Charset: ${validation.charset}`);
250
- } else {
251
- console.log(chalk.red('✗ Data is invalid'));
252
- console.log(` Error: ${validation.error}`);
253
- process.exit(1);
254
- }
255
- } catch (error) {
256
- console.error(chalk.red(`Validation error: ${error.message}`));
297
+ const validation = BarcodeGenerator.validate(options.data, options.type);
298
+
299
+ if (!validation.valid) {
300
+ process.stderr.write(`${color.red('✖ Data is invalid')}\n`);
301
+ process.stderr.write(` Error: ${validation.error}\n`);
257
302
  process.exit(1);
258
303
  }
259
- });
260
304
 
261
- // Helper function to convert hex to RGB
262
- function hexToRgb(hex) {
263
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
264
- return result
265
- ? [
266
- parseInt(result[1], 16),
267
- parseInt(result[2], 16),
268
- parseInt(result[3], 16),
269
- ]
270
- : [0, 0, 0];
271
- }
305
+ process.stderr.write(`${color.green('✔ Data is valid')}\n`);
306
+ process.stdout.write(` Type: ${validation.type}\n`);
307
+ process.stdout.write(` Length: ${validation.length}\n`);
308
+ process.stdout.write(` Charset: ${validation.charset}\n`);
309
+ });
272
310
 
273
- // Parse command line arguments
274
311
  program.parse();