somdigi-qr-generator 1.0.2 → 1.0.4
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/index.js +2 -2
- package/package.json +1 -1
- package/src/core.js +72 -54
- package/src/svg-helper.js +0 -49
package/index.js
CHANGED
package/package.json
CHANGED
package/src/core.js
CHANGED
|
@@ -1,77 +1,95 @@
|
|
|
1
1
|
const QRCode = require("qrcode");
|
|
2
2
|
const sharp = require("sharp");
|
|
3
|
-
const fs = require("fs/promises");
|
|
4
|
-
const path = require("path");
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
4
|
+
class QRGenerator {
|
|
5
|
+
/**
|
|
6
|
+
* Generate QR Buffer
|
|
7
|
+
* @param {Object} options
|
|
8
|
+
* @param {string} options.data (required)
|
|
9
|
+
* @param {"default"|"rounded"} options.type
|
|
10
|
+
* @param {number} options.size
|
|
11
|
+
* @param {string[]} options.gradient
|
|
12
|
+
* @param {string} options.logo (url / base64)
|
|
13
|
+
*/
|
|
14
|
+
static async generate(options) {
|
|
15
|
+
if (!options || !options.data) {
|
|
16
|
+
throw new Error("data is required");
|
|
17
|
+
}
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
// 🔵 DEFAULT QR (FASTEST)
|
|
20
|
+
if (!options.type || options.type === "default") {
|
|
21
|
+
return QRCode.toBuffer(options.data, {
|
|
22
|
+
width: options.size ?? 512,
|
|
23
|
+
margin: 2
|
|
24
|
+
});
|
|
25
|
+
}
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
// 🔵 ROUNDED QR
|
|
28
|
+
if (options.type === "rounded") {
|
|
29
|
+
return this._rounded(options);
|
|
30
|
+
}
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
throw new Error("invalid type");
|
|
33
|
+
}
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
:
|
|
35
|
+
static async _rounded(options) {
|
|
36
|
+
const rawSVG = await QRCode.toString(options.data, {
|
|
37
|
+
type: "svg",
|
|
38
|
+
errorCorrectionLevel: "H",
|
|
39
|
+
margin: 1
|
|
40
|
+
});
|
|
32
41
|
|
|
33
|
-
const
|
|
34
|
-
|
|
42
|
+
const viewBox = rawSVG.match(/viewBox="([^"]+)"/)[1];
|
|
43
|
+
const paths = rawSVG.match(/<path[^>]*>/g) || [];
|
|
44
|
+
|
|
45
|
+
const fill = options.gradient?.length
|
|
46
|
+
? "url(#g)"
|
|
47
|
+
: "#000";
|
|
48
|
+
|
|
49
|
+
const defs = options.gradient?.length
|
|
50
|
+
? `
|
|
51
|
+
<defs>
|
|
52
|
+
<linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
53
|
+
${options.gradient
|
|
54
|
+
.map(
|
|
55
|
+
(c, i) =>
|
|
56
|
+
`<stop offset="${(i / (options.gradient.length - 1)) * 100}%" stop-color="${c}" />`
|
|
57
|
+
)
|
|
58
|
+
.join("")}
|
|
59
|
+
</linearGradient>
|
|
60
|
+
</defs>`
|
|
35
61
|
: "";
|
|
36
62
|
|
|
37
|
-
const dots =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
63
|
+
const dots = paths
|
|
64
|
+
.map(p => {
|
|
65
|
+
const m = p.match(/M(\d+) (\d+)/);
|
|
66
|
+
if (!m) return "";
|
|
67
|
+
return `<circle cx="${+m[1] + 0.5}" cy="${+m[2] + 0.5}" r="0.45" fill="${fill}" />`;
|
|
68
|
+
})
|
|
69
|
+
.join("");
|
|
42
70
|
|
|
43
71
|
const logo = options.logo
|
|
44
|
-
?
|
|
72
|
+
? `
|
|
73
|
+
<image
|
|
74
|
+
href="${options.logo}"
|
|
75
|
+
x="35%" y="35%"
|
|
76
|
+
width="30%" height="30%"
|
|
77
|
+
preserveAspectRatio="xMidYMid meet"
|
|
78
|
+
/>`
|
|
45
79
|
: "";
|
|
46
80
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
81
|
+
const svg = `
|
|
82
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
|
83
|
+
width="${options.size ?? 512}"
|
|
84
|
+
height="${options.size ?? 512}"
|
|
85
|
+
viewBox="${viewBox}">
|
|
50
86
|
${defs}
|
|
51
87
|
${dots}
|
|
52
88
|
${logo}
|
|
53
89
|
</svg>`;
|
|
54
|
-
}
|
|
55
90
|
|
|
56
|
-
static async toPNG(text, options = {}) {
|
|
57
|
-
const svg = await this.toSVG(text, options);
|
|
58
91
|
return sharp(Buffer.from(svg)).png().toBuffer();
|
|
59
92
|
}
|
|
60
|
-
|
|
61
|
-
static async toFile(text, filename, options = {}) {
|
|
62
|
-
const dir = path.join(process.cwd(), "storage/qr-codes");
|
|
63
|
-
await fs.mkdir(dir, { recursive: true });
|
|
64
|
-
|
|
65
|
-
const fullPath = path.join(dir, filename);
|
|
66
|
-
|
|
67
|
-
if (filename.endsWith(".svg")) {
|
|
68
|
-
await fs.writeFile(fullPath, await this.toSVG(text, options));
|
|
69
|
-
} else {
|
|
70
|
-
await fs.writeFile(fullPath, await this.toPNG(text, options));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return fullPath;
|
|
74
|
-
}
|
|
75
93
|
}
|
|
76
94
|
|
|
77
|
-
module.exports =
|
|
95
|
+
module.exports = QRGenerator;
|
package/src/svg-helper.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
function extractPaths(svg) {
|
|
2
|
-
return svg.match(/<path[^>]*>/g) || [];
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function buildGradient(id, colors) {
|
|
6
|
-
return `
|
|
7
|
-
<defs>
|
|
8
|
-
<linearGradient id="${id}" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
9
|
-
${colors
|
|
10
|
-
.map(
|
|
11
|
-
(c, i) =>
|
|
12
|
-
`<stop offset="${(i / (colors.length - 1)) * 100}%" stop-color="${c}" />`
|
|
13
|
-
)
|
|
14
|
-
.join("")}
|
|
15
|
-
</linearGradient>
|
|
16
|
-
</defs>`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function pathsToDots(paths, radius, fill) {
|
|
20
|
-
return paths
|
|
21
|
-
.map(p => {
|
|
22
|
-
const m = p.match(/M(\d+) (\d+)/);
|
|
23
|
-
if (!m) return "";
|
|
24
|
-
const x = Number(m[1]) + radius;
|
|
25
|
-
const y = Number(m[2]) + radius;
|
|
26
|
-
return `<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" />`;
|
|
27
|
-
})
|
|
28
|
-
.join("");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function insertLogo(logo, size = 0.25) {
|
|
32
|
-
const percent = size * 100;
|
|
33
|
-
return `
|
|
34
|
-
<image
|
|
35
|
-
href="${logo}"
|
|
36
|
-
x="${50 - percent / 2}%"
|
|
37
|
-
y="${50 - percent / 2}%"
|
|
38
|
-
width="${percent}%"
|
|
39
|
-
height="${percent}%"
|
|
40
|
-
preserveAspectRatio="xMidYMid meet"
|
|
41
|
-
/>`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
module.exports = {
|
|
45
|
-
extractPaths,
|
|
46
|
-
buildGradient,
|
|
47
|
-
pathsToDots,
|
|
48
|
-
insertLogo
|
|
49
|
-
};
|