@liquid-js/qr-code-styling 1.6.0-rc.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.
@@ -0,0 +1,1294 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var fileType = require('file-type');
6
+ var jsdom = require('jsdom');
7
+ var fetch = require('node-fetch');
8
+ var sharp = require('sharp');
9
+ var qrcode = require('qrcode-generator');
10
+
11
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
+
13
+ var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
14
+ var sharp__default = /*#__PURE__*/_interopDefaultLegacy(sharp);
15
+ var qrcode__default = /*#__PURE__*/_interopDefaultLegacy(qrcode);
16
+
17
+ exports.DotType = void 0;
18
+ (function (DotType) {
19
+ DotType["dots"] = "dots";
20
+ DotType["rounded"] = "rounded";
21
+ DotType["classy"] = "classy";
22
+ DotType["classyRounded"] = "classy-rounded";
23
+ DotType["square"] = "square";
24
+ DotType["extraRounded"] = "extra-rounded";
25
+ })(exports.DotType || (exports.DotType = {}));
26
+ exports.CornerDotType = void 0;
27
+ (function (CornerDotType) {
28
+ CornerDotType["dot"] = "dot";
29
+ CornerDotType["square"] = "square";
30
+ })(exports.CornerDotType || (exports.CornerDotType = {}));
31
+ exports.CornerSquareType = void 0;
32
+ (function (CornerSquareType) {
33
+ CornerSquareType["dot"] = "dot";
34
+ CornerSquareType["square"] = "square";
35
+ CornerSquareType["extraRounded"] = "extra-rounded";
36
+ })(exports.CornerSquareType || (exports.CornerSquareType = {}));
37
+ exports.GradientType = void 0;
38
+ (function (GradientType) {
39
+ GradientType["radial"] = "radial";
40
+ GradientType["linear"] = "linear";
41
+ })(exports.GradientType || (exports.GradientType = {}));
42
+ exports.ShapeType = void 0;
43
+ (function (ShapeType) {
44
+ ShapeType["square"] = "square";
45
+ ShapeType["circle"] = "circle";
46
+ })(exports.ShapeType || (exports.ShapeType = {}));
47
+ const TypeNumber = Array(41)
48
+ .fill(0)
49
+ .map((_, i) => i);
50
+ exports.ErrorCorrectionLevel = void 0;
51
+ (function (ErrorCorrectionLevel) {
52
+ ErrorCorrectionLevel["L"] = "L";
53
+ ErrorCorrectionLevel["M"] = "M";
54
+ ErrorCorrectionLevel["Q"] = "Q";
55
+ ErrorCorrectionLevel["H"] = "H";
56
+ })(exports.ErrorCorrectionLevel || (exports.ErrorCorrectionLevel = {}));
57
+ exports.Mode = void 0;
58
+ (function (Mode) {
59
+ Mode["numeric"] = "Numeric";
60
+ Mode["alphanumeric"] = "Alphanumeric";
61
+ Mode["byte"] = "Byte";
62
+ Mode["kanji"] = "Kanji";
63
+ })(exports.Mode || (exports.Mode = {}));
64
+ exports.FileExtension = void 0;
65
+ (function (FileExtension) {
66
+ FileExtension["svg"] = "svg";
67
+ FileExtension["png"] = "png";
68
+ FileExtension["jpeg"] = "jpeg";
69
+ FileExtension["webp"] = "webp";
70
+ })(exports.FileExtension || (exports.FileExtension = {}));
71
+
72
+ const ErrorCorrectionPercents = {
73
+ [exports.ErrorCorrectionLevel.L]: 0.07,
74
+ [exports.ErrorCorrectionLevel.M]: 0.15,
75
+ [exports.ErrorCorrectionLevel.Q]: 0.25,
76
+ [exports.ErrorCorrectionLevel.H]: 0.3
77
+ };
78
+
79
+ function getMode(data) {
80
+ switch (true) {
81
+ case /^[0-9]*$/.test(data):
82
+ return exports.Mode.numeric;
83
+ case /^[0-9A-Z $%*+\-./:]*$/.test(data):
84
+ return exports.Mode.alphanumeric;
85
+ default:
86
+ return exports.Mode.byte;
87
+ }
88
+ }
89
+
90
+ const isObject = (obj) => !!obj && typeof obj === "object" && !Array.isArray(obj);
91
+ function mergeDeep(target, ...sources) {
92
+ if (!sources.length)
93
+ return target;
94
+ const source = sources.shift();
95
+ if (source === undefined || !isObject(target) || !isObject(source))
96
+ return target;
97
+ target = { ...target };
98
+ Object.keys(source).forEach((key) => {
99
+ const targetValue = target[key];
100
+ const sourceValue = source[key];
101
+ if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
102
+ target[key] = sourceValue;
103
+ }
104
+ else if (isObject(targetValue) && isObject(sourceValue)) {
105
+ target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue);
106
+ }
107
+ else {
108
+ target[key] = sourceValue;
109
+ }
110
+ });
111
+ return mergeDeep(target, ...sources);
112
+ }
113
+
114
+ function sanitizeGradient(gradient) {
115
+ const newGradient = { ...gradient };
116
+ if (!newGradient.colorStops || !newGradient.colorStops.length) {
117
+ throw "Field 'colorStops' is required in gradient";
118
+ }
119
+ if (newGradient.rotation) {
120
+ newGradient.rotation = Number(newGradient.rotation);
121
+ }
122
+ else {
123
+ newGradient.rotation = 0;
124
+ }
125
+ newGradient.colorStops = newGradient.colorStops.map((colorStop) => ({
126
+ ...colorStop,
127
+ offset: Number(colorStop.offset)
128
+ }));
129
+ return newGradient;
130
+ }
131
+ function sanitizeOptions(options) {
132
+ const newOptions = { ...options };
133
+ newOptions.width = Number(newOptions.width);
134
+ newOptions.height = Number(newOptions.height);
135
+ newOptions.margin = Number(newOptions.margin);
136
+ newOptions.imageOptions = {
137
+ ...newOptions.imageOptions,
138
+ hideBackgroundDots: Boolean(newOptions.imageOptions.hideBackgroundDots),
139
+ imageSize: Number(newOptions.imageOptions.imageSize),
140
+ margin: Number(newOptions.imageOptions.margin)
141
+ };
142
+ if (newOptions.margin > Math.min(newOptions.width, newOptions.height)) {
143
+ newOptions.margin = Math.min(newOptions.width, newOptions.height);
144
+ }
145
+ newOptions.dotsOptions = {
146
+ ...newOptions.dotsOptions
147
+ };
148
+ if (newOptions.dotsOptions.gradient) {
149
+ newOptions.dotsOptions.gradient = sanitizeGradient(newOptions.dotsOptions.gradient);
150
+ }
151
+ if (newOptions.cornersSquareOptions) {
152
+ newOptions.cornersSquareOptions = {
153
+ ...newOptions.cornersSquareOptions
154
+ };
155
+ if (newOptions.cornersSquareOptions.gradient) {
156
+ newOptions.cornersSquareOptions.gradient = sanitizeGradient(newOptions.cornersSquareOptions.gradient);
157
+ }
158
+ }
159
+ if (newOptions.cornersDotOptions) {
160
+ newOptions.cornersDotOptions = {
161
+ ...newOptions.cornersDotOptions
162
+ };
163
+ if (newOptions.cornersDotOptions.gradient) {
164
+ newOptions.cornersDotOptions.gradient = sanitizeGradient(newOptions.cornersDotOptions.gradient);
165
+ }
166
+ }
167
+ if (newOptions.backgroundOptions) {
168
+ newOptions.backgroundOptions = {
169
+ ...newOptions.backgroundOptions
170
+ };
171
+ if (newOptions.backgroundOptions.gradient) {
172
+ newOptions.backgroundOptions.gradient = sanitizeGradient(newOptions.backgroundOptions.gradient);
173
+ }
174
+ }
175
+ if (!newOptions.document)
176
+ newOptions.document = document;
177
+ return newOptions;
178
+ }
179
+
180
+ const defaultOptions = {
181
+ document: undefined,
182
+ shape: exports.ShapeType.square,
183
+ width: 300,
184
+ height: 300,
185
+ data: "",
186
+ margin: 0,
187
+ qrOptions: {
188
+ typeNumber: TypeNumber[0],
189
+ mode: undefined,
190
+ errorCorrectionLevel: exports.ErrorCorrectionLevel.Q
191
+ },
192
+ imageOptions: {
193
+ hideBackgroundDots: true,
194
+ imageSize: 0.4,
195
+ crossOrigin: undefined,
196
+ margin: 0
197
+ },
198
+ dotsOptions: {
199
+ type: exports.DotType.square,
200
+ color: "#000"
201
+ }
202
+ };
203
+
204
+ class QRCornerDot {
205
+ constructor({ svg, type, document }) {
206
+ this._svg = svg;
207
+ this._type = type;
208
+ this._document = document;
209
+ }
210
+ draw(x, y, size, rotation) {
211
+ const type = this._type;
212
+ let drawFunction;
213
+ switch (type) {
214
+ case exports.CornerDotType.square:
215
+ drawFunction = this._drawSquare;
216
+ break;
217
+ case exports.CornerDotType.dot:
218
+ default:
219
+ drawFunction = this._drawDot;
220
+ }
221
+ drawFunction.call(this, { x, y, size, rotation });
222
+ }
223
+ _rotateFigure({ x, y, size, rotation = 0, draw }) {
224
+ const cx = x + size / 2;
225
+ const cy = y + size / 2;
226
+ draw();
227
+ this._element?.setAttribute("transform", `rotate(${(180 * rotation) / Math.PI},${cx},${cy})`);
228
+ }
229
+ _basicDot(args) {
230
+ const { size, x, y } = args;
231
+ this._rotateFigure({
232
+ ...args,
233
+ draw: () => {
234
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "circle");
235
+ this._element.setAttribute("cx", String(x + size / 2));
236
+ this._element.setAttribute("cy", String(y + size / 2));
237
+ this._element.setAttribute("r", String(size / 2));
238
+ }
239
+ });
240
+ }
241
+ _basicSquare(args) {
242
+ const { size, x, y } = args;
243
+ this._rotateFigure({
244
+ ...args,
245
+ draw: () => {
246
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "rect");
247
+ this._element.setAttribute("x", String(x));
248
+ this._element.setAttribute("y", String(y));
249
+ this._element.setAttribute("width", String(size));
250
+ this._element.setAttribute("height", String(size));
251
+ }
252
+ });
253
+ }
254
+ _drawDot({ x, y, size, rotation }) {
255
+ this._basicDot({ x, y, size, rotation });
256
+ }
257
+ _drawSquare({ x, y, size, rotation }) {
258
+ this._basicSquare({ x, y, size, rotation });
259
+ }
260
+ }
261
+
262
+ class QRCornerSquare {
263
+ constructor({ svg, type, document }) {
264
+ this._svg = svg;
265
+ this._type = type;
266
+ this._document = document;
267
+ }
268
+ draw(x, y, size, rotation) {
269
+ const type = this._type;
270
+ let drawFunction;
271
+ switch (type) {
272
+ case exports.CornerSquareType.square:
273
+ drawFunction = this._drawSquare;
274
+ break;
275
+ case exports.CornerSquareType.extraRounded:
276
+ drawFunction = this._drawExtraRounded;
277
+ break;
278
+ case exports.CornerSquareType.dot:
279
+ default:
280
+ drawFunction = this._drawDot;
281
+ }
282
+ drawFunction.call(this, { x, y, size, rotation });
283
+ }
284
+ _rotateFigure({ x, y, size, rotation = 0, draw }) {
285
+ const cx = x + size / 2;
286
+ const cy = y + size / 2;
287
+ draw();
288
+ this._element?.setAttribute("transform", `rotate(${(180 * rotation) / Math.PI},${cx},${cy})`);
289
+ }
290
+ _basicDot(args) {
291
+ const { size, x, y } = args;
292
+ const dotSize = size / 7;
293
+ this._rotateFigure({
294
+ ...args,
295
+ draw: () => {
296
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "path");
297
+ this._element.setAttribute("clip-rule", "evenodd");
298
+ this._element.setAttribute("d", `M ${x + size / 2} ${y}` + // M cx, y // Move to top of ring
299
+ `a ${size / 2} ${size / 2} 0 1 0 0.1 0` + // a outerRadius, outerRadius, 0, 1, 0, 1, 0 // Draw outer arc, but don't close it
300
+ `z` + // Z // Close the outer shape
301
+ `m 0 ${dotSize}` + // m -1 outerRadius-innerRadius // Move to top point of inner radius
302
+ `a ${size / 2 - dotSize} ${size / 2 - dotSize} 0 1 1 -0.1 0` + // a innerRadius, innerRadius, 0, 1, 1, -1, 0 // Draw inner arc, but don't close it
303
+ `Z` // Z // Close the inner ring. Actually will still work without, but inner ring will have one unit missing in stroke
304
+ );
305
+ }
306
+ });
307
+ }
308
+ _basicSquare(args) {
309
+ const { size, x, y } = args;
310
+ const dotSize = size / 7;
311
+ this._rotateFigure({
312
+ ...args,
313
+ draw: () => {
314
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "path");
315
+ this._element.setAttribute("clip-rule", "evenodd");
316
+ this._element.setAttribute("d", `M ${x} ${y}` +
317
+ `v ${size}` +
318
+ `h ${size}` +
319
+ `v ${-size}` +
320
+ `z` +
321
+ `M ${x + dotSize} ${y + dotSize}` +
322
+ `h ${size - 2 * dotSize}` +
323
+ `v ${size - 2 * dotSize}` +
324
+ `h ${-size + 2 * dotSize}` +
325
+ `z`);
326
+ }
327
+ });
328
+ }
329
+ _basicExtraRounded(args) {
330
+ const { size, x, y } = args;
331
+ const dotSize = size / 7;
332
+ this._rotateFigure({
333
+ ...args,
334
+ draw: () => {
335
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "path");
336
+ this._element.setAttribute("clip-rule", "evenodd");
337
+ this._element.setAttribute("d", `M ${x} ${y + 2.5 * dotSize}` +
338
+ `v ${2 * dotSize}` +
339
+ `a ${2.5 * dotSize} ${2.5 * dotSize}, 0, 0, 0, ${dotSize * 2.5} ${dotSize * 2.5}` +
340
+ `h ${2 * dotSize}` +
341
+ `a ${2.5 * dotSize} ${2.5 * dotSize}, 0, 0, 0, ${dotSize * 2.5} ${-dotSize * 2.5}` +
342
+ `v ${-2 * dotSize}` +
343
+ `a ${2.5 * dotSize} ${2.5 * dotSize}, 0, 0, 0, ${-dotSize * 2.5} ${-dotSize * 2.5}` +
344
+ `h ${-2 * dotSize}` +
345
+ `a ${2.5 * dotSize} ${2.5 * dotSize}, 0, 0, 0, ${-dotSize * 2.5} ${dotSize * 2.5}` +
346
+ `M ${x + 2.5 * dotSize} ${y + dotSize}` +
347
+ `h ${2 * dotSize}` +
348
+ `a ${1.5 * dotSize} ${1.5 * dotSize}, 0, 0, 1, ${dotSize * 1.5} ${dotSize * 1.5}` +
349
+ `v ${2 * dotSize}` +
350
+ `a ${1.5 * dotSize} ${1.5 * dotSize}, 0, 0, 1, ${-dotSize * 1.5} ${dotSize * 1.5}` +
351
+ `h ${-2 * dotSize}` +
352
+ `a ${1.5 * dotSize} ${1.5 * dotSize}, 0, 0, 1, ${-dotSize * 1.5} ${-dotSize * 1.5}` +
353
+ `v ${-2 * dotSize}` +
354
+ `a ${1.5 * dotSize} ${1.5 * dotSize}, 0, 0, 1, ${dotSize * 1.5} ${-dotSize * 1.5}`);
355
+ }
356
+ });
357
+ }
358
+ _drawDot({ x, y, size, rotation }) {
359
+ this._basicDot({ x, y, size, rotation });
360
+ }
361
+ _drawSquare({ x, y, size, rotation }) {
362
+ this._basicSquare({ x, y, size, rotation });
363
+ }
364
+ _drawExtraRounded({ x, y, size, rotation }) {
365
+ this._basicExtraRounded({ x, y, size, rotation });
366
+ }
367
+ }
368
+
369
+ class QRDot {
370
+ constructor({ svg, type, document }) {
371
+ this._svg = svg;
372
+ this._type = type;
373
+ this._document = document;
374
+ }
375
+ draw(x, y, size, getNeighbor) {
376
+ const type = this._type;
377
+ let drawFunction;
378
+ switch (type) {
379
+ case exports.DotType.dots:
380
+ drawFunction = this._drawDot;
381
+ break;
382
+ case exports.DotType.classy:
383
+ drawFunction = this._drawClassy;
384
+ break;
385
+ case exports.DotType.classyRounded:
386
+ drawFunction = this._drawClassyRounded;
387
+ break;
388
+ case exports.DotType.rounded:
389
+ drawFunction = this._drawRounded;
390
+ break;
391
+ case exports.DotType.extraRounded:
392
+ drawFunction = this._drawExtraRounded;
393
+ break;
394
+ case exports.DotType.square:
395
+ default:
396
+ drawFunction = this._drawSquare;
397
+ }
398
+ drawFunction.call(this, { x, y, size, getNeighbor });
399
+ }
400
+ _rotateFigure({ x, y, size, rotation = 0, draw }) {
401
+ const cx = x + size / 2;
402
+ const cy = y + size / 2;
403
+ draw();
404
+ this._element?.setAttribute("transform", `rotate(${(180 * rotation) / Math.PI},${cx},${cy})`);
405
+ }
406
+ _basicDot(args) {
407
+ const { size, x, y } = args;
408
+ this._rotateFigure({
409
+ ...args,
410
+ draw: () => {
411
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "circle");
412
+ this._element.setAttribute("cx", String(x + size / 2));
413
+ this._element.setAttribute("cy", String(y + size / 2));
414
+ this._element.setAttribute("r", String(size / 2));
415
+ }
416
+ });
417
+ }
418
+ _basicSquare(args) {
419
+ const { size, x, y } = args;
420
+ this._rotateFigure({
421
+ ...args,
422
+ draw: () => {
423
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "rect");
424
+ this._element.setAttribute("x", String(x));
425
+ this._element.setAttribute("y", String(y));
426
+ this._element.setAttribute("width", String(size));
427
+ this._element.setAttribute("height", String(size));
428
+ }
429
+ });
430
+ }
431
+ //if rotation === 0 - right side is rounded
432
+ _basicSideRounded(args) {
433
+ const { size, x, y } = args;
434
+ this._rotateFigure({
435
+ ...args,
436
+ draw: () => {
437
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "path");
438
+ this._element.setAttribute("d", `M ${x} ${y}` + //go to top left position
439
+ `v ${size}` + //draw line to left bottom corner
440
+ `h ${size / 2}` + //draw line to left bottom corner + half of size right
441
+ `a ${size / 2} ${size / 2}, 0, 0, 0, 0 ${-size}` // draw rounded corner
442
+ );
443
+ }
444
+ });
445
+ }
446
+ //if rotation === 0 - top right corner is rounded
447
+ _basicCornerRounded(args) {
448
+ const { size, x, y } = args;
449
+ this._rotateFigure({
450
+ ...args,
451
+ draw: () => {
452
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "path");
453
+ this._element.setAttribute("d", `M ${x} ${y}` + //go to top left position
454
+ `v ${size}` + //draw line to left bottom corner
455
+ `h ${size}` + //draw line to right bottom corner
456
+ `v ${-size / 2}` + //draw line to right bottom corner + half of size top
457
+ `a ${size / 2} ${size / 2}, 0, 0, 0, ${-size / 2} ${-size / 2}` // draw rounded corner
458
+ );
459
+ }
460
+ });
461
+ }
462
+ //if rotation === 0 - top right corner is rounded
463
+ _basicCornerExtraRounded(args) {
464
+ const { size, x, y } = args;
465
+ this._rotateFigure({
466
+ ...args,
467
+ draw: () => {
468
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "path");
469
+ this._element.setAttribute("d", `M ${x} ${y}` + //go to top left position
470
+ `v ${size}` + //draw line to left bottom corner
471
+ `h ${size}` + //draw line to right bottom corner
472
+ `a ${size} ${size}, 0, 0, 0, ${-size} ${-size}` // draw rounded top right corner
473
+ );
474
+ }
475
+ });
476
+ }
477
+ //if rotation === 0 - left bottom and right top corners are rounded
478
+ _basicCornersRounded(args) {
479
+ const { size, x, y } = args;
480
+ this._rotateFigure({
481
+ ...args,
482
+ draw: () => {
483
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "path");
484
+ this._element.setAttribute("d", `M ${x} ${y}` + //go to left top position
485
+ `v ${size / 2}` + //draw line to left top corner + half of size bottom
486
+ `a ${size / 2} ${size / 2}, 0, 0, 0, ${size / 2} ${size / 2}` + // draw rounded left bottom corner
487
+ `h ${size / 2}` + //draw line to right bottom corner
488
+ `v ${-size / 2}` + //draw line to right bottom corner + half of size top
489
+ `a ${size / 2} ${size / 2}, 0, 0, 0, ${-size / 2} ${-size / 2}` // draw rounded right top corner
490
+ );
491
+ }
492
+ });
493
+ }
494
+ _drawDot({ x, y, size }) {
495
+ this._basicDot({ x, y, size, rotation: 0 });
496
+ }
497
+ _drawSquare({ x, y, size }) {
498
+ this._basicSquare({ x, y, size, rotation: 0 });
499
+ }
500
+ _drawRounded({ x, y, size, getNeighbor }) {
501
+ const leftNeighbor = getNeighbor ? +getNeighbor(-1, 0) : 0;
502
+ const rightNeighbor = getNeighbor ? +getNeighbor(1, 0) : 0;
503
+ const topNeighbor = getNeighbor ? +getNeighbor(0, -1) : 0;
504
+ const bottomNeighbor = getNeighbor ? +getNeighbor(0, 1) : 0;
505
+ const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor;
506
+ if (neighborsCount === 0) {
507
+ this._basicDot({ x, y, size, rotation: 0 });
508
+ return;
509
+ }
510
+ if (neighborsCount > 2 || (leftNeighbor && rightNeighbor) || (topNeighbor && bottomNeighbor)) {
511
+ this._basicSquare({ x, y, size, rotation: 0 });
512
+ return;
513
+ }
514
+ if (neighborsCount === 2) {
515
+ let rotation = 0;
516
+ if (leftNeighbor && topNeighbor) {
517
+ rotation = Math.PI / 2;
518
+ }
519
+ else if (topNeighbor && rightNeighbor) {
520
+ rotation = Math.PI;
521
+ }
522
+ else if (rightNeighbor && bottomNeighbor) {
523
+ rotation = -Math.PI / 2;
524
+ }
525
+ this._basicCornerRounded({ x, y, size, rotation });
526
+ return;
527
+ }
528
+ if (neighborsCount === 1) {
529
+ let rotation = 0;
530
+ if (topNeighbor) {
531
+ rotation = Math.PI / 2;
532
+ }
533
+ else if (rightNeighbor) {
534
+ rotation = Math.PI;
535
+ }
536
+ else if (bottomNeighbor) {
537
+ rotation = -Math.PI / 2;
538
+ }
539
+ this._basicSideRounded({ x, y, size, rotation });
540
+ return;
541
+ }
542
+ }
543
+ _drawExtraRounded({ x, y, size, getNeighbor }) {
544
+ const leftNeighbor = getNeighbor ? +getNeighbor(-1, 0) : 0;
545
+ const rightNeighbor = getNeighbor ? +getNeighbor(1, 0) : 0;
546
+ const topNeighbor = getNeighbor ? +getNeighbor(0, -1) : 0;
547
+ const bottomNeighbor = getNeighbor ? +getNeighbor(0, 1) : 0;
548
+ const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor;
549
+ if (neighborsCount === 0) {
550
+ this._basicDot({ x, y, size, rotation: 0 });
551
+ return;
552
+ }
553
+ if (neighborsCount > 2 || (leftNeighbor && rightNeighbor) || (topNeighbor && bottomNeighbor)) {
554
+ this._basicSquare({ x, y, size, rotation: 0 });
555
+ return;
556
+ }
557
+ if (neighborsCount === 2) {
558
+ let rotation = 0;
559
+ if (leftNeighbor && topNeighbor) {
560
+ rotation = Math.PI / 2;
561
+ }
562
+ else if (topNeighbor && rightNeighbor) {
563
+ rotation = Math.PI;
564
+ }
565
+ else if (rightNeighbor && bottomNeighbor) {
566
+ rotation = -Math.PI / 2;
567
+ }
568
+ this._basicCornerExtraRounded({ x, y, size, rotation });
569
+ return;
570
+ }
571
+ if (neighborsCount === 1) {
572
+ let rotation = 0;
573
+ if (topNeighbor) {
574
+ rotation = Math.PI / 2;
575
+ }
576
+ else if (rightNeighbor) {
577
+ rotation = Math.PI;
578
+ }
579
+ else if (bottomNeighbor) {
580
+ rotation = -Math.PI / 2;
581
+ }
582
+ this._basicSideRounded({ x, y, size, rotation });
583
+ return;
584
+ }
585
+ }
586
+ _drawClassy({ x, y, size, getNeighbor }) {
587
+ const leftNeighbor = getNeighbor ? +getNeighbor(-1, 0) : 0;
588
+ const rightNeighbor = getNeighbor ? +getNeighbor(1, 0) : 0;
589
+ const topNeighbor = getNeighbor ? +getNeighbor(0, -1) : 0;
590
+ const bottomNeighbor = getNeighbor ? +getNeighbor(0, 1) : 0;
591
+ const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor;
592
+ if (neighborsCount === 0) {
593
+ this._basicCornersRounded({ x, y, size, rotation: Math.PI / 2 });
594
+ return;
595
+ }
596
+ if (!leftNeighbor && !topNeighbor) {
597
+ this._basicCornerRounded({ x, y, size, rotation: -Math.PI / 2 });
598
+ return;
599
+ }
600
+ if (!rightNeighbor && !bottomNeighbor) {
601
+ this._basicCornerRounded({ x, y, size, rotation: Math.PI / 2 });
602
+ return;
603
+ }
604
+ this._basicSquare({ x, y, size, rotation: 0 });
605
+ }
606
+ _drawClassyRounded({ x, y, size, getNeighbor }) {
607
+ const leftNeighbor = getNeighbor ? +getNeighbor(-1, 0) : 0;
608
+ const rightNeighbor = getNeighbor ? +getNeighbor(1, 0) : 0;
609
+ const topNeighbor = getNeighbor ? +getNeighbor(0, -1) : 0;
610
+ const bottomNeighbor = getNeighbor ? +getNeighbor(0, 1) : 0;
611
+ const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor;
612
+ if (neighborsCount === 0) {
613
+ this._basicCornersRounded({ x, y, size, rotation: Math.PI / 2 });
614
+ return;
615
+ }
616
+ if (!leftNeighbor && !topNeighbor) {
617
+ this._basicCornerExtraRounded({ x, y, size, rotation: -Math.PI / 2 });
618
+ return;
619
+ }
620
+ if (!rightNeighbor && !bottomNeighbor) {
621
+ this._basicCornerExtraRounded({ x, y, size, rotation: Math.PI / 2 });
622
+ return;
623
+ }
624
+ this._basicSquare({ x, y, size, rotation: 0 });
625
+ }
626
+ }
627
+
628
+ function calculateImageSize({ originalHeight, originalWidth, maxHiddenDots, maxHiddenAxisDots, dotSize }) {
629
+ const hideDots = { x: 0, y: 0 };
630
+ const imageSize = { x: 0, y: 0 };
631
+ if (originalHeight <= 0 || originalWidth <= 0 || maxHiddenDots <= 0 || dotSize <= 0) {
632
+ return {
633
+ height: 0,
634
+ width: 0,
635
+ hideYDots: 0,
636
+ hideXDots: 0
637
+ };
638
+ }
639
+ const k = originalHeight / originalWidth;
640
+ //Getting the maximum possible axis hidden dots
641
+ hideDots.x = Math.floor(Math.sqrt(maxHiddenDots / k));
642
+ //The count of hidden dot's can't be less than 1
643
+ if (hideDots.x <= 0)
644
+ hideDots.x = 1;
645
+ //Check the limit of the maximum allowed axis hidden dots
646
+ if (maxHiddenAxisDots && maxHiddenAxisDots < hideDots.x)
647
+ hideDots.x = maxHiddenAxisDots;
648
+ //The count of dots should be odd
649
+ if (hideDots.x % 2 === 0)
650
+ hideDots.x--;
651
+ imageSize.x = hideDots.x * dotSize;
652
+ //Calculate opposite axis hidden dots based on axis value.
653
+ //The value will be odd.
654
+ //We use ceil to prevent dots covering by the image.
655
+ hideDots.y = 1 + 2 * Math.ceil((hideDots.x * k - 1) / 2);
656
+ imageSize.y = Math.round(imageSize.x * k);
657
+ //If the result dots count is bigger than max - then decrease size and calculate again
658
+ if (hideDots.y * hideDots.x > maxHiddenDots || (maxHiddenAxisDots && maxHiddenAxisDots < hideDots.y)) {
659
+ if (maxHiddenAxisDots && maxHiddenAxisDots < hideDots.y) {
660
+ hideDots.y = maxHiddenAxisDots;
661
+ if (hideDots.y % 2 === 0)
662
+ hideDots.x--;
663
+ }
664
+ else {
665
+ hideDots.y -= 2;
666
+ }
667
+ imageSize.y = hideDots.y * dotSize;
668
+ hideDots.x = 1 + 2 * Math.ceil((hideDots.y / k - 1) / 2);
669
+ imageSize.x = Math.round(imageSize.y / k);
670
+ }
671
+ return {
672
+ height: imageSize.y,
673
+ width: imageSize.x,
674
+ hideYDots: hideDots.y,
675
+ hideXDots: hideDots.x
676
+ };
677
+ }
678
+
679
+ const browserImageTools = {
680
+ toDataURL(url) {
681
+ return new Promise((resolve, reject) => {
682
+ if (typeof url == "string") {
683
+ const xhr = new XMLHttpRequest();
684
+ xhr.onload = function () {
685
+ const reader = new FileReader();
686
+ reader.onloadend = function () {
687
+ resolve(reader.result);
688
+ };
689
+ reader.readAsDataURL(xhr.response);
690
+ };
691
+ xhr.onerror = xhr.onabort = xhr.ontimeout = reject;
692
+ xhr.open("GET", url);
693
+ xhr.responseType = "blob";
694
+ xhr.send();
695
+ }
696
+ else {
697
+ const reader = new FileReader();
698
+ reader.onloadend = function () {
699
+ resolve(reader.result);
700
+ };
701
+ reader.readAsDataURL(url);
702
+ }
703
+ });
704
+ },
705
+ getSize(options) {
706
+ return new Promise((resolve, reject) => {
707
+ if (!options.image) {
708
+ return reject("Image is not defined");
709
+ }
710
+ const image = new Image();
711
+ if (typeof options.imageOptions.crossOrigin === "string") {
712
+ image.crossOrigin = options.imageOptions.crossOrigin;
713
+ }
714
+ image.onload = () => {
715
+ resolve({ width: image.width, height: image.height });
716
+ };
717
+ image.onerror = image.onabort = reject;
718
+ if (typeof options.image == "string")
719
+ image.src = options.image;
720
+ else
721
+ browserImageTools
722
+ .toDataURL(options.image)
723
+ .then((url) => (image.src = url))
724
+ .catch(reject);
725
+ });
726
+ }
727
+ };
728
+
729
+ const squareMask = [
730
+ [1, 1, 1, 1, 1, 1, 1],
731
+ [1, 0, 0, 0, 0, 0, 1],
732
+ [1, 0, 0, 0, 0, 0, 1],
733
+ [1, 0, 0, 0, 0, 0, 1],
734
+ [1, 0, 0, 0, 0, 0, 1],
735
+ [1, 0, 0, 0, 0, 0, 1],
736
+ [1, 1, 1, 1, 1, 1, 1]
737
+ ];
738
+ const dotMask = [
739
+ [0, 0, 0, 0, 0, 0, 0],
740
+ [0, 0, 0, 0, 0, 0, 0],
741
+ [0, 0, 1, 1, 1, 0, 0],
742
+ [0, 0, 1, 1, 1, 0, 0],
743
+ [0, 0, 1, 1, 1, 0, 0],
744
+ [0, 0, 0, 0, 0, 0, 0],
745
+ [0, 0, 0, 0, 0, 0, 0]
746
+ ];
747
+ class QRSVG {
748
+ //TODO don't pass all options to this class
749
+ constructor(options) {
750
+ this._document = options.document;
751
+ this._element = this._document.createElementNS("http://www.w3.org/2000/svg", "svg");
752
+ this._element.setAttribute("width", String(options.width));
753
+ this._element.setAttribute("height", String(options.height));
754
+ this._defs = this._document.createElementNS("http://www.w3.org/2000/svg", "defs");
755
+ this._element.appendChild(this._defs);
756
+ this._imageTools = options.imageTools || browserImageTools;
757
+ this._options = options;
758
+ }
759
+ get width() {
760
+ return this._options.width;
761
+ }
762
+ get height() {
763
+ return this._options.height;
764
+ }
765
+ getElement() {
766
+ return this._element;
767
+ }
768
+ async drawQR(qr) {
769
+ const count = qr.getModuleCount();
770
+ const minSize = Math.min(this._options.width, this._options.height) - this._options.margin * 2;
771
+ const realQRSize = this._options.shape === exports.ShapeType.circle ? minSize / Math.sqrt(2) : minSize;
772
+ const dotSize = Math.floor(realQRSize / count);
773
+ let drawImageSize = {
774
+ hideXDots: 0,
775
+ hideYDots: 0,
776
+ width: 0,
777
+ height: 0
778
+ };
779
+ this._qr = qr;
780
+ if (this._options.image) {
781
+ //We need it to get image size
782
+ const size = await this._imageTools.getSize(this._options);
783
+ const { imageOptions, qrOptions } = this._options;
784
+ const coverLevel = imageOptions.imageSize * ErrorCorrectionPercents[qrOptions.errorCorrectionLevel];
785
+ const maxHiddenDots = Math.floor(coverLevel * count * count);
786
+ drawImageSize = calculateImageSize({
787
+ originalWidth: size.width,
788
+ originalHeight: size.height,
789
+ maxHiddenDots,
790
+ maxHiddenAxisDots: count - 14,
791
+ dotSize
792
+ });
793
+ }
794
+ this.drawBackground();
795
+ this.drawDots((i, j) => {
796
+ if (this._options.imageOptions.hideBackgroundDots) {
797
+ if (i >= (count - drawImageSize.hideXDots) / 2 &&
798
+ i < (count + drawImageSize.hideXDots) / 2 &&
799
+ j >= (count - drawImageSize.hideYDots) / 2 &&
800
+ j < (count + drawImageSize.hideYDots) / 2) {
801
+ return false;
802
+ }
803
+ }
804
+ if (squareMask[i]?.[j] || squareMask[i - count + 7]?.[j] || squareMask[i]?.[j - count + 7]) {
805
+ return false;
806
+ }
807
+ if (dotMask[i]?.[j] || dotMask[i - count + 7]?.[j] || dotMask[i]?.[j - count + 7]) {
808
+ return false;
809
+ }
810
+ return true;
811
+ });
812
+ this.drawCorners();
813
+ if (this._options.image) {
814
+ await this.drawImage({ width: drawImageSize.width, height: drawImageSize.height, count, dotSize });
815
+ }
816
+ }
817
+ drawBackground() {
818
+ const element = this._element;
819
+ const options = this._options;
820
+ if (element && options.backgroundOptions) {
821
+ const gradientOptions = options.backgroundOptions.gradient;
822
+ const color = options.backgroundOptions.color;
823
+ this._createColor({
824
+ options: gradientOptions,
825
+ color: color,
826
+ additionalRotation: 0,
827
+ x: 0,
828
+ y: 0,
829
+ height: options.height,
830
+ width: options.width,
831
+ name: "background-color"
832
+ });
833
+ const size = Math.min(options.width, options.height);
834
+ const element = this._document.createElementNS("http://www.w3.org/2000/svg", "rect");
835
+ this._backgroundClipPath = this._document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
836
+ this._backgroundClipPath.setAttribute("id", "clip-path-background-color");
837
+ this._defs.appendChild(this._backgroundClipPath);
838
+ element.setAttribute("x", String((options.width - size) / 2));
839
+ element.setAttribute("y", String((options.height - size) / 2));
840
+ element.setAttribute("width", String(size));
841
+ element.setAttribute("height", String(size));
842
+ element.setAttribute("rx", String((size / 2) * (options.backgroundOptions.round || 0)));
843
+ this._backgroundClipPath.appendChild(element);
844
+ }
845
+ }
846
+ drawDots(filter) {
847
+ if (!this._qr) {
848
+ throw "QR code is not defined";
849
+ }
850
+ const options = this._options;
851
+ const count = this._qr.getModuleCount();
852
+ if (count > options.width || count > options.height) {
853
+ throw "The canvas is too small.";
854
+ }
855
+ const minSize = Math.min(options.width, options.height) - options.margin * 2;
856
+ const realQRSize = options.shape === exports.ShapeType.circle ? minSize / Math.sqrt(2) : minSize;
857
+ const dotSize = Math.floor(realQRSize / count);
858
+ const xBeginning = Math.floor((options.width - count * dotSize) / 2);
859
+ const yBeginning = Math.floor((options.height - count * dotSize) / 2);
860
+ const dot = new QRDot({ svg: this._element, type: options.dotsOptions.type, document: this._document });
861
+ this._dotsClipPath = this._document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
862
+ this._dotsClipPath.setAttribute("id", "clip-path-dot-color");
863
+ this._defs.appendChild(this._dotsClipPath);
864
+ this._createColor({
865
+ options: options.dotsOptions?.gradient,
866
+ color: options.dotsOptions.color,
867
+ additionalRotation: 0,
868
+ x: 0,
869
+ y: 0,
870
+ height: options.height,
871
+ width: options.width,
872
+ name: "dot-color"
873
+ });
874
+ for (let i = 0; i < count; i++) {
875
+ for (let j = 0; j < count; j++) {
876
+ if (filter && !filter(i, j)) {
877
+ continue;
878
+ }
879
+ if (!this._qr?.isDark(i, j)) {
880
+ continue;
881
+ }
882
+ dot.draw(xBeginning + i * dotSize, yBeginning + j * dotSize, dotSize, (xOffset, yOffset) => {
883
+ if (i + xOffset < 0 || j + yOffset < 0 || i + xOffset >= count || j + yOffset >= count)
884
+ return false;
885
+ if (filter && !filter(i + xOffset, j + yOffset))
886
+ return false;
887
+ return !!this._qr && this._qr.isDark(i + xOffset, j + yOffset);
888
+ });
889
+ if (dot._element && this._dotsClipPath) {
890
+ this._dotsClipPath.appendChild(dot._element);
891
+ }
892
+ }
893
+ }
894
+ if (options.shape === exports.ShapeType.circle) {
895
+ const additionalDots = Math.floor((minSize / dotSize - count) / 2);
896
+ const fakeCount = count + additionalDots * 2;
897
+ const xFakeBeginning = xBeginning - additionalDots * dotSize;
898
+ const yFakeBeginning = yBeginning - additionalDots * dotSize;
899
+ const fakeMatrix = [];
900
+ const center = Math.floor(fakeCount / 2);
901
+ for (let i = 0; i < fakeCount; i++) {
902
+ fakeMatrix[i] = [];
903
+ for (let j = 0; j < fakeCount; j++) {
904
+ if (i >= additionalDots - 1 &&
905
+ i <= fakeCount - additionalDots &&
906
+ j >= additionalDots - 1 &&
907
+ j <= fakeCount - additionalDots) {
908
+ fakeMatrix[i][j] = 0;
909
+ continue;
910
+ }
911
+ if (Math.sqrt((i - center) * (i - center) + (j - center) * (j - center)) > center) {
912
+ fakeMatrix[i][j] = 0;
913
+ continue;
914
+ }
915
+ //Get random dots from QR code to show it outside of QR code
916
+ fakeMatrix[i][j] = this._qr.isDark(j - 2 * additionalDots < 0 ? j : j >= count ? j - 2 * additionalDots : j - additionalDots, i - 2 * additionalDots < 0 ? i : i >= count ? i - 2 * additionalDots : i - additionalDots)
917
+ ? 1
918
+ : 0;
919
+ }
920
+ }
921
+ for (let i = 0; i < fakeCount; i++) {
922
+ for (let j = 0; j < fakeCount; j++) {
923
+ if (!fakeMatrix[i][j])
924
+ continue;
925
+ dot.draw(xFakeBeginning + i * dotSize, yFakeBeginning + j * dotSize, dotSize, (xOffset, yOffset) => {
926
+ return !!fakeMatrix[i + xOffset]?.[j + yOffset];
927
+ });
928
+ if (dot._element && this._dotsClipPath) {
929
+ this._dotsClipPath.appendChild(dot._element);
930
+ }
931
+ }
932
+ }
933
+ }
934
+ }
935
+ drawCorners() {
936
+ if (!this._qr) {
937
+ throw "QR code is not defined";
938
+ }
939
+ const element = this._element;
940
+ const options = this._options;
941
+ if (!element) {
942
+ throw "Element code is not defined";
943
+ }
944
+ const count = this._qr.getModuleCount();
945
+ const minSize = Math.min(options.width, options.height) - options.margin * 2;
946
+ const realQRSize = options.shape === exports.ShapeType.circle ? minSize / Math.sqrt(2) : minSize;
947
+ const dotSize = Math.floor(realQRSize / count);
948
+ const cornersSquareSize = dotSize * 7;
949
+ const cornersDotSize = dotSize * 3;
950
+ const xBeginning = Math.floor((options.width - count * dotSize) / 2);
951
+ const yBeginning = Math.floor((options.height - count * dotSize) / 2);
952
+ [
953
+ [0, 0, 0],
954
+ [1, 0, Math.PI / 2],
955
+ [0, 1, -Math.PI / 2]
956
+ ].forEach(([column, row, rotation]) => {
957
+ const x = xBeginning + column * dotSize * (count - 7);
958
+ const y = yBeginning + row * dotSize * (count - 7);
959
+ let cornersSquareClipPath = this._dotsClipPath;
960
+ let cornersDotClipPath = this._dotsClipPath;
961
+ if (options.cornersSquareOptions?.gradient || options.cornersSquareOptions?.color) {
962
+ cornersSquareClipPath = this._document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
963
+ cornersSquareClipPath.setAttribute("id", `clip-path-corners-square-color-${column}-${row}`);
964
+ this._defs.appendChild(cornersSquareClipPath);
965
+ this._cornersSquareClipPath = this._cornersDotClipPath = cornersDotClipPath = cornersSquareClipPath;
966
+ this._createColor({
967
+ options: options.cornersSquareOptions?.gradient,
968
+ color: options.cornersSquareOptions?.color,
969
+ additionalRotation: rotation,
970
+ x,
971
+ y,
972
+ height: cornersSquareSize,
973
+ width: cornersSquareSize,
974
+ name: `corners-square-color-${column}-${row}`
975
+ });
976
+ }
977
+ if (options.cornersSquareOptions?.type) {
978
+ const cornersSquare = new QRCornerSquare({
979
+ svg: this._element,
980
+ type: options.cornersSquareOptions.type,
981
+ document: this._document
982
+ });
983
+ cornersSquare.draw(x, y, cornersSquareSize, rotation);
984
+ if (cornersSquare._element && cornersSquareClipPath) {
985
+ cornersSquareClipPath.appendChild(cornersSquare._element);
986
+ }
987
+ }
988
+ else {
989
+ const dot = new QRDot({ svg: this._element, type: options.dotsOptions.type, document: this._document });
990
+ for (let i = 0; i < squareMask.length; i++) {
991
+ for (let j = 0; j < squareMask[i].length; j++) {
992
+ if (!squareMask[i]?.[j]) {
993
+ continue;
994
+ }
995
+ dot.draw(x + i * dotSize, y + j * dotSize, dotSize, (xOffset, yOffset) => !!squareMask[i + xOffset]?.[j + yOffset]);
996
+ if (dot._element && cornersSquareClipPath) {
997
+ cornersSquareClipPath.appendChild(dot._element);
998
+ }
999
+ }
1000
+ }
1001
+ }
1002
+ if (options.cornersDotOptions?.gradient || options.cornersDotOptions?.color) {
1003
+ cornersDotClipPath = this._document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
1004
+ cornersDotClipPath.setAttribute("id", `clip-path-corners-dot-color-${column}-${row}`);
1005
+ this._defs.appendChild(cornersDotClipPath);
1006
+ this._cornersDotClipPath = cornersDotClipPath;
1007
+ this._createColor({
1008
+ options: options.cornersDotOptions?.gradient,
1009
+ color: options.cornersDotOptions?.color,
1010
+ additionalRotation: rotation,
1011
+ x: x + dotSize * 2,
1012
+ y: y + dotSize * 2,
1013
+ height: cornersDotSize,
1014
+ width: cornersDotSize,
1015
+ name: `corners-dot-color-${column}-${row}`
1016
+ });
1017
+ }
1018
+ if (options.cornersDotOptions?.type) {
1019
+ const cornersDot = new QRCornerDot({
1020
+ svg: this._element,
1021
+ type: options.cornersDotOptions.type,
1022
+ document: this._document
1023
+ });
1024
+ cornersDot.draw(x + dotSize * 2, y + dotSize * 2, cornersDotSize, rotation);
1025
+ if (cornersDot._element && cornersDotClipPath) {
1026
+ cornersDotClipPath.appendChild(cornersDot._element);
1027
+ }
1028
+ }
1029
+ else {
1030
+ const dot = new QRDot({ svg: this._element, type: options.dotsOptions.type, document: this._document });
1031
+ for (let i = 0; i < dotMask.length; i++) {
1032
+ for (let j = 0; j < dotMask[i].length; j++) {
1033
+ if (!dotMask[i]?.[j]) {
1034
+ continue;
1035
+ }
1036
+ dot.draw(x + i * dotSize, y + j * dotSize, dotSize, (xOffset, yOffset) => !!dotMask[i + xOffset]?.[j + yOffset]);
1037
+ if (dot._element && cornersDotClipPath) {
1038
+ cornersDotClipPath.appendChild(dot._element);
1039
+ }
1040
+ }
1041
+ }
1042
+ }
1043
+ });
1044
+ }
1045
+ async drawImage({ width, height, count, dotSize }) {
1046
+ const options = this._options;
1047
+ const xBeginning = Math.floor((options.width - count * dotSize) / 2);
1048
+ const yBeginning = Math.floor((options.height - count * dotSize) / 2);
1049
+ const dx = xBeginning + options.imageOptions.margin + (count * dotSize - width) / 2;
1050
+ const dy = yBeginning + options.imageOptions.margin + (count * dotSize - height) / 2;
1051
+ const dw = width - options.imageOptions.margin * 2;
1052
+ const dh = height - options.imageOptions.margin * 2;
1053
+ const imageUrl = await this._imageTools.toDataURL(options.image || "");
1054
+ let image = this._document.createElementNS("http://www.w3.org/2000/svg", "image");
1055
+ image.setAttribute("href", imageUrl || "");
1056
+ if (imageUrl.startsWith("data:image/svg+xml;")) {
1057
+ let data = imageUrl.substring(19);
1058
+ const i = data.indexOf(",");
1059
+ const encoding = data.substring(0, i);
1060
+ data = data.substring(i + 1);
1061
+ switch (encoding) {
1062
+ case "base64":
1063
+ data = typeof atob == "function" ? atob(data) : Buffer.from(data, "base64").toString("utf8");
1064
+ break;
1065
+ default:
1066
+ data = "";
1067
+ break;
1068
+ }
1069
+ if (data) {
1070
+ const container = this._document.createElement("div");
1071
+ container.innerHTML = data;
1072
+ const rootEl = container.querySelector("svg");
1073
+ if (rootEl)
1074
+ image = rootEl;
1075
+ }
1076
+ }
1077
+ image.setAttribute("x", String(dx));
1078
+ image.setAttribute("y", String(dy));
1079
+ image.setAttribute("width", `${dw}px`);
1080
+ image.setAttribute("height", `${dh}px`);
1081
+ this._element.appendChild(image);
1082
+ }
1083
+ _createColor({ options, color, additionalRotation, x, y, height, width, name }) {
1084
+ const size = width > height ? width : height;
1085
+ const rect = this._document.createElementNS("http://www.w3.org/2000/svg", "rect");
1086
+ rect.setAttribute("x", String(x));
1087
+ rect.setAttribute("y", String(y));
1088
+ rect.setAttribute("height", String(height));
1089
+ rect.setAttribute("width", String(width));
1090
+ rect.setAttribute("clip-path", `url('#clip-path-${name}')`);
1091
+ if (options) {
1092
+ let gradient;
1093
+ if (options.type === exports.GradientType.radial) {
1094
+ gradient = this._document.createElementNS("http://www.w3.org/2000/svg", "radialGradient");
1095
+ gradient.setAttribute("id", name);
1096
+ gradient.setAttribute("gradientUnits", "userSpaceOnUse");
1097
+ gradient.setAttribute("fx", String(x + width / 2));
1098
+ gradient.setAttribute("fy", String(y + height / 2));
1099
+ gradient.setAttribute("cx", String(x + width / 2));
1100
+ gradient.setAttribute("cy", String(y + height / 2));
1101
+ gradient.setAttribute("r", String(size / 2));
1102
+ }
1103
+ else {
1104
+ const rotation = ((options.rotation || 0) + additionalRotation) % (2 * Math.PI);
1105
+ const positiveRotation = (rotation + 2 * Math.PI) % (2 * Math.PI);
1106
+ let x0 = x + width / 2;
1107
+ let y0 = y + height / 2;
1108
+ let x1 = x + width / 2;
1109
+ let y1 = y + height / 2;
1110
+ if ((positiveRotation >= 0 && positiveRotation <= 0.25 * Math.PI) ||
1111
+ (positiveRotation > 1.75 * Math.PI && positiveRotation <= 2 * Math.PI)) {
1112
+ x0 = x0 - width / 2;
1113
+ y0 = y0 - (height / 2) * Math.tan(rotation);
1114
+ x1 = x1 + width / 2;
1115
+ y1 = y1 + (height / 2) * Math.tan(rotation);
1116
+ }
1117
+ else if (positiveRotation > 0.25 * Math.PI && positiveRotation <= 0.75 * Math.PI) {
1118
+ y0 = y0 - height / 2;
1119
+ x0 = x0 - width / 2 / Math.tan(rotation);
1120
+ y1 = y1 + height / 2;
1121
+ x1 = x1 + width / 2 / Math.tan(rotation);
1122
+ }
1123
+ else if (positiveRotation > 0.75 * Math.PI && positiveRotation <= 1.25 * Math.PI) {
1124
+ x0 = x0 + width / 2;
1125
+ y0 = y0 + (height / 2) * Math.tan(rotation);
1126
+ x1 = x1 - width / 2;
1127
+ y1 = y1 - (height / 2) * Math.tan(rotation);
1128
+ }
1129
+ else if (positiveRotation > 1.25 * Math.PI && positiveRotation <= 1.75 * Math.PI) {
1130
+ y0 = y0 + height / 2;
1131
+ x0 = x0 + width / 2 / Math.tan(rotation);
1132
+ y1 = y1 - height / 2;
1133
+ x1 = x1 - width / 2 / Math.tan(rotation);
1134
+ }
1135
+ gradient = this._document.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
1136
+ gradient.setAttribute("id", name);
1137
+ gradient.setAttribute("gradientUnits", "userSpaceOnUse");
1138
+ gradient.setAttribute("x1", String(Math.round(x0)));
1139
+ gradient.setAttribute("y1", String(Math.round(y0)));
1140
+ gradient.setAttribute("x2", String(Math.round(x1)));
1141
+ gradient.setAttribute("y2", String(Math.round(y1)));
1142
+ }
1143
+ options.colorStops.forEach(({ offset, color }) => {
1144
+ const stop = this._document.createElementNS("http://www.w3.org/2000/svg", "stop");
1145
+ stop.setAttribute("offset", `${100 * offset}%`);
1146
+ stop.setAttribute("stop-color", color);
1147
+ gradient.appendChild(stop);
1148
+ });
1149
+ rect.setAttribute("fill", `url('#${name}')`);
1150
+ this._defs.appendChild(gradient);
1151
+ }
1152
+ else if (color) {
1153
+ rect.setAttribute("fill", color);
1154
+ }
1155
+ this._element.appendChild(rect);
1156
+ }
1157
+ }
1158
+
1159
+ class QRCodeStyling$1 {
1160
+ constructor(options) {
1161
+ this._options = options ? sanitizeOptions(mergeDeep(defaultOptions, options)) : defaultOptions;
1162
+ this.update();
1163
+ }
1164
+ static _clearContainer(container) {
1165
+ if (container) {
1166
+ container.innerHTML = "";
1167
+ }
1168
+ }
1169
+ _setupSvg() {
1170
+ if (!this._qr) {
1171
+ return;
1172
+ }
1173
+ const qrSVG = new QRSVG(this._options);
1174
+ this._svg = qrSVG.getElement();
1175
+ this._svgDrawingPromise = qrSVG.drawQR(this._qr).then(() => {
1176
+ if (!this._svg)
1177
+ return;
1178
+ this._extension?.(qrSVG.getElement(), this._options);
1179
+ });
1180
+ }
1181
+ async _getElement() {
1182
+ if (!this._qr)
1183
+ throw "QR code is empty";
1184
+ if (!this._svg || !this._svgDrawingPromise) {
1185
+ this._setupSvg();
1186
+ }
1187
+ await this._svgDrawingPromise;
1188
+ return this._svg;
1189
+ }
1190
+ update(options) {
1191
+ QRCodeStyling$1._clearContainer(this._container);
1192
+ this._options = options ? sanitizeOptions(mergeDeep(this._options, options)) : this._options;
1193
+ if (!this._options.data) {
1194
+ return;
1195
+ }
1196
+ this._qr = qrcode__default["default"](this._options.qrOptions.typeNumber, this._options.qrOptions.errorCorrectionLevel);
1197
+ this._qr.addData(this._options.data, this._options.qrOptions.mode || getMode(this._options.data));
1198
+ this._qr.make();
1199
+ this._setupSvg();
1200
+ this.append(this._container);
1201
+ }
1202
+ append(container) {
1203
+ if (!container) {
1204
+ return;
1205
+ }
1206
+ if (typeof container.appendChild !== "function") {
1207
+ throw "Container should be a single DOM node";
1208
+ }
1209
+ if (this._svg) {
1210
+ container.appendChild(this._svg);
1211
+ }
1212
+ this._container = container;
1213
+ }
1214
+ applyExtension(extension) {
1215
+ if (!extension) {
1216
+ throw "Extension function should be defined.";
1217
+ }
1218
+ this._extension = extension;
1219
+ this.update();
1220
+ }
1221
+ deleteExtension() {
1222
+ this._extension = undefined;
1223
+ this.update();
1224
+ }
1225
+ async serialize() {
1226
+ if (!this._qr)
1227
+ throw "QR code is empty";
1228
+ const element = await this._getElement();
1229
+ if (!element) {
1230
+ return;
1231
+ }
1232
+ const serializer = new XMLSerializer();
1233
+ let source = serializer.serializeToString(element);
1234
+ source = '<?xml version="1.0" standalone="no"?>\r\n' + source;
1235
+ return source;
1236
+ }
1237
+ }
1238
+
1239
+ class QRCodeStyling extends QRCodeStyling$1 {
1240
+ constructor(options) {
1241
+ const dom = new jsdom.JSDOM("<!DOCTYPE html>");
1242
+ const imageCache = new Map();
1243
+ const loadImage = async (url) => {
1244
+ if (typeof url == "string") {
1245
+ let pr = imageCache.get(url);
1246
+ if (pr)
1247
+ return pr;
1248
+ pr = fetch__default["default"](url).then((res) => res
1249
+ .arrayBuffer()
1250
+ .then((buff) => Buffer.from(buff))
1251
+ .then(async (data) => ({
1252
+ contentType: res.headers.get("Content-Type") || (await fileType.fromBuffer(data).then((r) => r?.mime)),
1253
+ data
1254
+ })));
1255
+ imageCache.set(url, pr);
1256
+ return pr;
1257
+ }
1258
+ else {
1259
+ const buff = Buffer.from(url);
1260
+ const mime = await fileType.fromBuffer(buff).then((r) => r?.mime);
1261
+ return {
1262
+ contentType: mime,
1263
+ data: buff
1264
+ };
1265
+ }
1266
+ };
1267
+ global.XMLSerializer = dom.window.XMLSerializer;
1268
+ super({
1269
+ ...options,
1270
+ document: options.document || dom.window.document,
1271
+ imageTools: options.imageTools || {
1272
+ toDataURL: (url) => loadImage(url).then(({ contentType, data }) => `data:${contentType?.replace("application/xml", "image/svg+xml")};base64,${data.toString("base64")}`),
1273
+ getSize: async (options) => {
1274
+ if (!options.image) {
1275
+ throw new Error("Image is not defined");
1276
+ }
1277
+ const { data } = Buffer.isBuffer(options.image) ? { data: options.image } : await loadImage(options.image);
1278
+ const meta = await sharp__default["default"](data).metadata();
1279
+ return {
1280
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1281
+ width: meta.width,
1282
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1283
+ height: meta.height
1284
+ };
1285
+ }
1286
+ }
1287
+ });
1288
+ }
1289
+ }
1290
+
1291
+ exports.ErrorCorrectionPercents = ErrorCorrectionPercents;
1292
+ exports.QRCodeStyling = QRCodeStyling;
1293
+ exports.TypeNumber = TypeNumber;
1294
+ //# sourceMappingURL=qr-code-styling-node.js.map