qrstylelib 1.0.0
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/example.js +135 -0
- package/package.json +38 -0
- package/src/core/analyzer.ts +55 -0
- package/src/core/qr/qrcodegen.ts +1032 -0
- package/src/default.ts +27 -0
- package/src/export.ts +56 -0
- package/src/frame-inset.ts +153 -0
- package/src/index.ts +669 -0
- package/src/renderer/shapes.ts +118 -0
- package/src/types.ts +104 -0
- package/test/readability.test.ts +71 -0
- package/test.js +160 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +10 -0
package/example.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const svgWithFrame = await QRCodeGenerate({
|
|
2
|
+
data: "https://wiki.org",
|
|
3
|
+
padding: 1,
|
|
4
|
+
width: 500,
|
|
5
|
+
height: 500,
|
|
6
|
+
borderRadius: 10,
|
|
7
|
+
frame: {
|
|
8
|
+
source: frameImg,
|
|
9
|
+
width: 1000,
|
|
10
|
+
height: 1000,
|
|
11
|
+
inset: { width: 500, height: 500 },
|
|
12
|
+
},
|
|
13
|
+
// --- Background ---
|
|
14
|
+
background: {
|
|
15
|
+
// Dark background so white dots are visible
|
|
16
|
+
color: "#1a1a2e",
|
|
17
|
+
// Uncomment to use a full-bleed background image instead:
|
|
18
|
+
image: bg1Img,
|
|
19
|
+
// gradient: {
|
|
20
|
+
// type: "linear",
|
|
21
|
+
// rotation: 90,
|
|
22
|
+
// colorStops: [
|
|
23
|
+
// { offset: "0%", color: "#021ffa" },
|
|
24
|
+
// { offset: "100%", color: "#ed0909" },
|
|
25
|
+
// ],
|
|
26
|
+
// },
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
// --- Images overlaid INSIDE the QR code (logos, icons, etc.) ---
|
|
30
|
+
// Multiple images are supported. Use excludeDots:true to clear dots underneath.
|
|
31
|
+
// Coordinates are in QR modules (same unit as the matrix grid).
|
|
32
|
+
images: [
|
|
33
|
+
{
|
|
34
|
+
// Center logo — inline SVG data URI (works without network)
|
|
35
|
+
// A simple WiFi icon as SVG data URI
|
|
36
|
+
source:
|
|
37
|
+
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='12' fill='%2316213e'/%3E%3Cpath d='M12 15.5a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3zm0-4a5.5 5.5 0 0 1 4.95 3.07l-1.6 1.6A3.5 3.5 0 0 0 12 14a3.5 3.5 0 0 0-3.35 2.17l-1.6-1.6A5.5 5.5 0 0 1 12 11.5zm0-4a9.5 9.5 0 0 1 8.49 5.24l-1.6 1.6A7.5 7.5 0 0 0 12 10a7.5 7.5 0 0 0-6.89 4.34l-1.6-1.6A9.5 9.5 0 0 1 12 7.5z' fill='%2300d4ff'/%3E%3C/svg%3E",
|
|
38
|
+
width: 5,
|
|
39
|
+
height: 5,
|
|
40
|
+
excludeDots: true,
|
|
41
|
+
},
|
|
42
|
+
// Second image — also inline SVG (a small star badge)
|
|
43
|
+
{
|
|
44
|
+
source:
|
|
45
|
+
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='12' fill='%23edf505'/%3E%3Cpolygon points='12,4 14.5,9.5 21,10.3 16.5,14.6 17.8,21 12,17.8 6.2,21 7.5,14.6 3,10.3 9.5,9.5' fill='%231a1a2e'/%3E%3C/svg%3E",
|
|
46
|
+
width: 3,
|
|
47
|
+
height: 3,
|
|
48
|
+
excludeDots: true,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
source:
|
|
52
|
+
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='12' fill='%23edf505'/%3E%3Cpolygon points='12,4 14.5,9.5 21,10.3 16.5,14.6 17.8,21 12,17.8 6.2,21 7.5,14.6 3,10.3 9.5,9.5' fill='%231a1a2e'/%3E%3C/svg%3E",
|
|
53
|
+
width: 3,
|
|
54
|
+
height: 3,
|
|
55
|
+
excludeDots: true,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
source:
|
|
59
|
+
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='12' fill='%23edf505'/%3E%3Cpolygon points='12,4 14.5,9.5 21,10.3 16.5,14.6 17.8,21 12,17.8 6.2,21 7.5,14.6 3,10.3 9.5,9.5' fill='%231a1a2e'/%3E%3C/svg%3E",
|
|
60
|
+
width: 3,
|
|
61
|
+
height: 3,
|
|
62
|
+
excludeDots: true,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
// Center logo — inline SVG data URI (works without network)
|
|
66
|
+
// A simple WiFi icon as SVG data URI
|
|
67
|
+
source:
|
|
68
|
+
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='12' fill='%2316213e'/%3E%3Cpath d='M12 15.5a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3zm0-4a5.5 5.5 0 0 1 4.95 3.07l-1.6 1.6A3.5 3.5 0 0 0 12 14a3.5 3.5 0 0 0-3.35 2.17l-1.6-1.6A5.5 5.5 0 0 1 12 11.5zm0-4a9.5 9.5 0 0 1 8.49 5.24l-1.6 1.6A7.5 7.5 0 0 0 12 10a7.5 7.5 0 0 0-6.89 4.34l-1.6-1.6A9.5 9.5 0 0 1 12 7.5z' fill='%2300d4ff'/%3E%3C/svg%3E",
|
|
69
|
+
width: 5,
|
|
70
|
+
height: 5,
|
|
71
|
+
excludeDots: true,
|
|
72
|
+
x: 1,
|
|
73
|
+
y: 2,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
// Center logo — inline SVG data URI (works without network)
|
|
77
|
+
// A simple WiFi icon as SVG data URI
|
|
78
|
+
source:
|
|
79
|
+
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='12' fill='%2316213e'/%3E%3Cpath d='M12 15.5a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3zm0-4a5.5 5.5 0 0 1 4.95 3.07l-1.6 1.6A3.5 3.5 0 0 0 12 14a3.5 3.5 0 0 0-3.35 2.17l-1.6-1.6A5.5 5.5 0 0 1 12 11.5zm0-4a9.5 9.5 0 0 1 8.49 5.24l-1.6 1.6A7.5 7.5 0 0 0 12 10a7.5 7.5 0 0 0-6.89 4.34l-1.6-1.6A9.5 9.5 0 0 1 12 7.5z' fill='%2300d4ff'/%3E%3C/svg%3E",
|
|
80
|
+
width: 5,
|
|
81
|
+
height: 5,
|
|
82
|
+
x: 18,
|
|
83
|
+
y: 20,
|
|
84
|
+
excludeDots: true,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
|
|
88
|
+
// 1. DOTS — use a simple square shape with visible color
|
|
89
|
+
dotsOptions: {
|
|
90
|
+
// shape: "square",
|
|
91
|
+
color: "#e0e0e0",
|
|
92
|
+
scale: 0.85,
|
|
93
|
+
// To use custom-icon shape, uncomment below and comment out shape/color above:
|
|
94
|
+
shape: "custom-icon",
|
|
95
|
+
// color: "white",
|
|
96
|
+
customIconPath: fullWLogo,
|
|
97
|
+
customIconViewBox: "0 0 41 36",
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// 2. CORNER SQUARE (outer eye frame)
|
|
101
|
+
cornersSquareOptions: {
|
|
102
|
+
shape: "square",
|
|
103
|
+
color: "black",
|
|
104
|
+
gradient: {
|
|
105
|
+
type: "linear",
|
|
106
|
+
rotation: 180,
|
|
107
|
+
colorStops: [
|
|
108
|
+
{ offset: "0%", color: "#EEAECA" },
|
|
109
|
+
{ offset: "100%", color: "#00D4FF" },
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// 3. CORNER DOT (inner eye ball)
|
|
115
|
+
cornersDotOptions: {
|
|
116
|
+
shape: "heart",
|
|
117
|
+
gradient: {
|
|
118
|
+
type: "linear",
|
|
119
|
+
rotation: 90,
|
|
120
|
+
colorStops: [
|
|
121
|
+
{ offset: "0%", color: "#edf505" },
|
|
122
|
+
{ offset: "100%", color: "#1aebd9" },
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
isSingle: true,
|
|
126
|
+
scale: 1.2,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const { matrixSize, eyeZones, getMaxPos } = getQRBounds('https://wiki.org');
|
|
131
|
+
// Для зображення розміром 3×3 модулі:
|
|
132
|
+
const { maxX, maxY } = getMaxPos(3, 3);
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
const { matrixSize, eyeZones, getMaxPos,svgWithFrame } = await QRCodeGenerate({...})
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "qrstylelib",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.cts",
|
|
16
|
+
"default": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch",
|
|
24
|
+
"test": "vitest",
|
|
25
|
+
"start": "node test.js"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [],
|
|
28
|
+
"author": "",
|
|
29
|
+
"license": "ISC",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^25.2.3",
|
|
32
|
+
"jsqr": "^1.4.0",
|
|
33
|
+
"sharp": "^0.34.5",
|
|
34
|
+
"tsup": "^8.5.1",
|
|
35
|
+
"typescript": "^5.9.3",
|
|
36
|
+
"vitest": "^4.0.18"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { QrCode, QrSegment } from "./qr/qrcodegen";
|
|
2
|
+
import { QRMatrix, ModuleType } from "../types";
|
|
3
|
+
|
|
4
|
+
export class QRAnalyzer {
|
|
5
|
+
private qr: QrCode;
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
text: string,
|
|
9
|
+
ecc: "L" | "M" | "Q" | "H" = "H",
|
|
10
|
+
minVersion: number = 1,
|
|
11
|
+
maxVersion: number = 40,
|
|
12
|
+
mask: number = -1,
|
|
13
|
+
) {
|
|
14
|
+
const eccMap = {
|
|
15
|
+
L: QrCode.Ecc.LOW,
|
|
16
|
+
M: QrCode.Ecc.MEDIUM,
|
|
17
|
+
Q: QrCode.Ecc.QUARTILE,
|
|
18
|
+
H: QrCode.Ecc.HIGH,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// encodeSegments allows more control than encodeText
|
|
22
|
+
const segs = QrSegment.makeSegments(text);
|
|
23
|
+
this.qr = QrCode.encodeSegments(
|
|
24
|
+
segs,
|
|
25
|
+
eccMap[ecc],
|
|
26
|
+
minVersion,
|
|
27
|
+
maxVersion,
|
|
28
|
+
mask,
|
|
29
|
+
true,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public getMatrix(): QRMatrix {
|
|
34
|
+
const size = this.qr.size;
|
|
35
|
+
const matrix: QRMatrix = [];
|
|
36
|
+
|
|
37
|
+
for (let y = 0; y < size; y++) {
|
|
38
|
+
const row: any[] = [];
|
|
39
|
+
for (let x = 0; x < size; x++) {
|
|
40
|
+
const isDark = this.qr.getModule(x, y);
|
|
41
|
+
const type = this.identifyType(x, y, size);
|
|
42
|
+
row.push({ x, y, isDark, type });
|
|
43
|
+
}
|
|
44
|
+
matrix.push(row);
|
|
45
|
+
}
|
|
46
|
+
return matrix;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private identifyType(x: number, y: number, size: number): ModuleType {
|
|
50
|
+
if (x < 7 && y < 7) return "pos-finder";
|
|
51
|
+
if (x > size - 8 && y < 7) return "pos-finder";
|
|
52
|
+
if (x < 7 && y > size - 8) return "pos-finder";
|
|
53
|
+
return "data";
|
|
54
|
+
}
|
|
55
|
+
}
|