somdigi-qr-generator 1.0.0 → 1.0.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.
- package/index.js +2 -4
- package/package.json +3 -4
- package/src/core.js +64 -45
- package/src/svg-helper.js +50 -0
- package/src/presets.js +0 -48
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "somdigi-qr-generator",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"description": "",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"qr-code-styling": "^1.9.2"
|
|
13
|
+
"qrcode": "^1.5.4",
|
|
14
|
+
"sharp": "^0.34.5"
|
|
16
15
|
}
|
|
17
16
|
}
|
package/src/core.js
CHANGED
|
@@ -1,61 +1,80 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
1
|
+
const QRCode = require("qrcode");
|
|
2
|
+
const sharp = require("sharp");
|
|
3
|
+
const fs = require("fs/promises");
|
|
4
|
+
const path = require("path");
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
const {
|
|
7
|
+
buildGradient,
|
|
8
|
+
squareToDot,
|
|
9
|
+
insertLogo
|
|
10
|
+
} = require("./svg-helper");
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
class FastDotQR {
|
|
13
|
+
static async toSVG(text, options = {}) {
|
|
14
|
+
if (!text) throw new Error("text required");
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
const rawSVG = await QRCode.toString(text, {
|
|
17
|
+
type: "svg",
|
|
18
|
+
errorCorrectionLevel: "H",
|
|
19
|
+
margin: options.margin ?? 1
|
|
20
|
+
});
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
const gradientId = "qrGradient";
|
|
23
|
+
const gradient =
|
|
24
|
+
options.gradient?.length > 1
|
|
25
|
+
? buildGradient(gradientId, options.gradient)
|
|
26
|
+
: "";
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
const fill =
|
|
29
|
+
options.gradient?.length > 1
|
|
30
|
+
? `url(#${gradientId})`
|
|
31
|
+
: options.color ?? "#000";
|
|
23
32
|
|
|
24
|
-
|
|
33
|
+
let content = squareToDot(
|
|
34
|
+
rawSVG,
|
|
35
|
+
options.radius ?? 1.4,
|
|
36
|
+
fill
|
|
37
|
+
);
|
|
25
38
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
if (options.logo) {
|
|
40
|
+
content = insertLogo(
|
|
41
|
+
content,
|
|
42
|
+
options.logo,
|
|
43
|
+
options.logoSize ?? 0.25
|
|
44
|
+
);
|
|
45
|
+
}
|
|
31
46
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
return rawSVG
|
|
48
|
+
.replace("</svg>", "")
|
|
49
|
+
.replace(
|
|
50
|
+
"<svg",
|
|
51
|
+
`<svg xmlns="http://www.w3.org/2000/svg">`
|
|
52
|
+
)
|
|
53
|
+
.replace(">", `>${gradient}`)
|
|
54
|
+
.replace("</svg>", `${content}</svg>`);
|
|
55
|
+
}
|
|
36
56
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
57
|
+
static async toPNG(text, options = {}) {
|
|
58
|
+
const svg = await this.toSVG(text, options);
|
|
59
|
+
return sharp(Buffer.from(svg)).png().toBuffer();
|
|
60
|
+
}
|
|
41
61
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
},
|
|
62
|
+
static async toFile(text, filename, options = {}) {
|
|
63
|
+
const dir = path.join(process.cwd(), "storage/qr-codes");
|
|
64
|
+
await fs.mkdir(dir, { recursive: true });
|
|
46
65
|
|
|
47
|
-
|
|
48
|
-
color: options.background || "#ffffff"
|
|
49
|
-
},
|
|
66
|
+
const fullPath = path.join(dir, filename);
|
|
50
67
|
|
|
51
|
-
|
|
52
|
-
|
|
68
|
+
if (filename.endsWith(".svg")) {
|
|
69
|
+
const svg = await this.toSVG(text, options);
|
|
70
|
+
await fs.writeFile(fullPath, svg);
|
|
71
|
+
} else {
|
|
72
|
+
const buffer = await this.toPNG(text, options);
|
|
73
|
+
await fs.writeFile(fullPath, buffer);
|
|
53
74
|
}
|
|
54
|
-
});
|
|
55
75
|
|
|
56
|
-
|
|
76
|
+
return fullPath;
|
|
77
|
+
}
|
|
57
78
|
}
|
|
58
79
|
|
|
59
|
-
module.exports =
|
|
60
|
-
generateQR
|
|
61
|
-
};
|
|
80
|
+
module.exports = FastDotQR;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
function buildGradient(id, colors) {
|
|
2
|
+
return `
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="${id}" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
5
|
+
${colors
|
|
6
|
+
.map(
|
|
7
|
+
(c, i) =>
|
|
8
|
+
`<stop offset="${(i / (colors.length - 1)) * 100}%" stop-color="${c}" />`
|
|
9
|
+
)
|
|
10
|
+
.join("")}
|
|
11
|
+
</linearGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function squareToDot(svg, radius, fill) {
|
|
17
|
+
return svg
|
|
18
|
+
.replace(/<path[^>]*>/g, match => {
|
|
19
|
+
const m = match.match(/M(\d+) (\d+)/);
|
|
20
|
+
if (!m) return "";
|
|
21
|
+
const x = Number(m[1]) + radius;
|
|
22
|
+
const y = Number(m[2]) + radius;
|
|
23
|
+
return `<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" />`;
|
|
24
|
+
})
|
|
25
|
+
.replace(/<svg[^>]*>/, s => s.replace(">", ` fill="none">`))
|
|
26
|
+
.replace(/<\/svg>/, "");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function insertLogo(svg, logo, sizePercent = 0.25) {
|
|
30
|
+
const size = `${sizePercent * 100}%`;
|
|
31
|
+
|
|
32
|
+
return `
|
|
33
|
+
${svg}
|
|
34
|
+
<image
|
|
35
|
+
href="${logo}"
|
|
36
|
+
x="50%"
|
|
37
|
+
y="50%"
|
|
38
|
+
width="${size}"
|
|
39
|
+
height="${size}"
|
|
40
|
+
transform="translate(-${sizePercent * 50}%, -${sizePercent * 50}%)"
|
|
41
|
+
preserveAspectRatio="xMidYMid meet"
|
|
42
|
+
/>
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
buildGradient,
|
|
48
|
+
squareToDot,
|
|
49
|
+
insertLogo
|
|
50
|
+
};
|
package/src/presets.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
const purpleBlue = {
|
|
2
|
-
type: "linear",
|
|
3
|
-
rotation: Math.PI / 4,
|
|
4
|
-
colorStops: [
|
|
5
|
-
{ offset: 0, color: "#7F00FF" },
|
|
6
|
-
{ offset: 1, color: "#00C6FF" }
|
|
7
|
-
]
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const sunset = {
|
|
11
|
-
type: "linear",
|
|
12
|
-
rotation: Math.PI / 2,
|
|
13
|
-
colorStops: [
|
|
14
|
-
{ offset: 0, color: "#FF512F" },
|
|
15
|
-
{ offset: 1, color: "#F09819" }
|
|
16
|
-
]
|
|
17
|
-
};
|
|
18
|
-
const black = {
|
|
19
|
-
type: "linear",
|
|
20
|
-
rotation: Math.PI / 2,
|
|
21
|
-
colorStops: [
|
|
22
|
-
{ offset: 0, color: "#000000" },
|
|
23
|
-
{ offset: 1, color: "#000000" }
|
|
24
|
-
]
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const generatePresets = ({
|
|
28
|
-
type = "linear",
|
|
29
|
-
rotation = Math.PI / 4,
|
|
30
|
-
colorPrimary = "#000000",
|
|
31
|
-
colorsSecondary = "#000000"
|
|
32
|
-
}) => {
|
|
33
|
-
return{
|
|
34
|
-
type,
|
|
35
|
-
rotation,
|
|
36
|
-
colorStops: [
|
|
37
|
-
{ offset: 0, color: colorPrimary },
|
|
38
|
-
{ offset: 1, color: colorsSecondary }
|
|
39
|
-
]
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
module.exports = {
|
|
44
|
-
purpleBlue,
|
|
45
|
-
sunset,
|
|
46
|
-
black,
|
|
47
|
-
generatePresets
|
|
48
|
-
};
|