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.
- package/CHANGELOG.md +107 -0
- package/LICENSE +21 -0
- package/README.md +319 -519
- package/bin/generate.js +171 -134
- package/browser.js +163 -170
- package/index.d.ts +497 -0
- package/index.js +118 -38
- package/package.json +68 -34
- package/src/builders/QrCodeBuilder.js +328 -165
- package/src/encoders/BarcodeEncoder.js +260 -0
- package/src/renderers/HTMLRenderer.js +36 -169
- package/src/renderers/PDFRenderer.js +108 -130
- package/src/renderers/PNGRenderer.js +7 -83
- package/src/renderers/RenderFormats.js +89 -38
- package/src/renderers/SVGRenderer.js +8 -182
- package/src/services/BarcodeService.js +96 -36
- package/src/types/BarcodeTypes.js +535 -144
- package/src/validators/Validator.js +140 -122
- package/.eslintrc.js +0 -29
- package/.prettierignore +0 -11
- package/.prettierrc +0 -11
- package/eslint.config.js +0 -67
- package/examples/basic-usage.js +0 -58
- package/examples/batch-example.json +0 -56
- package/examples/express-server.js +0 -163
- package/jest.config.js +0 -13
- package/tests/BarcodeGenerator.test.js +0 -187
- package/tests/BarcodeService.test.js +0 -76
- package/tests/QrCodeBuilder.test.js +0 -130
- package/tests/setup.js +0 -59
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barcode Encoder - Turns data into a real, scannable symbology
|
|
3
|
+
*
|
|
4
|
+
* All renderers go through this module so that PNG, SVG, HTML and PDF output
|
|
5
|
+
* describe the exact same encoding. Encoding is done by bwip-js (BWIPP), which
|
|
6
|
+
* covers every type listed in BarcodeTypes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const bwipjs = require('bwip-js');
|
|
10
|
+
const { BarcodeTypes } = require('../types/BarcodeTypes');
|
|
11
|
+
|
|
12
|
+
/** bwip-js expresses bar height in millimetres at 72dpi */
|
|
13
|
+
const PX_PER_MM = 72 / 25.4;
|
|
14
|
+
|
|
15
|
+
/** Types whose size is driven by the module scale rather than a bar height */
|
|
16
|
+
const MATRIX_CATEGORIES = new Set(['MATRIX_2D', 'STACKED']);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Quiet zone in modules used when the caller does not specify a margin.
|
|
20
|
+
* Most 1D specifications require at least 10 narrow modules of clear space;
|
|
21
|
+
* scanners reject codes with less.
|
|
22
|
+
*/
|
|
23
|
+
const DEFAULT_QUIET_ZONE_MODULES = 10;
|
|
24
|
+
|
|
25
|
+
const DEFAULT_OPTIONS = {
|
|
26
|
+
width: 2, // module (narrow bar) width in pixels
|
|
27
|
+
height: 100, // bar height in pixels - 1D symbologies only
|
|
28
|
+
displayValue: true, // print the human readable text
|
|
29
|
+
fontSize: 20, // human readable text size in pixels
|
|
30
|
+
textAlign: 'center', // center | left | right | justify
|
|
31
|
+
textMargin: 2, // gap between bars and text in pixels
|
|
32
|
+
background: '#ffffff',
|
|
33
|
+
lineColor: '#000000',
|
|
34
|
+
margin: null, // quiet zone in pixels; defaults to 10 modules when omitted
|
|
35
|
+
marginTop: null,
|
|
36
|
+
marginBottom: null,
|
|
37
|
+
marginLeft: null,
|
|
38
|
+
marginRight: null,
|
|
39
|
+
quietZone: DEFAULT_QUIET_ZONE_MODULES, // quiet zone in modules
|
|
40
|
+
rotate: 'N', // N | R | L | I (none, right, left, inverted)
|
|
41
|
+
size: null, // target size in pixels for 2D symbologies
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Strip the internal BWIPP error prefix so messages read like normal errors.
|
|
46
|
+
* "bwipp.ean13badLength#6878: EAN-13 must be 12 or 13 digits" -> "EAN-13 must be 12 or 13 digits"
|
|
47
|
+
* @param {Error|string} error - Error thrown by the encoder
|
|
48
|
+
* @returns {string} Human readable message
|
|
49
|
+
*/
|
|
50
|
+
function cleanMessage(error) {
|
|
51
|
+
const message = error && error.message ? error.message : String(error);
|
|
52
|
+
return message.replace(/^bwipp?\.[A-Za-z0-9]+(#\d+)?:\s*/, '');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Normalise a colour to the hex form bwip-js expects.
|
|
57
|
+
* @param {string} color - Colour as #rgb, #rrggbb or rrggbb
|
|
58
|
+
* @param {string} fallback - Value used when color is missing/invalid
|
|
59
|
+
* @returns {string} Hex colour without the leading '#'
|
|
60
|
+
*/
|
|
61
|
+
function toHex(color, fallback) {
|
|
62
|
+
const raw = String(color == null ? fallback : color)
|
|
63
|
+
.replace(/^#/, '')
|
|
64
|
+
.toLowerCase();
|
|
65
|
+
|
|
66
|
+
if (/^[0-9a-fA-F]{3}$/.test(raw)) {
|
|
67
|
+
return raw
|
|
68
|
+
.split('')
|
|
69
|
+
.map(c => c + c)
|
|
70
|
+
.join('');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (/^[0-9a-fA-F]{6}$/.test(raw) || /^[0-9a-fA-F]{8}$/.test(raw)) {
|
|
74
|
+
return raw;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return String(fallback).replace(/^#/, '');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Pick a positive number, falling back when the value is unusable.
|
|
82
|
+
* @param {*} value - Candidate value
|
|
83
|
+
* @param {number} fallback - Fallback value
|
|
84
|
+
* @returns {number} A finite positive number
|
|
85
|
+
*/
|
|
86
|
+
function positive(value, fallback) {
|
|
87
|
+
const num = Number(value);
|
|
88
|
+
return Number.isFinite(num) && num > 0 ? num : fallback;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class BarcodeEncoder {
|
|
92
|
+
/**
|
|
93
|
+
* Get the default render options
|
|
94
|
+
* @returns {Object} Default options
|
|
95
|
+
*/
|
|
96
|
+
static getDefaultOptions() {
|
|
97
|
+
return { ...DEFAULT_OPTIONS };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Check whether a type is a 2D/stacked symbology (sized by scale, not height)
|
|
102
|
+
* @param {string} type - The barcode type
|
|
103
|
+
* @returns {boolean} True for matrix and stacked symbologies
|
|
104
|
+
*/
|
|
105
|
+
static isMatrix(type) {
|
|
106
|
+
const info = BarcodeTypes.getTypeInfo(type);
|
|
107
|
+
return !!info && MATRIX_CATEGORIES.has(info.category);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Translate this package's options into bwip-js encoder options
|
|
112
|
+
* @param {string} data - The data to encode
|
|
113
|
+
* @param {string} type - The barcode type
|
|
114
|
+
* @param {Object} options - Render options
|
|
115
|
+
* @returns {Object} bwip-js options
|
|
116
|
+
*/
|
|
117
|
+
static buildOptions(data, type, options = {}) {
|
|
118
|
+
const bcid = BarcodeTypes.getSymbology(type);
|
|
119
|
+
if (!bcid) {
|
|
120
|
+
throw new Error(`Invalid barcode type: ${type}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const merged = { ...DEFAULT_OPTIONS, ...options };
|
|
124
|
+
const scale = Math.max(1, Math.round(positive(merged.width, 2)));
|
|
125
|
+
|
|
126
|
+
const encoded = {
|
|
127
|
+
bcid,
|
|
128
|
+
text: data,
|
|
129
|
+
scale,
|
|
130
|
+
includetext: merged.displayValue !== false,
|
|
131
|
+
textxalign: merged.textAlign || 'center',
|
|
132
|
+
textsize: Math.max(1, positive(merged.fontSize, 20) / scale),
|
|
133
|
+
textyoffset: Number(merged.textMargin) || 0,
|
|
134
|
+
barcolor: toHex(merged.lineColor, '#000000'),
|
|
135
|
+
backgroundcolor: toHex(merged.background, '#ffffff'),
|
|
136
|
+
textcolor: toHex(merged.textColor || merged.lineColor, '#000000'),
|
|
137
|
+
rotate: ['N', 'R', 'L', 'I'].includes(merged.rotate)
|
|
138
|
+
? merged.rotate
|
|
139
|
+
: 'N',
|
|
140
|
+
...BarcodeTypes.getEncodeOptions(type),
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Quiet zone. bwip-js padding is expressed in modules (it multiplies by
|
|
144
|
+
// scale), our public options are pixels. When no margin is given we fall
|
|
145
|
+
// back to the module count the specifications ask for.
|
|
146
|
+
const quietZone = Math.max(
|
|
147
|
+
0,
|
|
148
|
+
Math.round(
|
|
149
|
+
merged.quietZone === undefined || merged.quietZone === null
|
|
150
|
+
? DEFAULT_QUIET_ZONE_MODULES
|
|
151
|
+
: Number(merged.quietZone) || 0
|
|
152
|
+
)
|
|
153
|
+
);
|
|
154
|
+
const pad = value => {
|
|
155
|
+
const pixels =
|
|
156
|
+
value === undefined || value === null ? merged.margin : value;
|
|
157
|
+
if (pixels === undefined || pixels === null) {
|
|
158
|
+
return quietZone;
|
|
159
|
+
}
|
|
160
|
+
return Math.max(0, Math.round((Number(pixels) || 0) / scale));
|
|
161
|
+
};
|
|
162
|
+
encoded.paddingleft = pad(merged.marginLeft);
|
|
163
|
+
encoded.paddingright = pad(merged.marginRight);
|
|
164
|
+
encoded.paddingtop = pad(merged.marginTop);
|
|
165
|
+
encoded.paddingbottom = pad(merged.marginBottom);
|
|
166
|
+
|
|
167
|
+
if (this.isMatrix(type)) {
|
|
168
|
+
// 2D symbologies keep their own aspect ratio; an explicit target size is
|
|
169
|
+
// honoured by scaling the module size instead of stretching bars.
|
|
170
|
+
if (merged.size) {
|
|
171
|
+
encoded.scale = this.scaleForSize(
|
|
172
|
+
{ ...encoded, scale: 1 },
|
|
173
|
+
positive(merged.size, 300)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
encoded.height = positive(merged.height, 100) / (scale * PX_PER_MM);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Allow advanced callers to hand raw bwip-js options through untouched
|
|
181
|
+
if (options.bwipOptions && typeof options.bwipOptions === 'object') {
|
|
182
|
+
Object.assign(encoded, options.bwipOptions);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return encoded;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Work out the module scale needed to reach a target pixel size
|
|
190
|
+
* @param {Object} baseOptions - bwip-js options rendered at scale 1
|
|
191
|
+
* @param {number} targetPx - Desired width/height in pixels
|
|
192
|
+
* @returns {number} Module scale
|
|
193
|
+
*/
|
|
194
|
+
static scaleForSize(baseOptions, targetPx) {
|
|
195
|
+
try {
|
|
196
|
+
const svg = bwipjs.toSVG(baseOptions);
|
|
197
|
+
const viewBox = /viewBox="0 0 ([\d.]+) ([\d.]+)"/.exec(svg);
|
|
198
|
+
const baseSize = viewBox
|
|
199
|
+
? Math.max(Number(viewBox[1]), Number(viewBox[2]))
|
|
200
|
+
: 0;
|
|
201
|
+
|
|
202
|
+
if (!baseSize) {
|
|
203
|
+
return 4;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return Math.max(1, Math.round(targetPx / baseSize));
|
|
207
|
+
} catch {
|
|
208
|
+
return 4;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Encode as an SVG string (synchronous)
|
|
214
|
+
* @param {string} data - The data to encode
|
|
215
|
+
* @param {string} type - The barcode type
|
|
216
|
+
* @param {Object} options - Render options
|
|
217
|
+
* @returns {string} SVG markup
|
|
218
|
+
*/
|
|
219
|
+
static toSvg(data, type, options = {}) {
|
|
220
|
+
try {
|
|
221
|
+
return bwipjs.toSVG(this.buildOptions(data, type, options));
|
|
222
|
+
} catch (error) {
|
|
223
|
+
throw new Error(`Failed to encode ${type}: ${cleanMessage(error)}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Encode as a PNG buffer
|
|
229
|
+
* @param {string} data - The data to encode
|
|
230
|
+
* @param {string} type - The barcode type
|
|
231
|
+
* @param {Object} options - Render options
|
|
232
|
+
* @returns {Promise<Buffer>} PNG buffer
|
|
233
|
+
*/
|
|
234
|
+
static async toPng(data, type, options = {}) {
|
|
235
|
+
try {
|
|
236
|
+
return await bwipjs.toBuffer(this.buildOptions(data, type, options));
|
|
237
|
+
} catch (error) {
|
|
238
|
+
throw new Error(`Failed to encode ${type}: ${cleanMessage(error)}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Get the pixel dimensions an encoding will produce
|
|
244
|
+
* @param {string} data - The data to encode
|
|
245
|
+
* @param {string} type - The barcode type
|
|
246
|
+
* @param {Object} options - Render options
|
|
247
|
+
* @returns {{width: number, height: number}} Dimensions in pixels
|
|
248
|
+
*/
|
|
249
|
+
static getDimensions(data, type, options = {}) {
|
|
250
|
+
const svg = this.toSvg(data, type, options);
|
|
251
|
+
const viewBox = /viewBox="0 0 ([\d.]+) ([\d.]+)"/.exec(svg);
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
width: viewBox ? Math.round(Number(viewBox[1])) : 0,
|
|
255
|
+
height: viewBox ? Math.round(Number(viewBox[2])) : 0,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
module.exports = { BarcodeEncoder, cleanMessage };
|
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTML Renderer - Renders barcodes as HTML
|
|
2
|
+
* HTML Renderer - Renders barcodes as an HTML fragment wrapping a real SVG
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
const { BarcodeEncoder } = require('../encoders/BarcodeEncoder');
|
|
6
|
+
|
|
7
|
+
let counter = 0;
|
|
8
|
+
|
|
5
9
|
class HTMLRenderer {
|
|
6
10
|
constructor() {
|
|
7
11
|
this.defaultOptions = {
|
|
8
|
-
|
|
9
|
-
height: 100,
|
|
10
|
-
displayValue: true,
|
|
11
|
-
fontSize: 20,
|
|
12
|
-
textAlign: 'center',
|
|
13
|
-
textPosition: 'bottom',
|
|
14
|
-
textMargin: 2,
|
|
15
|
-
background: '#ffffff',
|
|
16
|
-
lineColor: '#000000',
|
|
17
|
-
margin: 10,
|
|
18
|
-
marginTop: 10,
|
|
19
|
-
marginBottom: 10,
|
|
20
|
-
marginLeft: 10,
|
|
21
|
-
marginRight: 10,
|
|
12
|
+
...BarcodeEncoder.getDefaultOptions(),
|
|
22
13
|
className: 'barcode',
|
|
23
14
|
id: null,
|
|
15
|
+
includeStyles: true,
|
|
24
16
|
};
|
|
25
17
|
}
|
|
26
18
|
|
|
@@ -29,169 +21,31 @@ class HTMLRenderer {
|
|
|
29
21
|
* @param {string} data - The data to encode
|
|
30
22
|
* @param {string} type - The barcode type
|
|
31
23
|
* @param {Object} options - Render options
|
|
32
|
-
* @returns {string} HTML
|
|
24
|
+
* @returns {string} HTML markup
|
|
33
25
|
*/
|
|
34
26
|
render(data, type, options = {}) {
|
|
35
27
|
try {
|
|
36
|
-
// Merge options with defaults
|
|
37
28
|
const renderOptions = { ...this.defaultOptions, ...options };
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
29
|
+
const svg = BarcodeEncoder.toSvg(data, type, renderOptions);
|
|
30
|
+
const id = renderOptions.id || `barcode-${++counter}`;
|
|
31
|
+
const className = renderOptions.className || 'barcode';
|
|
32
|
+
|
|
33
|
+
const styles = renderOptions.includeStyles
|
|
34
|
+
? ` style="display:inline-block;background:${escapeAttribute(
|
|
35
|
+
renderOptions.background
|
|
36
|
+
)};text-align:center"`
|
|
37
|
+
: '';
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
`<div id="${escapeAttribute(id)}" class="${escapeAttribute(className)}"` +
|
|
41
|
+
`${styles} data-barcode-type="${escapeAttribute(type)}"` +
|
|
42
|
+
` data-barcode-value="${escapeAttribute(data)}">${svg}</div>`
|
|
43
|
+
);
|
|
42
44
|
} catch (error) {
|
|
43
45
|
throw new Error(`HTML rendering failed: ${error.message}`);
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
/**
|
|
48
|
-
* Create HTML structure for barcode
|
|
49
|
-
* @param {string} data - The data to encode
|
|
50
|
-
* @param {string} type - The barcode type
|
|
51
|
-
* @param {Object} options - Render options
|
|
52
|
-
* @returns {string} HTML string
|
|
53
|
-
*/
|
|
54
|
-
createHTMLStructure(data, type, options) {
|
|
55
|
-
const containerId =
|
|
56
|
-
options.id ||
|
|
57
|
-
`barcode-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
58
|
-
const containerClass = options.className || 'barcode';
|
|
59
|
-
|
|
60
|
-
let html = `<div id="${containerId}" class="${containerClass}" style="${this.getContainerStyles(
|
|
61
|
-
options
|
|
62
|
-
)}">`;
|
|
63
|
-
|
|
64
|
-
// Add barcode representation
|
|
65
|
-
if (type === 'qrcode') {
|
|
66
|
-
html += this.createQRCodeHTML(data, options);
|
|
67
|
-
} else {
|
|
68
|
-
html += this.createLinearBarcodeHTML(data, type, options);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Add text if displayValue is true
|
|
72
|
-
if (options.displayValue) {
|
|
73
|
-
html += `<div class="barcode-text" style="${this.getTextStyles(
|
|
74
|
-
options
|
|
75
|
-
)}">${data}</div>`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
html += '</div>';
|
|
79
|
-
|
|
80
|
-
// Add CSS styles
|
|
81
|
-
html += this.getCSSStyles(containerId, options);
|
|
82
|
-
|
|
83
|
-
return html;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Create QR Code HTML representation
|
|
88
|
-
* @param {string} data - The data to encode
|
|
89
|
-
* @param {Object} options - Render options
|
|
90
|
-
* @returns {string} HTML content
|
|
91
|
-
*/
|
|
92
|
-
createQRCodeHTML(data, options) {
|
|
93
|
-
// This is a simplified representation
|
|
94
|
-
// In a real implementation, you would use a QR code library
|
|
95
|
-
const size = 200;
|
|
96
|
-
const cellSize = 10;
|
|
97
|
-
const cells = size / cellSize;
|
|
98
|
-
|
|
99
|
-
let html = `<div class="qr-code" style="width: ${size}px; height: ${size}px; display: grid; grid-template-columns: repeat(${cells}, 1fr); gap: 1px; background: ${options.background}; padding: 10px;">`;
|
|
100
|
-
|
|
101
|
-
// Generate a simple pattern based on data
|
|
102
|
-
for (let i = 0; i < cells * cells; i++) {
|
|
103
|
-
const shouldFill = (data.charCodeAt(i % data.length) + i) % 2 === 0;
|
|
104
|
-
const color = shouldFill ? options.lineColor : options.background;
|
|
105
|
-
html += `<div style="background-color: ${color}; width: 100%; height: 100%;"></div>`;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
html += '</div>';
|
|
109
|
-
return html;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Create linear barcode HTML representation
|
|
114
|
-
* @param {string} data - The data to encode
|
|
115
|
-
* @param {string} type - The barcode type
|
|
116
|
-
* @param {Object} options - Render options
|
|
117
|
-
* @returns {string} HTML content
|
|
118
|
-
*/
|
|
119
|
-
createLinearBarcodeHTML(data, type, options) {
|
|
120
|
-
const barWidth = options.width || 2;
|
|
121
|
-
const barHeight = options.height || 100;
|
|
122
|
-
|
|
123
|
-
let html = `<div class="linear-barcode" style="display: flex; align-items: flex-start; background: ${options.background}; padding: 10px;">`;
|
|
124
|
-
|
|
125
|
-
// Generate bars based on data
|
|
126
|
-
for (let i = 0; i < data.length; i++) {
|
|
127
|
-
const char = data.charCodeAt(i);
|
|
128
|
-
const barCount = (char % 5) + 1;
|
|
129
|
-
|
|
130
|
-
for (let j = 0; j < barCount; j++) {
|
|
131
|
-
html += `<div style="width: ${barWidth}px; height: ${barHeight}px; background-color: ${options.lineColor}; margin-right: ${barWidth}px;"></div>`;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
html += '</div>';
|
|
136
|
-
return html;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Get container styles
|
|
141
|
-
* @param {Object} options - Render options
|
|
142
|
-
* @returns {string} CSS styles
|
|
143
|
-
*/
|
|
144
|
-
getContainerStyles(options) {
|
|
145
|
-
return `
|
|
146
|
-
display: inline-block;
|
|
147
|
-
background: ${options.background};
|
|
148
|
-
padding: ${options.margin}px;
|
|
149
|
-
margin: ${options.marginTop}px ${options.marginRight}px ${options.marginBottom}px ${options.marginLeft}px;
|
|
150
|
-
border: 1px solid #ccc;
|
|
151
|
-
text-align: center;
|
|
152
|
-
`;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Get text styles
|
|
157
|
-
* @param {Object} options - Render options
|
|
158
|
-
* @returns {string} CSS styles
|
|
159
|
-
*/
|
|
160
|
-
getTextStyles(options) {
|
|
161
|
-
return `
|
|
162
|
-
font-family: Arial, sans-serif;
|
|
163
|
-
font-size: ${options.fontSize}px;
|
|
164
|
-
color: ${options.lineColor};
|
|
165
|
-
text-align: ${options.textAlign};
|
|
166
|
-
margin-top: ${options.textMargin}px;
|
|
167
|
-
`;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Get CSS styles for the barcode
|
|
172
|
-
* @param {string} containerId - Container ID
|
|
173
|
-
* @param {Object} options - Render options
|
|
174
|
-
* @returns {string} CSS styles
|
|
175
|
-
*/
|
|
176
|
-
getCSSStyles(containerId) {
|
|
177
|
-
return `
|
|
178
|
-
<style>
|
|
179
|
-
#${containerId} {
|
|
180
|
-
font-family: Arial, sans-serif;
|
|
181
|
-
}
|
|
182
|
-
#${containerId} .barcode-text {
|
|
183
|
-
font-weight: bold;
|
|
184
|
-
}
|
|
185
|
-
#${containerId} .qr-code {
|
|
186
|
-
margin: 0 auto;
|
|
187
|
-
}
|
|
188
|
-
#${containerId} .linear-barcode {
|
|
189
|
-
justify-content: center;
|
|
190
|
-
}
|
|
191
|
-
</style>
|
|
192
|
-
`;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
49
|
/**
|
|
196
50
|
* Get default options
|
|
197
51
|
* @returns {Object} Default options
|
|
@@ -209,4 +63,17 @@ class HTMLRenderer {
|
|
|
209
63
|
}
|
|
210
64
|
}
|
|
211
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Escape a value for use inside a double quoted HTML attribute
|
|
68
|
+
* @param {string} value - Raw value
|
|
69
|
+
* @returns {string} Escaped value
|
|
70
|
+
*/
|
|
71
|
+
function escapeAttribute(value) {
|
|
72
|
+
return String(value == null ? '' : value)
|
|
73
|
+
.replace(/&/g, '&')
|
|
74
|
+
.replace(/"/g, '"')
|
|
75
|
+
.replace(/</g, '<')
|
|
76
|
+
.replace(/>/g, '>');
|
|
77
|
+
}
|
|
78
|
+
|
|
212
79
|
module.exports = HTMLRenderer;
|