quadqr-js 0.7.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 ADDED
@@ -0,0 +1,390 @@
1
+ # QuadQR Format v5
2
+
3
+ ## Status
4
+
5
+ Experimental custom symbology. It is not ISO QR Code.
6
+
7
+ Only this RGBW format is implemented. Earlier RGB/ternary formats are intentionally not supported.
8
+
9
+ ## Matrix sizes
10
+
11
+ ```text
12
+ size = 21 + 4 * (version - 1)
13
+ ```
14
+
15
+ Supported versions: 1 through 40.
16
+
17
+ ## Cell alphabet
18
+
19
+ Structural black:
20
+
21
+ ```text
22
+ BLACK = -1
23
+ ```
24
+
25
+ Four data states:
26
+
27
+ | Cell | 2-bit value | Internal value |
28
+ |---|---|---:|
29
+ | Red | `00` | 0 |
30
+ | Green | `01` | 1 |
31
+ | Blue | `10` | 2 |
32
+ | White | `11` | 3 |
33
+
34
+ Structural white and data white intentionally share the same visible/internal value. Reserved-position geometry distinguishes their roles.
35
+
36
+ ## Finder structures
37
+
38
+ 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.
39
+
40
+ ## Alignment patterns
41
+
42
+ QuadQR always keeps exactly three primary 7×7 finder patterns. Larger versions do **not** add more primary finders. Instead, versions 2 through 40 use distributed black/white alignment markers following the same center-position schedule used by standard QR Code versions. Exactly one bottom-right **primary alignment marker remains 5×5**. Every additional distributed alignment marker is **3×3**, encoded as a black outer ring with a white center.
43
+
44
+ The three alignment positions that would overlap the primary finder corners are omitted. This produces progressively more alignment references as the matrix grows. Examples:
45
+
46
+ ```text
47
+ v2 -> 1 alignment pattern
48
+ v7 -> 6 alignment patterns
49
+ v14 -> 13 alignment patterns
50
+ v28 -> 33 alignment patterns
51
+ v40 -> 46 alignment patterns
52
+ ```
53
+
54
+ Version 1 is a QuadQR-specific exception. Standard QR v1 has no alignment pattern, but QuadQR keeps one legacy 5×5 bottom-right bootstrap alignment marker with a one-cell white separator so the camera scanner still has a fourth projective reference point.
55
+
56
+ For versions 2 through 40, the scanner uses the 5×5 bottom-right member of the distributed alignment grid as the primary fourth homography reference and then scores the full expected grid, including the 3×3 secondary markers, to strengthen version/geometry validation.
57
+
58
+ ## Timing structures
59
+
60
+ Alternating black/white timing cells use row 6 and column 6 between the main finder regions.
61
+
62
+ ## Color calibration
63
+
64
+ Twelve reserved cells provide three 2×2 color patches:
65
+
66
+ - 4 red cells
67
+ - 4 green cells
68
+ - 4 blue cells
69
+
70
+ Known finder/separator/alignment cells provide black and white references. Therefore white is calibrated without requiring a separate white swatch.
71
+
72
+ ## Data placement
73
+
74
+ Data positions use a two-column vertical zig-zag beginning at the bottom-right. Reserved finder, separator, timing, alignment, and calibration cells are skipped.
75
+
76
+ ## Byte-to-cell mapping
77
+
78
+ Each byte is serialized most-significant pair first:
79
+
80
+ ```text
81
+ bits 7..6 -> cell 0
82
+ bits 5..4 -> cell 1
83
+ bits 3..2 -> cell 2
84
+ bits 1..0 -> cell 3
85
+ ```
86
+
87
+ Example:
88
+
89
+ ```text
90
+ 11001001
91
+ 11 00 10 01
92
+ W R B G
93
+ ```
94
+
95
+ Therefore:
96
+
97
+ ```text
98
+ 1 byte = exactly 4 RGBW data cells
99
+ 1 RGBW data cell = exactly 2 raw bits
100
+ ```
101
+
102
+ ## Masks
103
+
104
+ Four masks are defined. Each returns a 2-bit value 0..3:
105
+
106
+ ```text
107
+ mask 0 = (row + col) mod 4
108
+ mask 1 = (2*row + col) mod 4
109
+ mask 2 = (row + 2*col) mod 4
110
+ mask 3 = (row*col + row + col) mod 4
111
+ ```
112
+
113
+ Encoding and decoding use XOR:
114
+
115
+ ```text
116
+ visible = raw XOR mask
117
+ raw = visible XOR mask
118
+ ```
119
+
120
+ The encoder evaluates all four masks using run-length and four-state balance penalties. The mask ID is not serialized. The decoder tries all four and accepts only a path whose protected header, ECC, and CRC validate.
121
+
122
+ ## Header
123
+
124
+ ### Versions 2 through 40
125
+
126
+ The normal logical header is 10 bytes.
127
+
128
+ | Offset | Size | Meaning |
129
+ |---|---:|---|
130
+ | 0 | 4 | ASCII magic `QQRW` |
131
+ | 4 | 1 | format version `5` |
132
+ | 5 | 1 | flags |
133
+ | 6 | 4 | payload byte length, big-endian |
134
+
135
+ It is protected with 8 RS parity bytes:
136
+
137
+ ```text
138
+ 10 data bytes + 8 parity bytes = 18 RS bytes
139
+ 18 bytes * 4 cells/byte = 72 data cells
140
+ ```
141
+
142
+ The normal header can correct up to four damaged byte symbols.
143
+
144
+ ### Version 1 compact header
145
+
146
+ The 21×21 symbol uses a compact 4-byte header because the matrix size already identifies version 1.
147
+
148
+ | Offset | Size | Meaning |
149
+ |---|---:|---|
150
+ | 0 | 1 | compact format marker `0xC3` |
151
+ | 1 | 1 | flags |
152
+ | 2 | 1 | payload byte length |
153
+ | 3 | 1 | payload length XOR `0xFF` |
154
+
155
+ The compact header is protected with 4 RS parity bytes:
156
+
157
+ ```text
158
+ 4 data bytes + 4 parity bytes = 8 RS bytes
159
+ 8 bytes * 4 cells/byte = 32 data cells
160
+ ```
161
+
162
+ It corrects up to two damaged header byte symbols. The complemented length byte provides an additional structural validity check.
163
+
164
+ Flags for both header forms:
165
+
166
+ ```text
167
+ bit 0 UTF-8 text flag
168
+ bits 1..2 ECC profile id
169
+ bit 3 Secure Payload envelope flag
170
+ bits 4..7 reserved
171
+ ```
172
+
173
+ ECC ids:
174
+
175
+ ```text
176
+ 0 = L
177
+ 1 = M
178
+ 2 = Q
179
+ 3 = H
180
+ ```
181
+
182
+ ## Secure Payload v1
183
+
184
+ When header flag bit 3 is set, the body payload bytes contain a versioned encrypted envelope instead of plaintext application bytes. Spectrum ECC and CRC operate on the envelope exactly like any other byte payload.
185
+
186
+ The envelope begins with this fixed 24-byte metadata block:
187
+
188
+ | Offset | Size | Field |
189
+ |---:|---:|---|
190
+ | 0 | 4 | ASCII magic `QSEC` |
191
+ | 4 | 1 | Secure Payload version (`1`) |
192
+ | 5 | 1 | Mode (`1` password, `2` raw 256-bit key) |
193
+ | 6 | 1 | Algorithm (`1` AES-256-GCM) |
194
+ | 7 | 1 | KDF (`1` PBKDF2-HMAC-SHA-256, `0` none) |
195
+ | 8 | 1 | Security flags |
196
+ | 9 | 1 | Salt length |
197
+ | 10 | 1 | Nonce length |
198
+ | 11 | 1 | Authentication-tag length |
199
+ | 12 | 1 | Key-ID length |
200
+ | 13 | 3 | Reserved, zero |
201
+ | 16 | 4 | PBKDF2 iterations, big-endian (`0` for raw-key mode) |
202
+ | 20 | 4 | Plaintext byte length, big-endian |
203
+
204
+ The fixed header is followed by:
205
+
206
+ ```text
207
+ keyId || salt || nonce || ciphertext || GCM tag
208
+ ```
209
+
210
+ Current required sizes:
211
+
212
+ ```text
213
+ AES-GCM nonce = 12 bytes
214
+ AES-GCM tag = 16 bytes
215
+ password salt = 16 bytes
216
+ ```
217
+
218
+ ### Password mode
219
+
220
+ Password bytes are UTF-8 encoded and processed with:
221
+
222
+ ```text
223
+ PBKDF2-HMAC-SHA-256
224
+ output = 256-bit AES key
225
+ default iterations = 600000
226
+ ```
227
+
228
+ A fresh random 16-byte salt and 12-byte nonce are generated for every encryption operation.
229
+
230
+ ### Raw 256-bit key mode
231
+
232
+ The application supplies an exact 32-byte key. No password KDF or salt is used. Unless disabled or overridden, the encoder stores the first 8 bytes of `SHA-256(rawKey)` as a non-secret key fingerprint. This key ID is only a routing hint and is not sufficient to decrypt the payload.
233
+
234
+ ### Authentication
235
+
236
+ AES-256-GCM additional authenticated data (AAD) is:
237
+
238
+ ```text
239
+ fixed security header || keyId || salt || nonce
240
+ ```
241
+
242
+ Therefore the mode, KDF settings, key ID, salt, nonce, plaintext length, ciphertext, and authentication tag are cryptographically bound. Decryption must fail if the password/key is wrong or authenticated envelope data was changed.
243
+
244
+ The QuadQR CRC still protects the decoded encrypted envelope against scanner/ECC corruption before any decryption is attempted. AES-GCM authentication then protects the secure payload cryptographically.
245
+
246
+ ## Payload and CRC
247
+
248
+ CRC-32 is calculated over:
249
+
250
+ ```text
251
+ header || payload
252
+ ```
253
+
254
+ Four CRC bytes are appended to the payload before body ECC.
255
+
256
+ ## GF(256) Reed-Solomon
257
+
258
+ Header and body ECC use:
259
+
260
+ ```text
261
+ GF(2^8) = GF(256)
262
+ ```
263
+
264
+ Primitive polynomial:
265
+
266
+ ```text
267
+ x^8 + x^4 + x^3 + x^2 + 1
268
+ 0x11d
269
+ ```
270
+
271
+ A codeword is limited to 255 byte symbols.
272
+
273
+ ## ECC profiles
274
+
275
+ ### Versions 2 through 40
276
+
277
+ | Profile | Parity bytes per body block | Correctable byte symbols per block |
278
+ |---|---:|---:|
279
+ | L | 12 | 6 |
280
+ | M | 24 | 12 |
281
+ | Q | 36 | 18 |
282
+ | H | 48 | 24 |
283
+
284
+ ### Version 1 compact body ECC
285
+
286
+ | Profile | Parity bytes | Correctable byte symbols |
287
+ |---|---:|---:|
288
+ | L | 4 | 2 |
289
+ | M | 8 | 4 |
290
+ | Q | 12 | 6 |
291
+ | H | 16 | 8 |
292
+
293
+ This scaling prevents fixed parity overhead from consuming the entire 21×21 symbol. With the unchanged geometry and CRC-32, v1-M now carries 24 user payload bytes.
294
+
295
+ For each profile:
296
+
297
+ ```text
298
+ max data bytes per block = 255 - parityBytes
299
+ ```
300
+
301
+ Larger bodies are split into multiple RS blocks.
302
+
303
+ ## Interleaving
304
+
305
+ ### Reed-Solomon block interleaving
306
+
307
+ Encoded RS blocks are interleaved column-wise:
308
+
309
+ ```text
310
+ block0[0], block1[0], block2[0], ...,
311
+ block0[1], block1[1], block2[1], ...
312
+ ```
313
+
314
+ The decoder derives block lengths from payload length and ECC profile, reverses the interleaving, and corrects each block independently.
315
+
316
+ ### Spectral-spatial cell interleaving
317
+
318
+ After bytes are split into four 2-bit RGBW cells, the complete logical data-cell stream is mapped through a deterministic version/length-dependent permutation before being written to physical data positions. The permutation is a seeded Fisher-Yates shuffle and contains every physical data-position index exactly once.
319
+
320
+ This layer has **zero capacity overhead**: no extra cells or parity symbols are added. Its purpose is to spread adjacent logical codeword cells across distant physical modules so localized damage is distributed over many RS symbols.
321
+
322
+ Masking is applied using the final physical row/column position. Decoding therefore performs operations in this order:
323
+
324
+ ```text
325
+ physical sampled cells
326
+ -> physical-position unmasking
327
+ -> reverse spectral-spatial permutation
328
+ -> byte reconstruction
329
+ -> RS block deinterleaving
330
+ ```
331
+
332
+ The decoder also tries legacy physical order as a fallback for older QuadQR matrices.
333
+
334
+ ## Padding
335
+
336
+ Unused data positions are filled with deterministic pseudo-random values in the range 0..3. Padding is not semantically decoded.
337
+
338
+ ## Confidence-aware error/erasure decoding
339
+
340
+ For image/camera scans, classification retains more than the winning RGBW state. Each sampled module also receives a confidence score derived from the separation between its nearest and second-nearest calibrated palette states.
341
+
342
+ One GF(256) symbol corresponds to four 2-bit data cells. The symbol confidence is the minimum confidence of those four constituent cells because an error in any one cell changes the reconstructed byte.
343
+
344
+ Decoding first attempts normal hard-decision Reed-Solomon correction. If that fails, low-confidence byte positions are progressively promoted to known erasures and the decoder retries error/erasure RS correction. Valid correction requires syndrome verification and the complete QuadQR payload still must pass CRC-32.
345
+
346
+ The RS budget follows the usual error/erasure relationship:
347
+
348
+ ```text
349
+ 2 * unknownErrors + knownErasures <= paritySymbols
350
+ ```
351
+
352
+ No additional parity is added for this feature, so payload capacity is unchanged.
353
+
354
+ ## Scanner pipeline
355
+
356
+ ```text
357
+ RGB frame
358
+ -> grayscale conversion
359
+ -> global Otsu threshold for structural detection
360
+ -> 1:1:3:1:1 finder candidate detection
361
+ -> three-finder geometric ordering
362
+ -> candidate version estimation
363
+ -> primary bottom-right alignment search
364
+ -> four-point homography
365
+ -> distributed alignment-grid validation
366
+ -> projective module sampling
367
+ -> observed black/white/R/G/B calibration
368
+ -> nearest calibrated RGBW classification + confidence
369
+ -> four-state XOR unmasking
370
+ -> reverse spectral-spatial permutation
371
+ -> protected header GF(256) hard RS decode
372
+ -> confidence-guided error/erasure retry when needed
373
+ -> body block deinterleaving
374
+ -> body GF(256) error/erasure correction
375
+ -> CRC-32 verification
376
+ ```
377
+
378
+ The scanner also has an axis-aligned fallback.
379
+
380
+ ## Rotation
381
+
382
+ Matrix decoding tries 0°, 90°, 180°, and 270° rotations.
383
+
384
+ ## Compatibility
385
+
386
+ Format v5 is intentionally incompatible with standard QR scanners and with the project's older ternary prototypes. The current encoder writes codeword cells using spectral-spatial placement. The current decoder also tries the pre-interleaver physical order as a compatibility fallback for older RGBW QuadQR matrices. Version 5 keeps the distributed alignment-center schedule introduced in v4, but shrinks every non-primary alignment marker from 5×5 to 3×3 while retaining the bottom-right primary marker at 5×5. Because reserved-cell geometry changed, v4 and v5 large-symbol matrices are not wire-compatible.
387
+
388
+ ## Rendering profiles are not part of the wire format
389
+
390
+ 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.