somdigi-qr-generator 1.0.3 → 1.0.5

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 CHANGED
@@ -1,5 +1,5 @@
1
- const generateQr = require("./src/core");
1
+ const QRGenerator = require("./src/core");
2
2
 
3
3
  module.exports = {
4
- generateQr
4
+ QRGenerator
5
5
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "somdigi-qr-generator",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
package/src/core.js CHANGED
@@ -1,77 +1,103 @@
1
1
  const QRCode = require("qrcode");
2
2
  const sharp = require("sharp");
3
- const fs = require("fs/promises");
4
- const path = require("path");
5
-
6
- const {
7
- extractPaths,
8
- buildGradient,
9
- pathsToDots,
10
- insertLogo
11
- } = require("./svg-helper");
12
-
13
- class generateQr {
14
- static async toSVG(text, options = {}) {
15
- const rawSVG = await QRCode.toString(text, {
16
- type: "svg",
17
- errorCorrectionLevel: "H",
18
- margin: options.margin ?? 1
19
- });
20
-
21
- const sizeMatch = rawSVG.match(/viewBox="([^"]+)"/);
22
- const viewBox = sizeMatch ? sizeMatch[1] : "0 0 100 100";
23
-
24
- const paths = extractPaths(rawSVG);
25
-
26
- const gradientId = "qrGradient";
27
- const useGradient = options.gradient?.length > 1;
28
-
29
- const fill = useGradient
30
- ? `url(#${gradientId})`
31
- : options.color ?? "#000";
32
-
33
- const defs = useGradient
34
- ? buildGradient(gradientId, options.gradient)
35
- : "";
36
-
37
- const dots = pathsToDots(
38
- paths,
39
- options.radius ?? 1.4,
40
- fill
41
- );
42
-
43
- const logo = options.logo
44
- ? insertLogo(options.logo, options.logoSize ?? 0.25)
45
- : "";
46
-
47
- // 🔒 SVG FINAL — VALID 100%
48
- return `
49
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}">
50
- ${defs}
51
- ${dots}
52
- ${logo}
53
- </svg>`;
54
- }
55
3
 
56
- static async toPNG(text, options = {}) {
57
- const svg = await this.toSVG(text, options);
58
- return sharp(Buffer.from(svg)).png().toBuffer();
59
- }
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 });
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
+ }
64
18
 
65
- const fullPath = path.join(dir, filename);
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
+ }
66
26
 
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));
27
+ // 🔵 ROUNDED QR
28
+ if (options.type === "rounded") {
29
+ return this._rounded(options);
71
30
  }
72
31
 
73
- return fullPath;
32
+ throw new Error("invalid type");
74
33
  }
34
+
35
+ static async _rounded(options) {
36
+ const rawSVG = await QRCode.toString(options.data, {
37
+ type: "svg",
38
+ errorCorrectionLevel: "H",
39
+ margin: 1
40
+ });
41
+
42
+ const viewBox = rawSVG.match(/viewBox="([^"]+)"/)[1];
43
+ const paths = rawSVG.match(/<path[^>]*>/g) || [];
44
+
45
+ const useGradient = options.gradient?.length > 1;
46
+ const fill = useGradient ? "url(#g)" : "#000";
47
+
48
+ const defs = useGradient
49
+ ? `
50
+ <defs>
51
+ <linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
52
+ ${options.gradient
53
+ .map(
54
+ (c, i) =>
55
+ `<stop offset="${(i / (options.gradient.length - 1)) * 100}%" stop-color="${c}" />`
56
+ )
57
+ .join("")}
58
+ </linearGradient>
59
+ </defs>`
60
+ : "";
61
+
62
+ const dots = paths
63
+ .map(p => {
64
+ const m = p.match(/M(\d+) (\d+)/);
65
+ if (!m) return "";
66
+ return `<circle cx="${+m[1] + 0.5}" cy="${+m[2] + 0.5}" r="0.45" fill="${fill}" />`;
67
+ })
68
+ .join("");
69
+
70
+ const logo = options.logo
71
+ ? `
72
+ <image
73
+ href="${options.logo}"
74
+ x="35%" y="35%"
75
+ width="30%" height="30%"
76
+ preserveAspectRatio="xMidYMid meet"
77
+ />`
78
+ : "";
79
+
80
+ const svg = `
81
+ <svg xmlns="http://www.w3.org/2000/svg"
82
+ width="${options.size ?? 512}"
83
+ height="${options.size ?? 512}"
84
+ viewBox="${viewBox}">
85
+
86
+ <!-- BACKGROUND WAJIB -->
87
+ <rect width="100%" height="100%" fill="#fff"/>
88
+
89
+ ${defs}
90
+ ${dots}
91
+ ${logo}
92
+ </svg>`;
93
+
94
+ return sharp(Buffer.from(svg))
95
+ .png({
96
+ background: "#ffffff"
97
+ })
98
+ .toBuffer();
99
+ }
100
+
75
101
  }
76
102
 
77
- module.exports = generateQr;
103
+ 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
- };