fastqr 1.0.1
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.
- checksums.yaml +7 -0
- data/BUILD.md +482 -0
- data/CHANGELOG.md +51 -0
- data/CMakeLists.txt +126 -0
- data/CONTRIBUTING.md +63 -0
- data/DISTRIBUTION.md +730 -0
- data/INSTALL.md +171 -0
- data/LICENSE +39 -0
- data/PREBUILT.md +171 -0
- data/README.md +312 -0
- data/VERSION +1 -0
- data/bindings/nodejs/binding.gyp +38 -0
- data/bindings/nodejs/fastqr_node.cpp +125 -0
- data/bindings/nodejs/index.d.ts +80 -0
- data/bindings/nodejs/index.js +187 -0
- data/bindings/nodejs/lib/platform.js +72 -0
- data/bindings/nodejs/package.json +45 -0
- data/bindings/nodejs/test/test.js +45 -0
- data/bindings/php/fastqr_php.cpp +85 -0
- data/bindings/php/src/FastQR.php +316 -0
- data/bindings/php/tests/FastQRTest.php +97 -0
- data/bindings/ruby/extconf.rb +29 -0
- data/bindings/ruby/fastqr_ruby.cpp +122 -0
- data/bindings/ruby/lib/fastqr/platform.rb +70 -0
- data/bindings/ruby/lib/fastqr/version.rb +6 -0
- data/bindings/ruby/lib/fastqr.rb +129 -0
- data/bindings/ruby/prebuilt/macos-arm64.tar.gz +1 -0
- data/bindings/ruby/prebuilt/macos-x86_64.tar.gz +1 -0
- data/build.sh +109 -0
- data/cmake/fastqrConfig.cmake.in +6 -0
- data/composer.json +36 -0
- data/docs/CLI_USAGE.md +478 -0
- data/docs/NODEJS_USAGE.md +694 -0
- data/docs/PHP_USAGE.md +815 -0
- data/docs/README.md +191 -0
- data/docs/RUBY_USAGE.md +537 -0
- data/examples/CMakeLists.txt +7 -0
- data/examples/basic.cpp +58 -0
- data/include/fastqr.h +97 -0
- data/include/stb_image.h +7988 -0
- data/include/stb_image_write.h +1724 -0
- data/phpunit.xml +18 -0
- data/prebuilt/README.md +131 -0
- data/scripts/README.md +248 -0
- data/scripts/build-binaries.sh +98 -0
- data/scripts/build-local.sh +18 -0
- data/scripts/install.sh +87 -0
- data/scripts/release.sh +78 -0
- data/scripts/update-version.sh +58 -0
- data/src/cli.cpp +316 -0
- data/src/fastqr.cpp +665 -0
- data/test.sh +86 -0
- metadata +155 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
# FastQR Node.js Usage Guide
|
|
2
|
+
|
|
3
|
+
Complete guide for using FastQR in Node.js and JavaScript applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install fastqr
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or with Yarn:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
yarn add fastqr
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Note:** No system dependencies required! Pre-built binaries are included. 🎉
|
|
18
|
+
|
|
19
|
+
## Basic Usage
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
const fastqr = require('fastqr');
|
|
23
|
+
|
|
24
|
+
// Generate QR code
|
|
25
|
+
fastqr.generate('Hello World', 'qr.png');
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## API Reference
|
|
29
|
+
|
|
30
|
+
### `fastqr.generate(data, outputPath, options)`
|
|
31
|
+
|
|
32
|
+
Generate a QR code and save to file.
|
|
33
|
+
|
|
34
|
+
**Parameters:**
|
|
35
|
+
- `data` (string, required) - Data to encode (UTF-8 supported)
|
|
36
|
+
- `outputPath` (string, required) - Path to save the QR code image
|
|
37
|
+
- `options` (object, optional) - Generation options
|
|
38
|
+
|
|
39
|
+
**Returns:** `boolean` - `true` if successful
|
|
40
|
+
|
|
41
|
+
**Throws:** `Error` if generation fails
|
|
42
|
+
|
|
43
|
+
### Options
|
|
44
|
+
|
|
45
|
+
| Option | Type | Default | Description |
|
|
46
|
+
|--------|------|---------|-------------|
|
|
47
|
+
| `size` | number | `300` | Output size in pixels (QR codes are square) |
|
|
48
|
+
| `optimizeSize` | boolean | `false` | Auto round-up for best performance |
|
|
49
|
+
| `width` | number | - | (Deprecated) Use `size` instead |
|
|
50
|
+
| `height` | number | - | (Deprecated) Use `size` instead |
|
|
51
|
+
| `foreground` | Array[3] | `[0, 0, 0]` | QR code color (RGB) |
|
|
52
|
+
| `background` | Array[3] | `[255, 255, 255]` | Background color (RGB) |
|
|
53
|
+
| `errorLevel` | string | `'M'` | Error correction: 'L', 'M', 'Q', 'H' |
|
|
54
|
+
| `logo` | string | `undefined` | Path to logo image |
|
|
55
|
+
| `logoSize` | number | `20` | Logo size as percentage (1-50) |
|
|
56
|
+
| `quality` | number | `95` | Image quality (1-100) |
|
|
57
|
+
| `format` | string | `'png'` | Output format: 'png', 'jpg', 'webp' |
|
|
58
|
+
|
|
59
|
+
### `fastqr.generateBatch(dataArray, outputDir, options)`
|
|
60
|
+
|
|
61
|
+
Generate multiple QR codes at once - **7x faster** than calling `generate` multiple times!
|
|
62
|
+
|
|
63
|
+
**Parameters:**
|
|
64
|
+
- `dataArray` (Array[string], required) - Array of strings to encode
|
|
65
|
+
- `outputDir` (string, required) - Directory to save QR codes (created if doesn't exist)
|
|
66
|
+
- `options` (object, optional) - Same options as `generate`
|
|
67
|
+
|
|
68
|
+
**Returns:** `object` - `{ success: number, failed: number }`
|
|
69
|
+
|
|
70
|
+
**Example:**
|
|
71
|
+
```javascript
|
|
72
|
+
const data = ['QR 1', 'QR 2', 'QR 3'];
|
|
73
|
+
const result = fastqr.generateBatch(data, 'output/', { size: 500, optimizeSize: true });
|
|
74
|
+
// Creates: output/1.png, output/2.png, output/3.png
|
|
75
|
+
console.log(`Generated ${result.success} QR codes`);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `fastqr.version()`
|
|
79
|
+
|
|
80
|
+
Get library version.
|
|
81
|
+
|
|
82
|
+
**Returns:** string (e.g., "1.0.0")
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
console.log(fastqr.version());
|
|
86
|
+
// => "1.0.0"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### `fastqr.VERSION`
|
|
90
|
+
|
|
91
|
+
Version constant.
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
console.log(fastqr.VERSION);
|
|
95
|
+
// => "1.0.0"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Examples
|
|
99
|
+
|
|
100
|
+
### 1. Basic QR Code
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
const fastqr = require('fastqr');
|
|
104
|
+
|
|
105
|
+
fastqr.generate('https://example.com', 'qr.png');
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2. Custom Size
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
fastqr.generate('Large QR', 'large.png', {
|
|
112
|
+
size: 1000
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 3. Optimized Size (faster generation)
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
fastqr.generate('Fast QR', 'fast.png', {
|
|
120
|
+
size: 500,
|
|
121
|
+
optimizeSize: true
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 4. Colored QR Code
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
// Red QR on yellow background
|
|
129
|
+
fastqr.generate('Colored', 'colored.png', {
|
|
130
|
+
size: 500,
|
|
131
|
+
foreground: [255, 0, 0], // Red
|
|
132
|
+
background: [255, 255, 200] // Light yellow
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 5. QR Code with Logo
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
fastqr.generate('Company', 'company.png', {
|
|
140
|
+
size: 800,
|
|
141
|
+
logo: 'logo.png',
|
|
142
|
+
logoSize: 25,
|
|
143
|
+
errorLevel: 'H' // High error correction for logo
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 6. High Error Correction
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
fastqr.generate('Important Data', 'qr.png', {
|
|
151
|
+
errorLevel: 'H' // ~30% recovery capability
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 7. UTF-8 Support
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
// Vietnamese
|
|
159
|
+
fastqr.generate('Xin chào Việt Nam! 🇻🇳', 'vietnamese.png');
|
|
160
|
+
|
|
161
|
+
// Japanese
|
|
162
|
+
fastqr.generate('こんにちは日本', 'japanese.png');
|
|
163
|
+
|
|
164
|
+
// Emoji
|
|
165
|
+
fastqr.generate('Hello 👋 World 🌍', 'emoji.png');
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 8. Batch Generation (7x faster!)
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
// Generate 1000 QR codes
|
|
172
|
+
const data = Array.from({ length: 1000 }, (_, i) => `Product ${i + 1}`);
|
|
173
|
+
|
|
174
|
+
// Old way (slow - ~3 seconds)
|
|
175
|
+
// for (let i = 0; i < data.length; i++) {
|
|
176
|
+
// fastqr.generate(data[i], `qr_${i+1}.png`, { size: 500 });
|
|
177
|
+
// }
|
|
178
|
+
|
|
179
|
+
// New way (fast - ~0.4 seconds!)
|
|
180
|
+
const result = fastqr.generateBatch(data, 'qr_codes/', {
|
|
181
|
+
size: 500,
|
|
182
|
+
optimizeSize: true
|
|
183
|
+
});
|
|
184
|
+
console.log(`Generated ${result.success} QR codes`);
|
|
185
|
+
// Creates: qr_codes/1.png, qr_codes/2.png, ..., qr_codes/1000.png
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 7. Different Formats
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
// PNG (default)
|
|
192
|
+
fastqr.generate('Data', 'output.png');
|
|
193
|
+
|
|
194
|
+
// JPEG
|
|
195
|
+
fastqr.generate('Data', 'output.jpg', { quality: 90 });
|
|
196
|
+
|
|
197
|
+
// WebP
|
|
198
|
+
fastqr.generate('Data', 'output.webp', { quality: 85 });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Express.js Integration
|
|
202
|
+
|
|
203
|
+
### Basic Route
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
const express = require('express');
|
|
207
|
+
const fastqr = require('fastqr');
|
|
208
|
+
const path = require('path');
|
|
209
|
+
const fs = require('fs').promises;
|
|
210
|
+
|
|
211
|
+
const app = express();
|
|
212
|
+
app.use(express.json());
|
|
213
|
+
|
|
214
|
+
// Generate QR code endpoint
|
|
215
|
+
app.post('/api/qr/generate', async (req, res) => {
|
|
216
|
+
try {
|
|
217
|
+
const { data, options = {} } = req.body;
|
|
218
|
+
|
|
219
|
+
if (!data) {
|
|
220
|
+
return res.status(400).json({ error: 'Data is required' });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Generate unique filename
|
|
224
|
+
const filename = `qr_${Date.now()}_${Math.random().toString(36).substr(2, 9)}.png`;
|
|
225
|
+
const filepath = path.join(__dirname, 'public', 'qrcodes', filename);
|
|
226
|
+
|
|
227
|
+
// Ensure directory exists
|
|
228
|
+
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
|
229
|
+
|
|
230
|
+
// Generate QR code
|
|
231
|
+
fastqr.generate(data, filepath, {
|
|
232
|
+
width: options.width || 500,
|
|
233
|
+
height: options.height || 500,
|
|
234
|
+
errorLevel: options.errorLevel || 'M'
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
res.json({
|
|
238
|
+
success: true,
|
|
239
|
+
url: `/qrcodes/${filename}`,
|
|
240
|
+
filename
|
|
241
|
+
});
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error('QR generation error:', error);
|
|
244
|
+
res.status(500).json({ error: 'Failed to generate QR code' });
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
app.listen(3000, () => {
|
|
249
|
+
console.log('Server running on http://localhost:3000');
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Advanced Route with Options
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
app.post('/api/qr/advanced', async (req, res) => {
|
|
257
|
+
try {
|
|
258
|
+
const {
|
|
259
|
+
data,
|
|
260
|
+
width = 600,
|
|
261
|
+
height = 600,
|
|
262
|
+
foreground = [0, 0, 0],
|
|
263
|
+
background = [255, 255, 255],
|
|
264
|
+
errorLevel = 'M',
|
|
265
|
+
logo,
|
|
266
|
+
logoSize = 20
|
|
267
|
+
} = req.body;
|
|
268
|
+
|
|
269
|
+
if (!data) {
|
|
270
|
+
return res.status(400).json({ error: 'Data is required' });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const filename = `qr_${Date.now()}.png`;
|
|
274
|
+
const filepath = path.join(__dirname, 'tmp', filename);
|
|
275
|
+
|
|
276
|
+
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
|
277
|
+
|
|
278
|
+
const options = {
|
|
279
|
+
width,
|
|
280
|
+
height,
|
|
281
|
+
foreground,
|
|
282
|
+
background,
|
|
283
|
+
errorLevel
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
if (logo) {
|
|
287
|
+
options.logo = logo;
|
|
288
|
+
options.logoSize = logoSize;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
fastqr.generate(data, filepath, options);
|
|
292
|
+
|
|
293
|
+
res.sendFile(filepath);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
res.status(500).json({ error: error.message });
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Middleware Example
|
|
301
|
+
|
|
302
|
+
```javascript
|
|
303
|
+
// QR code generation middleware
|
|
304
|
+
function qrGenerator(options = {}) {
|
|
305
|
+
return async (req, res, next) => {
|
|
306
|
+
req.generateQR = async (data, customOptions = {}) => {
|
|
307
|
+
const filename = `qr_${Date.now()}.png`;
|
|
308
|
+
const filepath = path.join(__dirname, 'tmp', filename);
|
|
309
|
+
|
|
310
|
+
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
|
311
|
+
|
|
312
|
+
const qrOptions = { ...options, ...customOptions };
|
|
313
|
+
|
|
314
|
+
fastqr.generate(data, filepath, qrOptions);
|
|
315
|
+
|
|
316
|
+
return { filepath, filename };
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
next();
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Use middleware
|
|
324
|
+
app.use(qrGenerator({ size: 500, optimizeSize: true }));
|
|
325
|
+
|
|
326
|
+
app.post('/qr', async (req, res) => {
|
|
327
|
+
const { data } = req.body;
|
|
328
|
+
const { filepath } = await req.generateQR(data);
|
|
329
|
+
res.sendFile(filepath);
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Return Base64 Image
|
|
334
|
+
|
|
335
|
+
```javascript
|
|
336
|
+
app.post('/api/qr/base64', async (req, res) => {
|
|
337
|
+
try {
|
|
338
|
+
const { data } = req.body;
|
|
339
|
+
const filename = `qr_${Date.now()}.png`;
|
|
340
|
+
const filepath = path.join(__dirname, 'tmp', filename);
|
|
341
|
+
|
|
342
|
+
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
|
343
|
+
|
|
344
|
+
// Generate QR code
|
|
345
|
+
fastqr.generate(data, filepath, {
|
|
346
|
+
width: 500,
|
|
347
|
+
height: 500
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Read file and convert to base64
|
|
351
|
+
const imageBuffer = await fs.readFile(filepath);
|
|
352
|
+
const base64Image = imageBuffer.toString('base64');
|
|
353
|
+
|
|
354
|
+
// Clean up temp file
|
|
355
|
+
await fs.unlink(filepath);
|
|
356
|
+
|
|
357
|
+
res.json({
|
|
358
|
+
success: true,
|
|
359
|
+
image: `data:image/png;base64,${base64Image}`
|
|
360
|
+
});
|
|
361
|
+
} catch (error) {
|
|
362
|
+
res.status(500).json({ error: error.message });
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## TypeScript Usage
|
|
368
|
+
|
|
369
|
+
### Type Definitions
|
|
370
|
+
|
|
371
|
+
FastQR includes TypeScript definitions (`index.d.ts`).
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import * as fastqr from 'fastqr';
|
|
375
|
+
|
|
376
|
+
// Generate with type checking
|
|
377
|
+
fastqr.generate('Hello TypeScript', 'output.png', {
|
|
378
|
+
width: 600,
|
|
379
|
+
height: 600,
|
|
380
|
+
foreground: [0, 0, 255],
|
|
381
|
+
errorLevel: 'H'
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Options interface
|
|
385
|
+
interface QROptions {
|
|
386
|
+
width?: number;
|
|
387
|
+
height?: number;
|
|
388
|
+
foreground?: [number, number, number];
|
|
389
|
+
background?: [number, number, number];
|
|
390
|
+
errorLevel?: 'L' | 'M' | 'Q' | 'H';
|
|
391
|
+
logo?: string;
|
|
392
|
+
logoSize?: number;
|
|
393
|
+
quality?: number;
|
|
394
|
+
format?: 'png' | 'jpg' | 'jpeg' | 'webp';
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const options: QROptions = {
|
|
398
|
+
width: 800,
|
|
399
|
+
height: 800,
|
|
400
|
+
errorLevel: 'H',
|
|
401
|
+
foreground: [255, 0, 0]
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
fastqr.generate('TypeScript QR', 'ts_qr.png', options);
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Express with TypeScript
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
import express, { Request, Response } from 'express';
|
|
411
|
+
import * as fastqr from 'fastqr';
|
|
412
|
+
import path from 'path';
|
|
413
|
+
import fs from 'fs/promises';
|
|
414
|
+
|
|
415
|
+
interface QRRequest {
|
|
416
|
+
data: string;
|
|
417
|
+
options?: fastqr.QROptions;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const app = express();
|
|
421
|
+
app.use(express.json());
|
|
422
|
+
|
|
423
|
+
app.post('/api/qr', async (req: Request<{}, {}, QRRequest>, res: Response) => {
|
|
424
|
+
try {
|
|
425
|
+
const { data, options = {} } = req.body;
|
|
426
|
+
|
|
427
|
+
const filename = `qr_${Date.now()}.png`;
|
|
428
|
+
const filepath = path.join(__dirname, 'public', filename);
|
|
429
|
+
|
|
430
|
+
fastqr.generate(data, filepath, options);
|
|
431
|
+
|
|
432
|
+
res.json({ success: true, filename });
|
|
433
|
+
} catch (error) {
|
|
434
|
+
res.status(500).json({ error: 'Generation failed' });
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## Async/Await Patterns
|
|
440
|
+
|
|
441
|
+
### Promise Wrapper
|
|
442
|
+
|
|
443
|
+
```javascript
|
|
444
|
+
function generateQRAsync(data, outputPath, options) {
|
|
445
|
+
return new Promise((resolve, reject) => {
|
|
446
|
+
try {
|
|
447
|
+
const result = fastqr.generate(data, outputPath, options);
|
|
448
|
+
resolve(result);
|
|
449
|
+
} catch (error) {
|
|
450
|
+
reject(error);
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Usage
|
|
456
|
+
async function createQR() {
|
|
457
|
+
try {
|
|
458
|
+
await generateQRAsync('Async Data', 'async_qr.png', {
|
|
459
|
+
width: 500,
|
|
460
|
+
height: 500
|
|
461
|
+
});
|
|
462
|
+
console.log('✓ QR code generated');
|
|
463
|
+
} catch (error) {
|
|
464
|
+
console.error('✗ Generation failed:', error);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Batch Generation
|
|
470
|
+
|
|
471
|
+
```javascript
|
|
472
|
+
async function generateBatch(items) {
|
|
473
|
+
const promises = items.map(async (item, index) => {
|
|
474
|
+
const filename = `qr_${index}.png`;
|
|
475
|
+
const filepath = path.join(__dirname, 'output', filename);
|
|
476
|
+
|
|
477
|
+
return generateQRAsync(item.data, filepath, {
|
|
478
|
+
width: item.width || 500,
|
|
479
|
+
height: item.height || 500
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
try {
|
|
484
|
+
await Promise.all(promises);
|
|
485
|
+
console.log(`✓ Generated ${items.length} QR codes`);
|
|
486
|
+
} catch (error) {
|
|
487
|
+
console.error('✗ Batch generation failed:', error);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Old way (slow - loop with individual calls)
|
|
492
|
+
// const items = [
|
|
493
|
+
// { data: 'https://example.com/1', size: 500 },
|
|
494
|
+
// { data: 'https://example.com/2', size: 600 },
|
|
495
|
+
// { data: 'https://example.com/3', size: 700 }
|
|
496
|
+
// ];
|
|
497
|
+
|
|
498
|
+
// New way (fast - batch mode)
|
|
499
|
+
const dataArray = ['https://example.com/1', 'https://example.com/2', 'https://example.com/3'];
|
|
500
|
+
fastqr.generateBatch(dataArray, 'output/', { size: 500, optimizeSize: true });
|
|
501
|
+
|
|
502
|
+
generateBatch(items);
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Advanced Examples
|
|
506
|
+
|
|
507
|
+
### Dynamic Logo QR
|
|
508
|
+
|
|
509
|
+
```javascript
|
|
510
|
+
const sharp = require('sharp'); // For logo processing
|
|
511
|
+
|
|
512
|
+
async function generateBrandedQR(data, brandColor, logoPath) {
|
|
513
|
+
const [r, g, b] = brandColor;
|
|
514
|
+
const filename = `branded_${Date.now()}.png`;
|
|
515
|
+
|
|
516
|
+
fastqr.generate(data, filename, {
|
|
517
|
+
width: 800,
|
|
518
|
+
height: 800,
|
|
519
|
+
foreground: [r, g, b],
|
|
520
|
+
logo: logoPath,
|
|
521
|
+
logoSize: 25,
|
|
522
|
+
errorLevel: 'H'
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
return filename;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Usage
|
|
529
|
+
const filename = await generateBrandedQR(
|
|
530
|
+
'Company Data',
|
|
531
|
+
[0, 120, 215], // Microsoft blue
|
|
532
|
+
'company_logo.png'
|
|
533
|
+
);
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### QR Code Service Class
|
|
537
|
+
|
|
538
|
+
```javascript
|
|
539
|
+
class QRCodeService {
|
|
540
|
+
constructor(outputDir = 'public/qrcodes') {
|
|
541
|
+
this.outputDir = outputDir;
|
|
542
|
+
this.ensureDirectory();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async ensureDirectory() {
|
|
546
|
+
const fs = require('fs').promises;
|
|
547
|
+
await fs.mkdir(this.outputDir, { recursive: true });
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
generate(data, options = {}) {
|
|
551
|
+
const filename = `qr_${Date.now()}_${Math.random().toString(36).substr(2, 9)}.png`;
|
|
552
|
+
const filepath = path.join(this.outputDir, filename);
|
|
553
|
+
|
|
554
|
+
const defaultOptions = {
|
|
555
|
+
width: 500,
|
|
556
|
+
height: 500,
|
|
557
|
+
errorLevel: 'M'
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
fastqr.generate(data, filepath, { ...defaultOptions, ...options });
|
|
561
|
+
|
|
562
|
+
return { filepath, filename };
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
generateWithLogo(data, logoPath, options = {}) {
|
|
566
|
+
return this.generate(data, {
|
|
567
|
+
...options,
|
|
568
|
+
logo: logoPath,
|
|
569
|
+
logoSize: options.logoSize || 25,
|
|
570
|
+
errorLevel: 'H'
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
async cleanup(olderThan = 24 * 60 * 60 * 1000) { // 24 hours
|
|
575
|
+
const fs = require('fs').promises;
|
|
576
|
+
const files = await fs.readdir(this.outputDir);
|
|
577
|
+
const now = Date.now();
|
|
578
|
+
|
|
579
|
+
for (const file of files) {
|
|
580
|
+
const filepath = path.join(this.outputDir, file);
|
|
581
|
+
const stats = await fs.stat(filepath);
|
|
582
|
+
|
|
583
|
+
if (now - stats.mtimeMs > olderThan) {
|
|
584
|
+
await fs.unlink(filepath);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Usage
|
|
591
|
+
const qrService = new QRCodeService();
|
|
592
|
+
|
|
593
|
+
const { filename } = qrService.generate('Hello World', {
|
|
594
|
+
width: 600,
|
|
595
|
+
height: 600
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
const { filename: logoQR } = qrService.generateWithLogo(
|
|
599
|
+
'Company',
|
|
600
|
+
'logo.png',
|
|
601
|
+
{ logoSize: 30 }
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
// Cleanup old files daily
|
|
605
|
+
setInterval(() => qrService.cleanup(), 24 * 60 * 60 * 1000);
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
## Testing
|
|
609
|
+
|
|
610
|
+
### Jest Example
|
|
611
|
+
|
|
612
|
+
```javascript
|
|
613
|
+
const fastqr = require('fastqr');
|
|
614
|
+
const fs = require('fs');
|
|
615
|
+
const path = require('path');
|
|
616
|
+
|
|
617
|
+
describe('FastQR', () => {
|
|
618
|
+
const outputPath = path.join(__dirname, 'test_qr.png');
|
|
619
|
+
|
|
620
|
+
afterEach(() => {
|
|
621
|
+
if (fs.existsSync(outputPath)) {
|
|
622
|
+
fs.unlinkSync(outputPath);
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
test('generates basic QR code', () => {
|
|
627
|
+
const result = fastqr.generate('Test Data', outputPath);
|
|
628
|
+
|
|
629
|
+
expect(result).toBe(true);
|
|
630
|
+
expect(fs.existsSync(outputPath)).toBe(true);
|
|
631
|
+
expect(fs.statSync(outputPath).size).toBeGreaterThan(0);
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
test('generates QR code with options', () => {
|
|
635
|
+
const result = fastqr.generate('Test', outputPath, {
|
|
636
|
+
width: 500,
|
|
637
|
+
height: 500,
|
|
638
|
+
errorLevel: 'H'
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
expect(result).toBe(true);
|
|
642
|
+
expect(fs.existsSync(outputPath)).toBe(true);
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
test('throws error for empty data', () => {
|
|
646
|
+
expect(() => {
|
|
647
|
+
fastqr.generate('', outputPath);
|
|
648
|
+
}).toThrow();
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
test('returns version', () => {
|
|
652
|
+
const version = fastqr.version();
|
|
653
|
+
expect(version).toMatch(/^\d+\.\d+\.\d+$/);
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
## Performance Tips
|
|
659
|
+
|
|
660
|
+
- Generate QR codes in worker threads for CPU-intensive operations
|
|
661
|
+
- Cache generated QR codes using data hash as filename
|
|
662
|
+
- Use lower quality for web display (`quality: 75`)
|
|
663
|
+
- Use PNG for best quality, JPG for smaller files
|
|
664
|
+
|
|
665
|
+
## Troubleshooting
|
|
666
|
+
|
|
667
|
+
### "Binary not found" Error
|
|
668
|
+
|
|
669
|
+
The package includes pre-built binaries. If you see this error:
|
|
670
|
+
|
|
671
|
+
```javascript
|
|
672
|
+
const platform = require('./lib/platform');
|
|
673
|
+
console.log('Platform:', platform.detectPlatform());
|
|
674
|
+
console.log('Binary available:', platform.isPrebuiltAvailable());
|
|
675
|
+
console.log('Binary path:', platform.getPrebuiltPath());
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
### File Permission Errors
|
|
679
|
+
|
|
680
|
+
Ensure output directory has write permissions:
|
|
681
|
+
|
|
682
|
+
```javascript
|
|
683
|
+
const fs = require('fs').promises;
|
|
684
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
## See Also
|
|
688
|
+
|
|
689
|
+
- [CLI Usage](CLI_USAGE.md) - Command-line usage
|
|
690
|
+
- [Ruby Usage](RUBY_USAGE.md) - Ruby/Rails guide
|
|
691
|
+
- [PHP Usage](PHP_USAGE.md) - PHP guide
|
|
692
|
+
- [GitHub Repository](https://github.com/tranhuucanh/fastqr)
|
|
693
|
+
- [npm Package](https://www.npmjs.com/package/fastqr)
|
|
694
|
+
|