somdigi-qr-generator 1.0.1 → 1.0.3

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 generateQr = require("./src/core");
2
2
 
3
3
  module.exports = {
4
- generateQR
4
+ generateQr
5
5
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "somdigi-qr-generator",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
package/src/core.js CHANGED
@@ -4,54 +4,53 @@ const fs = require("fs/promises");
4
4
  const path = require("path");
5
5
 
6
6
  const {
7
+ extractPaths,
7
8
  buildGradient,
8
- squareToDot,
9
+ pathsToDots,
9
10
  insertLogo
10
11
  } = require("./svg-helper");
11
12
 
12
- class FastDotQR {
13
+ class generateQr {
13
14
  static async toSVG(text, options = {}) {
14
- if (!text) throw new Error("text required");
15
-
16
15
  const rawSVG = await QRCode.toString(text, {
17
16
  type: "svg",
18
17
  errorCorrectionLevel: "H",
19
18
  margin: options.margin ?? 1
20
19
  });
21
20
 
21
+ const sizeMatch = rawSVG.match(/viewBox="([^"]+)"/);
22
+ const viewBox = sizeMatch ? sizeMatch[1] : "0 0 100 100";
23
+
24
+ const paths = extractPaths(rawSVG);
25
+
22
26
  const gradientId = "qrGradient";
23
- const gradient =
24
- options.gradient?.length > 1
25
- ? buildGradient(gradientId, options.gradient)
26
- : "";
27
-
28
- const fill =
29
- options.gradient?.length > 1
30
- ? `url(#${gradientId})`
31
- : options.color ?? "#000";
32
-
33
- let content = squareToDot(
34
- rawSVG,
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,
35
39
  options.radius ?? 1.4,
36
40
  fill
37
41
  );
38
42
 
39
- if (options.logo) {
40
- content = insertLogo(
41
- content,
42
- options.logo,
43
- options.logoSize ?? 0.25
44
- );
45
- }
43
+ const logo = options.logo
44
+ ? insertLogo(options.logo, options.logoSize ?? 0.25)
45
+ : "";
46
46
 
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>`);
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>`;
55
54
  }
56
55
 
57
56
  static async toPNG(text, options = {}) {
@@ -66,15 +65,13 @@ class FastDotQR {
66
65
  const fullPath = path.join(dir, filename);
67
66
 
68
67
  if (filename.endsWith(".svg")) {
69
- const svg = await this.toSVG(text, options);
70
- await fs.writeFile(fullPath, svg);
68
+ await fs.writeFile(fullPath, await this.toSVG(text, options));
71
69
  } else {
72
- const buffer = await this.toPNG(text, options);
73
- await fs.writeFile(fullPath, buffer);
70
+ await fs.writeFile(fullPath, await this.toPNG(text, options));
74
71
  }
75
72
 
76
73
  return fullPath;
77
74
  }
78
75
  }
79
76
 
80
- module.exports = FastDotQR;
77
+ module.exports = generateQr;
package/src/svg-helper.js CHANGED
@@ -1,50 +1,49 @@
1
+ function extractPaths(svg) {
2
+ return svg.match(/<path[^>]*>/g) || [];
3
+ }
4
+
1
5
  function buildGradient(id, colors) {
2
6
  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
- `;
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>`;
14
17
  }
15
18
 
16
- function squareToDot(svg, radius, fill) {
17
- return svg
18
- .replace(/<path[^>]*>/g, match => {
19
- const m = match.match(/M(\d+) (\d+)/);
19
+ function pathsToDots(paths, radius, fill) {
20
+ return paths
21
+ .map(p => {
22
+ const m = p.match(/M(\d+) (\d+)/);
20
23
  if (!m) return "";
21
24
  const x = Number(m[1]) + radius;
22
25
  const y = Number(m[2]) + radius;
23
26
  return `<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" />`;
24
27
  })
25
- .replace(/<svg[^>]*>/, s => s.replace(">", ` fill="none">`))
26
- .replace(/<\/svg>/, "");
28
+ .join("");
27
29
  }
28
30
 
29
- function insertLogo(svg, logo, sizePercent = 0.25) {
30
- const size = `${sizePercent * 100}%`;
31
-
31
+ function insertLogo(logo, size = 0.25) {
32
+ const percent = size * 100;
32
33
  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
- `;
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
+ />`;
44
42
  }
45
43
 
46
44
  module.exports = {
45
+ extractPaths,
47
46
  buildGradient,
48
- squareToDot,
47
+ pathsToDots,
49
48
  insertLogo
50
49
  };