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/CHANGELOG.md +36 -0
- package/README.md +323 -323
- package/package.json +71 -71
- package/qr/qr-core.js +333 -178
- package/renderers/svg.js +23 -40
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
|
|
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
|
|
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
|
|
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
|
|
70
|
+
const i = y * n + x;
|
|
80
71
|
if (!modules[i]) continue;
|
|
81
|
-
const px = ql + x * 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
|
|
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
|
|
106
|
-
margin
|
|
107
|
-
fg
|
|
108
|
-
bg
|
|
109
|
-
title
|
|
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
|
|
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 => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
125
107
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
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); }
|