qr-core 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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -0
  3. package/dist/bench/bench.d.ts +1 -0
  4. package/dist/bench/bench.js +38 -0
  5. package/dist/src/api/index.d.ts +9 -0
  6. package/dist/src/api/index.js +224 -0
  7. package/dist/src/core/bit-buffer.d.ts +11 -0
  8. package/dist/src/core/bit-buffer.js +53 -0
  9. package/dist/src/core/error.d.ts +8 -0
  10. package/dist/src/core/error.js +16 -0
  11. package/dist/src/core/gf.d.ts +4 -0
  12. package/dist/src/core/gf.js +52 -0
  13. package/dist/src/core/rs.d.ts +1 -0
  14. package/dist/src/core/rs.js +60 -0
  15. package/dist/src/encoding/bitstream.d.ts +7 -0
  16. package/dist/src/encoding/bitstream.js +68 -0
  17. package/dist/src/encoding/encoders.d.ts +13 -0
  18. package/dist/src/encoding/encoders.js +54 -0
  19. package/dist/src/encoding/interleave.d.ts +5 -0
  20. package/dist/src/encoding/interleave.js +49 -0
  21. package/dist/src/encoding/segmentation.d.ts +12 -0
  22. package/dist/src/encoding/segmentation.js +43 -0
  23. package/dist/src/index.d.ts +3 -0
  24. package/dist/src/index.js +3 -0
  25. package/dist/src/layout/patterns.d.ts +12 -0
  26. package/dist/src/layout/patterns.js +120 -0
  27. package/dist/src/mapping/zigzag.d.ts +5 -0
  28. package/dist/src/mapping/zigzag.js +28 -0
  29. package/dist/src/mask/patterns.d.ts +6 -0
  30. package/dist/src/mask/patterns.js +13 -0
  31. package/dist/src/mask/penalty.d.ts +5 -0
  32. package/dist/src/mask/penalty.js +173 -0
  33. package/dist/src/mask/selection.d.ts +12 -0
  34. package/dist/src/mask/selection.js +178 -0
  35. package/dist/src/matrix/bit-matrix.d.ts +46 -0
  36. package/dist/src/matrix/bit-matrix.js +139 -0
  37. package/dist/src/spec/bch.d.ts +11 -0
  38. package/dist/src/spec/bch.js +30 -0
  39. package/dist/src/spec/constants.d.ts +40 -0
  40. package/dist/src/spec/constants.js +37 -0
  41. package/dist/src/spec/tables.d.ts +20 -0
  42. package/dist/src/spec/tables.js +1099 -0
  43. package/dist/src/strategies/index.d.ts +3 -0
  44. package/dist/src/strategies/index.js +3 -0
  45. package/dist/src/strategies/mask.d.ts +16 -0
  46. package/dist/src/strategies/mask.js +9 -0
  47. package/dist/src/strategies/segmentation.d.ts +12 -0
  48. package/dist/src/strategies/segmentation.js +41 -0
  49. package/dist/src/strategies/version.d.ts +12 -0
  50. package/dist/src/strategies/version.js +22 -0
  51. package/dist/src/types/index.d.ts +51 -0
  52. package/dist/src/types/index.js +1 -0
  53. package/dist/tests/golden/golden.test.d.ts +1 -0
  54. package/dist/tests/golden/golden.test.js +26 -0
  55. package/dist/tests/unit/api.test.d.ts +1 -0
  56. package/dist/tests/unit/api.test.js +35 -0
  57. package/dist/tests/unit/bit-matrix.test.d.ts +1 -0
  58. package/dist/tests/unit/bit-matrix.test.js +46 -0
  59. package/dist/tests/unit/core.test.d.ts +1 -0
  60. package/dist/tests/unit/core.test.js +88 -0
  61. package/dist/tests/unit/encoding.test.d.ts +1 -0
  62. package/dist/tests/unit/encoding.test.js +63 -0
  63. package/dist/tests/unit/layout.test.d.ts +1 -0
  64. package/dist/tests/unit/layout.test.js +62 -0
  65. package/dist/tests/unit/masking.test.d.ts +1 -0
  66. package/dist/tests/unit/masking.test.js +31 -0
  67. package/package.json +45 -0
@@ -0,0 +1,178 @@
1
+ import { Matrix } from "../matrix/bit-matrix.js";
2
+ import { getFormatInfo } from "../spec/bch.js";
3
+ import { calculatePenalty } from "./penalty.js";
4
+ /**
5
+ * Evaluates all 8 masks and applies the one with the lowest penalty.
6
+ * Implements the deterministic tie-break (lowest ID).
7
+ */
8
+ export function selectAndApplyBestMask(matrix, ecc) {
9
+ let minPenalty = Infinity;
10
+ let bestMask = 0;
11
+ const penalties = [];
12
+ // Pre-allocate work matrix to avoid garbage
13
+ const workMatrix = new Matrix(matrix.size);
14
+ for (let id = 0; id < 8; id++) {
15
+ const maskId = id;
16
+ // Reset work matrix
17
+ workMatrix._data.set(matrix._data);
18
+ const currentPenalty = evaluateMask(workMatrix, ecc, maskId);
19
+ penalties.push(currentPenalty);
20
+ if (currentPenalty < minPenalty) {
21
+ minPenalty = currentPenalty;
22
+ bestMask = maskId;
23
+ }
24
+ }
25
+ // Final apply of the winner
26
+ applyMask(matrix, bestMask);
27
+ writeFormatInformation(matrix, ecc, bestMask);
28
+ return { mask: bestMask, penalties };
29
+ }
30
+ function evaluateMask(workMatrix, ecc, maskId) {
31
+ // workMatrix is already a copy/reset
32
+ applyMask(workMatrix, maskId);
33
+ writeFormatInformation(workMatrix, ecc, maskId);
34
+ return calculatePenalty(workMatrix);
35
+ }
36
+ export function applyMask(matrix, maskId) {
37
+ const size = matrix.size;
38
+ const data = matrix._data; // Direct access
39
+ switch (maskId) {
40
+ case 0:
41
+ for (let y = 0; y < size; y++) {
42
+ for (let x = 0; x < size; x++) {
43
+ const idx = y * size + x;
44
+ const val = data[idx] ?? 0;
45
+ if (!(val & 2)) {
46
+ if (((x + y) & 1) === 0)
47
+ data[idx] = val ^ 1;
48
+ }
49
+ }
50
+ }
51
+ break;
52
+ case 1:
53
+ for (let y = 0; y < size; y++) {
54
+ for (let x = 0; x < size; x++) {
55
+ const idx = y * size + x;
56
+ const val = data[idx] ?? 0;
57
+ if (!(val & 2)) {
58
+ if ((y & 1) === 0)
59
+ data[idx] = val ^ 1;
60
+ }
61
+ }
62
+ }
63
+ break;
64
+ case 2:
65
+ for (let y = 0; y < size; y++) {
66
+ for (let x = 0; x < size; x++) {
67
+ const idx = y * size + x;
68
+ const val = data[idx] ?? 0;
69
+ if (!(val & 2)) {
70
+ if (x % 3 === 0)
71
+ data[idx] = val ^ 1;
72
+ }
73
+ }
74
+ }
75
+ break;
76
+ case 3:
77
+ for (let y = 0; y < size; y++) {
78
+ for (let x = 0; x < size; x++) {
79
+ const idx = y * size + x;
80
+ const val = data[idx] ?? 0;
81
+ if (!(val & 2)) {
82
+ if ((x + y) % 3 === 0)
83
+ data[idx] = val ^ 1;
84
+ }
85
+ }
86
+ }
87
+ break;
88
+ case 4:
89
+ for (let y = 0; y < size; y++) {
90
+ for (let x = 0; x < size; x++) {
91
+ const idx = y * size + x;
92
+ const val = data[idx] ?? 0;
93
+ if (!(val & 2)) {
94
+ if ((((y >>> 1) + ((x / 3) | 0)) & 1) === 0)
95
+ data[idx] = val ^ 1;
96
+ }
97
+ }
98
+ }
99
+ break;
100
+ case 5:
101
+ for (let y = 0; y < size; y++) {
102
+ for (let x = 0; x < size; x++) {
103
+ const idx = y * size + x;
104
+ const val = data[idx] ?? 0;
105
+ if (!(val & 2)) {
106
+ const temp = x * y;
107
+ if ((temp & 1) + (temp % 3) === 0)
108
+ data[idx] = val ^ 1;
109
+ }
110
+ }
111
+ }
112
+ break;
113
+ case 6:
114
+ for (let y = 0; y < size; y++) {
115
+ for (let x = 0; x < size; x++) {
116
+ const idx = y * size + x;
117
+ const val = data[idx] ?? 0;
118
+ if (!(val & 2)) {
119
+ const temp = x * y;
120
+ if ((((temp & 1) + (temp % 3)) & 1) === 0)
121
+ data[idx] = val ^ 1;
122
+ }
123
+ }
124
+ }
125
+ break;
126
+ case 7:
127
+ for (let y = 0; y < size; y++) {
128
+ for (let x = 0; x < size; x++) {
129
+ const idx = y * size + x;
130
+ const val = data[idx] ?? 0;
131
+ if (!(val & 2)) {
132
+ if (((((x + y) & 1) + ((x * y) % 3)) & 1) === 0)
133
+ data[idx] = val ^ 1;
134
+ }
135
+ }
136
+ }
137
+ break;
138
+ }
139
+ }
140
+ export function writeFormatInformation(matrix, ecc, maskId) {
141
+ const formatBits = getFormatInfo(ecc, maskId);
142
+ const size = matrix.size;
143
+ for (let i = 0; i < 15; i++) {
144
+ const bit = (formatBits >>> i) & 1;
145
+ // Top-left
146
+ let x;
147
+ let y;
148
+ if (i < 6) {
149
+ x = i;
150
+ y = 8;
151
+ }
152
+ else if (i < 8) {
153
+ x = i + 1;
154
+ y = 8;
155
+ }
156
+ else if (i === 8) {
157
+ x = 8;
158
+ y = 7;
159
+ }
160
+ else {
161
+ x = 8;
162
+ y = 14 - i;
163
+ }
164
+ matrix.setReserved(x, y, bit === 1);
165
+ // Split area
166
+ let x2;
167
+ let y2;
168
+ if (i < 8) {
169
+ x2 = 8;
170
+ y2 = size - 1 - i;
171
+ }
172
+ else {
173
+ x2 = size - 15 + i;
174
+ y2 = 8;
175
+ }
176
+ matrix.setReserved(x2, y2, bit === 1);
177
+ }
178
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * QR Matris Yönetimi
3
+ */
4
+ import type { BitMatrix } from "../types/index.js";
5
+ export declare class Matrix implements BitMatrix {
6
+ readonly size: number;
7
+ private data;
8
+ constructor(size: number);
9
+ /**
10
+ * Internal access to raw data for performance-critical operations (e.g. penalty calculation).
11
+ */
12
+ get _data(): Uint8Array;
13
+ /**
14
+ * Hücrenin değerini döndürür (Sadece 0 veya 1)
15
+ */
16
+ get(x: number, y: number): 0 | 1;
17
+ /**
18
+ * Hücrenin "Reserved" (ayrılmış) olup olmadığını kontrol eder
19
+ */
20
+ isReserved(x: number, y: number): boolean;
21
+ /**
22
+ * Hücreyi ayarlar ve rezerve eder (Fonksiyonel desenler için)
23
+ */
24
+ setReserved(x: number, y: number, isDark: boolean): void;
25
+ /**
26
+ * Sadece veri modülleri için (Ayrılmamış alanlara yazar)
27
+ */
28
+ set(x: number, y: number, isDark: boolean): void;
29
+ /**
30
+ * RFC 5.2: Matrisi satırlar halinde döndürür
31
+ */
32
+ toRows(): ReadonlyArray<Uint8Array>;
33
+ /**
34
+ * RFC 5.2: Matrisi paketlenmiş (MSB-first) bayt dizisi olarak döndürür
35
+ */
36
+ toPacked(): Uint8Array;
37
+ clone(): Matrix;
38
+ }
39
+ export declare class ReadOnlyMatrix implements BitMatrix {
40
+ readonly size: number;
41
+ private readonly inner;
42
+ constructor(matrix: Matrix);
43
+ get(x: number, y: number): 0 | 1;
44
+ toRows(): ReadonlyArray<Uint8Array>;
45
+ toPacked(): Uint8Array;
46
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * QR Matris Yönetimi
3
+ */
4
+ /**
5
+ * Matris hücre durumları (Dahili kullanım)
6
+ */
7
+ const STATUS = {
8
+ LIGHT: 0,
9
+ DARK: 1,
10
+ RESERVED_LIGHT: 2,
11
+ RESERVED_DARK: 3,
12
+ };
13
+ export class Matrix {
14
+ size;
15
+ data;
16
+ constructor(size) {
17
+ this.size = size;
18
+ // Varsayılan olarak tüm hücreler "light" (0)
19
+ this.data = new Uint8Array(size * size);
20
+ }
21
+ /**
22
+ * Internal access to raw data for performance-critical operations (e.g. penalty calculation).
23
+ */
24
+ get _data() {
25
+ return this.data;
26
+ }
27
+ /**
28
+ * Hücrenin değerini döndürür (Sadece 0 veya 1)
29
+ */
30
+ get(x, y) {
31
+ if (x < 0 || x >= this.size || y < 0 || y >= this.size) {
32
+ throw new Error(`Matrix access out of bounds: (${x},${y})`);
33
+ }
34
+ const idx = y * this.size + x;
35
+ const val = this.data[idx];
36
+ if (val === undefined) {
37
+ // Should be bounds checked or checked at call site, but defensive coding here:
38
+ throw new Error(`Matrix access out of bounds: (${x},${y})`);
39
+ }
40
+ return (val & 1);
41
+ }
42
+ /**
43
+ * Hücrenin "Reserved" (ayrılmış) olup olmadığını kontrol eder
44
+ */
45
+ isReserved(x, y) {
46
+ if (x < 0 || x >= this.size || y < 0 || y >= this.size) {
47
+ throw new Error(`Matrix access out of bounds: (${x},${y})`);
48
+ }
49
+ const idx = y * this.size + x;
50
+ const val = this.data[idx];
51
+ if (val === undefined) {
52
+ throw new Error(`Matrix access out of bounds: (${x},${y})`);
53
+ }
54
+ return (val & 2) !== 0;
55
+ }
56
+ /**
57
+ * Hücreyi ayarlar ve rezerve eder (Fonksiyonel desenler için)
58
+ */
59
+ setReserved(x, y, isDark) {
60
+ if (x < 0 || x >= this.size || y < 0 || y >= this.size) {
61
+ throw new Error(`Matrix access out of bounds: (${x},${y})`);
62
+ }
63
+ const idx = y * this.size + x;
64
+ if (idx < 0 || idx >= this.data.length) {
65
+ throw new Error(`Matrix access out of bounds: (${x},${y})`);
66
+ }
67
+ this.data[idx] = isDark ? STATUS.RESERVED_DARK : STATUS.RESERVED_LIGHT;
68
+ }
69
+ /**
70
+ * Sadece veri modülleri için (Ayrılmamış alanlara yazar)
71
+ */
72
+ set(x, y, isDark) {
73
+ if (x < 0 || x >= this.size || y < 0 || y >= this.size) {
74
+ throw new Error(`Matrix access out of bounds: (${x},${y})`);
75
+ }
76
+ if (this.isReserved(x, y))
77
+ return;
78
+ const idx = y * this.size + x;
79
+ if (idx < 0 || idx >= this.data.length) {
80
+ // Double safety
81
+ throw new Error(`Matrix access out of bounds: (${x},${y})`);
82
+ }
83
+ this.data[idx] = isDark ? STATUS.DARK : STATUS.LIGHT;
84
+ }
85
+ /**
86
+ * RFC 5.2: Matrisi satırlar halinde döndürür
87
+ */
88
+ toRows() {
89
+ const rows = [];
90
+ for (let y = 0; y < this.size; y++) {
91
+ rows.push(this.data
92
+ .slice(y * this.size, (y + 1) * this.size)
93
+ .map((v) => (v & 1)));
94
+ }
95
+ return rows;
96
+ }
97
+ /**
98
+ * RFC 5.2: Matrisi paketlenmiş (MSB-first) bayt dizisi olarak döndürür
99
+ */
100
+ toPacked() {
101
+ const rowBytes = Math.ceil(this.size / 8);
102
+ const packed = new Uint8Array(rowBytes * this.size);
103
+ for (let y = 0; y < this.size; y++) {
104
+ for (let x = 0; x < this.size; x++) {
105
+ if (this.get(x, y)) {
106
+ const idx = y * rowBytes + (x >>> 3);
107
+ const current = packed[idx];
108
+ // safe access or initialized 0
109
+ if (current !== undefined) {
110
+ packed[idx] = current | (0x80 >>> (x & 7));
111
+ }
112
+ }
113
+ }
114
+ }
115
+ return packed;
116
+ }
117
+ clone() {
118
+ const copy = new Matrix(this.size);
119
+ copy.data.set(this.data);
120
+ return copy;
121
+ }
122
+ }
123
+ export class ReadOnlyMatrix {
124
+ size;
125
+ inner;
126
+ constructor(matrix) {
127
+ this.inner = matrix;
128
+ this.size = matrix.size;
129
+ }
130
+ get(x, y) {
131
+ return this.inner.get(x, y);
132
+ }
133
+ toRows() {
134
+ return this.inner.toRows();
135
+ }
136
+ toPacked() {
137
+ return this.inner.toPacked();
138
+ }
139
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * BCH (Bose-Chaudhuri-Hocquenghem) Error Correction for QR Metadata
3
+ */
4
+ /**
5
+ * Format Info BCH (15, 5)
6
+ */
7
+ export declare function getFormatInfo(eccLevel: "L" | "M" | "Q" | "H", mask: number): number;
8
+ /**
9
+ * Version Info BCH (18, 6)
10
+ */
11
+ export declare function getVersionInfo(version: number): number;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * BCH (Bose-Chaudhuri-Hocquenghem) Error Correction for QR Metadata
3
+ */
4
+ /**
5
+ * Format Info BCH (15, 5)
6
+ */
7
+ export function getFormatInfo(eccLevel, mask) {
8
+ const eccBits = { L: 1, M: 0, Q: 3, H: 2 };
9
+ const bits = eccBits[eccLevel];
10
+ if (bits === undefined)
11
+ throw new Error(`Invalid ecc level: ${eccLevel}`);
12
+ const data = (bits << 3) | mask;
13
+ let rem = data;
14
+ for (let i = 0; i < 10; i++) {
15
+ rem = (rem << 1) ^ (rem >>> 9 ? 0x537 : 0);
16
+ }
17
+ return ((data << 10) | rem) ^ 0x5412;
18
+ }
19
+ /**
20
+ * Version Info BCH (18, 6)
21
+ */
22
+ export function getVersionInfo(version) {
23
+ if (version < 7)
24
+ return 0;
25
+ let rem = version;
26
+ for (let i = 0; i < 12; i++) {
27
+ rem = (rem << 1) ^ (rem >>> 11 ? 0x1f25 : 0);
28
+ }
29
+ return (version << 12) | rem;
30
+ }
@@ -0,0 +1,40 @@
1
+ import type { QrMode } from "../types/index.js";
2
+ /**
3
+ * QR Code constants and lookup tables
4
+ */
5
+ export declare const MIN_VERSION = 1;
6
+ export declare const MAX_VERSION = 40;
7
+ /**
8
+ * Mode indicators (4-bit values)
9
+ */
10
+ export declare const MODE_INDICATOR: Record<Exclude<QrMode, "auto">, number>;
11
+ /**
12
+ * Bit lengths for Character Count Indicator based on version groups
13
+ * Groups: 1-9, 10-26, 27-40
14
+ */
15
+ export declare const CHAR_COUNT_INDICATOR_BITS: Record<Exclude<QrMode, "auto">, [
16
+ number,
17
+ number,
18
+ number
19
+ ]>;
20
+ /**
21
+ * Version group index for Character Count Indicator
22
+ */
23
+ export declare function getVersionGroupIndex(version: number): 0 | 1 | 2;
24
+ /**
25
+ * Alphanumeric character set mapping
26
+ */
27
+ export declare const ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
28
+ /**
29
+ * ECC Codeword tables, Alignment pattern positions, etc. will go here or in sub-files.
30
+ * For now, defining the structure for capacity lookup.
31
+ */
32
+ export interface EccBlockConfig {
33
+ totalCodewords: number;
34
+ dataCodewords: number;
35
+ eccCodewordsPerBlock: number;
36
+ blocksGroup1: number;
37
+ dataPerBlockGroup1: number;
38
+ blocksGroup2: number;
39
+ dataPerBlockGroup2: number;
40
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * QR Code constants and lookup tables
3
+ */
4
+ export const MIN_VERSION = 1;
5
+ export const MAX_VERSION = 40;
6
+ /**
7
+ * Mode indicators (4-bit values)
8
+ */
9
+ export const MODE_INDICATOR = {
10
+ numeric: 0b0001,
11
+ alphanumeric: 0b0010,
12
+ byte: 0b0100,
13
+ };
14
+ /**
15
+ * Bit lengths for Character Count Indicator based on version groups
16
+ * Groups: 1-9, 10-26, 27-40
17
+ */
18
+ export const CHAR_COUNT_INDICATOR_BITS = {
19
+ numeric: [10, 12, 14],
20
+ alphanumeric: [9, 11, 13],
21
+ byte: [8, 16, 16],
22
+ };
23
+ /**
24
+ * Version group index for Character Count Indicator
25
+ */
26
+ export function getVersionGroupIndex(version) {
27
+ if (version <= 9)
28
+ return 0;
29
+ if (version <= 26)
30
+ return 1;
31
+ return 2;
32
+ }
33
+ /**
34
+ * Alphanumeric character set mapping
35
+ */
36
+ export const ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
37
+ // These tables are large and will be populated in src/spec/tables.ts
@@ -0,0 +1,20 @@
1
+ import type { EccLevel } from "../types/index.js";
2
+ /**
3
+ * Alignment pattern center coordinates for each version (1-40)
4
+ */
5
+ export declare const ALIGNMENT_PATTERN_POSITIONS: number[][];
6
+ export interface EccBlockInfo {
7
+ numBlocks: number;
8
+ dataCodewords: number;
9
+ }
10
+ export interface VersionInfo {
11
+ totalCodewords: number;
12
+ eccPerBlock: number;
13
+ group1: EccBlockInfo;
14
+ group2: EccBlockInfo;
15
+ }
16
+ /**
17
+ * ECC capacity table for V1 and V2.
18
+ */
19
+ export declare const ECC_TABLE: Record<number, Record<EccLevel, VersionInfo>>;
20
+ export declare function getDataCapacity(version: number, ecc: EccLevel): number;