@juit/qrcode 0.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 (82) hide show
  1. package/LICENSE.md +211 -0
  2. package/NOTICE.md +13 -0
  3. package/README.md +175 -0
  4. package/dist/encode.cjs +147 -0
  5. package/dist/encode.cjs.map +6 -0
  6. package/dist/encode.d.ts +11 -0
  7. package/dist/encode.mjs +122 -0
  8. package/dist/encode.mjs.map +6 -0
  9. package/dist/images/path.cjs +145 -0
  10. package/dist/images/path.cjs.map +6 -0
  11. package/dist/images/path.d.ts +6 -0
  12. package/dist/images/path.mjs +120 -0
  13. package/dist/images/path.mjs.map +6 -0
  14. package/dist/images/pdf.cjs +96 -0
  15. package/dist/images/pdf.cjs.map +6 -0
  16. package/dist/images/pdf.d.ts +3 -0
  17. package/dist/images/pdf.mjs +71 -0
  18. package/dist/images/pdf.mjs.map +6 -0
  19. package/dist/images/png.cjs +86 -0
  20. package/dist/images/png.cjs.map +6 -0
  21. package/dist/images/png.d.ts +3 -0
  22. package/dist/images/png.mjs +61 -0
  23. package/dist/images/png.mjs.map +6 -0
  24. package/dist/images/svg.cjs +64 -0
  25. package/dist/images/svg.cjs.map +6 -0
  26. package/dist/images/svg.d.ts +54 -0
  27. package/dist/images/svg.mjs +38 -0
  28. package/dist/images/svg.mjs.map +6 -0
  29. package/dist/index.cjs +110 -0
  30. package/dist/index.cjs.map +6 -0
  31. package/dist/index.d.ts +74 -0
  32. package/dist/index.mjs +74 -0
  33. package/dist/index.mjs.map +6 -0
  34. package/dist/matrix.cjs +360 -0
  35. package/dist/matrix.cjs.map +6 -0
  36. package/dist/matrix.d.ts +3 -0
  37. package/dist/matrix.mjs +335 -0
  38. package/dist/matrix.mjs.map +6 -0
  39. package/dist/qrcode.cjs +183 -0
  40. package/dist/qrcode.cjs.map +6 -0
  41. package/dist/qrcode.d.ts +20 -0
  42. package/dist/qrcode.mjs +158 -0
  43. package/dist/qrcode.mjs.map +6 -0
  44. package/dist/utils/crc32.cjs +53 -0
  45. package/dist/utils/crc32.cjs.map +6 -0
  46. package/dist/utils/crc32.d.ts +9 -0
  47. package/dist/utils/crc32.mjs +28 -0
  48. package/dist/utils/crc32.mjs.map +6 -0
  49. package/dist/utils/dataurl.cjs +35 -0
  50. package/dist/utils/dataurl.cjs.map +6 -0
  51. package/dist/utils/dataurl.d.ts +1 -0
  52. package/dist/utils/dataurl.mjs +10 -0
  53. package/dist/utils/dataurl.mjs.map +6 -0
  54. package/dist/utils/deflate.cjs +39 -0
  55. package/dist/utils/deflate.cjs.map +6 -0
  56. package/dist/utils/deflate.d.ts +2 -0
  57. package/dist/utils/deflate.mjs +14 -0
  58. package/dist/utils/deflate.mjs.map +6 -0
  59. package/dist/utils/ecc.cjs +93 -0
  60. package/dist/utils/ecc.cjs.map +6 -0
  61. package/dist/utils/ecc.d.ts +2 -0
  62. package/dist/utils/ecc.mjs +68 -0
  63. package/dist/utils/ecc.mjs.map +6 -0
  64. package/dist/utils/merge.cjs +41 -0
  65. package/dist/utils/merge.cjs.map +6 -0
  66. package/dist/utils/merge.d.ts +2 -0
  67. package/dist/utils/merge.mjs +16 -0
  68. package/dist/utils/merge.mjs.map +6 -0
  69. package/package.json +61 -0
  70. package/src/encode.ts +180 -0
  71. package/src/images/path.ts +131 -0
  72. package/src/images/pdf.ts +76 -0
  73. package/src/images/png.ts +105 -0
  74. package/src/images/svg.ts +102 -0
  75. package/src/index.ts +147 -0
  76. package/src/matrix.ts +392 -0
  77. package/src/qrcode.ts +217 -0
  78. package/src/utils/crc32.ts +50 -0
  79. package/src/utils/dataurl.ts +7 -0
  80. package/src/utils/deflate.ts +17 -0
  81. package/src/utils/ecc.ts +95 -0
  82. package/src/utils/merge.ts +13 -0
package/src/qrcode.ts ADDED
@@ -0,0 +1,217 @@
1
+ import { calculateEcc } from './utils/ecc'
2
+
3
+ import type { QrCodeMessage } from './encode'
4
+ import type { ECLevel } from './index'
5
+
6
+ /* ========================================================================== *
7
+ * TYPES *
8
+ * ========================================================================== */
9
+
10
+ interface Template {
11
+ readonly version: number,
12
+ readonly ecLevel: ECLevel,
13
+ readonly dataLen: number,
14
+ readonly ecLen: number,
15
+ readonly blockLengths: readonly number[],
16
+ }
17
+
18
+ type Version = {
19
+ readonly [ k in ECLevel ] : Template
20
+ }
21
+
22
+ /** The internal structure of a QR code */
23
+ export interface QrCodeData {
24
+ /** The version of the QR code (1...40) */
25
+ readonly version: number,
26
+ /** The error correction level for the QR code */
27
+ readonly ecLevel: ECLevel,
28
+ /** The data blocks of the QR code */
29
+ readonly blockData: number[][],
30
+ /** The ECC blocks of the QR code */
31
+ readonly ecData: number[][],
32
+ }
33
+
34
+ /* ========================================================================== *
35
+ * CONSTANTS *
36
+ * ========================================================================== */
37
+
38
+ // Error correction levels, in order
39
+ const EC_LEVELS = [ 'L', 'M', 'Q', 'H' ] as const
40
+
41
+ // Total number of codewords, (number of ec codewords, number of blocks) * ( L, M, Q, H )
42
+ const CODEWORDS = [
43
+ [ -1, -1, -1, -1, -1, -1, -1, -1, -1 ], // there is no version 0
44
+ [ 26, 7, 1, 10, 1, 13, 1, 17, 1 ],
45
+ [ 44, 10, 1, 16, 1, 22, 1, 28, 1 ],
46
+ [ 70, 15, 1, 26, 1, 36, 2, 44, 2 ],
47
+ [ 100, 20, 1, 36, 2, 52, 2, 64, 4 ],
48
+ [ 134, 26, 1, 48, 2, 72, 4, 88, 4 ], // 5
49
+ [ 172, 36, 2, 64, 4, 96, 4, 112, 4 ],
50
+ [ 196, 40, 2, 72, 4, 108, 6, 130, 5 ],
51
+ [ 242, 48, 2, 88, 4, 132, 6, 156, 6 ],
52
+ [ 292, 60, 2, 110, 5, 160, 8, 192, 8 ],
53
+ [ 346, 72, 4, 130, 5, 192, 8, 224, 8 ], // 10
54
+ [ 404, 80, 4, 150, 5, 224, 8, 264, 11 ],
55
+ [ 466, 96, 4, 176, 8, 260, 10, 308, 11 ],
56
+ [ 532, 104, 4, 198, 9, 288, 12, 352, 16 ],
57
+ [ 581, 120, 4, 216, 9, 320, 16, 384, 16 ],
58
+ [ 655, 132, 6, 240, 10, 360, 12, 432, 18 ], // 15
59
+ [ 733, 144, 6, 280, 10, 408, 17, 480, 16 ],
60
+ [ 815, 168, 6, 308, 11, 448, 16, 532, 19 ],
61
+ [ 901, 180, 6, 338, 13, 504, 18, 588, 21 ],
62
+ [ 991, 196, 7, 364, 14, 546, 21, 650, 25 ],
63
+ [ 1085, 224, 8, 416, 16, 600, 20, 700, 25 ], // 20
64
+ [ 1156, 224, 8, 442, 17, 644, 23, 750, 25 ],
65
+ [ 1258, 252, 9, 476, 17, 690, 23, 816, 34 ],
66
+ [ 1364, 270, 9, 504, 18, 750, 25, 900, 30 ],
67
+ [ 1474, 300, 10, 560, 20, 810, 27, 960, 32 ],
68
+ [ 1588, 312, 12, 588, 21, 870, 29, 1050, 35 ], // 25
69
+ [ 1706, 336, 12, 644, 23, 952, 34, 1110, 37 ],
70
+ [ 1828, 360, 12, 700, 25, 1020, 34, 1200, 40 ],
71
+ [ 1921, 390, 13, 728, 26, 1050, 35, 1260, 42 ],
72
+ [ 2051, 420, 14, 784, 28, 1140, 38, 1350, 45 ],
73
+ [ 2185, 450, 15, 812, 29, 1200, 40, 1440, 48 ], // 30
74
+ [ 2323, 480, 16, 868, 31, 1290, 43, 1530, 51 ],
75
+ [ 2465, 510, 17, 924, 33, 1350, 45, 1620, 54 ],
76
+ [ 2611, 540, 18, 980, 35, 1440, 48, 1710, 57 ],
77
+ [ 2761, 570, 19, 1036, 37, 1530, 51, 1800, 60 ],
78
+ [ 2876, 570, 19, 1064, 38, 1590, 53, 1890, 63 ], // 35
79
+ [ 3034, 600, 20, 1120, 40, 1680, 56, 1980, 66 ],
80
+ [ 3196, 630, 21, 1204, 43, 1770, 59, 2100, 70 ],
81
+ [ 3362, 660, 22, 1260, 45, 1860, 62, 2220, 74 ],
82
+ [ 3532, 720, 24, 1316, 47, 1950, 65, 2310, 77 ],
83
+ [ 3706, 750, 25, 1372, 49, 2040, 68, 2430, 81 ], // 40
84
+ ]
85
+
86
+ const VERSIONS: Version[] = CODEWORDS.map((v: number[], index): Version => {
87
+ if (! index) return null as any
88
+
89
+ const res: Record<string, Template> = {}
90
+ for (let i = 1; i < 8; i += 2) {
91
+ const length = v[0]! - v[i]!
92
+ const template = v[i+1]!
93
+ const ecLevel = EC_LEVELS[(i/2)|0]!
94
+ const blocks: number[] = []
95
+
96
+ for (let k = template, n = length; k > 0; k--) {
97
+ const block = (n / k) | 0
98
+ blocks.push(block)
99
+ n -= block
100
+ }
101
+
102
+ res[ecLevel] = {
103
+ version: index,
104
+ ecLevel: ecLevel,
105
+ dataLen: length,
106
+ ecLen: v[i]! / template,
107
+ blockLengths: blocks,
108
+ }
109
+ }
110
+ return res as Version
111
+ })
112
+
113
+ /* ========================================================================== *
114
+ * INTERNALS *
115
+ * ========================================================================== */
116
+
117
+ // Get the template for the specified message and error correction level
118
+ function getTemplate(message: QrCodeMessage, ecLevel: ECLevel): Template {
119
+ let len: number = NaN
120
+ let i = 1
121
+
122
+ if (message.data1) {
123
+ len = Math.ceil(message.data1.length / 8)
124
+ } else {
125
+ i = 10
126
+ }
127
+
128
+ for (/* i */; i < 10; i++) {
129
+ const version = VERSIONS[i]![ecLevel]
130
+ if (version.dataLen >= len) {
131
+ return structuredClone(version)
132
+ }
133
+ }
134
+
135
+ if (message.data10) {
136
+ len = Math.ceil(message.data10.length / 8)
137
+ } else {
138
+ i = 27
139
+ }
140
+
141
+ for (/* i */; i < 27; i++) {
142
+ const version = VERSIONS[i]![ecLevel]
143
+ if (version.dataLen >= len) {
144
+ return structuredClone(version)
145
+ }
146
+ }
147
+
148
+ len = Math.ceil(message.data27.length / 8)
149
+ for (/* i */; i < 41; i++) {
150
+ const version = VERSIONS[i]![ecLevel]
151
+ if (version.dataLen >= len) {
152
+ return structuredClone(version)
153
+ }
154
+ }
155
+
156
+ throw new Error('Too much data to encode in QR code')
157
+ }
158
+
159
+ // Fill in a template and prepare a QR code
160
+ function fillTemplate(encoded: QrCodeMessage, template: Template): QrCodeData {
161
+ const blocks = new Array<number>(template.dataLen).fill(0)
162
+
163
+ let message: boolean[]
164
+ if (template.version < 10) {
165
+ message = encoded.data1!
166
+ } else if (template.version < 27) {
167
+ message = encoded.data10!
168
+ } else {
169
+ message = encoded.data27!
170
+ }
171
+
172
+ const len = message.length
173
+
174
+ for (let i = 0; i < len; i += 8) {
175
+ let b = 0
176
+ for (let j = 0; j < 8; j++) {
177
+ b = (b << 1) | (message[i + j] ? 1 : 0)
178
+ }
179
+ blocks[i / 8] = b
180
+ }
181
+
182
+ let pad = 236
183
+ for (let i = Math.ceil((len + 4) / 8); i < blocks.length; i++) {
184
+ blocks[i] = pad
185
+ pad = (pad == 236) ? 17 : 236
186
+ }
187
+
188
+ let offset = 0
189
+ const ecData: number[][] = []
190
+ const blockData = template.blockLengths.map((n) => {
191
+ const b = blocks.slice(offset, offset + n)
192
+ offset += n
193
+ ecData.push(calculateEcc(b, template.ecLen))
194
+ return b
195
+ })
196
+
197
+ return {
198
+ version: template.version,
199
+ ecLevel: template.ecLevel,
200
+ ecData,
201
+ blockData,
202
+ }
203
+ }
204
+
205
+ /* ========================================================================== *
206
+ * EXPORTED *
207
+ * ========================================================================== */
208
+
209
+ /**
210
+ * Create the QR code structure for the given text or binary data.
211
+ *
212
+ * @param data The {@link QrCodeMessage} structure containing the message
213
+ * @param ecLevel The error correction level for the QR code
214
+ */
215
+ export function generateQrCodeData(data: QrCodeMessage, ecLevel: ECLevel): QrCodeData {
216
+ return fillTemplate(data, getTemplate(data, ecLevel))
217
+ }
@@ -0,0 +1,50 @@
1
+ /* ========================================================================== *
2
+ * CONSTANTS *
3
+ * ========================================================================== */
4
+
5
+ // Initialization table for CRC32
6
+ const CRC_TABLE: number[] = (() => {
7
+ const table: number[] = []
8
+ for (let n = 0; n < 256; n++) {
9
+ let c = n
10
+ for (let k = 0; k < 8; k++) {
11
+ if (c & 1) {
12
+ c = 0xedb88320 ^ (c >>> 1)
13
+ } else {
14
+ c = c >>> 1
15
+ }
16
+ }
17
+ table[n] = c >>> 0
18
+ }
19
+ return table
20
+ })()
21
+
22
+ /* ========================================================================== *
23
+ * EXPORTED *
24
+ * ========================================================================== */
25
+
26
+ /**
27
+ * Calculate the CRC32 checksum for a given `Uint8Array`
28
+ *
29
+ * @param array The `Uint8Array` to use for checksum calculation
30
+ * @param offset The offset in the array to start calculating from
31
+ * @param length The number of bytes to use for calculation, if _negative_
32
+ * this be considered to be relative to the end of the array.
33
+ * */
34
+ export function crc32(
35
+ array: Uint8Array,
36
+ offset: number = 0,
37
+ length?: number,
38
+ ): number {
39
+ let crc = -1
40
+ const end =
41
+ length === undefined ? array.length :
42
+ length > 0 ? offset + length :
43
+ array.length + length
44
+
45
+ for (let i = offset; i < end; i ++) {
46
+ crc = CRC_TABLE[(crc ^ array[i]!) & 0x0FF]! ^ (crc >>> 8)
47
+ }
48
+
49
+ return (crc ^ -1) >>> 0
50
+ }
@@ -0,0 +1,7 @@
1
+ export function generateDataUrl(data: string | Uint8Array, mimeType: string): string {
2
+ // Do not use the text decoder here, otherwise UTF8 sequences will be
3
+ // represented in proper characters, and we might loose some data...
4
+ const string = typeof data === 'string' ? data : String.fromCharCode(...data)
5
+ const encoded = btoa(string)
6
+ return `data:${mimeType};base64,${encoded}`
7
+ }
@@ -0,0 +1,17 @@
1
+ /** Compress a Uint8Array using "deflate" */
2
+ export function deflate(data: Uint8Array): Promise<Uint8Array> {
3
+ return new Promise<Uint8Array>((resolve, reject) => {
4
+ const chunks: Uint8Array[] = []
5
+
6
+ const writer = new WritableStream<Uint8Array>({
7
+ write: (chunk) => void chunks.push(chunk),
8
+ })
9
+
10
+ return new Blob([ data ]).stream() // stream the data
11
+ .pipeThrough(new CompressionStream('deflate')) // compress it
12
+ .pipeTo(writer) // write it to our "writer" collecting chungs
13
+ .then(() => new Blob(chunks).arrayBuffer()) // merge the chunks
14
+ .then((buffer) => new Uint8Array(buffer)) // make a new array
15
+ .then(resolve, reject) // resolve or reject
16
+ })
17
+ }
@@ -0,0 +1,95 @@
1
+ /* ========================================================================== *
2
+ * CONSTANTS *
3
+ * ========================================================================== */
4
+
5
+ // Galois Field Math
6
+ const GF256_BASE = 285
7
+ const EXP_TABLE: number[] = [ 1 ]
8
+ const LOG_TABLE: number[] = []
9
+
10
+ // Generator Polynomials
11
+ const POLYNOMIALS = [
12
+ [ 0 ], // a^0 x^0
13
+ [ 0, 0 ], // a^0 x^1 + a^0 x^0
14
+ [ 0, 25, 1 ], // a^0 x^2 + a^25 x^1 + a^1 x^0
15
+ // and so on...
16
+ ]
17
+
18
+ // Prepare the exp table
19
+ for (let i = 1; i < 256; i++) {
20
+ let n = EXP_TABLE[i - 1]! << 1
21
+ if (n > 255) n = n ^ GF256_BASE
22
+ EXP_TABLE[i] = n
23
+ }
24
+
25
+ // Prepare the log table
26
+ for (let i = 0; i < 255; i++) {
27
+ LOG_TABLE[EXP_TABLE[i]!] = i
28
+ }
29
+
30
+ /* ========================================================================== *
31
+ * INTERNALS *
32
+ * ========================================================================== */
33
+
34
+ // Get the exp for a number
35
+ function exp(k: number): number {
36
+ // coverage ignore next
37
+ while (k < 0) k += 255
38
+ while (k > 255) k -= 255
39
+ return EXP_TABLE[k]!
40
+ }
41
+
42
+ // Get the log for a number
43
+ function log(k: number): number {
44
+ // coverage ignore next
45
+ if ((k < 0) || (k > 255)) throw new Error(`Bad log(${k})`)
46
+ return LOG_TABLE[k]!
47
+ }
48
+
49
+
50
+ // Generate the polynomial up to N (recursively)
51
+ function generatePolynomial(num: number): number[] {
52
+ const poly = POLYNOMIALS[num]
53
+ if (poly) return poly
54
+
55
+ const prev = generatePolynomial(num - 1)
56
+ const res: number[] = []
57
+
58
+ res[0] = prev[0]!
59
+ for (let i = 1; i <= num; i++) {
60
+ res[i] = log(exp(prev[i]!) ^ exp(prev[i - 1]! + num - 1))
61
+ }
62
+
63
+ return POLYNOMIALS[num] = res
64
+ }
65
+
66
+ /* ========================================================================== *
67
+ * EXPORTED *
68
+ * ========================================================================== */
69
+
70
+ /** Calculate the Error Correction Code (Reed Solomon) for the given data */
71
+ export function calculateEcc(buf: number[], length: number): number[] {
72
+ // `msg` could be array or buffer
73
+ // convert `msg` to array
74
+ const msg = ([] as number[]).slice.call(buf)
75
+
76
+ // Generator Polynomial
77
+ const poly = generatePolynomial(length)
78
+
79
+ // Extend the array...
80
+ for (let i = 0; i < length; i++) msg.push(0)
81
+
82
+ while (msg.length > length) {
83
+ if (!msg[0]) {
84
+ msg.shift()
85
+ continue
86
+ }
87
+ const logK = log(msg[0])
88
+ for (let i = 0; i <= length; i++) {
89
+ msg[i] = msg[i]! ^ exp(poly[i]! + logK)
90
+ }
91
+ msg.shift()
92
+ }
93
+
94
+ return msg
95
+ }
@@ -0,0 +1,13 @@
1
+ /** Merge a number of different {@link Uint8Array}s */
2
+ export function mergeArrays(...arrays: Uint8Array[]): Uint8Array {
3
+ const chunks: [ offset: number, array: Uint8Array ][] = []
4
+
5
+ const size = arrays.reduce((size, array) => {
6
+ chunks.push([ size, array ])
7
+ return size + array.length
8
+ }, 0)
9
+
10
+ const result = new Uint8Array(size)
11
+ for (const [ offset, array ] of chunks) result.set(array, offset)
12
+ return result
13
+ }