quadqr-js 1.0.2 → 1.2.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 +11 -0
- package/README.md +165 -11
- package/SPECIFICATION.md +332 -0
- package/bin/quadqr.js +102 -13
- package/dist/esm/benchmark.js +80 -0
- package/dist/esm/node.js +13 -1
- package/dist/esm/quadqr.js +1435 -74
- package/dist/quadqr.js +1435 -76
- package/dist/quadqr.min.js +1429 -76
- package/docs/API.md +220 -4
- package/docs/BROWSER_CDN.md +51 -6
- package/docs/CLI.md +150 -76
- package/docs/GETTING_STARTED.md +80 -7
- package/docs/NODE.md +137 -123
- package/docs/README.md +59 -41
- package/docs/WASM.md +2 -2
- package/package.json +8 -3
- package/types/benchmark.d.ts +4 -3
- package/types/index.d.ts +115 -1
- package/types/node.d.ts +2 -0
package/docs/API.md
CHANGED
|
@@ -110,12 +110,32 @@ Renders to an HTML canvas.
|
|
|
110
110
|
|
|
111
111
|
```js
|
|
112
112
|
renderToCanvas(code, canvas, {
|
|
113
|
-
|
|
113
|
+
imageSize: 720,
|
|
114
114
|
quietZone: 4,
|
|
115
|
-
style: "classic"
|
|
115
|
+
style: "classic",
|
|
116
|
+
logo: {
|
|
117
|
+
source: loadedImage,
|
|
118
|
+
size: 0.12,
|
|
119
|
+
clearBackground: true,
|
|
120
|
+
padding: 0.65,
|
|
121
|
+
radius: 0.8
|
|
122
|
+
}
|
|
116
123
|
});
|
|
117
124
|
```
|
|
118
125
|
|
|
126
|
+
`imageSize` sets the exact square output width/height in pixels. The default is `720` when neither `imageSize` nor `moduleSize` is provided. `moduleSize` remains supported as a lower-level pixels-per-module sizing option and is used when `imageSize` is omitted.
|
|
127
|
+
|
|
128
|
+
`quietZone` is measured in modules. The default is `4`.
|
|
129
|
+
|
|
130
|
+
Logo options:
|
|
131
|
+
|
|
132
|
+
- `source`: loaded CanvasImageSource for canvas rendering, ImageData-like RGBA data for `renderToImageData()`, or URL/data URL for SVG.
|
|
133
|
+
- `size`: fraction of the matrix width/height, clamped to `0.05..0.30`. Default `0.18`.
|
|
134
|
+
- `clearBackground`: clears modules behind the logo using a solid background before drawing the logo.
|
|
135
|
+
- `padding`: clear-background padding in modules. Default `0.65`.
|
|
136
|
+
- `radius`: clear-background corner radius in modules. Default `0.8`.
|
|
137
|
+
- `backgroundColor`: background color when clearing. Defaults to palette white.
|
|
138
|
+
|
|
119
139
|
Supported styles:
|
|
120
140
|
|
|
121
141
|
- `classic`
|
|
@@ -135,6 +155,23 @@ Returns runtime-neutral RGBA pixels:
|
|
|
135
155
|
}
|
|
136
156
|
```
|
|
137
157
|
|
|
158
|
+
### `renderToSVG(codeOrMatrix, options?)`
|
|
159
|
+
|
|
160
|
+
Returns a standalone SVG string. It supports the same exact `imageSize`, palette, render style, quiet-zone, and logo geometry options as the browser canvas renderer. Because the preview/export is vector, it stays sharp when displayed above or below its nominal pixel size.
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
const svg = renderToSVG(code, {
|
|
164
|
+
imageSize: 720,
|
|
165
|
+
quietZone: 6,
|
|
166
|
+
style: "soft",
|
|
167
|
+
logo: {
|
|
168
|
+
source: "data:image/png;base64,...",
|
|
169
|
+
size: 0.12,
|
|
170
|
+
clearBackground: true
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
138
175
|
## Image and camera scanning
|
|
139
176
|
|
|
140
177
|
### `scanImageData(imageData, options?)`
|
|
@@ -225,7 +262,22 @@ Writes a PNG file.
|
|
|
225
262
|
|
|
226
263
|
```js
|
|
227
264
|
await savePNG(code, "quadqr.png", {
|
|
228
|
-
|
|
265
|
+
imageSize: 720,
|
|
266
|
+
quietZone: 4
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### `toSVG(codeOrMatrix, options?)`
|
|
271
|
+
|
|
272
|
+
Returns the standalone SVG string from the Node entry point.
|
|
273
|
+
|
|
274
|
+
### `saveSVG(codeOrMatrix, filename, options?)`
|
|
275
|
+
|
|
276
|
+
Writes SVG directly to disk.
|
|
277
|
+
|
|
278
|
+
```js
|
|
279
|
+
await saveSVG(code, "quadqr.svg", {
|
|
280
|
+
imageSize: 720,
|
|
229
281
|
quietZone: 4
|
|
230
282
|
});
|
|
231
283
|
```
|
|
@@ -315,6 +367,10 @@ Common exported constants include:
|
|
|
315
367
|
- `CELL`
|
|
316
368
|
- `DEFAULT_PALETTE`
|
|
317
369
|
- `RENDER_STYLES`
|
|
370
|
+
- `RENDER_MODES`
|
|
371
|
+
- `COMPRESSION_MODES`
|
|
372
|
+
- `SIGNATURE_ALGORITHMS`
|
|
373
|
+
- `STRESS_PROFILES`
|
|
318
374
|
- `ECC_LEVELS`
|
|
319
375
|
|
|
320
376
|
## Benchmark entry
|
|
@@ -324,6 +380,166 @@ Benchmark helpers are available from `quadqr-js/benchmark`:
|
|
|
324
380
|
```js
|
|
325
381
|
import {
|
|
326
382
|
buildCapacityComparison,
|
|
327
|
-
benchmarkCodec
|
|
383
|
+
benchmarkCodec,
|
|
384
|
+
calculateCapacityPlan
|
|
328
385
|
} from "quadqr-js/benchmark";
|
|
329
386
|
```
|
|
387
|
+
|
|
388
|
+
### `calculateCapacityPlan(options)`
|
|
389
|
+
|
|
390
|
+
Estimates the smallest QuadQR version for a byte count or concrete payload and reports remaining capacity plus the standard QR byte-mode version at the same nominal ECC letter. When only a byte count is known, compression gain is intentionally not guessed.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Compression
|
|
395
|
+
|
|
396
|
+
### `encodeText(text, options?)` / `encodeBytes(input, options?)`
|
|
397
|
+
|
|
398
|
+
Both normal encoding APIs accept an optional compression mode:
|
|
399
|
+
|
|
400
|
+
```js
|
|
401
|
+
const code = QuadQR.encodeText("hello hello hello", {
|
|
402
|
+
compression: "auto",
|
|
403
|
+
ecc: "M"
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
`compression` may be `none`, `auto`, or `lz`.
|
|
408
|
+
|
|
409
|
+
- `none` stores the payload directly.
|
|
410
|
+
- `auto` compresses only when the result is meaningfully smaller. If compression does not help, no internal envelope is added.
|
|
411
|
+
- `lz` always stores the payload through QuadQR's portable LZ compressor.
|
|
412
|
+
|
|
413
|
+
There is no public content-type registry. Text remains text and byte arrays remain byte arrays.
|
|
414
|
+
|
|
415
|
+
### `compressPayload(input)` / `decompressPayload(input, expectedLength?)`
|
|
416
|
+
|
|
417
|
+
Portable synchronous LZSS-style compression helpers. They do not require Node zlib or browser `CompressionStream`.
|
|
418
|
+
|
|
419
|
+
## Binary convenience APIs
|
|
420
|
+
|
|
421
|
+
### `encodeUint8Array(input, options?)`
|
|
422
|
+
|
|
423
|
+
Explicit byte-oriented alias of `encodeBytes()`. It supports the same optional `compression` setting.
|
|
424
|
+
|
|
425
|
+
### `decodeUint8Array(matrix, options?)`
|
|
426
|
+
|
|
427
|
+
Decodes a matrix and returns only the application payload bytes.
|
|
428
|
+
|
|
429
|
+
## Signed QuadQR
|
|
430
|
+
|
|
431
|
+
### `generateSigningKeyPair()`
|
|
432
|
+
|
|
433
|
+
Generates an extractable Ed25519 key pair using Web Crypto and returns the CryptoKeys, raw public-key bytes, PKCS#8 private-key bytes, and a compact SHA-256-derived `keyId`.
|
|
434
|
+
|
|
435
|
+
### `encodeSignedText(text, options)` / `encodeSignedBytes(input, options)`
|
|
436
|
+
|
|
437
|
+
Signs the normal application payload with Ed25519. Any required signature and compression metadata is handled internally.
|
|
438
|
+
|
|
439
|
+
```js
|
|
440
|
+
const pair = await QuadQR.generateSigningKeyPair();
|
|
441
|
+
const code = await QuadQR.encodeSignedText("certificate payload", {
|
|
442
|
+
ecc: "Q",
|
|
443
|
+
compression: "auto",
|
|
444
|
+
privateKey: pair.privateKey,
|
|
445
|
+
keyId: pair.keyId
|
|
446
|
+
});
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Only the private key is required for signing. `publicKey` is accepted only for the explicit `embedPublicKey: true` compatibility mode.
|
|
450
|
+
|
|
451
|
+
### `verifyDecodedSignature(result, options?)`
|
|
452
|
+
|
|
453
|
+
Verifies a signed decode result against a trusted external Ed25519 public key.
|
|
454
|
+
|
|
455
|
+
```js
|
|
456
|
+
const decoded = QuadQR.decodeMatrix(code.matrix);
|
|
457
|
+
const verified = await QuadQR.verifyDecodedSignature(decoded, {
|
|
458
|
+
publicKey: knownPublicKey
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
For multiple issuers, pass a `trustedKeys` object or `Map` keyed by the embedded `keyId`:
|
|
463
|
+
|
|
464
|
+
```js
|
|
465
|
+
const verified = await QuadQR.verifyDecodedSignature(decoded, {
|
|
466
|
+
trustedKeys: {
|
|
467
|
+
[decoded.signingKeyId]: knownPublicKey
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
`signatureVerified: true` means the signature is mathematically valid. `signatureTrusted: true` means it was checked using an external trusted key. `allowEmbeddedKey: true` may be used for compatibility/self-contained integrity checks, but such a result is not considered a trusted signer.
|
|
473
|
+
|
|
474
|
+
### Signing + encryption
|
|
475
|
+
|
|
476
|
+
`encodeSecureText()` and `encodeSecureBytes()` accept an optional `signing` object alongside `security` and `compression`. The internal pipeline is compression → signing → AES-256-GCM → Spectrum ECC.
|
|
477
|
+
|
|
478
|
+
```js
|
|
479
|
+
const code = await QuadQR.encodeSecureText("private signed payload", {
|
|
480
|
+
compression: "auto",
|
|
481
|
+
security: { mode: "password", password: "secret" },
|
|
482
|
+
signing: {
|
|
483
|
+
privateKey: pair.privateKey,
|
|
484
|
+
keyId: pair.keyId
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
## Print rendering
|
|
490
|
+
|
|
491
|
+
All render APIs accept:
|
|
492
|
+
|
|
493
|
+
```js
|
|
494
|
+
{ mode: "screen" | "print" }
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Print mode uses a darker print-safe palette, forces Classic modules by default, and enforces a minimum 4-module quiet zone unless `allowUnsafePrintQuietZone: true` is explicitly set.
|
|
498
|
+
|
|
499
|
+
### `getPrintGuidance(codeOrMatrix, options?)`
|
|
500
|
+
|
|
501
|
+
Returns physical module size, pixels/module at a DPI, recommended minimum physical size, and print recommendations.
|
|
502
|
+
|
|
503
|
+
## Automatic logo safety
|
|
504
|
+
|
|
505
|
+
A logo can use:
|
|
506
|
+
|
|
507
|
+
```js
|
|
508
|
+
logo: {
|
|
509
|
+
source: logo,
|
|
510
|
+
size: "auto",
|
|
511
|
+
clearBackground: true
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
`estimateSafeLogoSize()` exposes the conservative ratio used by auto mode. `findMaxSafeLogoSize()` performs an empirical binary search when the logo source is ImageData-like.
|
|
516
|
+
|
|
517
|
+
## Scanner confidence and debug mode
|
|
518
|
+
|
|
519
|
+
Normal successful scans now include:
|
|
520
|
+
|
|
521
|
+
- `confidence`
|
|
522
|
+
- `geometryConfidence`
|
|
523
|
+
- `calibrationConfidence`
|
|
524
|
+
- `structureConfidence`
|
|
525
|
+
- `eccUtilization`
|
|
526
|
+
- `correctedErrors`
|
|
527
|
+
- `diagnostics`
|
|
528
|
+
|
|
529
|
+
Use `scanImageData(image, { debug: true })` for detailed geometry/sampling information. `debugScanImageData()` is a non-throwing helper that returns `{ ok, result, debug }` or `{ ok: false, error, debug }`.
|
|
530
|
+
|
|
531
|
+
## Scanability and stress testing
|
|
532
|
+
|
|
533
|
+
### `applyStressDistortion(imageData, type, severity?)`
|
|
534
|
+
|
|
535
|
+
Deterministically applies a selected synthetic distortion.
|
|
536
|
+
|
|
537
|
+
### `runImageStressTest(imageData, expected?, options?)`
|
|
538
|
+
|
|
539
|
+
Runs the standard torture profiles and returns a 0–100 score plus per-scenario decode results.
|
|
540
|
+
|
|
541
|
+
### `assessScanability(code, renderOptions?, options?)`
|
|
542
|
+
|
|
543
|
+
Renders a code and runs the standard test suite. It returns `Excellent`, `Good`, `Risky`, or `Likely unscannable` together with recommendations.
|
|
544
|
+
|
|
545
|
+
Synthetic scores are regression aids and do not replace physical camera/print testing.
|
package/docs/BROWSER_CDN.md
CHANGED
|
@@ -8,6 +8,7 @@ With Vite, webpack, Rollup, Next.js client code, or another browser bundler:
|
|
|
8
8
|
import {
|
|
9
9
|
encodeText,
|
|
10
10
|
renderToCanvas,
|
|
11
|
+
renderToSVG,
|
|
11
12
|
scanFile,
|
|
12
13
|
startCameraScanner
|
|
13
14
|
} from "quadqr-js/browser";
|
|
@@ -21,14 +22,58 @@ The main `quadqr-js` entry also works in modern browser bundlers for runtime-neu
|
|
|
21
22
|
const code = encodeText("Hello browser");
|
|
22
23
|
|
|
23
24
|
renderToCanvas(code, document.querySelector("#qr"), {
|
|
24
|
-
|
|
25
|
+
imageSize: 720,
|
|
25
26
|
quietZone: 4,
|
|
26
27
|
style: "classic"
|
|
27
28
|
});
|
|
29
|
+
|
|
30
|
+
const svg = renderToSVG(code, {
|
|
31
|
+
imageSize: 720,
|
|
32
|
+
quietZone: 4
|
|
33
|
+
});
|
|
28
34
|
```
|
|
29
35
|
|
|
36
|
+
`imageSize` controls the exact square output size. If neither `imageSize` nor `moduleSize` is supplied, the renderer defaults to 720 × 720 px.
|
|
37
|
+
|
|
30
38
|
Available styles are `classic`, `depth`, `soft`, and `inset`.
|
|
31
39
|
|
|
40
|
+
## Compressed, signed, and print-safe output
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
import {
|
|
44
|
+
assessScanability,
|
|
45
|
+
decodeMatrix,
|
|
46
|
+
encodeText,
|
|
47
|
+
encodeSignedText,
|
|
48
|
+
generateSigningKeyPair,
|
|
49
|
+
renderToCanvas,
|
|
50
|
+
verifyDecodedSignature
|
|
51
|
+
} from "quadqr-js/browser";
|
|
52
|
+
|
|
53
|
+
const compressed = encodeText("repeat repeat repeat repeat", {
|
|
54
|
+
compression: "auto"
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const keys = await generateSigningKeyPair();
|
|
58
|
+
const signed = await encodeSignedText("ticket", {
|
|
59
|
+
compression: "auto",
|
|
60
|
+
privateKey: keys.privateKey,
|
|
61
|
+
keyId: keys.keyId
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const checked = await verifyDecodedSignature(decodeMatrix(signed.matrix), {
|
|
65
|
+
publicKey: keys.publicKey
|
|
66
|
+
});
|
|
67
|
+
console.log(checked.signatureVerified, checked.signatureTrusted);
|
|
68
|
+
|
|
69
|
+
const canvas = document.querySelector("#qr");
|
|
70
|
+
renderToCanvas(compressed, canvas, { mode: "print", imageSize: 720 });
|
|
71
|
+
const report = assessScanability(compressed, { imageSize: 480 });
|
|
72
|
+
console.log(report.score, report.rating);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Compression and signing use internal metadata only when required. There is no public content-type mode to configure. The private key signs, while the public verification key stays outside the QuadQR by default. Use the stored `keyId` to select an application-trusted public key. Scanability testing is deterministic synthetic regression testing and should be supplemented with real devices and print samples.
|
|
76
|
+
|
|
32
77
|
## Scan an uploaded image
|
|
33
78
|
|
|
34
79
|
```js
|
|
@@ -88,12 +133,12 @@ The global browser build exposes `window.QuadQR` / `globalThis.QuadQR`.
|
|
|
88
133
|
|
|
89
134
|
```html
|
|
90
135
|
<canvas id="qr"></canvas>
|
|
91
|
-
<script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.0
|
|
136
|
+
<script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.1.0/dist/quadqr.min.js"></script>
|
|
92
137
|
<script>
|
|
93
138
|
const code = QuadQR.encodeText("Hello CDN");
|
|
94
139
|
|
|
95
140
|
QuadQR.renderToCanvas(code, document.querySelector("#qr"), {
|
|
96
|
-
|
|
141
|
+
imageSize: 720,
|
|
97
142
|
quietZone: 4
|
|
98
143
|
});
|
|
99
144
|
</script>
|
|
@@ -102,7 +147,7 @@ The global browser build exposes `window.QuadQR` / `globalThis.QuadQR`.
|
|
|
102
147
|
## unpkg
|
|
103
148
|
|
|
104
149
|
```html
|
|
105
|
-
<script src="https://unpkg.com/quadqr-js@1.0
|
|
150
|
+
<script src="https://unpkg.com/quadqr-js@1.1.0/dist/quadqr.min.js"></script>
|
|
106
151
|
```
|
|
107
152
|
|
|
108
153
|
## Direct CDN ESM
|
|
@@ -112,7 +157,7 @@ The global browser build exposes `window.QuadQR` / `globalThis.QuadQR`.
|
|
|
112
157
|
import {
|
|
113
158
|
encodeText,
|
|
114
159
|
renderToCanvas
|
|
115
|
-
} from "https://cdn.jsdelivr.net/npm/quadqr-js@1.0
|
|
160
|
+
} from "https://cdn.jsdelivr.net/npm/quadqr-js@1.1.0/dist/browser.js";
|
|
116
161
|
|
|
117
162
|
const code = encodeText("ES module CDN");
|
|
118
163
|
renderToCanvas(code, document.querySelector("#qr"));
|
|
@@ -139,4 +184,4 @@ WASM is optional. The normal JavaScript implementation remains available if it c
|
|
|
139
184
|
|
|
140
185
|
## Production recommendation
|
|
141
186
|
|
|
142
|
-
Pin a concrete package version such as `@1.0
|
|
187
|
+
Pin a concrete package version such as `@1.1.0` in CDN URLs so an existing site does not silently change when a newer package version becomes available.
|
package/docs/CLI.md
CHANGED
|
@@ -1,76 +1,150 @@
|
|
|
1
|
-
# Command Line Interface
|
|
2
|
-
|
|
3
|
-
The npm package includes the `quadqr` executable and can be used directly through `npx`.
|
|
4
|
-
|
|
5
|
-
## Encode text
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npx quadqr-js encode "Hello QuadQR" -o hello.png
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
1
|
+
# Command Line Interface
|
|
2
|
+
|
|
3
|
+
The npm package includes the `quadqr` executable and can be used directly through `npx`.
|
|
4
|
+
|
|
5
|
+
## Encode text
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx quadqr-js encode "Hello QuadQR" -o hello.png
|
|
9
|
+
npx quadqr-js encode "Hello QuadQR" -o hello.svg
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Optional encoding controls:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx quadqr-js encode "Hello" --ecc M --version auto --image-size 720 --quiet-zone 4 -o hello.png
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Use the print-safe rendering profile when the symbol is intended for physical output:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx quadqr-js encode "Print me" --print -o print.svg
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Print mode enforces a minimum four-module quiet zone and uses the print-safe rendering defaults.
|
|
25
|
+
|
|
26
|
+
## Compression
|
|
27
|
+
|
|
28
|
+
Compression works directly with normal text payloads:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx quadqr-js encode "repeat repeat repeat repeat" \
|
|
32
|
+
--compression auto \
|
|
33
|
+
-o compressed.png
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Compression modes are `none`, `auto`, and `lz`. `auto` keeps the original payload untouched when compression does not make it smaller. No separate payload mode is required.
|
|
37
|
+
|
|
38
|
+
## Signed QuadQR
|
|
39
|
+
|
|
40
|
+
Generate an Ed25519 signing-key bundle:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx quadqr-js signkeygen -o quadqr-signing-key.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Keep this file secret because it contains the private signing key. Encode a signed payload with:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx quadqr-js encode "Offline-verifiable ticket" \
|
|
50
|
+
--sign-key quadqr-signing-key.json \
|
|
51
|
+
-o signed.png
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The generated key bundle contains both keys plus a compact `keyId`. Only the private key signs. The public key is **not embedded** in the QuadQR by default and should be distributed separately to trusted scanners or stored in a trusted-key registry.
|
|
55
|
+
|
|
56
|
+
To override the identifier stored in the symbol:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx quadqr-js encode "Offline-verifiable ticket" \
|
|
60
|
+
--sign-key quadqr-signing-key.json \
|
|
61
|
+
--key-id event-main-2026 \
|
|
62
|
+
-o signed.png
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Signing can be combined with password or raw-key encryption:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx quadqr-js encode "Signed and private" \
|
|
69
|
+
--sign-key quadqr-signing-key.json \
|
|
70
|
+
--password "my-password" \
|
|
71
|
+
-o signed-secure.png
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Decode an image
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx quadqr-js decode hello.png
|
|
78
|
+
npx quadqr-js decode signed.png --verify-key quadqr-signing-key.json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
For an unencrypted text payload, the decoded text is printed to stdout. To verify a signed symbol against a trusted public key, pass the signing bundle with `--verify-key`.
|
|
82
|
+
|
|
83
|
+
Add scanner diagnostics without changing the normal stdout payload:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx quadqr-js decode hello.png --debug
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Diagnostics are written to stderr and include confidence, geometry/color confidence, ECC utilization, signing state, and the scanner diagnostics object when available.
|
|
90
|
+
|
|
91
|
+
## Password-protected payloads
|
|
92
|
+
|
|
93
|
+
Encode:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx quadqr-js encode "Private data" --password "my-password" -o secure.png
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Decode:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npx quadqr-js decode secure.png --password "my-password"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If an encrypted symbol is decoded without a credential, the CLI reports that decryption is required instead of exposing plaintext.
|
|
106
|
+
|
|
107
|
+
## Raw 256-bit key mode
|
|
108
|
+
|
|
109
|
+
Generate a random 256-bit encryption key:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx quadqr-js keygen
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The output is a 64-character hexadecimal key. Store it securely and do not place it inside the same QuadQR symbol.
|
|
116
|
+
|
|
117
|
+
Encode using the key:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npx quadqr-js encode "Application secret" --key <64-hex-key> -o secure-key.png
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Decode using the key:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npx quadqr-js decode secure-key.png --key <64-hex-key>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Options
|
|
130
|
+
|
|
131
|
+
| Option | Purpose |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| `-o, --output <file>` | Output PNG/SVG path, or signing-key JSON path for `signkeygen` |
|
|
134
|
+
| `--ecc <L|M|Q|H>` | QuadQR ECC profile. Default: `M` |
|
|
135
|
+
| `--version <auto|1..40>` | Symbol version. Default: `auto` |
|
|
136
|
+
| `--compression <mode>` | `none`, `auto`, or `lz`. Default: `auto` |
|
|
137
|
+
| `--sign-key <file>` | Sign using a key bundle generated by `signkeygen` |
|
|
138
|
+
| `--key-id <id>` | Override the signing key ID stored in the symbol |
|
|
139
|
+
| `--embed-public-key` | Explicit compatibility mode that embeds the public key |
|
|
140
|
+
| `--verify-key <file>` | Verify a signed symbol with a trusted Ed25519 key bundle |
|
|
141
|
+
| `--password <text>` | Password-mode encryption/decryption |
|
|
142
|
+
| `--key <hex>` | Raw 256-bit key encryption/decryption |
|
|
143
|
+
| `--print` | Use the print-safe render profile |
|
|
144
|
+
| `--image-size <px>` | Exact square output size in pixels. Default: `720` |
|
|
145
|
+
| `--module-size <px>` | Legacy pixels-per-module sizing. Used when `--image-size` is omitted |
|
|
146
|
+
| `--quiet-zone <modules>` | Quiet-zone size in modules. Default: `4` |
|
|
147
|
+
| `--debug` | Emit scanner diagnostics to stderr when decoding |
|
|
148
|
+
| `-h, --help` | Show CLI help |
|
|
149
|
+
|
|
150
|
+
Password mode and raw-key mode are mutually exclusive for a single operation.
|