qr-kit 2.1.0 → 2.2.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/renderers/svg.js CHANGED
@@ -22,37 +22,29 @@ import { computeLayout } from '../utils/layout.js';
22
22
  export function makeQrPath(model, { size = 256, margin = 16, rounded = false } = {}) {
23
23
  const { moduleSize, quietLeft, quietTop } = computeLayout(model.size, size, margin);
24
24
  const { modules, size: n } = model;
25
-
26
25
  if (!rounded) {
27
- // Fastest path: one M + h + v + h + z per dark module
28
26
  let d = '';
29
27
  const ms = moduleSize;
30
28
  for (let y = 0; y < n; y++) {
31
29
  for (let x = 0; x < n; x++) {
32
30
  if (modules[y * n + x]) {
33
31
  const px = quietLeft + x * ms;
34
- const py = quietTop + y * ms;
32
+ const py = quietTop + y * ms;
35
33
  d += `M${px} ${py}h${ms}v${ms}h-${ms}z`;
36
34
  }
37
35
  }
38
36
  }
39
37
  return d;
40
38
  }
41
-
42
- // Rounded corners: proper arc commands
43
39
  const ms = moduleSize;
44
- const r = Math.max(1, Math.round(ms * 0.35));
45
- let d = '';
40
+ const r = Math.max(1, Math.round(ms * 0.35));
41
+ let d = '';
46
42
  for (let y = 0; y < n; y++) {
47
43
  for (let x = 0; x < n; x++) {
48
44
  if (modules[y * n + x]) {
49
45
  const px = quietLeft + x * ms;
50
- const py = quietTop + y * ms;
51
- d += `M${px + r},${py}`
52
- + `h${ms - 2 * r}a${r},${r} 0 0 1 ${r},${r}`
53
- + `v${ms - 2 * r}a${r},${r} 0 0 1 -${r},${r}`
54
- + `h-${ms - 2 * r}a${r},${r} 0 0 1 -${r},-${r}`
55
- + `v-${ms - 2 * r}a${r},${r} 0 0 1 ${r},-${r}z`;
46
+ const py = quietTop + y * ms;
47
+ d += `M${px + r},${py}h${ms - 2 * r}a${r},${r} 0 0 1 ${r},${r}v${ms - 2 * r}a${r},${r} 0 0 1 -${r},${r}h-${ms - 2 * r}a${r},${r} 0 0 1 -${r},-${r}v-${ms - 2 * r}a${r},${r} 0 0 1 ${r},-${r}z`;
56
48
  }
57
49
  }
58
50
  }
@@ -73,15 +65,15 @@ export function makeQrPathSplit(model, { size = 256, margin = 16 } = {}) {
73
65
  const { moduleSize: ms, quietLeft: ql, quietTop: qt } = computeLayout(model.size, size, margin);
74
66
  const { modules, functionMask, size: n } = model;
75
67
  let dataPath = '', functionPath = '';
76
-
77
68
  for (let y = 0; y < n; y++) {
78
69
  for (let x = 0; x < n; x++) {
79
- const i = y * n + x;
70
+ const i = y * n + x;
80
71
  if (!modules[i]) continue;
81
- const px = ql + x * ms, py = qt + y * ms;
72
+ const px = ql + x * ms;
73
+ const py = qt + y * ms;
82
74
  const seg = `M${px} ${py}h${ms}v${ms}h-${ms}z`;
83
75
  if (functionMask[i]) functionPath += seg;
84
- else dataPath += seg;
76
+ else dataPath += seg;
85
77
  }
86
78
  }
87
79
  return { dataPath, functionPath };
@@ -102,31 +94,22 @@ export function makeQrPathSplit(model, { size = 256, margin = 16 } = {}) {
102
94
  * @returns {string} Complete SVG markup.
103
95
  */
104
96
  export function makeQrSvgString(model, {
105
- size = 256,
106
- margin = 16,
107
- fg = '#000',
108
- bg = '#fff',
109
- title = 'QR Code',
97
+ size = 256,
98
+ margin = 16,
99
+ fg = '#000',
100
+ bg = '#fff',
101
+ title = 'QR Code',
110
102
  rounded = false,
103
+ fnColor = null,
111
104
  } = {}) {
112
105
  const { outer } = computeLayout(model.size, size, margin);
113
- const d = makeQrPath(model, { size, margin, rounded });
114
-
115
- return `<svg xmlns="http://www.w3.org/2000/svg" `
116
- + `width="${outer}" height="${outer}" viewBox="0 0 ${outer} ${outer}" `
117
- + `role="img" aria-label="${escapeAttr(title)}" shape-rendering="crispEdges">`
118
- + `<title>${escapeXml(title)}</title>`
119
- + `<rect width="100%" height="100%" fill="${escapeAttr(bg)}"/>`
120
- + `<path fill="${escapeAttr(fg)}" d="${d}"/>`
121
- + `</svg>`;
122
- }
123
-
124
- // ─── Helpers ──────────────────────────────────────────────────────────────────
106
+ const escape = s => String(s).replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
125
107
 
126
- function escapeXml(s) {
127
- return String(s).replace(/[&<>"']/g, c => ({
128
- '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
129
- }[c]));
108
+ if (fnColor) {
109
+ const { dataPath, functionPath } = makeQrPathSplit(model, { size, margin });
110
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${outer}" height="${outer}" viewBox="0 0 ${outer} ${outer}" role="img" aria-label="${escape(title)}" shape-rendering="crispEdges"><title>${escape(title)}</title><rect width="100%" height="100%" fill="${escape(bg)}"/><path fill="${escape(fg)}" d="${dataPath}"/><path fill="${escape(fnColor)}" d="${functionPath}"/></svg>`;
111
+ } else {
112
+ const d = makeQrPath(model, { size, margin, rounded });
113
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${outer}" height="${outer}" viewBox="0 0 ${outer} ${outer}" role="img" aria-label="${escape(title)}" shape-rendering="crispEdges"><title>${escape(title)}</title><rect width="100%" height="100%" fill="${escape(bg)}"/><path fill="${escape(fg)}" d="${d}"/></svg>`;
114
+ }
130
115
  }
131
-
132
- function escapeAttr(s) { return escapeXml(s); }