quadqr-js 1.0.2 → 1.3.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/FORMAT.md +40 -3
- package/README.md +199 -20
- package/SPECIFICATION.md +337 -0
- package/bin/quadqr.js +105 -14
- package/dist/esm/benchmark.js +113 -8
- package/dist/esm/node.js +13 -1
- package/dist/esm/quadqr.js +2312 -299
- package/dist/esm/vision.js +452 -14
- package/dist/quadqr.js +2746 -299
- package/dist/quadqr.min.js +2736 -297
- package/docs/API.md +230 -8
- package/docs/BROWSER_CDN.md +51 -6
- package/docs/CLI.md +156 -76
- package/docs/GETTING_STARTED.md +80 -7
- package/docs/HIGH_DENSITY_MODE.md +100 -0
- package/docs/NODE.md +137 -123
- package/docs/README.md +60 -41
- package/docs/TRIANGLE16.md +108 -0
- package/docs/WASM.md +2 -2
- package/package.json +10 -3
- package/types/benchmark.d.ts +4 -3
- package/types/index.d.ts +144 -2
- package/types/node.d.ts +2 -0
package/FORMAT.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Experimental custom symbology. It is not ISO QR Code.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Normal mode uses the default 4-state RGBW data cells. An optional experimental **High Density Mode** uses the 16-state Triangle16 physical layout for payload/body cells. Earlier RGB/ternary formats are intentionally not supported.
|
|
8
8
|
|
|
9
9
|
## Matrix sizes
|
|
10
10
|
|
|
@@ -33,6 +33,17 @@ Four data states:
|
|
|
33
33
|
|
|
34
34
|
Structural white and data white intentionally share the same visible/internal value. Reserved-position geometry distinguishes their roles.
|
|
35
35
|
|
|
36
|
+
### High Density Mode (Triangle16)
|
|
37
|
+
|
|
38
|
+
High Density Mode is identified by header flag bit 6 and implemented with Triangle16 cells. Each non-reserved payload module is split by a fixed `/` diagonal into an upper-left triangle and a lower-right triangle. Each triangle independently uses the RGBW 2-bit alphabet. The packed internal value is:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
cell = (upperLeftColor << 2) | lowerRightColor
|
|
42
|
+
range = 0..15
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Therefore Triangle16 has 16 visible states and carries 4 raw bits per body data cell. Same-color pairs such as R/R or B/B render as a visually solid module. Structural modules never use Triangle16 packing.
|
|
46
|
+
|
|
36
47
|
## Finder structures
|
|
37
48
|
|
|
38
49
|
Three 7×7 black/white finder structures are placed at top-left, top-right, and bottom-left, with white separator cells where they fit inside the matrix.
|
|
@@ -99,6 +110,18 @@ Therefore:
|
|
|
99
110
|
1 RGBW data cell = exactly 2 raw bits
|
|
100
111
|
```
|
|
101
112
|
|
|
113
|
+
For Triangle16 ECC/body bytes:
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
bits 7..4 -> Triangle16 cell 0
|
|
117
|
+
bits 3..0 -> Triangle16 cell 1
|
|
118
|
+
|
|
119
|
+
1 byte = exactly 2 Triangle16 body cells
|
|
120
|
+
1 Triangle16 body cell = exactly 4 raw bits
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The protected header remains encoded as four solid-color RGBW-equivalent cells per byte even when Triangle16 is selected. Internally those solid header cells are represented as R/R, G/G, B/B, or W/W. This keeps the bootstrap/header substantially easier to recover from blur and perspective distortion.
|
|
124
|
+
|
|
102
125
|
## Masks
|
|
103
126
|
|
|
104
127
|
Four masks are defined. Each returns a 2-bit value 0..3:
|
|
@@ -117,7 +140,7 @@ visible = raw XOR mask
|
|
|
117
140
|
raw = visible XOR mask
|
|
118
141
|
```
|
|
119
142
|
|
|
120
|
-
The encoder evaluates all four masks using run-length and
|
|
143
|
+
The encoder evaluates all four masks using run-length and color-balance penalties. In Triangle16 body cells, two deterministic 2-bit masks are packed into one 4-bit XOR mask; protected solid-color header cells use the normal RGBW mask on both halves so they remain solid. The mask ID is not serialized. The decoder tries all four and accepts only a path whose protected header, ECC, and CRC validate.
|
|
121
144
|
|
|
122
145
|
## Header
|
|
123
146
|
|
|
@@ -167,7 +190,10 @@ Flags for both header forms:
|
|
|
167
190
|
bit 0 UTF-8 text flag
|
|
168
191
|
bits 1..2 ECC profile id
|
|
169
192
|
bit 3 Secure Payload envelope flag
|
|
170
|
-
|
|
193
|
+
bit 4 internal payload-extension metadata present
|
|
194
|
+
bit 5 signed-payload hint
|
|
195
|
+
bit 6 High Density Mode flag
|
|
196
|
+
bit 7 reserved
|
|
171
197
|
```
|
|
172
198
|
|
|
173
199
|
ECC ids:
|
|
@@ -388,3 +414,14 @@ Format v5 is intentionally incompatible with standard QR scanners and with the p
|
|
|
388
414
|
## Rendering profiles are not part of the wire format
|
|
389
415
|
|
|
390
416
|
The canonical QuadQR matrix is independent of presentation style. Renderers may offer styles such as `classic`, `depth`, `soft`, or `inset`, provided structural finder/timing/alignment/calibration references remain sufficiently faithful for decoding. Style selection is not encoded in the payload/header and does not change matrix cell values.
|
|
417
|
+
|
|
418
|
+
## Compression and signature flags
|
|
419
|
+
|
|
420
|
+
Format v5 keeps the physical matrix and ECC framing unchanged while reserving two protected-header flags for optional internal payload metadata:
|
|
421
|
+
|
|
422
|
+
```text
|
|
423
|
+
bit 4 = internal payload-extension metadata present
|
|
424
|
+
bit 5 = signed-payload hint
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
These bits are not a public payload-type mode. Applications continue to encode normal UTF-8 text or arbitrary bytes. Compression and Ed25519 signing use a compact internal extension envelope only when metadata is required. The envelope, print profile, diagnostics, and scanability test model are specified in [`SPECIFICATION.md`](./SPECIFICATION.md). None of these features changes RGBW cell mapping or Spectrum ECC.
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<strong>
|
|
8
|
+
<strong>Normal RGBW mode by default, with an optional experimental High Density Mode using Triangle16 split cells.</strong>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
@@ -21,12 +21,14 @@
|
|
|
21
21
|
·
|
|
22
22
|
<a href="docs/README.md">Markdown Docs</a>
|
|
23
23
|
·
|
|
24
|
-
<a href="FORMAT.md">Format
|
|
24
|
+
<a href="FORMAT.md">Matrix Format</a>
|
|
25
|
+
·
|
|
26
|
+
<a href="SPECIFICATION.md">Technical Specification</a>
|
|
25
27
|
</p>
|
|
26
28
|
|
|
27
|
-
**QuadQR** is an experimental open-source 2D matrix code that uses
|
|
29
|
+
**QuadQR** is an experimental open-source 2D matrix code that uses RGBW color states instead of the two states used by a traditional black-and-white QR module. Normal mode stores **2 bits per RGBW data cell**. This experimental branch also includes an optional **High Density Mode**, implemented with Triangle16 split cells, that stores **4 raw bits per body cell**. High Density Mode is disabled by default.
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
Default RGBW mapping:
|
|
30
32
|
|
|
31
33
|
| Color | Bits |
|
|
32
34
|
|---|---|
|
|
@@ -37,6 +39,28 @@ Each QuadQR data cell represents exactly **2 bits**:
|
|
|
37
39
|
|
|
38
40
|
That gives QuadQR a four-symbol alphabet and a raw density of **2 bits per data cell**.
|
|
39
41
|
|
|
42
|
+
### Experimental High Density Mode
|
|
43
|
+
|
|
44
|
+
When `highDensity: true` is enabled, Triangle16 splits each payload cell along one fixed `/` diagonal. The upper-left and lower-right triangles independently use Red, Green, Blue, or White:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
4 colors × 4 colors = 16 states
|
|
48
|
+
log2(16) = 4 bits per data cell
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The protected bootstrap/header deliberately remains solid-color even in High Density Mode, while the ECC-protected body uses the full 16-state alphabet. This sacrifices a small amount of theoretical capacity to make mode detection and damaged-camera recovery more reliable. Finder, timing, alignment, calibration, ECC, CRC, and matrix dimensions remain unchanged.
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
const code = encodeText("High-density QuadQR", {
|
|
55
|
+
ecc: "M",
|
|
56
|
+
highDensity: true
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Image and camera scanning automatically detect High Density Mode, so a separate scanner mode is not required. High Density Mode is experimental and should be stress-tested at the intended physical size and camera distance.
|
|
61
|
+
|
|
62
|
+
See [`docs/HIGH_DENSITY_MODE.md`](docs/HIGH_DENSITY_MODE.md) for the physical cell mapping, protected-header strategy, scanner sampling rules, and reliability caveats.
|
|
63
|
+
|
|
40
64
|
QuadQR keeps the parts that make QR-like codes practical, such as a square matrix, finder patterns, timing structures, error correction, masking, perspective recovery, and camera scanning, while experimenting with a higher-density color-based data layer.
|
|
41
65
|
|
|
42
66
|
> **Important:** QuadQR is an experimental custom format. It is **not ISO QR Code**, and normal QR scanner apps cannot decode it.
|
|
@@ -129,7 +153,8 @@ So at the raw data-cell level:
|
|
|
129
153
|
| Format | States per data cell | Raw information |
|
|
130
154
|
|---|---:|---:|
|
|
131
155
|
| Binary QR | 2 | 1 bit |
|
|
132
|
-
| QuadQR | 4 | 2 bits |
|
|
156
|
+
| QuadQR RGBW | 4 | 2 bits |
|
|
157
|
+
| QuadQR High Density Mode (Triangle16) | 16 | 4 bits |
|
|
133
158
|
|
|
134
159
|
This is a **2× raw symbol-density advantage**.
|
|
135
160
|
|
|
@@ -179,6 +204,56 @@ The scanner never expects the secret to be embedded in the QuadQR itself. Raw-ke
|
|
|
179
204
|
|
|
180
205
|
---
|
|
181
206
|
|
|
207
|
+
## Compression, signatures, and robustness tooling
|
|
208
|
+
|
|
209
|
+
QuadQR keeps the application payload simple: **text stays text and bytes stay bytes**. There is no public payload-type registry to maintain. Compression and signing metadata are added only as internal implementation details when those features are enabled.
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
import {
|
|
213
|
+
encodeText,
|
|
214
|
+
encodeSignedText,
|
|
215
|
+
generateSigningKeyPair,
|
|
216
|
+
decodeMatrix,
|
|
217
|
+
verifyDecodedSignature
|
|
218
|
+
} from "quadqr-js";
|
|
219
|
+
|
|
220
|
+
// Auto compression is zero-overhead when it does not help.
|
|
221
|
+
const compressed = encodeText("repeated repeated repeated", {
|
|
222
|
+
compression: "auto",
|
|
223
|
+
ecc: "M"
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const keys = await generateSigningKeyPair();
|
|
227
|
+
const signed = await encodeSignedText("verified offline", {
|
|
228
|
+
compression: "auto",
|
|
229
|
+
privateKey: keys.privateKey,
|
|
230
|
+
keyId: keys.keyId
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const decoded = decodeMatrix(signed.matrix);
|
|
234
|
+
const verified = await verifyDecodedSignature(decoded, {
|
|
235
|
+
publicKey: keys.publicKey
|
|
236
|
+
});
|
|
237
|
+
console.log(verified.signatureVerified); // true
|
|
238
|
+
console.log(verified.signatureTrusted); // true
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Compression modes are `none`, `auto`, and `lz`. `auto` keeps the original payload untouched when compression would not save space. Ed25519 signing stores the signature plus an optional compact `keyId`; the public verification key stays outside the QuadQR by default. Applications do not need to choose or maintain content types.
|
|
242
|
+
|
|
243
|
+
Signing can also be composed with Secure Payload. QuadQR compresses if requested, signs the normal payload with the private key, then encrypts the protected bytes with AES-256-GCM. A verifier supplies the trusted public key separately, or resolves it from `keyId`.
|
|
244
|
+
|
|
245
|
+
The renderer supports an explicit `mode: "print"`. Print mode enforces a minimum 4-module quiet zone, uses darker print-safe RGB defaults, and prefers Classic solid modules. `getPrintGuidance()` converts a chosen physical size into module millimeters/pixels so print layouts can be checked before production testing.
|
|
246
|
+
|
|
247
|
+
Centered logos support `size: "auto"`, which estimates a conservative ECC-aware ratio from code utilization and rendering choices. `findMaxSafeLogoSize()` can additionally probe ImageData output and empirically search for the largest size that still decodes.
|
|
248
|
+
|
|
249
|
+
Scanner results include normalized diagnostics such as `confidence`, `geometryConfidence`, `calibrationConfidence`, `structureConfidence`, `eccUtilization`, and `correctedErrors`. Set `debug: true` or call `debugScanImageData()` to inspect finder/geometry candidates, the sampled matrix, color-confidence data, ECC stages, and the stage that failed.
|
|
250
|
+
|
|
251
|
+
For regression and demo testing, `runImageStressTest()` / `assessScanability()` apply deterministic blur, brightness, exposure, shadow, contrast, perspective, JPEG-like artifacts, and downscaling. The browser demo exposes the same tools as an interactive stress-test lab and shows an overall scanability rating.
|
|
252
|
+
|
|
253
|
+
The interoperability details are documented in [`SPECIFICATION.md`](./SPECIFICATION.md).
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
182
257
|
## Current benchmark
|
|
183
258
|
|
|
184
259
|
Run:
|
|
@@ -228,7 +303,7 @@ Therefore:
|
|
|
228
303
|
|
|
229
304
|
> The capacity benchmark is a same-dimension and same-label comparison, not yet an equal-damage-tolerance comparison.
|
|
230
305
|
|
|
231
|
-
The
|
|
306
|
+
The published baseline benchmark uses **normal RGBW mode at exactly 2 bits per data cell**. Ratios approaching ~3× in that RGBW usable-payload benchmark are caused by differences in total structural and ECC overhead between the two formats, not because an RGBW QuadQR cell contains 3 bits. The experimental **Triangle16** profile is a separate 4-bit/body-cell mode and should be benchmarked independently because its real-world advantage depends on camera resolution, perspective, blur, resizing, and print quality.
|
|
232
307
|
|
|
233
308
|
A future goal is to add **equal-reliability benchmarking**, where QuadQR and standard QR are compared after calibrating both to similar real-world damage recovery.
|
|
234
309
|
|
|
@@ -390,7 +465,7 @@ White → (255, 255, 255)
|
|
|
390
465
|
|
|
391
466
|
Real camera input is not expected to match those exact values.
|
|
392
467
|
|
|
393
|
-
QuadQR includes calibration and nearest-color classification so the scanner can work with observed colors after lighting, camera processing, perspective changes, and other image transformations. The clean-frame path stays fast: QuadQR tries the normal detected geometry and observed palette first. Only after
|
|
468
|
+
QuadQR includes calibration and nearest-color classification so the scanner can work with observed colors after lighting, camera processing, perspective changes, and other image transformations. The clean-frame path stays fast: QuadQR tries the normal detected geometry and observed palette first. Dense versions can refine an imperfect four-point homography with reliable secondary alignment markers already present in the matrix, without reserving any new cells. If a steep angle leaves exactly two strong finder patterns, a bounded looser third-finder pass runs before heavier color recovery. Only after geometry/color decoding still fails does QuadQR progressively try stronger recovery, including white balancing, spatial normalization, Auto Tone / Auto Contrast / Auto Color-style enhancement, and bounded sub-module geometry refinement. For live video, QuadQR scans the CSS-visible `object-fit: cover` camera region instead of the hidden full sensor frame, so the code keeps the same apparent size/resolution the user sees in the guide. When a dense frame already exposes at least two finders, the camera scanner can also retry the visible ROI at up to 1600 px before expensive color recovery. If finder geometry is already strong but color decoding fails, a QR-only rectified pixel enhancement retry is performed immediately; whole-frame enhancement remains reserved for harder locator failures.
|
|
394
469
|
|
|
395
470
|
---
|
|
396
471
|
|
|
@@ -452,9 +527,11 @@ version hypothesis
|
|
|
452
527
|
↓
|
|
453
528
|
primary alignment search
|
|
454
529
|
↓
|
|
455
|
-
homography / perspective correction
|
|
530
|
+
initial homography / perspective correction
|
|
456
531
|
↓
|
|
457
532
|
distributed alignment-grid validation
|
|
533
|
+
↓ (when geometry is plausible but imperfect)
|
|
534
|
+
secondary alignment multi-point homography refinement
|
|
458
535
|
↓
|
|
459
536
|
module-grid reconstruction
|
|
460
537
|
↓
|
|
@@ -508,7 +585,7 @@ The `inset` style preserves the exact encoded R/G/B/W color around the center of
|
|
|
508
585
|
|
|
509
586
|
```js
|
|
510
587
|
renderToCanvas(code, canvas, {
|
|
511
|
-
|
|
588
|
+
imageSize: 720,
|
|
512
589
|
quietZone: 4,
|
|
513
590
|
style: "inset" // classic | depth | soft | inset
|
|
514
591
|
});
|
|
@@ -516,13 +593,58 @@ renderToCanvas(code, canvas, {
|
|
|
516
593
|
|
|
517
594
|
The styling is deterministic. Generating the same matrix with the same style produces the same visual tile treatment rather than changing randomly on every render.
|
|
518
595
|
|
|
596
|
+
`imageSize` sets the exact square output size in pixels. When neither `imageSize` nor `moduleSize` is supplied, QuadQR renders at **720 × 720 px** by default. `moduleSize` remains available as the lower-level legacy sizing control; when `imageSize` is supplied, the exact image size takes precedence.
|
|
597
|
+
|
|
598
|
+
### Logo overlays, quiet zones, and SVG export
|
|
599
|
+
|
|
600
|
+
The renderer can place a centered logo over the symbol. Transparent pixels in the logo stay transparent, so the QuadQR modules remain visible through those areas. Enable `clearBackground` when you want a clean padded white area behind the logo instead.
|
|
601
|
+
|
|
602
|
+
```js
|
|
603
|
+
const logoImage = new Image();
|
|
604
|
+
logoImage.src = "/brand-mark.png";
|
|
605
|
+
await logoImage.decode();
|
|
606
|
+
|
|
607
|
+
renderToCanvas(code, canvas, {
|
|
608
|
+
imageSize: 720,
|
|
609
|
+
quietZone: 6,
|
|
610
|
+
style: "classic",
|
|
611
|
+
logo: {
|
|
612
|
+
source: logoImage,
|
|
613
|
+
size: 0.12,
|
|
614
|
+
clearBackground: true,
|
|
615
|
+
padding: 0.65,
|
|
616
|
+
radius: 0.8
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
`quietZone` is measured in modules and can be set to `0` or increased for print/camera use. Four modules remains the recommended default.
|
|
622
|
+
|
|
623
|
+
SVG uses the same matrix, palette, styles, quiet-zone size, and logo geometry:
|
|
624
|
+
|
|
625
|
+
```js
|
|
626
|
+
import { renderToSVG } from "quadqr-js";
|
|
627
|
+
|
|
628
|
+
const svg = renderToSVG(code, {
|
|
629
|
+
imageSize: 720,
|
|
630
|
+
quietZone: 4,
|
|
631
|
+
logo: {
|
|
632
|
+
source: "data:image/png;base64,...",
|
|
633
|
+
size: 0.12,
|
|
634
|
+
clearBackground: true
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
Logo overlays intentionally consume some ECC margin because they cover encoded cells. Keep logos conservative, especially with `L`/`M` ECC. The browser demo verifies the final rendered image before enabling downloads.
|
|
640
|
+
|
|
519
641
|
---
|
|
520
642
|
|
|
521
643
|
## Demo
|
|
522
644
|
|
|
523
645
|
**Live demo:** https://akanshsirohi.github.io/QuadQR/demo/
|
|
524
646
|
|
|
525
|
-
The browser demo runs directly on GitHub Pages and is split into separate views so the interface does not become overloaded.
|
|
647
|
+
The browser demo runs directly on GitHub Pages and is split into separate views so the interface does not become overloaded. The generator keeps only payload, version, and ECC visible by default; optional capabilities are grouped into independent advanced accordions.
|
|
526
648
|
|
|
527
649
|
### Generator & Image Scanner
|
|
528
650
|
|
|
@@ -531,9 +653,13 @@ Use this tab to:
|
|
|
531
653
|
- enter text/data;
|
|
532
654
|
- generate a QuadQR code;
|
|
533
655
|
- select ECC;
|
|
656
|
+
- open Output & Rendering controls for image size, quiet zone, style, and print mode;
|
|
657
|
+
- open the Center Logo accordion for branding;
|
|
658
|
+
- enable internal automatic compression without choosing a payload type;
|
|
659
|
+
- optionally add an Ed25519 signature;
|
|
534
660
|
- optionally encrypt using a password or raw 256-bit key;
|
|
535
661
|
- inspect version and capacity;
|
|
536
|
-
- download
|
|
662
|
+
- download PNG or SVG output;
|
|
537
663
|
- scan an uploaded image.
|
|
538
664
|
|
|
539
665
|
### Camera Scanner
|
|
@@ -552,7 +678,43 @@ The benchmark tab provides an easier visual view of:
|
|
|
552
678
|
- comparison with standard QR;
|
|
553
679
|
- byte gain;
|
|
554
680
|
- capacity ratio;
|
|
555
|
-
- codec timing
|
|
681
|
+
- codec timing;
|
|
682
|
+
- capacity planning;
|
|
683
|
+
- interactive stress testing.
|
|
684
|
+
|
|
685
|
+
Benchmark tools are also separated into accordions so the page stays compact until a tool is needed.
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
## New API highlights
|
|
690
|
+
|
|
691
|
+
```js
|
|
692
|
+
import {
|
|
693
|
+
encodeText,
|
|
694
|
+
encodeUint8Array,
|
|
695
|
+
decodeUint8Array,
|
|
696
|
+
renderToImageData,
|
|
697
|
+
assessScanability,
|
|
698
|
+
getPrintGuidance
|
|
699
|
+
} from "quadqr-js";
|
|
700
|
+
|
|
701
|
+
const binary = encodeUint8Array(new Uint8Array([1, 2, 3, 4]), { ecc: "M" });
|
|
702
|
+
const bytes = decodeUint8Array(binary.matrix);
|
|
703
|
+
|
|
704
|
+
const code = encodeText("Hello hello hello hello", {
|
|
705
|
+
compression: "auto",
|
|
706
|
+
ecc: "Q"
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
const printImage = renderToImageData(code, {
|
|
710
|
+
imageSize: 1200,
|
|
711
|
+
mode: "print",
|
|
712
|
+
logo: { source: logoImageData, size: "auto", clearBackground: true }
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
console.log(getPrintGuidance(code, { physicalSizeMm: 45, dpi: 300 }));
|
|
716
|
+
console.log(assessScanability(code, { imageSize: 480 }));
|
|
717
|
+
```
|
|
556
718
|
|
|
557
719
|
---
|
|
558
720
|
|
|
@@ -583,14 +745,15 @@ const result = decodeMatrix(code.matrix);
|
|
|
583
745
|
console.log(result.text);
|
|
584
746
|
```
|
|
585
747
|
|
|
586
|
-
Node PNG usage:
|
|
748
|
+
Node PNG/SVG usage:
|
|
587
749
|
|
|
588
750
|
```js
|
|
589
751
|
import { encodeText } from "quadqr-js";
|
|
590
|
-
import { savePNG, scanFile } from "quadqr-js/node";
|
|
752
|
+
import { savePNG, saveSVG, scanFile } from "quadqr-js/node";
|
|
591
753
|
|
|
592
754
|
const code = encodeText("Generated on Node.js");
|
|
593
|
-
await savePNG(code, "quadqr.png", {
|
|
755
|
+
await savePNG(code, "quadqr.png", { imageSize: 720, quietZone: 4 });
|
|
756
|
+
await saveSVG(code, "quadqr.svg", { imageSize: 720, quietZone: 4 });
|
|
594
757
|
|
|
595
758
|
const result = await scanFile("quadqr.png");
|
|
596
759
|
console.log(result.text);
|
|
@@ -601,7 +764,7 @@ console.log(result.text);
|
|
|
601
764
|
The `quadqr-js` package can be loaded directly from npm-backed CDNs:
|
|
602
765
|
|
|
603
766
|
```html
|
|
604
|
-
<script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.0
|
|
767
|
+
<script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.1.0/dist/quadqr.min.js"></script>
|
|
605
768
|
<script>
|
|
606
769
|
const code = QuadQR.encodeText("Hello from a script tag");
|
|
607
770
|
</script>
|
|
@@ -618,6 +781,16 @@ npx quadqr-js encode "Hello QuadQR" -o hello.png
|
|
|
618
781
|
npx quadqr-js decode hello.png
|
|
619
782
|
```
|
|
620
783
|
|
|
784
|
+
Compressed, print-safe, and signed output are also available:
|
|
785
|
+
|
|
786
|
+
```bash
|
|
787
|
+
npx quadqr-js encode "repeat repeat repeat" --compression auto -o compressed.svg
|
|
788
|
+
npx quadqr-js encode "Print me" --print -o print.svg
|
|
789
|
+
npx quadqr-js signkeygen -o signing-key.json
|
|
790
|
+
npx quadqr-js encode "Verified ticket" --sign-key signing-key.json -o signed.png
|
|
791
|
+
npx quadqr-js decode signed.png --verify-key signing-key.json
|
|
792
|
+
```
|
|
793
|
+
|
|
621
794
|
Password-protected payloads use the same commands:
|
|
622
795
|
|
|
623
796
|
```bash
|
|
@@ -625,7 +798,7 @@ npx quadqr-js encode "Private data" --password "my-password" -o secure.png
|
|
|
625
798
|
npx quadqr-js decode secure.png --password "my-password"
|
|
626
799
|
```
|
|
627
800
|
|
|
628
|
-
See [`docs/CLI.md`](docs/CLI.md) for all CLI options, including raw 256-bit key mode.
|
|
801
|
+
See [`docs/CLI.md`](docs/CLI.md) for all CLI options, including compression, raw 256-bit key mode, signing, print mode, and scanner diagnostics.
|
|
629
802
|
|
|
630
803
|
### Run from source
|
|
631
804
|
|
|
@@ -862,7 +1035,7 @@ The result reports fields such as `spectralInterleaving`, `confidenceAssisted`,
|
|
|
862
1035
|
|
|
863
1036
|
### `renderToCanvas(codeOrMatrix, canvas, options?)`
|
|
864
1037
|
|
|
865
|
-
Renders a QuadQR symbol into a browser canvas. `options.style` supports `classic`, `depth`, `soft`, and `inset`.
|
|
1038
|
+
Renders a QuadQR symbol into a browser canvas. `imageSize` sets the exact square pixel output and defaults to 720 when neither sizing option is supplied. `moduleSize` remains available for legacy pixels-per-module sizing. `options.style` supports `classic`, `depth`, `soft`, and `inset`. `quietZone` controls the border in modules. `logo` accepts a loaded image/canvas source or `{ source, size, clearBackground, padding, radius, backgroundColor }`.
|
|
866
1039
|
|
|
867
1040
|
### `renderToImageData(codeOrMatrix, options?)`
|
|
868
1041
|
|
|
@@ -878,9 +1051,15 @@ Returns an ImageData-like object and supports the same rendering styles as `rend
|
|
|
878
1051
|
|
|
879
1052
|
This is also useful for tests and non-DOM workflows.
|
|
880
1053
|
|
|
1054
|
+
When a logo is used with `renderToImageData()`, its source must be an ImageData-like `{ width, height, data }` object so the renderer can composite it without DOM APIs.
|
|
1055
|
+
|
|
1056
|
+
### `renderToSVG(codeOrMatrix, options?)`
|
|
1057
|
+
|
|
1058
|
+
Returns a standalone SVG string using the same exact `imageSize`, render styles, and quiet-zone controls. SVG logo sources can be a URL/data URL string or an object with a `src` string. The SVG remains vector-sharp regardless of how large the preview is displayed.
|
|
1059
|
+
|
|
881
1060
|
### `scanImageData(imageData, options?)`
|
|
882
1061
|
|
|
883
|
-
Runs the complete perspective-aware and color-aware image scanner. The scanner first tries the normal detected geometry with the observed RGB palette, preserving the fast path for clean images. Only after that fails does it progressively fall back to per-channel white balancing, spatial black/white normalization, tighter centre sampling, a cheap module-grid Auto Tone / Auto Contrast / Auto Color-style recovery, a rectified QR-region pixel enhancement pass, and finally bounded sub-module geometry micro-refinement. If locator detection itself is weakened by a flat/yellow frame, a full-image enhancement retry is also available. RGBW confidence values are carried into Reed-Solomon so ambiguous cells can be treated as erasures when ordinary hard-decision ECC is insufficient.
|
|
1062
|
+
Runs the complete perspective-aware and color-aware image scanner. The scanner first tries the normal detected geometry with the observed RGB palette, preserving the fast path for clean images. Dense versions use distributed alignment markers to refine a plausible but imperfect projective solution, and a two-finder recovery pass can rescue a third locator that has been stretched by perspective. Only after that fails does it progressively fall back to per-channel white balancing, spatial black/white normalization, tighter centre sampling, a cheap module-grid Auto Tone / Auto Contrast / Auto Color-style recovery, a rectified QR-region pixel enhancement pass, and finally bounded sub-module geometry micro-refinement. If locator detection itself is weakened by a flat/yellow frame, a full-image enhancement retry is also available. RGBW confidence values are carried into Reed-Solomon so ambiguous cells can be treated as erasures when ordinary hard-decision ECC is insufficient.
|
|
884
1063
|
|
|
885
1064
|
### `scanFile(file, options?)`
|
|
886
1065
|
|
|
@@ -892,7 +1071,7 @@ Scans one frame from an HTML video element. By default, if the video is displaye
|
|
|
892
1071
|
|
|
893
1072
|
### `startCameraScanner(video, options?)`
|
|
894
1073
|
|
|
895
|
-
Starts a reusable live-camera scanning loop. On supported browsers it requests continuous focus/exposure/white-balance camera modes and scans the CSS-visible preview crop. A normal frame always gets the fast RGB-value finder pass first. If
|
|
1074
|
+
Starts a reusable live-camera scanning loop. On supported browsers it requests continuous focus/exposure/white-balance camera modes and scans the CSS-visible preview crop. A normal frame always gets the fast RGB-value finder pass first. If a miss still exposes at least two strong finder patterns, QuadQR retries the visible camera ROI at up to 1600 px by default so dense symbols retain more pixels per module. This high-resolution retry is bounded and does not run on empty frames. If it still fails, the **same captured frame** enters a QR-guide recovery path: QuadQR progressively crops away 8%, 16%, and 22% of the surrounding camera frame (then tries the full frame as a final fallback), applies the Photoshop-style Auto Color correction inside that code-centric region, and runs finder detection again. This matters because a live preview can contain dark room pixels, browser chrome, a monitor bezel, or other content that completely changes global Auto Color/Otsu statistics even though a manually cropped screenshot scans instantly. Normal scanning stays unchanged and fast because these recovery paths run only after a miss. Finder-only recovery also tries multiple center-weighted Auto Color histograms before threshold bracketing. `cameraHighResolutionMaxDimension` defaults to 1600, `cameraHighResolutionEvery` defaults to 2, and `cameraAutoColorEvery` defaults to 1. Multi-frame voting remains enabled by default with a four-frame history. The optional `onDiagnostic(event)` callback exposes finder candidates, active locator method, crop/geometry/version hypothesis, recovery method, timing, and scan dimensions. `onResult(result, frame)` receives the exact raw decoded camera frame and, when Auto Color was used, the enhanced recovery pixels and their crop rectangle, so UIs can keep the frozen frame and finder overlay aligned.
|
|
896
1075
|
|
|
897
1076
|
### `getVersionInfo(version, options?)`
|
|
898
1077
|
|