@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/index.ts ADDED
@@ -0,0 +1,147 @@
1
+ import { encodeQrCodeMessage } from './encode'
2
+ import { generatePdf } from './images/pdf'
3
+ import { generatePng } from './images/png'
4
+ import { generateSvg } from './images/svg'
5
+ import { generateQrCodeMatrix } from './matrix'
6
+ import { generateQrCodeData } from './qrcode'
7
+ import { generateDataUrl } from './utils/dataurl'
8
+
9
+ export { generatePdf } from './images/pdf'
10
+ export { generatePng } from './images/png'
11
+ export { generateSvg, generateSvgPath } from './images/svg'
12
+
13
+ /**
14
+ * The error correction level for QR codes (`L`, `M`, `Q`, or `H`)
15
+ *
16
+ * * `L`: approximately 7% of error correction capability
17
+ * * `M`: approximately 15% of error correction capability
18
+ * * `Q`: approximately 25% of error correction capability
19
+ * * `H`: approximately 30% of error correction capability
20
+ */
21
+ export type ECLevel = 'L' | 'M' | 'Q' | 'H'
22
+
23
+ /** The structure of a _generated_ QR code */
24
+ export interface QrCode {
25
+ /** The version of the QR code (1...40) */
26
+ readonly version: number,
27
+ /** The error correction level for the QR code */
28
+ readonly ecLevel: ECLevel,
29
+ /** The size (in pixels) of the QR code */
30
+ readonly size: number,
31
+ /** The QR code matrix */
32
+ readonly matrix: readonly boolean[][]
33
+ }
34
+
35
+ /** Options for the generation of a {@link QrCode} */
36
+ export interface QrCodeGenerationOptions {
37
+ /** The error correction level for the QR code (default: `M`) */
38
+ ecLevel?: ECLevel,
39
+ /** Whether to optimize URLs in QR codes (default: `false`) */
40
+ url?: boolean,
41
+ }
42
+
43
+ /** Options to render a {@link QrCode} into an image */
44
+ export interface QrCodeImageOptions {
45
+ /**
46
+ * The number of pixels used for each dot in the matrix
47
+ * (default: `1` for PNG/SVG and `9` for PDF)
48
+ */
49
+ scale?: number,
50
+ /**
51
+ * The size of the margin around the QR code in matrix modules
52
+ * (default: `4` as per the QR code specification - the _quiet area_)
53
+ */
54
+ margin?: number,
55
+ }
56
+
57
+ /** QR code options */
58
+ export interface QrCodeOptions extends QrCodeGenerationOptions, QrCodeImageOptions {}
59
+
60
+ /** Generate a {@link QrCode} from a string or binary message */
61
+ export function generateQrCode(message: string | Uint8Array, options?: QrCodeGenerationOptions): QrCode {
62
+ const { ecLevel = 'M', url = false } = { ...options }
63
+
64
+ const encoded = encodeQrCodeMessage(message, url)
65
+ const qrcode = generateQrCodeData(encoded, ecLevel)
66
+ const matrix = generateQrCodeMatrix(qrcode)
67
+
68
+ return {
69
+ version: qrcode.version,
70
+ ecLevel: qrcode.ecLevel,
71
+ size: matrix.length,
72
+ matrix,
73
+ }
74
+ }
75
+
76
+ /* ===== SINGLE FORMAT (MORE "BUNDLE FRIENDLY" FOR TREE SHAKING) ============ */
77
+
78
+ /** Generate a QR code in PNG format from a string or binary message */
79
+ export async function generatePngQrCode(message: string | Uint8Array, options?: QrCodeOptions): Promise<Uint8Array> {
80
+ return await generatePng(generateQrCode(message, options), options)
81
+ }
82
+
83
+ /** Generate a QR code in PDF format from a string or binary message */
84
+ export async function generatePdfQrCode(message: string | Uint8Array, options?: QrCodeOptions): Promise<Uint8Array> {
85
+ return await generatePdf(generateQrCode(message, options), options)
86
+ }
87
+
88
+ /** Generate a QR code in SVG format from a string or binary message */
89
+ export function generateSvgQrCode(message: string | Uint8Array, options?: QrCodeOptions): string {
90
+ return generateSvg(generateQrCode(message, options), options)
91
+ }
92
+
93
+ /** Generate a QR code as a PNG data URL from a string or binary message */
94
+ export async function generatePngDataQrCode(message: string | Uint8Array, options?: QrCodeOptions): Promise<string> {
95
+ return generateDataUrl(await generatePng(generateQrCode(message, options), options), 'image/png')
96
+ }
97
+
98
+ /** Generate a QR code as a PDF data URL from a string or binary message */
99
+ export async function generatePdfDataQrCode(message: string | Uint8Array, options?: QrCodeOptions): Promise<string> {
100
+ return generateDataUrl(await generatePdf(generateQrCode(message, options), options), 'application/pdf')
101
+ }
102
+
103
+ /** Generate a QR code as a SVG data URL from a string or binary message */
104
+ export function generateSvgDataQrCode(message: string | Uint8Array, options?: QrCodeOptions): string {
105
+ return generateDataUrl(generateSvg(generateQrCode(message, options), options), 'image/svg+xml')
106
+ }
107
+
108
+ /* ===== DEFINITELY NOT "BUNDLE FRIENDLY" =================================== */
109
+
110
+ /** Generate a QR code in PNG format from a string or binary message */
111
+ export function generate(message: string | Uint8Array, format: 'png', options?: QrCodeOptions): Promise<Uint8Array>
112
+ /** Generate a QR code in PDF format from a string or binary message */
113
+ export function generate(message: string | Uint8Array, format: 'pdf', options?: QrCodeOptions): Promise<Uint8Array>
114
+ /** Generate a QR code in SVG format from a string or binary message */
115
+ export function generate(message: string | Uint8Array, format: 'svg', options?: QrCodeOptions): Promise<string>
116
+ /** Generate a QR code as a PNG data URL from a string or binary message */
117
+ export function generate(message: string | Uint8Array, format: 'pngData', options?: QrCodeOptions): Promise<string>
118
+ /** Generate a QR code as a PDF data URL from a string or binary message */
119
+ export function generate(message: string | Uint8Array, format: 'pdfData', options?: QrCodeOptions): Promise<string>
120
+ /** Generate a QR code as a SVG data URL from a string or binary message */
121
+ export function generate(message: string | Uint8Array, format: 'svgData', options?: QrCodeOptions): Promise<string>
122
+ /** Generate a QR code as an image */
123
+ export async function generate<Format extends 'png' | 'pdf' | 'svg' | 'pngData' | 'pdfData' | 'svgData'>(
124
+ message: string | Uint8Array,
125
+ format: Format,
126
+ options?: QrCodeOptions,
127
+ ): Promise<Format extends 'png' ? Uint8Array : Format extends 'pdf' ? Uint8Array : string>
128
+
129
+ // Overloaded function implementations
130
+ export async function generate(
131
+ message: string | Uint8Array,
132
+ format: 'png' | 'pdf' | 'svg' | 'pngData' | 'pdfData' | 'svgData',
133
+ options?: QrCodeOptions,
134
+ ): Promise<Uint8Array | string> {
135
+ switch (format) {
136
+ // plain images
137
+ case 'png': return await generatePngQrCode(message, options)
138
+ case 'pdf': return await generatePdfQrCode(message, options)
139
+ case 'svg': return generateSvgQrCode(message, options)
140
+ // images as data URLs
141
+ case 'pngData': return await generatePngDataQrCode(message, options)
142
+ case 'pdfData': return await generatePdfDataQrCode(message, options)
143
+ case 'svgData': return generateSvgDataQrCode(message, options)
144
+ // coverage ignore next
145
+ default: throw new Error(`Unsupported format "${format}"`)
146
+ }
147
+ }
package/src/matrix.ts ADDED
@@ -0,0 +1,392 @@
1
+ import type { ECLevel } from './index'
2
+ import type { QrCodeData } from './qrcode'
3
+
4
+ /* ========================================================================== *
5
+ * INTERNALS *
6
+ * ========================================================================== */
7
+
8
+ // Initialize a matrix with all zeros
9
+ function init(version: number): number[][] {
10
+ const n = (version * 4) + 17
11
+ const matrix = new Array<number[]>(n)
12
+ for (let i = 0; i < n; i++) {
13
+ matrix[i] = new Array<number>(n).fill(0)
14
+ }
15
+ return matrix
16
+ }
17
+
18
+ /* ========================================================================== */
19
+
20
+ // Put finders into matrix
21
+ function fillFinders(matrix: number[][]): void {
22
+ const n = matrix.length
23
+ for (let i = -3; i <= 3; i++) {
24
+ for (let j = -3; j <= 3; j++) {
25
+ const max = Math.max(i, j)
26
+ const min = Math.min(i, j)
27
+ const pixel = (max == 2 && min >= -2) || (min == -2 && max <= 2) ? 0x80 : 0x81
28
+ matrix[3 + i]![3 + j] = pixel
29
+ matrix[3 + i]![n - 4 + j] = pixel
30
+ matrix[n - 4 + i]![3 + j] = pixel
31
+ }
32
+ }
33
+ for (let i = 0; i < 8; i++) {
34
+ matrix[7]![i] = matrix[i]![7] =
35
+ matrix[7]![n - i - 1] = matrix[i]![n - 8] =
36
+ matrix[n - 8]![i] = matrix[n - 1 - i]![7] = 0x80
37
+ }
38
+ }
39
+
40
+ /* ========================================================================== */
41
+
42
+ // Put align and timinig
43
+ function fillAlignAndTiming(matrix: number[][]): void {
44
+ const n = matrix.length
45
+ if (n > 21) {
46
+ const len = n - 13
47
+ let delta = Math.round(len / Math.ceil(len / 28))
48
+ // coverage ignore if // can't seem to cover it...
49
+ if (delta % 2) delta++
50
+ const res = []
51
+ for (let p = len + 6; p > 10; p -= delta) {
52
+ res.unshift(p)
53
+ }
54
+ res.unshift(6)
55
+ for (let i = 0; i < res.length; i++) {
56
+ for (let j = 0; j < res.length; j++) {
57
+ const x = res[i]!
58
+ const y = res[j]!
59
+ if (matrix[x]![y]) continue
60
+ for (let r = -2; r <=2; r++) {
61
+ for (let c = -2; c <=2; c++) {
62
+ const max = Math.max(r, c)
63
+ const min = Math.min(r, c)
64
+ const pixel = (max == 1 && min >= -1) || (min == -1 && max <= 1) ? 0x80 : 0x81
65
+ matrix[x + r]![y + c] = pixel
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ for (let i = 8; i < n - 8; i++) {
72
+ matrix[6]![i] = matrix[i]![6] = i % 2 ? 0x80 : 0x81
73
+ }
74
+ }
75
+
76
+ /* ========================================================================== */
77
+
78
+ // Fill reserved areas with zeroes
79
+ function fillStub(matrix: number[][]): void {
80
+ const n = matrix.length
81
+ for (let i = 0; i < 8; i++) {
82
+ if (i != 6) {
83
+ matrix[8]![i] = matrix[i]![8] = 0x80
84
+ }
85
+ matrix[8]![n - 1 - i] = 0x80
86
+ matrix[n - 1 - i]![8] = 0x80
87
+ }
88
+ matrix[8]![8] = 0x80
89
+ matrix[n - 8]![8] = 0x81
90
+
91
+ if (n < 45) return
92
+
93
+ for (let i = n - 11; i < n - 8; i++) {
94
+ for (let j = 0; j < 6; j++) {
95
+ matrix[i]![j] = matrix[j]![i] = 0x80
96
+ }
97
+ }
98
+ }
99
+
100
+ /* ========================================================================== */
101
+
102
+ // Fill reserved areas
103
+ const fillReserved = ((): ((matrix: number[][], ec_level: ECLevel, mask: number) => void) => {
104
+ const FORMATS = new Array<number>(32)
105
+ const VERSIONS = new Array<number>(40)
106
+
107
+ const gf15 = 0x0537
108
+ const gf18 = 0x1f25
109
+ const formatsMask = 0x5412
110
+
111
+ for (let format = 0; format < 32; format++) {
112
+ let res = format << 10
113
+ for (let i = 5; i > 0; i--) {
114
+ if (res >>> (9 + i)) {
115
+ res = res ^ (gf15 << (i - 1))
116
+ }
117
+ }
118
+ FORMATS[format] = (res | (format << 10)) ^ formatsMask
119
+ }
120
+
121
+ for (let version = 7; version <= 40; version++) {
122
+ let res = version << 12
123
+ for (let i = 6; i > 0; i--) {
124
+ if (res >>> (11 + i)) {
125
+ res = res ^ (gf18 << (i - 1))
126
+ }
127
+ }
128
+ VERSIONS[version] = (res | (version << 12))
129
+ }
130
+
131
+ const EC_LEVELS = { L: 1, M: 0, Q: 3, H: 2 }
132
+
133
+ return function fillReserved(matrix: number[][], ecLevel: ECLevel, mask: number) {
134
+ const N = matrix.length
135
+ const format = FORMATS[EC_LEVELS[ecLevel] << 3 | mask]!
136
+ function _f(k: number): number {
137
+ return format >> k & 1 ? 0x81 : 0x80
138
+ }
139
+ for (let i = 0; i < 8; i++) {
140
+ matrix[8]![N - 1 - i] = _f(i)
141
+ if (i < 6) matrix[i]![8] = _f(i)
142
+ }
143
+ for (let i = 8; i < 15; i++) {
144
+ matrix[N - 15 + i]![8] = _f(i)
145
+ if (i > 8) matrix[8]![14 - i] = _f(i)
146
+ }
147
+ matrix[7]![8] = _f(6)
148
+ matrix[8]![8] = _f(7)
149
+ matrix[8]![7] = _f(8)
150
+
151
+ const version = VERSIONS[(N - 17)/4]!
152
+ if (!version) return
153
+
154
+ function _v(k: number): number {
155
+ return version >> k & 1 ? 0x81 : 0x80
156
+ }
157
+ for (let i = 0; i < 6; i++) {
158
+ for (let j = 0; j < 3; j++) {
159
+ matrix[N - 11 + j]![i] = matrix[i]![N - 11 + j] = _v(i * 3 + j)
160
+ }
161
+ }
162
+ }
163
+ })()
164
+
165
+ /* ========================================================================== */
166
+
167
+ // Fill data
168
+ const fillData = ((): ((matrix: number[][], data: any, mask: number) => void) => {
169
+ const MASK_FUNCTIONS = [
170
+ function(i: number, j: number): boolean {
171
+ return (i + j) % 2 == 0
172
+ },
173
+ function(i: number, j: number): boolean {
174
+ void j
175
+ return i % 2 == 0
176
+ },
177
+ function(i: number, j: number): boolean {
178
+ void i
179
+ return j % 3 == 0
180
+ },
181
+ function(i: number, j: number): boolean {
182
+ return (i + j) % 3 == 0
183
+ },
184
+ function(i: number, j: number): boolean {
185
+ return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0
186
+ },
187
+ function(i: number, j: number): boolean {
188
+ return (i * j) % 2 + (i * j) % 3 == 0
189
+ },
190
+ function(i: number, j: number): boolean {
191
+ return ( (i * j) % 2 + (i * j) % 3) % 2 == 0
192
+ },
193
+ function(i: number, j: number): boolean {
194
+ return ( (i * j) % 3 + (i + j) % 2) % 2 == 0
195
+ },
196
+ ]
197
+
198
+ return function fillData(matrix: number[][], data: QrCodeData, mask: number): void {
199
+ const N = matrix.length
200
+ let row: number
201
+ let col: number
202
+ let dir = -1
203
+ row = col = N - 1
204
+ const maskFn = MASK_FUNCTIONS[mask]!
205
+ let len = data.blockData[data.blockData.length - 1]!.length
206
+
207
+ for (let i = 0; i < len; i++) {
208
+ for (let b = 0; b < data.blockData.length; b++) {
209
+ if (data.blockData[b]!.length <= i) continue
210
+ put(data.blockData[b]![i]!)
211
+ }
212
+ }
213
+
214
+ len = data.ecData[0]!.length
215
+ for (let i = 0; i < len; i++) {
216
+ for (let b = 0; b < data.ecData.length; b++) {
217
+ put(data.ecData[b]![i]!)
218
+ }
219
+ }
220
+
221
+ if (col > -1) {
222
+ do {
223
+ matrix[row]![col] = maskFn(row, col) ? 1 : 0
224
+ } while (next())
225
+ }
226
+
227
+ function put(byte: number): void {
228
+ for (let mask = 0x80; mask; mask = mask >> 1) {
229
+ let pixel = !!(mask & byte)
230
+ if (maskFn(row, col)) pixel = !pixel
231
+ matrix[row]![col] = pixel ? 1 : 0
232
+ next()
233
+ }
234
+ }
235
+
236
+ function next(): boolean {
237
+ do {
238
+ if ((col % 2) ^ (col < 6 ? 1 : 0)) {
239
+ if (dir < 0 && row == 0 || dir > 0 && row == N - 1) {
240
+ col--
241
+ dir = -dir
242
+ } else {
243
+ col++
244
+ row += dir
245
+ }
246
+ } else {
247
+ col--
248
+ }
249
+ if (col == 6) {
250
+ col--
251
+ }
252
+ if (col < 0) {
253
+ return false
254
+ }
255
+ } while (matrix[row]![col]! & 0xf0)
256
+ return true
257
+ }
258
+ }
259
+ })()
260
+
261
+ /* ========================================================================== */
262
+
263
+ // Calculate penalty
264
+ function calculatePenalty(matrix: number[][]): number {
265
+ const N = matrix.length
266
+ let penalty = 0
267
+ // Rule 1
268
+ for (let i = 0; i < N; i++) {
269
+ let pixel = matrix[i]![0]! & 1
270
+ let len = 1
271
+ for (let j = 1; j < N; j++) {
272
+ const p = matrix[i]![j]! & 1
273
+ if (p == pixel) {
274
+ len++
275
+ continue
276
+ }
277
+ if (len >= 5) {
278
+ penalty += len - 2
279
+ }
280
+ pixel = p
281
+ len = 1
282
+ }
283
+ if (len >= 5) {
284
+ penalty += len - 2
285
+ }
286
+ }
287
+ for (let j = 0; j < N; j++) {
288
+ let pixel = matrix[0]![j]! & 1
289
+ let len = 1
290
+ for (let i = 1; i < N; i++) {
291
+ const p = matrix[i]![j]! & 1
292
+ if (p == pixel) {
293
+ len++
294
+ continue
295
+ }
296
+ if (len >= 5) {
297
+ penalty += len - 2
298
+ }
299
+ pixel = p
300
+ len = 1
301
+ }
302
+ if (len >= 5) {
303
+ penalty += len - 2
304
+ }
305
+ }
306
+
307
+ // Rule 2
308
+ for (let i = 0; i < N - 1; i++) {
309
+ for (let j = 0; j < N - 1; j++) {
310
+ const s = matrix[i]![j]! + matrix[i]![j + 1]! + matrix[i + 1]![j]! + matrix[i + 1]![j + 1]! & 7
311
+ if (s == 0 || s == 4) {
312
+ penalty += 3
313
+ }
314
+ }
315
+ }
316
+
317
+ // Rule 3
318
+ let i: number
319
+ let j: number
320
+
321
+ function _i(k: number): number {
322
+ return matrix[i]![j + k]! & 1
323
+ }
324
+ function _j(k: number): number {
325
+ return matrix[i + k]![j]! & 1
326
+ }
327
+ for (i = 0; i < N; i++) {
328
+ for (j = 0; j < N; j++) {
329
+ if (j < N - 6 && _i(0) && !_i(1) && _i(2) && _i(3) && _i(4) && !_i(5) && _i(6)) {
330
+ if (j >= 4 && !(_i(-4) || _i(-3) || _i(-2) || _i(-1))) {
331
+ penalty += 40
332
+ }
333
+ if (j < N - 10 && !(_i(7) || _i(8) || _i(9) || _i(10))) {
334
+ penalty += 40
335
+ }
336
+ }
337
+
338
+ if (i < N - 6 && _j(0) && !_j(1) && _j(2) && _j(3) && _j(4) && !_j(5) && _j(6)) {
339
+ if (i >= 4 && !(_j(-4) || _j(-3) || _j(-2) || _j(-1))) {
340
+ penalty += 40
341
+ }
342
+ if (i < N - 10 && !(_j(7) || _j(8) || _j(9) || _j(10))) {
343
+ penalty += 40
344
+ }
345
+ }
346
+ }
347
+ }
348
+
349
+ // Rule 4
350
+ let numDark = 0
351
+ for (let i = 0; i < N; i++) {
352
+ for (let j = 0; j < N; j++) {
353
+ if (matrix[i]![j]! & 1) numDark++
354
+ }
355
+ }
356
+ penalty += 10 * Math.floor(Math.abs(10 - 20 * numDark/(N * N)))
357
+
358
+ return penalty
359
+ }
360
+
361
+ /* ========================================================================== *
362
+ * EXPORTED *
363
+ * ========================================================================== */
364
+
365
+ /** Generate a matrix from a QR code data structure */
366
+ export function generateQrCodeMatrix(code: QrCodeData): boolean[][] {
367
+ const matrix = init(code.version)
368
+ fillFinders(matrix)
369
+ fillAlignAndTiming(matrix)
370
+ fillStub(matrix)
371
+
372
+ let penalty = Infinity
373
+ let bestMask = 0
374
+ for (let mask = 0; mask < 8; mask++) {
375
+ fillData(matrix, code, mask)
376
+ fillReserved(matrix, code.ecLevel, mask)
377
+ const p = calculatePenalty(matrix)
378
+ if (p < penalty) {
379
+ penalty = p
380
+ bestMask = mask
381
+ }
382
+ }
383
+
384
+ fillData(matrix, code, bestMask)
385
+ fillReserved(matrix, code.ecLevel, bestMask)
386
+
387
+ return matrix.map((row) => {
388
+ return row.map((cell) => {
389
+ return !! (cell & 1)
390
+ })
391
+ })
392
+ }