isahaq-barcode 1.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/.eslintrc.js +29 -0
- package/README.md +549 -0
- package/bin/generate.js +274 -0
- package/eslint.config.js +68 -0
- package/examples/basic-usage.js +58 -0
- package/examples/batch-example.json +56 -0
- package/examples/express-server.js +159 -0
- package/index.js +134 -0
- package/jest.config.js +13 -0
- package/package.json +66 -0
- package/src/builders/QrCodeBuilder.js +401 -0
- package/src/renderers/HTMLRenderer.js +212 -0
- package/src/renderers/PDFRenderer.js +184 -0
- package/src/renderers/PNGRenderer.js +123 -0
- package/src/renderers/RenderFormats.js +87 -0
- package/src/renderers/SVGRenderer.js +221 -0
- package/src/services/BarcodeService.js +175 -0
- package/src/types/BarcodeTypes.js +195 -0
- package/src/validators/Validator.js +352 -0
- package/tests/BarcodeGenerator.test.js +187 -0
- package/tests/BarcodeService.test.js +76 -0
- package/tests/QrCodeBuilder.test.js +130 -0
- package/tests/setup.js +59 -0
package/index.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isahaq Barcode Generator
|
|
3
|
+
* A universal barcode generator package supporting multiple barcode types and output formats
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const BarcodeService = require('./src/services/BarcodeService');
|
|
7
|
+
const QrCodeBuilder = require('./src/builders/QrCodeBuilder');
|
|
8
|
+
const { BarcodeTypes } = require('./src/types/BarcodeTypes');
|
|
9
|
+
const { RenderFormats } = require('./src/renderers/RenderFormats');
|
|
10
|
+
|
|
11
|
+
// Main service instance
|
|
12
|
+
const barcodeService = new BarcodeService();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Main Barcode Generator Class
|
|
16
|
+
*/
|
|
17
|
+
class BarcodeGenerator {
|
|
18
|
+
/**
|
|
19
|
+
* Generate a barcode in PNG format
|
|
20
|
+
* @param {string} data - The data to encode
|
|
21
|
+
* @param {string} type - The barcode type
|
|
22
|
+
* @param {Object} options - Additional options
|
|
23
|
+
* @returns {Buffer} PNG buffer
|
|
24
|
+
*/
|
|
25
|
+
static png(data, type = 'code128', options = {}) {
|
|
26
|
+
return barcodeService.generate(data, type, 'png', options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generate a barcode in SVG format
|
|
31
|
+
* @param {string} data - The data to encode
|
|
32
|
+
* @param {string} type - The barcode type
|
|
33
|
+
* @param {Object} options - Additional options
|
|
34
|
+
* @returns {string} SVG string
|
|
35
|
+
*/
|
|
36
|
+
static svg(data, type = 'code128', options = {}) {
|
|
37
|
+
return barcodeService.generate(data, type, 'svg', options);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Generate a barcode in HTML format
|
|
42
|
+
* @param {string} data - The data to encode
|
|
43
|
+
* @param {string} type - The barcode type
|
|
44
|
+
* @param {Object} options - Additional options
|
|
45
|
+
* @returns {string} HTML string
|
|
46
|
+
*/
|
|
47
|
+
static html(data, type = 'code128', options = {}) {
|
|
48
|
+
return barcodeService.generate(data, type, 'html', options);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Generate a barcode in PDF format
|
|
53
|
+
* @param {string} data - The data to encode
|
|
54
|
+
* @param {string} type - The barcode type
|
|
55
|
+
* @param {Object} options - Additional options
|
|
56
|
+
* @returns {Buffer} PDF buffer
|
|
57
|
+
*/
|
|
58
|
+
static pdf(data, type = 'code128', options = {}) {
|
|
59
|
+
return barcodeService.generate(data, type, 'pdf', options);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate a QR code with advanced features
|
|
64
|
+
* @param {Object} options - QR code options
|
|
65
|
+
* @returns {Object} QR code result
|
|
66
|
+
*/
|
|
67
|
+
static modernQr(options = {}) {
|
|
68
|
+
return QrCodeBuilder.create(options);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get available barcode types
|
|
73
|
+
* @returns {Array} Array of barcode types
|
|
74
|
+
*/
|
|
75
|
+
static getBarcodeTypes() {
|
|
76
|
+
return BarcodeTypes.getAll();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get available render formats
|
|
81
|
+
* @returns {Array} Array of render formats
|
|
82
|
+
*/
|
|
83
|
+
static getRenderFormats() {
|
|
84
|
+
return RenderFormats.getAll();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Validate data for a specific barcode type
|
|
89
|
+
* @param {string} data - The data to validate
|
|
90
|
+
* @param {string} type - The barcode type
|
|
91
|
+
* @returns {Object} Validation result
|
|
92
|
+
*/
|
|
93
|
+
static validate(data, type) {
|
|
94
|
+
return barcodeService.validate(data, type);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Generate multiple barcodes in batch
|
|
99
|
+
* @param {Array} items - Array of barcode generation items
|
|
100
|
+
* @returns {Array} Array of generated barcodes
|
|
101
|
+
*/
|
|
102
|
+
static batch(items) {
|
|
103
|
+
return barcodeService.batch(items);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get watermark positions for QR codes
|
|
108
|
+
* @returns {Array} Array of watermark positions
|
|
109
|
+
*/
|
|
110
|
+
static getWatermarkPositions() {
|
|
111
|
+
return [
|
|
112
|
+
'top-left',
|
|
113
|
+
'top-right',
|
|
114
|
+
'bottom-left',
|
|
115
|
+
'bottom-right',
|
|
116
|
+
'center',
|
|
117
|
+
'top-center',
|
|
118
|
+
'bottom-center',
|
|
119
|
+
'left-center',
|
|
120
|
+
'right-center',
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Export main class and utilities
|
|
126
|
+
module.exports = BarcodeGenerator;
|
|
127
|
+
module.exports.BarcodeGenerator = BarcodeGenerator;
|
|
128
|
+
module.exports.BarcodeService = BarcodeService;
|
|
129
|
+
module.exports.QrCodeBuilder = QrCodeBuilder;
|
|
130
|
+
module.exports.BarcodeTypes = BarcodeTypes;
|
|
131
|
+
module.exports.RenderFormats = RenderFormats;
|
|
132
|
+
|
|
133
|
+
// Default export
|
|
134
|
+
module.exports.default = BarcodeGenerator;
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
testEnvironment: 'node',
|
|
3
|
+
testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'],
|
|
4
|
+
collectCoverageFrom: [
|
|
5
|
+
'src/**/*.js',
|
|
6
|
+
'!src/**/*.test.js',
|
|
7
|
+
'!src/**/__tests__/**',
|
|
8
|
+
],
|
|
9
|
+
coverageDirectory: 'coverage',
|
|
10
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
11
|
+
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
|
|
12
|
+
testTimeout: 10000,
|
|
13
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "isahaq-barcode",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A universal barcode generator package supporting multiple barcode types and output formats, with extra features like batch generation, watermarking, validation, CLI, and Express.js integration",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"barcode-generate": "./bin/generate.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "npx jest",
|
|
11
|
+
"test:watch": "npx jest --watch",
|
|
12
|
+
"test:coverage": "npx jest --coverage",
|
|
13
|
+
"lint": "npx eslint .",
|
|
14
|
+
"lint:fix": "npx eslint . --fix",
|
|
15
|
+
"build": "npm run lint",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"barcode",
|
|
20
|
+
"qrcode",
|
|
21
|
+
"generator",
|
|
22
|
+
"code128",
|
|
23
|
+
"code39",
|
|
24
|
+
"ean13",
|
|
25
|
+
"upc",
|
|
26
|
+
"datamatrix",
|
|
27
|
+
"pdf417",
|
|
28
|
+
"aztec",
|
|
29
|
+
"svg",
|
|
30
|
+
"png",
|
|
31
|
+
"pdf",
|
|
32
|
+
"cli",
|
|
33
|
+
"express",
|
|
34
|
+
"node"
|
|
35
|
+
],
|
|
36
|
+
"author": "hmisahaq01@gmail.com",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"canvas": "^2.11.2",
|
|
40
|
+
"jsbarcode": "^3.11.5",
|
|
41
|
+
"qrcode": "^1.5.3",
|
|
42
|
+
"pdfkit": "^0.14.0",
|
|
43
|
+
"commander": "^11.1.0",
|
|
44
|
+
"chalk": "^4.1.2",
|
|
45
|
+
"ora": "^5.4.1"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"jest": "^29.7.0",
|
|
49
|
+
"eslint": "^8.57.0",
|
|
50
|
+
"eslint-config-standard": "^17.1.0",
|
|
51
|
+
"eslint-plugin-import": "^2.29.1",
|
|
52
|
+
"eslint-plugin-node": "^11.1.0",
|
|
53
|
+
"eslint-plugin-promise": "^6.1.1"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=14.0.0"
|
|
57
|
+
},
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "https://github.com/isahaq1/barcode-generator-npm.git"
|
|
61
|
+
},
|
|
62
|
+
"bugs": {
|
|
63
|
+
"url": "https://github.com/isahaq1/barcode-generator-npm/issues"
|
|
64
|
+
},
|
|
65
|
+
"homepage": "https://github.com/isahaq1/barcode-generator-npm#readme"
|
|
66
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QR Code Builder - Advanced QR code generation with logo and watermark support
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const QRCode = require('qrcode');
|
|
6
|
+
const { createCanvas, loadImage } = require('canvas');
|
|
7
|
+
const fs = require('fs').promises;
|
|
8
|
+
|
|
9
|
+
class QrCodeBuilder {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.options = {
|
|
12
|
+
data: '',
|
|
13
|
+
size: 300,
|
|
14
|
+
margin: 10,
|
|
15
|
+
errorCorrectionLevel: 'M',
|
|
16
|
+
foregroundColor: [0, 0, 0],
|
|
17
|
+
backgroundColor: [255, 255, 255],
|
|
18
|
+
logoPath: null,
|
|
19
|
+
logoSize: 60, // Percentage of QR code size
|
|
20
|
+
label: null,
|
|
21
|
+
labelFont: null,
|
|
22
|
+
labelFontSize: 16,
|
|
23
|
+
watermark: null,
|
|
24
|
+
watermarkPosition: 'center',
|
|
25
|
+
format: 'png',
|
|
26
|
+
...options,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a new QR Code Builder instance
|
|
32
|
+
* @param {Object} options - Initial options
|
|
33
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
34
|
+
*/
|
|
35
|
+
static create(options = {}) {
|
|
36
|
+
return new QrCodeBuilder(options);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set the data to encode
|
|
41
|
+
* @param {string} data - The data to encode
|
|
42
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
43
|
+
*/
|
|
44
|
+
data(data) {
|
|
45
|
+
this.options.data = data;
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Set the QR code size
|
|
51
|
+
* @param {number} size - Size in pixels
|
|
52
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
53
|
+
*/
|
|
54
|
+
size(size) {
|
|
55
|
+
this.options.size = size;
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Set the margin
|
|
61
|
+
* @param {number} margin - Margin in pixels
|
|
62
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
63
|
+
*/
|
|
64
|
+
margin(margin) {
|
|
65
|
+
this.options.margin = margin;
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Set the error correction level
|
|
71
|
+
* @param {string} level - Error correction level (L, M, Q, H)
|
|
72
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
73
|
+
*/
|
|
74
|
+
errorCorrectionLevel(level) {
|
|
75
|
+
this.options.errorCorrectionLevel = level;
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Set the foreground color
|
|
81
|
+
* @param {Array} color - RGB color array [r, g, b]
|
|
82
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
83
|
+
*/
|
|
84
|
+
foregroundColor(color) {
|
|
85
|
+
this.options.foregroundColor = color;
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Set the background color
|
|
91
|
+
* @param {Array} color - RGB color array [r, g, b]
|
|
92
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
93
|
+
*/
|
|
94
|
+
backgroundColor(color) {
|
|
95
|
+
this.options.backgroundColor = color;
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Set the logo path
|
|
101
|
+
* @param {string} logoPath - Path to logo image
|
|
102
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
103
|
+
*/
|
|
104
|
+
logoPath(logoPath) {
|
|
105
|
+
this.options.logoPath = logoPath;
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Set the logo size
|
|
111
|
+
* @param {number} logoSize - Logo size as percentage of QR code size
|
|
112
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
113
|
+
*/
|
|
114
|
+
logoSize(logoSize) {
|
|
115
|
+
this.options.logoSize = logoSize;
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Set the label text
|
|
121
|
+
* @param {string} label - Label text
|
|
122
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
123
|
+
*/
|
|
124
|
+
label(label) {
|
|
125
|
+
this.options.label = label;
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Set the label font
|
|
131
|
+
* @param {string} fontPath - Path to font file
|
|
132
|
+
* @param {number} fontSize - Font size
|
|
133
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
134
|
+
*/
|
|
135
|
+
labelFont(fontPath, fontSize = 16) {
|
|
136
|
+
this.options.labelFont = fontPath;
|
|
137
|
+
this.options.labelFontSize = fontSize;
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Set the watermark
|
|
143
|
+
* @param {string} watermark - Watermark text or image path
|
|
144
|
+
* @param {string} position - Watermark position
|
|
145
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
146
|
+
*/
|
|
147
|
+
watermark(watermark, position = 'center') {
|
|
148
|
+
this.options.watermark = watermark;
|
|
149
|
+
this.options.watermarkPosition = position;
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Set the output format
|
|
155
|
+
* @param {string} format - Output format (png, svg, jpg)
|
|
156
|
+
* @returns {QrCodeBuilder} Builder instance
|
|
157
|
+
*/
|
|
158
|
+
format(format) {
|
|
159
|
+
this.options.format = format;
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Build the QR code
|
|
165
|
+
* @returns {Object} QR code result
|
|
166
|
+
*/
|
|
167
|
+
build() {
|
|
168
|
+
return new QrCodeResult(this.options);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Generate QR code and return as buffer
|
|
173
|
+
* @returns {Promise<Buffer>} QR code buffer
|
|
174
|
+
*/
|
|
175
|
+
async generate() {
|
|
176
|
+
const result = this.build();
|
|
177
|
+
return await result.generate();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Generate QR code and save to file
|
|
182
|
+
* @param {string} filePath - Output file path
|
|
183
|
+
* @returns {Promise<void>}
|
|
184
|
+
*/
|
|
185
|
+
async saveToFile(filePath) {
|
|
186
|
+
const result = this.build();
|
|
187
|
+
return await result.saveToFile(filePath);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Generate QR code and return as data URI
|
|
192
|
+
* @returns {Promise<string>} Data URI
|
|
193
|
+
*/
|
|
194
|
+
async getDataUri() {
|
|
195
|
+
const result = this.build();
|
|
196
|
+
return await result.getDataUri();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* QR Code Result - Handles QR code generation and output
|
|
202
|
+
*/
|
|
203
|
+
class QrCodeResult {
|
|
204
|
+
constructor(options) {
|
|
205
|
+
this.options = options;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Generate the QR code
|
|
210
|
+
* @returns {Promise<Buffer>} QR code buffer
|
|
211
|
+
*/
|
|
212
|
+
async generate() {
|
|
213
|
+
try {
|
|
214
|
+
// Create canvas
|
|
215
|
+
const canvas = createCanvas(this.options.size, this.options.size);
|
|
216
|
+
const ctx = canvas.getContext('2d');
|
|
217
|
+
|
|
218
|
+
// Set background
|
|
219
|
+
ctx.fillStyle = this.rgbToHex(this.options.backgroundColor);
|
|
220
|
+
ctx.fillRect(0, 0, this.options.size, this.options.size);
|
|
221
|
+
|
|
222
|
+
// Generate QR code
|
|
223
|
+
const qrCodeDataURL = await QRCode.toDataURL(this.options.data, {
|
|
224
|
+
width: this.options.size - this.options.margin * 2,
|
|
225
|
+
margin: 0,
|
|
226
|
+
color: {
|
|
227
|
+
dark: this.rgbToHex(this.options.foregroundColor),
|
|
228
|
+
light: this.rgbToHex(this.options.backgroundColor),
|
|
229
|
+
},
|
|
230
|
+
errorCorrectionLevel: this.options.errorCorrectionLevel,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Load QR code image
|
|
234
|
+
const qrCodeImage = await loadImage(qrCodeDataURL);
|
|
235
|
+
ctx.drawImage(qrCodeImage, this.options.margin, this.options.margin);
|
|
236
|
+
|
|
237
|
+
// Add logo if specified
|
|
238
|
+
if (this.options.logoPath) {
|
|
239
|
+
await this.addLogo(ctx);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Add watermark if specified
|
|
243
|
+
if (this.options.watermark) {
|
|
244
|
+
await this.addWatermark(ctx);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Add label if specified
|
|
248
|
+
if (this.options.label) {
|
|
249
|
+
this.addLabel(ctx);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Convert to buffer
|
|
253
|
+
return canvas.toBuffer(`image/${this.options.format}`);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
throw new Error(`QR code generation failed: ${error.message}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Add logo to QR code
|
|
261
|
+
* @param {CanvasRenderingContext2D} ctx - Canvas context
|
|
262
|
+
*/
|
|
263
|
+
async addLogo(ctx) {
|
|
264
|
+
try {
|
|
265
|
+
const logoImage = await loadImage(this.options.logoPath);
|
|
266
|
+
const logoSize = (this.options.size * this.options.logoSize) / 100;
|
|
267
|
+
const logoX = (this.options.size - logoSize) / 2;
|
|
268
|
+
const logoY = (this.options.size - logoSize) / 2;
|
|
269
|
+
|
|
270
|
+
// Draw white background for logo
|
|
271
|
+
ctx.fillStyle = this.rgbToHex(this.options.backgroundColor);
|
|
272
|
+
ctx.fillRect(logoX - 5, logoY - 5, logoSize + 10, logoSize + 10);
|
|
273
|
+
|
|
274
|
+
// Draw logo
|
|
275
|
+
ctx.drawImage(logoImage, logoX, logoY, logoSize, logoSize);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.warn(`Failed to add logo: ${error.message}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Add watermark to QR code
|
|
283
|
+
* @param {CanvasRenderingContext2D} ctx - Canvas context
|
|
284
|
+
*/
|
|
285
|
+
async addWatermark(ctx) {
|
|
286
|
+
try {
|
|
287
|
+
// Check if watermark is an image path
|
|
288
|
+
if (
|
|
289
|
+
this.options.watermark.includes('.') &&
|
|
290
|
+
this.options.watermark.includes('/')
|
|
291
|
+
) {
|
|
292
|
+
const watermarkImage = await loadImage(this.options.watermark);
|
|
293
|
+
const watermarkSize = this.options.size / 4;
|
|
294
|
+
const position = this.getWatermarkPosition(watermarkSize);
|
|
295
|
+
|
|
296
|
+
ctx.drawImage(
|
|
297
|
+
watermarkImage,
|
|
298
|
+
position.x,
|
|
299
|
+
position.y,
|
|
300
|
+
watermarkSize,
|
|
301
|
+
watermarkSize
|
|
302
|
+
);
|
|
303
|
+
} else {
|
|
304
|
+
// Text watermark
|
|
305
|
+
ctx.font = `${this.options.labelFontSize}px Arial`;
|
|
306
|
+
ctx.fillStyle = this.rgbToHex(this.options.foregroundColor);
|
|
307
|
+
ctx.textAlign = 'center';
|
|
308
|
+
|
|
309
|
+
const position = this.getWatermarkPosition(0);
|
|
310
|
+
ctx.fillText(this.options.watermark, position.x, position.y);
|
|
311
|
+
}
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.warn(`Failed to add watermark: ${error.message}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Add label to QR code
|
|
319
|
+
* @param {CanvasRenderingContext2D} ctx - Canvas context
|
|
320
|
+
*/
|
|
321
|
+
addLabel(ctx) {
|
|
322
|
+
ctx.font = `${this.options.labelFontSize}px Arial`;
|
|
323
|
+
ctx.fillStyle = this.rgbToHex(this.options.foregroundColor);
|
|
324
|
+
ctx.textAlign = 'center';
|
|
325
|
+
|
|
326
|
+
const labelY = this.options.size - 10;
|
|
327
|
+
ctx.fillText(this.options.label, this.options.size / 2, labelY);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Get watermark position
|
|
332
|
+
* @param {number} size - Watermark size
|
|
333
|
+
* @returns {Object} Position coordinates
|
|
334
|
+
*/
|
|
335
|
+
getWatermarkPosition(size) {
|
|
336
|
+
const positions = {
|
|
337
|
+
'top-left': { x: 10, y: 10 },
|
|
338
|
+
'top-right': { x: this.options.size - size - 10, y: 10 },
|
|
339
|
+
'bottom-left': { x: 10, y: this.options.size - size - 10 },
|
|
340
|
+
'bottom-right': {
|
|
341
|
+
x: this.options.size - size - 10,
|
|
342
|
+
y: this.options.size - size - 10,
|
|
343
|
+
},
|
|
344
|
+
center: {
|
|
345
|
+
x: (this.options.size - size) / 2,
|
|
346
|
+
y: (this.options.size - size) / 2,
|
|
347
|
+
},
|
|
348
|
+
'top-center': { x: (this.options.size - size) / 2, y: 10 },
|
|
349
|
+
'bottom-center': {
|
|
350
|
+
x: (this.options.size - size) / 2,
|
|
351
|
+
y: this.options.size - size - 10,
|
|
352
|
+
},
|
|
353
|
+
'left-center': { x: 10, y: (this.options.size - size) / 2 },
|
|
354
|
+
'right-center': {
|
|
355
|
+
x: this.options.size - size - 10,
|
|
356
|
+
y: (this.options.size - size) / 2,
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
return positions[this.options.watermarkPosition] || positions.center;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Convert RGB array to hex color
|
|
365
|
+
* @param {Array} rgb - RGB color array
|
|
366
|
+
* @returns {string} Hex color
|
|
367
|
+
*/
|
|
368
|
+
rgbToHex(rgb) {
|
|
369
|
+
return `#${rgb.map((c) => c.toString(16).padStart(2, '0')).join('')}`;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Save QR code to file
|
|
374
|
+
* @param {string} filePath - Output file path
|
|
375
|
+
* @returns {Promise<void>}
|
|
376
|
+
*/
|
|
377
|
+
async saveToFile(filePath) {
|
|
378
|
+
const buffer = await this.generate();
|
|
379
|
+
await fs.writeFile(filePath, buffer);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Get QR code as data URI
|
|
384
|
+
* @returns {Promise<string>} Data URI
|
|
385
|
+
*/
|
|
386
|
+
async getDataUri() {
|
|
387
|
+
const buffer = await this.generate();
|
|
388
|
+
const base64 = buffer.toString('base64');
|
|
389
|
+
return `data:image/${this.options.format};base64,${base64}`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Get QR code as string
|
|
394
|
+
* @returns {Promise<string>} QR code string
|
|
395
|
+
*/
|
|
396
|
+
async getString() {
|
|
397
|
+
return await this.generate();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
module.exports = QrCodeBuilder;
|