qreator 9.0.6
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/LICENSE +20 -0
- package/README.md +92 -0
- package/lib/browser/encode.d.ts +2 -0
- package/lib/browser/errorcode.d.ts +1 -0
- package/lib/browser/matrix.d.ts +9 -0
- package/lib/browser/pdf.d.ts +2 -0
- package/lib/browser/pdf.umd.js +26816 -0
- package/lib/browser/pdf.umd.js.map +1 -0
- package/lib/browser/png.d.ts +6 -0
- package/lib/browser/png.umd.js +1540 -0
- package/lib/browser/png.umd.js.map +1 -0
- package/lib/browser/png_browser.d.ts +5 -0
- package/lib/browser/qr-base.d.ts +4 -0
- package/lib/browser/qr.d.ts +2 -0
- package/lib/browser/svg.d.ts +7 -0
- package/lib/browser/svg.umd.js +1536 -0
- package/lib/browser/svg.umd.js.map +1 -0
- package/lib/browser/tests/_common.d.ts +4 -0
- package/lib/browser/tests/browser.test.d.ts +1 -0
- package/lib/browser/tests/test.d.ts +1 -0
- package/lib/browser/typing/types.d.ts +28 -0
- package/lib/browser/utils.d.ts +17 -0
- package/lib/encode.d.ts +2 -0
- package/lib/encode.js +137 -0
- package/lib/encode.js.map +1 -0
- package/lib/errorcode.d.ts +1 -0
- package/lib/errorcode.js +62 -0
- package/lib/errorcode.js.map +1 -0
- package/lib/matrix.d.ts +9 -0
- package/lib/matrix.js +347 -0
- package/lib/matrix.js.map +1 -0
- package/lib/pdf.d.ts +2 -0
- package/lib/pdf.js +64 -0
- package/lib/pdf.js.map +1 -0
- package/lib/png.d.ts +6 -0
- package/lib/png.js +30 -0
- package/lib/png.js.map +1 -0
- package/lib/png_browser.d.ts +5 -0
- package/lib/png_browser.js +53 -0
- package/lib/png_browser.js.map +1 -0
- package/lib/qr-base.d.ts +4 -0
- package/lib/qr-base.js +151 -0
- package/lib/qr-base.js.map +1 -0
- package/lib/qr.d.ts +2 -0
- package/lib/qr.js +2 -0
- package/lib/qr.js.map +1 -0
- package/lib/svg.d.ts +7 -0
- package/lib/svg.js +49 -0
- package/lib/svg.js.map +1 -0
- package/lib/tests/_common.d.ts +4 -0
- package/lib/tests/_common.js +36 -0
- package/lib/tests/_common.js.map +1 -0
- package/lib/tests/browser.test.d.ts +1 -0
- package/lib/tests/browser.test.js +193 -0
- package/lib/tests/browser.test.js.map +1 -0
- package/lib/tests/test.d.ts +1 -0
- package/lib/tests/test.js +238 -0
- package/lib/tests/test.js.map +1 -0
- package/lib/typing/types.d.ts +28 -0
- package/lib/typing/types.js +2 -0
- package/lib/typing/types.js.map +1 -0
- package/lib/utils.d.ts +17 -0
- package/lib/utils.js +68 -0
- package/lib/utils.js.map +1 -0
- package/package.json +105 -0
- package/src/encode.ts +176 -0
- package/src/errorcode.ts +75 -0
- package/src/matrix.ts +393 -0
- package/src/pdf.ts +80 -0
- package/src/png.ts +42 -0
- package/src/png_browser.ts +73 -0
- package/src/qr-base.ts +180 -0
- package/src/qr.ts +2 -0
- package/src/svg.ts +89 -0
- package/src/tests/_common.ts +40 -0
- package/src/tests/browser.test.ts +209 -0
- package/src/tests/test.ts +250 -0
- package/src/typing/index.d.ts +5 -0
- package/src/typing/types.ts +99 -0
- package/src/utils.ts +86 -0
package/src/qr-base.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { encode } from "./encode.js";
|
|
2
|
+
import { calculateEC } from "./errorcode.js";
|
|
3
|
+
import { getMatrix } from "./matrix.js";
|
|
4
|
+
import { Data, EcLevel, NumberData } from "./typing/types";
|
|
5
|
+
|
|
6
|
+
interface LevelNumber {
|
|
7
|
+
[key: string]: Pick<Data, "data_len" | "ec_level">;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const EC_LEVELS: EcLevel[] = ["L", "M", "Q", "H"];
|
|
11
|
+
|
|
12
|
+
// {{{1 Get version template
|
|
13
|
+
export function getTemplate(message: NumberData, ec_level: EcLevel): Data {
|
|
14
|
+
let i = 1;
|
|
15
|
+
let len;
|
|
16
|
+
|
|
17
|
+
if (message.data1) {
|
|
18
|
+
len = Math.ceil(message.data1.length / 8);
|
|
19
|
+
} else {
|
|
20
|
+
i = 10;
|
|
21
|
+
}
|
|
22
|
+
for (; /* i */ i < 10; i++) {
|
|
23
|
+
let version = mappedVersions[i][ec_level];
|
|
24
|
+
if (version.data_len >= len) {
|
|
25
|
+
return deepCopy(version);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (message.data10) {
|
|
30
|
+
len = Math.ceil(message.data10.length / 8);
|
|
31
|
+
} else {
|
|
32
|
+
i = 27;
|
|
33
|
+
}
|
|
34
|
+
for (; /* i */ i < 27; i++) {
|
|
35
|
+
let version = mappedVersions[i][ec_level];
|
|
36
|
+
if (version.data_len >= len) {
|
|
37
|
+
return deepCopy(version);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
len = Math.ceil(message.data27.length / 8);
|
|
42
|
+
for (; /* i */ i < 41; i++) {
|
|
43
|
+
let version = mappedVersions[i][ec_level];
|
|
44
|
+
if (version.data_len >= len) {
|
|
45
|
+
return deepCopy(version);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
throw new Error("Too much data");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// {{{1 Fill template
|
|
52
|
+
export function fillTemplate(message: NumberData, template: Data): Data {
|
|
53
|
+
const blocks = new Uint8Array(template.data_len);
|
|
54
|
+
let messageUpdated: number[];
|
|
55
|
+
|
|
56
|
+
if (template.version < 10) {
|
|
57
|
+
messageUpdated = message.data1;
|
|
58
|
+
} else if (template.version < 27) {
|
|
59
|
+
messageUpdated = message.data10;
|
|
60
|
+
} else {
|
|
61
|
+
messageUpdated = message.data27;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const len = messageUpdated.length;
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < len; i += 8) {
|
|
67
|
+
let b = 0;
|
|
68
|
+
for (let j = 0; j < 8; j++) {
|
|
69
|
+
b = (b << 1) | (messageUpdated[i + j] ? 1 : 0);
|
|
70
|
+
}
|
|
71
|
+
blocks[i / 8] = b;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let pad = 236;
|
|
75
|
+
for (let i = Math.ceil((len + 4) / 8); i < blocks.length; i++) {
|
|
76
|
+
blocks[i] = pad;
|
|
77
|
+
pad = pad == 236 ? 17 : 236;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let offset = 0;
|
|
81
|
+
// TODO any
|
|
82
|
+
template.blocks = template.blocks.map((n: any) => {
|
|
83
|
+
const b = blocks.slice(offset, offset + n) as unknown as number[];
|
|
84
|
+
offset += n;
|
|
85
|
+
template.ec.push(
|
|
86
|
+
calculateEC(b, template.ec_len) as unknown as number[]
|
|
87
|
+
);
|
|
88
|
+
return b;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return template;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// {{{1 All-in-one
|
|
95
|
+
export function QR(text: string, ec_level: EcLevel, parse_url: boolean) {
|
|
96
|
+
ec_level = EC_LEVELS.includes(ec_level) ? ec_level : "M";
|
|
97
|
+
const message = encode(text, parse_url);
|
|
98
|
+
const data = fillTemplate(message, getTemplate(message, ec_level));
|
|
99
|
+
return getMatrix(data);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const deepCopy = typeof structuredClone !== "undefined" ? structuredClone : ((obj: object) => JSON.parse(JSON.stringify(obj))) as typeof structuredClone;
|
|
103
|
+
|
|
104
|
+
// {{{1 Versions
|
|
105
|
+
const versions: (number[] | LevelNumber | {})[] = [
|
|
106
|
+
[], // there is no version 0
|
|
107
|
+
// total number of codewords, (number of ec codewords, number of blocks) * ( L, M, Q, H )
|
|
108
|
+
[26, 7, 1, 10, 1, 13, 1, 17, 1],
|
|
109
|
+
[44, 10, 1, 16, 1, 22, 1, 28, 1],
|
|
110
|
+
[70, 15, 1, 26, 1, 36, 2, 44, 2],
|
|
111
|
+
[100, 20, 1, 36, 2, 52, 2, 64, 4],
|
|
112
|
+
[134, 26, 1, 48, 2, 72, 4, 88, 4], // 5
|
|
113
|
+
[172, 36, 2, 64, 4, 96, 4, 112, 4],
|
|
114
|
+
[196, 40, 2, 72, 4, 108, 6, 130, 5],
|
|
115
|
+
[242, 48, 2, 88, 4, 132, 6, 156, 6],
|
|
116
|
+
[292, 60, 2, 110, 5, 160, 8, 192, 8],
|
|
117
|
+
[346, 72, 4, 130, 5, 192, 8, 224, 8], // 10
|
|
118
|
+
[404, 80, 4, 150, 5, 224, 8, 264, 11],
|
|
119
|
+
[466, 96, 4, 176, 8, 260, 10, 308, 11],
|
|
120
|
+
[532, 104, 4, 198, 9, 288, 12, 352, 16],
|
|
121
|
+
[581, 120, 4, 216, 9, 320, 16, 384, 16],
|
|
122
|
+
[655, 132, 6, 240, 10, 360, 12, 432, 18], // 15
|
|
123
|
+
[733, 144, 6, 280, 10, 408, 17, 480, 16],
|
|
124
|
+
[815, 168, 6, 308, 11, 448, 16, 532, 19],
|
|
125
|
+
[901, 180, 6, 338, 13, 504, 18, 588, 21],
|
|
126
|
+
[991, 196, 7, 364, 14, 546, 21, 650, 25],
|
|
127
|
+
[1085, 224, 8, 416, 16, 600, 20, 700, 25], // 20
|
|
128
|
+
[1156, 224, 8, 442, 17, 644, 23, 750, 25],
|
|
129
|
+
[1258, 252, 9, 476, 17, 690, 23, 816, 34],
|
|
130
|
+
[1364, 270, 9, 504, 18, 750, 25, 900, 30],
|
|
131
|
+
[1474, 300, 10, 560, 20, 810, 27, 960, 32],
|
|
132
|
+
[1588, 312, 12, 588, 21, 870, 29, 1050, 35], // 25
|
|
133
|
+
[1706, 336, 12, 644, 23, 952, 34, 1110, 37],
|
|
134
|
+
[1828, 360, 12, 700, 25, 1020, 34, 1200, 40],
|
|
135
|
+
[1921, 390, 13, 728, 26, 1050, 35, 1260, 42],
|
|
136
|
+
[2051, 420, 14, 784, 28, 1140, 38, 1350, 45],
|
|
137
|
+
[2185, 450, 15, 812, 29, 1200, 40, 1440, 48], // 30
|
|
138
|
+
[2323, 480, 16, 868, 31, 1290, 43, 1530, 51],
|
|
139
|
+
[2465, 510, 17, 924, 33, 1350, 45, 1620, 54],
|
|
140
|
+
[2611, 540, 18, 980, 35, 1440, 48, 1710, 57],
|
|
141
|
+
[2761, 570, 19, 1036, 37, 1530, 51, 1800, 60],
|
|
142
|
+
[2876, 570, 19, 1064, 38, 1590, 53, 1890, 63], // 35
|
|
143
|
+
[3034, 600, 20, 1120, 40, 1680, 56, 1980, 66],
|
|
144
|
+
[3196, 630, 21, 1204, 43, 1770, 59, 2100, 70],
|
|
145
|
+
[3362, 660, 22, 1260, 45, 1860, 62, 2220, 74],
|
|
146
|
+
[3532, 720, 24, 1316, 47, 1950, 65, 2310, 77],
|
|
147
|
+
[3706, 750, 25, 1372, 49, 2040, 68, 2430, 81], // 40
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
const mappedVersions = versions.map((el, index: number) => {
|
|
151
|
+
if (!index) {
|
|
152
|
+
return Object.create(null);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const res = Object.create(null) as {
|
|
156
|
+
[K in EcLevel]: Omit<Data, "blocks"> & { blocks: number[] };
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
for (let i = 1; i < 8; i += 2) {
|
|
160
|
+
const length = (el as number[])[0] - (el as number[])[i];
|
|
161
|
+
const num_template = (el as number[])[i + 1];
|
|
162
|
+
const ec_level = EC_LEVELS[(i / 2) | 0];
|
|
163
|
+
const level: Omit<Data, "blocks"> & { blocks: number[] } = {
|
|
164
|
+
version: index,
|
|
165
|
+
ec_level,
|
|
166
|
+
data_len: length,
|
|
167
|
+
ec_len: (el as number[])[i] / num_template,
|
|
168
|
+
blocks: [],
|
|
169
|
+
ec: [],
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
for (let k = num_template, n = length; k > 0; k--) {
|
|
173
|
+
const block = (n / k) | 0;
|
|
174
|
+
level.blocks.push(block);
|
|
175
|
+
n -= block;
|
|
176
|
+
}
|
|
177
|
+
res[ec_level] = level;
|
|
178
|
+
}
|
|
179
|
+
return res;
|
|
180
|
+
});
|
package/src/qr.ts
ADDED
package/src/svg.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { QR } from "./qr-base.js";
|
|
2
|
+
import { ImageOptions, Matrix } from "./typing/types";
|
|
3
|
+
import { getOptions, colorToHex, getSVGPath } from "./utils.js";
|
|
4
|
+
import { Base64 } from 'js-base64';
|
|
5
|
+
|
|
6
|
+
interface FillSVGOptions
|
|
7
|
+
extends Pick<ImageOptions, "color" | "bgColor" | "size" | "margin" | "borderRadius"> {
|
|
8
|
+
blockSize?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function getSVG(text: string, inOptions: ImageOptions = {}) {
|
|
12
|
+
const options = getOptions({...inOptions, type: "svg"});
|
|
13
|
+
const matrix = QR(text, options.ec_level, options.parse_url);
|
|
14
|
+
return createSVG({ matrix, ...options });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const te = new TextEncoder();
|
|
18
|
+
|
|
19
|
+
export async function createSVG({
|
|
20
|
+
matrix,
|
|
21
|
+
margin,
|
|
22
|
+
size,
|
|
23
|
+
logo,
|
|
24
|
+
logoWidth,
|
|
25
|
+
logoHeight,
|
|
26
|
+
color,
|
|
27
|
+
bgColor,
|
|
28
|
+
imageWidth,
|
|
29
|
+
imageHeight,
|
|
30
|
+
borderRadius,
|
|
31
|
+
}: ImageOptions & {
|
|
32
|
+
matrix: Matrix;
|
|
33
|
+
imageWidth?: number;
|
|
34
|
+
imageHeight?: number;
|
|
35
|
+
}): Promise<Uint8Array> {
|
|
36
|
+
const actualSize = size || 9;
|
|
37
|
+
const X = matrix.length + 2 * margin;
|
|
38
|
+
const XY = X * (actualSize || 1);
|
|
39
|
+
const imageWidthStr = imageWidth ? ` width="${imageWidth}"` : "";
|
|
40
|
+
const imageHeightStr = imageHeight ? `height="${imageWidth}" ` : "";
|
|
41
|
+
const xmlTag = `<?xml version="1.0" encoding="utf-8"?>`;
|
|
42
|
+
const svgOpeningTag = `<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink"${imageWidthStr} ${imageHeightStr}viewBox="0 0 ${XY} ${XY}">`;
|
|
43
|
+
const svgBody = getSVGBody(matrix, {
|
|
44
|
+
color,
|
|
45
|
+
bgColor,
|
|
46
|
+
size: XY,
|
|
47
|
+
margin,
|
|
48
|
+
blockSize: actualSize,
|
|
49
|
+
borderRadius,
|
|
50
|
+
});
|
|
51
|
+
const svgEndTag = "</svg>";
|
|
52
|
+
const logoImage = logo ? getLogoImage(logo, XY, logoWidth, logoHeight) : "";
|
|
53
|
+
|
|
54
|
+
return te.encode(
|
|
55
|
+
xmlTag + svgOpeningTag + svgBody + logoImage + svgEndTag
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getSVGBody(matrix: Matrix, options: FillSVGOptions): string {
|
|
60
|
+
const path = getSVGPath(matrix, options.blockSize, options.margin * options.blockSize, options.borderRadius);
|
|
61
|
+
let svgBody =
|
|
62
|
+
`<rect width="${options.size}" height="${options.size}" ` +
|
|
63
|
+
`fill="${colorToHex(options.bgColor)}"></rect>`;
|
|
64
|
+
svgBody += '<path shape-rendering="geometricPrecision" d="' + path + '" fill="' + colorToHex(options.color) + '"/>';
|
|
65
|
+
return svgBody;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getLogoImage(
|
|
69
|
+
logo: ImageOptions["logo"],
|
|
70
|
+
XY: number,
|
|
71
|
+
logoWidth: ImageOptions["logoWidth"],
|
|
72
|
+
logoHeight: ImageOptions["logoHeight"]
|
|
73
|
+
): string {
|
|
74
|
+
const imageBase64 = `data:image/png;base64,${
|
|
75
|
+
typeof Buffer !== "undefined" && Buffer.isBuffer(logo)
|
|
76
|
+
? logo.toString("base64")
|
|
77
|
+
: Base64.fromUint8Array(new Uint8Array(logo))
|
|
78
|
+
}`;
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
`<image ` +
|
|
82
|
+
`width="${(logoWidth / 100) * XY}" ` +
|
|
83
|
+
`height="${(logoHeight / 100) * XY}" ` +
|
|
84
|
+
`xlink:href="${imageBase64}" ` +
|
|
85
|
+
`x="${XY / 2 - ((logoWidth / 100) * XY) / 2}" ` +
|
|
86
|
+
`y="${XY / 2 - ((logoHeight / 100) * XY) / 2}">` +
|
|
87
|
+
`</image>`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ExecutionContext } from "ava";
|
|
2
|
+
import looksSame from "looks-same";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { dirname } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import * as diff from "diff";
|
|
7
|
+
import XMLFormatter from "xml-formatter";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
chalk.level = 3;
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
12
|
+
const __dirname = dirname(__filename)
|
|
13
|
+
|
|
14
|
+
export const goldenDir = `${__dirname}/../../test_data/golden`;
|
|
15
|
+
export const generatedImageDir = `${__dirname}/../../test_data/generated`;
|
|
16
|
+
|
|
17
|
+
export const assertEqual = async (t: ExecutionContext<unknown>, filename: string) => {
|
|
18
|
+
if (filename.endsWith(".png")) {
|
|
19
|
+
const lsRes = await looksSame(
|
|
20
|
+
`${generatedImageDir}/${filename}`,
|
|
21
|
+
`${goldenDir}/${filename}`,
|
|
22
|
+
{ strict: true }
|
|
23
|
+
);
|
|
24
|
+
t.assert(lsRes.equal, `Images are different: ${filename}`);
|
|
25
|
+
|
|
26
|
+
} else if (!filename.endsWith("pdf")) {
|
|
27
|
+
const f1 = (await readFile(`${generatedImageDir}/${filename}`)).toString();
|
|
28
|
+
const f2 = (await readFile(`${goldenDir}/${filename}`)).toString();
|
|
29
|
+
if (f1 !== f2) {
|
|
30
|
+
for (const el of diff.diffLines(XMLFormatter(f1), XMLFormatter(f2), { newlineIsToken: true })) {
|
|
31
|
+
console.log(el.added ? chalk.bold.green(el.value) : chalk.bold.red(el.value));
|
|
32
|
+
};
|
|
33
|
+
t.fail(`Files are different: ${generatedImageDir}/${filename} and ${goldenDir}/${filename}`);
|
|
34
|
+
} else {
|
|
35
|
+
t.pass();
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
t.pass();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import test from "ava";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { writeFile } from "node:fs/promises";
|
|
4
|
+
import { ImageType } from '../typing/types.js';
|
|
5
|
+
import { QRImageOptions } from '../qr.js';
|
|
6
|
+
import { JSDOM } from 'jsdom';
|
|
7
|
+
import { assertEqual, generatedImageDir, goldenDir } from "./_common.js";
|
|
8
|
+
import { Path2D } from "canvas";
|
|
9
|
+
|
|
10
|
+
let functions: Record<ImageType, (text: string, options: QRImageOptions) => Promise<ArrayBuffer>>;
|
|
11
|
+
const { window } = new JSDOM(``, { runScripts: "dangerously", resources: "usable" });
|
|
12
|
+
|
|
13
|
+
test.before(async () => {
|
|
14
|
+
window.globalThis.TextEncoder = TextEncoder;
|
|
15
|
+
window.globalThis.TextDecoder = TextDecoder;
|
|
16
|
+
window.globalThis.Path2D = Path2D;
|
|
17
|
+
|
|
18
|
+
for (const scriptType of ['png', 'svg', 'pdf']) {
|
|
19
|
+
const scriptEl = window.document.createElement('script')
|
|
20
|
+
scriptEl.textContent = readFileSync(`./lib/browser/${scriptType}.umd.js`).toString()
|
|
21
|
+
scriptEl.type = 'text/javascript'
|
|
22
|
+
window.document.body.appendChild(scriptEl);
|
|
23
|
+
}
|
|
24
|
+
functions = {
|
|
25
|
+
"png": window.globalThis.pngQrCode.getPNG,
|
|
26
|
+
"svg": window.globalThis.svgQrCode.getSVG,
|
|
27
|
+
"pdf": window.globalThis.pdfQrCode.getPDF,
|
|
28
|
+
};
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
const text = "I \u2764\uFE0F QR code!";
|
|
33
|
+
// const text = 'https://yadi.sk/d/FuzPeEg-QyaZN?qr';
|
|
34
|
+
interface TestParams {
|
|
35
|
+
name: string;
|
|
36
|
+
type: ImageType;
|
|
37
|
+
filename: string;
|
|
38
|
+
params: QRImageOptions;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const defaultParams = {
|
|
42
|
+
ec_level: "Q" as const,
|
|
43
|
+
margin: 1,
|
|
44
|
+
parse_url: true,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
([
|
|
48
|
+
{
|
|
49
|
+
name: "PNG",
|
|
50
|
+
type: "png",
|
|
51
|
+
filename: "qr.png",
|
|
52
|
+
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "PNG with border radius",
|
|
56
|
+
type: "png",
|
|
57
|
+
filename: "qr_with_border_radius.png",
|
|
58
|
+
params: {
|
|
59
|
+
borderRadius: 3,
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "PNG with colors",
|
|
64
|
+
type: "png",
|
|
65
|
+
filename: "qr_with_colors.png",
|
|
66
|
+
params: {
|
|
67
|
+
color: 0x0000a0ff,
|
|
68
|
+
bgColor: 0xffa0ffff,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "PNG with colors (hex)",
|
|
73
|
+
type: "png",
|
|
74
|
+
filename: "qr_with_colors.png",
|
|
75
|
+
params: {
|
|
76
|
+
color: '#0000a0',
|
|
77
|
+
bgColor: '#ffa0ff',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "PNG with logo (PNG)",
|
|
82
|
+
type: "png",
|
|
83
|
+
filename: "qr_with_logo.png",
|
|
84
|
+
params: { logo: readFileSync(`${goldenDir}/logo.png`).buffer },
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "PNG with logo (JPG)",
|
|
88
|
+
type: "png",
|
|
89
|
+
filename: "qr_with_logo_jpg.png",
|
|
90
|
+
params: { logo: readFileSync(`${goldenDir}/logo.jpg`).buffer },
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "PNG with colors (rgba)",
|
|
94
|
+
type: "png",
|
|
95
|
+
filename: "qr_with_colors_rgba.png",
|
|
96
|
+
params: {
|
|
97
|
+
color: 'rgba(255, 0, 0, 0.5)',
|
|
98
|
+
bgColor: 'rgba(255, 255, 255, 0.1)',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "SVG",
|
|
103
|
+
type: "svg",
|
|
104
|
+
filename: "qr.svg",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "SVG with border radius",
|
|
108
|
+
type: "svg",
|
|
109
|
+
filename: "qr_with_border_radius.svg",
|
|
110
|
+
params: {
|
|
111
|
+
borderRadius: 2,
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "SVG with EC level",
|
|
116
|
+
type: "svg",
|
|
117
|
+
filename: "qr_with_ec_level.svg",
|
|
118
|
+
params: {
|
|
119
|
+
ec_level: "H",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "SVG with size",
|
|
124
|
+
type: "svg",
|
|
125
|
+
filename: "qr_with_size.svg",
|
|
126
|
+
params: {
|
|
127
|
+
size: 6,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "SVG with colors (hex)",
|
|
132
|
+
type: "svg",
|
|
133
|
+
filename: "qr_with_colors_hex.svg",
|
|
134
|
+
params: {
|
|
135
|
+
color: '#ff0000',
|
|
136
|
+
bgColor: '#00ff00',
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "SVG with colors (number)",
|
|
141
|
+
type: "svg",
|
|
142
|
+
filename: "qr_with_colors.svg",
|
|
143
|
+
params: {
|
|
144
|
+
color: 0xff0000ff,
|
|
145
|
+
bgColor: 0x00ff00ff,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "SVG with logo as arraybuffer (PNG)",
|
|
150
|
+
type: "svg",
|
|
151
|
+
filename: "qr_with_logo_as_arraybuffer.svg",
|
|
152
|
+
params: {
|
|
153
|
+
logo: readFileSync(`${goldenDir}/logo.png`).buffer,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "SVG with logo as arraybuffer (JPG)",
|
|
158
|
+
type: "svg",
|
|
159
|
+
filename: "qr_with_logo_as_arraybuffer_jpg.svg",
|
|
160
|
+
params: {
|
|
161
|
+
logo: readFileSync(`${goldenDir}/logo.jpg`).buffer,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "PDF",
|
|
166
|
+
type: "pdf",
|
|
167
|
+
filename: "qr.pdf",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: "PDF with border radius",
|
|
171
|
+
type: "pdf",
|
|
172
|
+
filename: "qr_with_border_radius.pdf",
|
|
173
|
+
params: {
|
|
174
|
+
borderRadius: 2,
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "PDF with colors",
|
|
179
|
+
type: "pdf",
|
|
180
|
+
filename: "qr_with_colors.pdf",
|
|
181
|
+
params: { color: 0xff0000ff, bgColor: 0x00ff00ff },
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "PDF with arraybuffer",
|
|
185
|
+
type: "pdf",
|
|
186
|
+
filename: "qr_logo_arraybuffer.pdf",
|
|
187
|
+
params: {
|
|
188
|
+
logo: new window.Uint8Array(readFileSync(`${goldenDir}/logo.png`).buffer),
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "PDF with arraybuffer (JPG)",
|
|
193
|
+
type: "pdf",
|
|
194
|
+
filename: "qr_logo_arraybuffer.pdf",
|
|
195
|
+
params: {
|
|
196
|
+
logo: new window.Uint8Array(readFileSync(`${goldenDir}/logo.jpg`).buffer),
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
] as TestParams[]) .forEach((testData) => {
|
|
200
|
+
test(`browser > ${testData.name}`, async (t) => {
|
|
201
|
+
const image = await functions[testData.type](text, {
|
|
202
|
+
type: testData.type,
|
|
203
|
+
...defaultParams,
|
|
204
|
+
...testData.params,
|
|
205
|
+
});
|
|
206
|
+
await writeFile(`${generatedImageDir}/browser_${testData.filename}`, new Uint8Array(image));
|
|
207
|
+
await assertEqual(t, 'browser_' + testData.filename);
|
|
208
|
+
});
|
|
209
|
+
});
|