@atooyu/uxto-ui 1.1.37 → 1.1.39

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.
@@ -45,60 +45,30 @@ const cellSize = computed(() => {
45
45
  })
46
46
 
47
47
  //---------------------------------------------------------------------------
48
- // QR Code Generator - Inlined from qrcode-generator by kazuhikoarase
48
+ // QR Code Generator - Based on qrcode-generator by kazuhikoarase
49
49
  // https://github.com/kazuhikoarase/qrcode-generator
50
50
  // License: MIT
51
51
  //---------------------------------------------------------------------------
52
52
 
53
- // Error correction levels
54
- const QRErrorCorrectionLevel = { L: 1, M: 0, Q: 3, H: 2 }
53
+ // Error correction levels: L=1, M=0, Q=3, H=2
54
+ const QRErrorCorrectionLevel: Record<string, number> = { L: 1, M: 0, Q: 3, H: 2 }
55
55
 
56
56
  // Padding bytes
57
57
  const PAD0 = 0xEC
58
58
  const PAD1 = 0x11
59
59
 
60
- // Pattern position table (alignment patterns)
60
+ // Pattern position table (alignment patterns) - index is typeNumber - 1
61
61
  const PATTERN_POSITION_TABLE: number[][] = [
62
- [],
63
- [6, 18],
64
- [6, 22],
65
- [6, 26],
66
- [6, 30],
67
- [6, 34],
68
- [6, 22, 38],
69
- [6, 24, 42],
70
- [6, 26, 46],
71
- [6, 28, 50],
72
- [6, 30, 54],
73
- [6, 32, 58],
74
- [6, 34, 62],
75
- [6, 26, 46, 66],
76
- [6, 26, 48, 70],
77
- [6, 26, 50, 74],
78
- [6, 30, 54, 78],
79
- [6, 30, 56, 82],
80
- [6, 30, 58, 86],
81
- [6, 34, 62, 90],
82
- [6, 28, 50, 72, 94],
83
- [6, 26, 50, 74, 98],
84
- [6, 30, 54, 78, 102],
85
- [6, 28, 54, 80, 106],
86
- [6, 32, 58, 84, 110],
87
- [6, 30, 58, 86, 114],
88
- [6, 34, 62, 90, 118],
89
- [6, 26, 50, 74, 98, 122],
90
- [6, 30, 54, 78, 102, 126],
91
- [6, 26, 52, 78, 104, 130],
92
- [6, 30, 56, 82, 108, 134],
93
- [6, 34, 60, 86, 112, 138],
94
- [6, 30, 58, 86, 114, 142],
95
- [6, 34, 62, 90, 118, 146],
96
- [6, 30, 54, 78, 102, 126, 150],
97
- [6, 24, 50, 76, 102, 128, 154],
98
- [6, 28, 54, 80, 106, 132, 158],
99
- [6, 32, 58, 84, 110, 136, 162],
100
- [6, 26, 54, 82, 110, 138, 166],
101
- [6, 30, 58, 86, 114, 142, 170]
62
+ [], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34],
63
+ [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54],
64
+ [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74],
65
+ [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90],
66
+ [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106],
67
+ [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122],
68
+ [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134],
69
+ [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146],
70
+ [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158],
71
+ [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170]
102
72
  ]
103
73
 
104
74
  // BCH codes
@@ -106,7 +76,7 @@ const G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (
106
76
  const G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0)
107
77
  const G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1)
108
78
 
109
- // RS Block table
79
+ // RS Block table - each entry is for one typeNumber, 4 error levels
110
80
  const RS_BLOCK_TABLE: number[][] = [
111
81
  [1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9],
112
82
  [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16],
@@ -127,44 +97,18 @@ const RS_BLOCK_TABLE: number[][] = [
127
97
  [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15],
128
98
  [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15],
129
99
  [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14],
130
- [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16],
131
- [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17],
132
- [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13],
133
- [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16],
134
- [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17],
135
- [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16],
136
- [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17],
137
- [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16],
138
- [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16],
139
- [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16],
140
- [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16],
141
- [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16],
142
- [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16],
143
- [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16],
144
- [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17],
145
- [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16],
146
- [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16],
147
- [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16],
148
- [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16],
149
- [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16],
150
- [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16]
100
+ [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16]
151
101
  ]
152
102
 
153
- // QR Math - Galois Field operations
154
- const EXP_TABLE: number[] = new Array(256)
155
- const LOG_TABLE: number[] = new Array(256)
103
+ // Galois Field tables
104
+ const EXP_TABLE: number[] = []
105
+ const LOG_TABLE: number[] = []
156
106
 
157
- // Initialize EXP_TABLE
158
- for (let i = 0; i < 8; i++) {
159
- EXP_TABLE[i] = 1 << i
160
- }
161
- for (let i = 8; i < 256; i++) {
162
- EXP_TABLE[i] = EXP_TABLE[i - 4] ^ EXP_TABLE[i - 5] ^ EXP_TABLE[i - 6] ^ EXP_TABLE[i - 8]
163
- }
164
- // Initialize LOG_TABLE
165
- for (let i = 0; i < 255; i++) {
166
- LOG_TABLE[EXP_TABLE[i]] = i
167
- }
107
+ // Initialize GF tables
108
+ for (let i = 0; i < 8; i++) EXP_TABLE[i] = 1 << i
109
+ for (let i = 8; i < 256; i++) EXP_TABLE[i] = EXP_TABLE[i - 4] ^ EXP_TABLE[i - 5] ^ EXP_TABLE[i - 6] ^ EXP_TABLE[i - 8]
110
+ for (let i = 0; i < 255; i++) LOG_TABLE[EXP_TABLE[i]] = i
111
+ for (let i = 255; i < 512; i++) EXP_TABLE[i] = EXP_TABLE[i - 255]
168
112
 
169
113
  function glog(n: number): number {
170
114
  if (n < 1) throw new Error('glog(' + n + ')')
@@ -177,80 +121,23 @@ function gexp(n: number): number {
177
121
  return EXP_TABLE[n]
178
122
  }
179
123
 
180
- // Polynomial operations
181
- interface Polynomial {
182
- getAt(index: number): number
183
- getLength(): number
184
- multiply(e: Polynomial): Polynomial
185
- mod(e: Polynomial): Polynomial
186
- }
187
-
188
- function createPolynomial(num: number[], shift: number): Polynomial {
189
- // Remove leading zeros
190
- let offset = 0
191
- while (offset < num.length && num[offset] === 0) offset++
192
- const _num = new Array(num.length - offset + shift).fill(0)
193
- for (let i = 0; i < num.length - offset; i++) {
194
- _num[i] = num[i + offset]
195
- }
196
-
197
- return {
198
- getAt: (index: number) => _num[index],
199
- getLength: () => _num.length,
200
- multiply: (e: Polynomial): Polynomial => {
201
- const result = new Array(_num.length + e.getLength() - 1).fill(0)
202
- for (let i = 0; i < _num.length; i++) {
203
- for (let j = 0; j < e.getLength(); j++) {
204
- result[i + j] ^= gexp(glog(_num[i]) + glog(e.getAt(j)))
205
- }
206
- }
207
- return createPolynomial(result, 0)
208
- },
209
- mod: (e: Polynomial): Polynomial => {
210
- const lenDiff = _num.length - e.getLength()
211
- if (lenDiff < 0) return createPolynomial(_num, 0)
212
-
213
- const ratio = glog(_num[0]) - glog(e.getAt(0))
214
- const result = [..._num]
215
- for (let i = 0; i < e.getLength(); i++) {
216
- result[i] ^= gexp(glog(e.getAt(i)) + ratio)
217
- }
218
- return createPolynomial(result, 0).mod(e)
219
- }
220
- }
221
- }
222
-
223
124
  // Get BCH digit count
224
125
  function getBCHDigit(data: number): number {
225
126
  let digit = 0
226
- while (data !== 0) {
227
- digit++
228
- data >>>= 1
229
- }
127
+ while (data !== 0) { digit++; data >>>= 1 }
230
128
  return digit
231
129
  }
232
130
 
233
131
  // Get BCH type info
234
132
  function getBCHTypeInfo(data: number): number {
235
133
  let d = data << 10
236
- while (getBCHDigit(d) - getBCHDigit(G15) >= 0) {
237
- d ^= G15 << (getBCHDigit(d) - getBCHDigit(G15))
238
- }
134
+ while (getBCHDigit(d) - getBCHDigit(G15) >= 0) d ^= G15 << (getBCHDigit(d) - getBCHDigit(G15))
239
135
  return ((data << 10) | d) ^ G15_MASK
240
136
  }
241
137
 
242
- // Get BCH type number
243
- function getBCHTypeNumber(data: number): number {
244
- let d = data << 12
245
- while (getBCHDigit(d) - getBCHDigit(G18) >= 0) {
246
- d ^= G18 << (getBCHDigit(d) - getBCHDigit(G18))
247
- }
248
- return (data << 12) | d
249
- }
250
-
251
138
  // Get pattern position
252
139
  function getPatternPosition(typeNumber: number): number[] {
253
- return PATTERN_POSITION_TABLE[typeNumber - 1]
140
+ return PATTERN_POSITION_TABLE[typeNumber - 1] || []
254
141
  }
255
142
 
256
143
  // Get mask function
@@ -264,507 +151,372 @@ function getMaskFunction(maskPattern: number): (i: number, j: number) => boolean
264
151
  case 5: return (i, j) => (i * j) % 2 + (i * j) % 3 === 0
265
152
  case 6: return (i, j) => ((i * j) % 2 + (i * j) % 3) % 2 === 0
266
153
  case 7: return (i, j) => ((i * j) % 3 + (i + j) % 2) % 2 === 0
267
- default: throw new Error('bad maskPattern:' + maskPattern)
268
- }
269
- }
270
-
271
- // Get error correct polynomial
272
- function getErrorCorrectPolynomial(errorCorrectLength: number): Polynomial {
273
- let a = createPolynomial([1], 0)
274
- for (let i = 0; i < errorCorrectLength; i++) {
275
- a = a.multiply(createPolynomial([1, gexp(i)], 0))
276
- }
277
- return a
278
- }
279
-
280
- // Get length in bits for mode
281
- function getLengthInBits(mode: number, type: number): number {
282
- if (1 <= type && type < 10) {
283
- switch (mode) {
284
- case 1: return 10 // NUMBER
285
- case 2: return 9 // ALPHA_NUM
286
- case 4: return 8 // 8BIT_BYTE
287
- case 8: return 8 // KANJI
288
- default: throw new Error('mode:' + mode)
289
- }
290
- } else if (type < 27) {
291
- switch (mode) {
292
- case 1: return 12
293
- case 2: return 11
294
- case 4: return 16
295
- case 8: return 10
296
- default: throw new Error('mode:' + mode)
297
- }
298
- } else if (type < 41) {
299
- switch (mode) {
300
- case 1: return 14
301
- case 2: return 13
302
- case 4: return 16
303
- case 8: return 12
304
- default: throw new Error('mode:' + mode)
305
- }
306
- } else {
307
- throw new Error('type:' + type)
154
+ default: return () => false
308
155
  }
309
156
  }
310
157
 
311
158
  // Get RS blocks
312
159
  function getRSBlocks(typeNumber: number, errorCorrectionLevel: number): { totalCount: number; dataCount: number }[] {
313
- const rsBlock = RS_BLOCK_TABLE[(typeNumber - 1) * 4 + errorCorrectionLevel]
314
- if (!rsBlock) throw new Error('bad rs block')
160
+ const index = (typeNumber - 1) * 4 + errorCorrectionLevel
161
+ const block = RS_BLOCK_TABLE[index]
162
+ if (!block) throw new Error('bad rs block')
315
163
 
316
- const length = rsBlock.length / 3
317
164
  const list: { totalCount: number; dataCount: number }[] = []
165
+ const length = Math.floor(block.length / 3)
318
166
 
319
167
  for (let i = 0; i < length; i++) {
320
- const count = rsBlock[i * 3 + 0]
321
- const totalCount = rsBlock[i * 3 + 1]
322
- const dataCount = rsBlock[i * 3 + 2]
323
- for (let j = 0; j < count; j++) {
324
- list.push({ totalCount, dataCount })
325
- }
168
+ const count = block[i * 3]
169
+ const totalCount = block[i * 3 + 1]
170
+ const dataCount = block[i * 3 + 2]
171
+ for (let j = 0; j < count; j++) list.push({ totalCount, dataCount })
326
172
  }
327
173
  return list
328
174
  }
329
175
 
330
- // Bit buffer
176
+ // Bit buffer class
331
177
  class BitBuffer {
332
- private buffer: number[] = []
333
- private length = 0
178
+ buffer: number[] = []
179
+ length = 0
334
180
 
335
- getBuffer(): number[] { return this.buffer }
336
- getLengthInBits(): number { return this.length }
181
+ getBuffer() { return this.buffer }
182
+ getLengthInBits() { return this.length }
337
183
 
338
184
  put(num: number, len: number) {
339
- for (let i = 0; i < len; i++) {
340
- this.putBit(((num >>> (len - i - 1)) & 1) === 1)
341
- }
185
+ for (let i = 0; i < len; i++) this.putBit(((num >>> (len - i - 1)) & 1) === 1)
342
186
  }
343
187
 
344
188
  putBit(bit: boolean) {
345
189
  const bufIndex = Math.floor(this.length / 8)
346
- if (this.buffer.length <= bufIndex) {
347
- this.buffer.push(0)
348
- }
349
- if (bit) {
350
- this.buffer[bufIndex] |= (0x80 >>> (this.length % 8))
351
- }
190
+ if (this.buffer.length <= bufIndex) this.buffer.push(0)
191
+ if (bit) this.buffer[bufIndex] |= 0x80 >>> (this.length % 8)
352
192
  this.length++
353
193
  }
354
194
  }
355
195
 
356
- // String to bytes (UTF-8)
196
+ // String to bytes
357
197
  function stringToBytes(s: string): number[] {
358
198
  const bytes: number[] = []
359
- for (let i = 0; i < s.length; i++) {
360
- const c = s.charCodeAt(i)
361
- bytes.push(c & 0xff)
362
- }
199
+ for (let i = 0; i < s.length; i++) bytes.push(s.charCodeAt(i) & 0xff)
363
200
  return bytes
364
201
  }
365
202
 
366
- // Create data bytes
367
- function createData(typeNumber: number, errorCorrectionLevel: number, dataList: { mode: number; data: string }[]): number[] {
368
- const rsBlocks = getRSBlocks(typeNumber, errorCorrectionLevel)
369
- const buffer = new BitBuffer()
203
+ // Get length in bits for mode
204
+ function getLengthInBits(mode: number, type: number): number {
205
+ if (type < 10) return mode === 4 ? 8 : 8
206
+ if (type < 27) return mode === 4 ? 16 : 10
207
+ return mode === 4 ? 16 : 12
208
+ }
370
209
 
371
- // Add data
372
- for (const data of dataList) {
373
- buffer.put(data.mode, 4)
374
- buffer.put(stringToBytes(data.data).length, getLengthInBits(data.mode, typeNumber))
375
- for (const byte of stringToBytes(data.data)) {
376
- buffer.put(byte, 8)
210
+ // Polynomial multiplication
211
+ function multiplyPolynomials(a: number[], b: number[]): number[] {
212
+ const result = new Array(a.length + b.length - 1).fill(0)
213
+ for (let i = 0; i < a.length; i++) {
214
+ for (let j = 0; j < b.length; j++) {
215
+ result[i + j] ^= gexp(glog(a[i]) + glog(b[j]))
377
216
  }
378
217
  }
218
+ return result
219
+ }
379
220
 
380
- // Calc total data count
381
- let totalDataCount = 0
382
- for (const block of rsBlocks) {
383
- totalDataCount += block.dataCount
221
+ // Polynomial modulo
222
+ function modPolynomial(a: number[], b: number[]): number[] {
223
+ let result = [...a]
224
+ while (result.length >= b.length) {
225
+ const ratio = glog(result[0]) - glog(b[0])
226
+ for (let i = 0; i < b.length; i++) {
227
+ result[i] ^= gexp(glog(b[i]) + ratio)
228
+ }
229
+ while (result.length > 0 && result[0] === 0) result.shift()
384
230
  }
231
+ return result
232
+ }
385
233
 
386
- if (buffer.getLengthInBits() > totalDataCount * 8) {
387
- throw new Error('code length overflow')
234
+ // Get error correction codewords
235
+ function getErrorCorrectPolynomial(errorCorrectLength: number): number[] {
236
+ let poly = [1]
237
+ for (let i = 0; i < errorCorrectLength; i++) {
238
+ poly = multiplyPolynomials(poly, [1, gexp(i)])
388
239
  }
240
+ return poly
241
+ }
389
242
 
390
- // End code
391
- if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
392
- buffer.put(0, 4)
393
- }
243
+ // Create data bytes with error correction
244
+ function createData(typeNumber: number, errorCorrectionLevel: number, data: string): number[] {
245
+ const rsBlocks = getRSBlocks(typeNumber, errorCorrectionLevel)
394
246
 
395
- // Padding to byte boundary
396
- while (buffer.getLengthInBits() % 8 !== 0) {
397
- buffer.putBit(false)
398
- }
247
+ // Calculate total data count
248
+ let totalDataCount = 0
249
+ for (const block of rsBlocks) totalDataCount += block.dataCount
399
250
 
400
- // Padding bytes
401
- while (true) {
402
- if (buffer.getLengthInBits() >= totalDataCount * 8) break
403
- buffer.put(PAD0, 8)
404
- if (buffer.getLengthInBits() >= totalDataCount * 8) break
405
- buffer.put(PAD1, 8)
406
- }
251
+ const buffer = new BitBuffer()
407
252
 
408
- return createBytes(buffer, rsBlocks)
409
- }
253
+ // Mode indicator (4 = byte mode)
254
+ buffer.put(4, 4)
410
255
 
411
- // Create bytes with error correction
412
- function createBytes(buffer: BitBuffer, rsBlocks: { totalCount: number; dataCount: number }[]): number[] {
413
- let offset = 0
414
- let maxDcCount = 0
415
- let maxEcCount = 0
256
+ // Character count
257
+ const bytes = stringToBytes(data)
258
+ buffer.put(bytes.length, getLengthInBits(4, typeNumber))
416
259
 
417
- const dcdata: number[][] = []
418
- const ecdata: number[][] = []
260
+ // Data
261
+ for (const byte of bytes) buffer.put(byte, 8)
419
262
 
420
- for (const block of rsBlocks) {
421
- const dcCount = block.dataCount
422
- const ecCount = block.totalCount - dcCount
263
+ // Terminator
264
+ if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) buffer.put(0, 4)
423
265
 
424
- maxDcCount = Math.max(maxDcCount, dcCount)
425
- maxEcCount = Math.max(maxEcCount, ecCount)
266
+ // Pad to byte boundary
267
+ while (buffer.getLengthInBits() % 8 !== 0) buffer.putBit(false)
426
268
 
427
- const dc: number[] = []
428
- for (let i = 0; i < dcCount; i++) {
429
- dc.push(0xff & buffer.getBuffer()[i + offset])
430
- }
431
- dcdata.push(dc)
432
- offset += dcCount
269
+ // Padding bytes
270
+ let padIndex = 0
271
+ const padBytes = [PAD0, PAD1]
272
+ while (buffer.getLengthInBits() < totalDataCount * 8) {
273
+ buffer.put(padBytes[padIndex], 8)
274
+ padIndex = (padIndex + 1) % 2
275
+ }
276
+
277
+ // Create data and EC codewords
278
+ const dataBuffer = buffer.getBuffer()
279
+ let dataIndex = 0
280
+ const dcData: number[][] = []
281
+ const ecData: number[][] = []
282
+
283
+ for (const block of rsBlocks) {
284
+ const dc = dataBuffer.slice(dataIndex, dataIndex + block.dataCount)
285
+ dataIndex += block.dataCount
286
+ dcData.push(dc)
433
287
 
434
- // Generate error correction
288
+ const ecCount = block.totalCount - block.dataCount
435
289
  const rsPoly = getErrorCorrectPolynomial(ecCount)
436
- const rawPoly = createPolynomial(dc, rsPoly.getLength() - 1)
437
- const modPoly = rawPoly.mod(rsPoly)
438
290
 
439
- const ec: number[] = new Array(rsPoly.getLength() - 1).fill(0)
440
- for (let i = 0; i < ec.length; i++) {
441
- const modIndex = i + modPoly.getLength() - ec.length
442
- ec[i] = modIndex >= 0 ? modPoly.getAt(modIndex) : 0
443
- }
444
- ecdata.push(ec)
291
+ // Pad data polynomial
292
+ const dataPoly = [...dc, ...new Array(ecCount).fill(0)]
293
+ const ec = modPolynomial(dataPoly, rsPoly)
294
+
295
+ ecData.push(ec.slice(-ecCount))
445
296
  }
446
297
 
447
- // Interleave
298
+ // Interleave data codewords
448
299
  const result: number[] = []
300
+ let maxDcCount = 0
301
+ let maxEcCount = 0
302
+ for (const block of rsBlocks) {
303
+ maxDcCount = Math.max(maxDcCount, block.dataCount)
304
+ maxEcCount = Math.max(maxEcCount, block.totalCount - block.dataCount)
305
+ }
306
+
449
307
  for (let i = 0; i < maxDcCount; i++) {
450
- for (let j = 0; j < dcdata.length; j++) {
451
- if (i < dcdata[j].length) result.push(dcdata[j][i])
308
+ for (const dc of dcData) {
309
+ if (i < dc.length) result.push(dc[i])
452
310
  }
453
311
  }
312
+
454
313
  for (let i = 0; i < maxEcCount; i++) {
455
- for (let j = 0; j < ecdata.length; j++) {
456
- if (i < ecdata[j].length) result.push(ecdata[j][i])
314
+ for (const ec of ecData) {
315
+ if (i < ec.length) result.push(ec[i])
457
316
  }
458
317
  }
459
318
 
460
319
  return result
461
320
  }
462
321
 
463
- // QR Code class
464
- class QRCode {
465
- private typeNumber: number
466
- private errorCorrectionLevel: number
467
- private modules: (boolean | null)[][] = []
468
- private moduleCount = 0
469
- private dataCache: number[] | null = null
470
-
471
- constructor(typeNumber: number, errorCorrectionLevel: string) {
472
- this.typeNumber = typeNumber
473
- this.errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel]
474
- }
322
+ // Main QR Code generator
323
+ function generateQRCode(text: string, errorLevel: string): { modules: boolean[][]; moduleCount: number } {
324
+ const ecl = QRErrorCorrectionLevel[errorLevel] ?? 0
475
325
 
476
- addData(data: string) {
477
- const dataList = [{ mode: 4, data }] // 4 = 8BIT_BYTE mode
478
- this.dataCache = createData(this.typeNumber, this.errorCorrectionLevel, dataList)
479
- }
480
-
481
- make() {
482
- if (this.typeNumber < 1) {
483
- // Auto detect type number
484
- let typeNumber = 1
485
- for (; typeNumber < 40; typeNumber++) {
486
- try {
487
- const testData = createData(typeNumber, this.errorCorrectionLevel, [{ mode: 4, data: 'test' }])
488
- // Just check if it works
489
- this.dataCache = createData(typeNumber, this.errorCorrectionLevel, [{ mode: 4, data: '' }])
490
- break
491
- } catch {
492
- continue
493
- }
494
- }
495
- this.typeNumber = typeNumber
496
- // Recreate data cache with correct type number
497
- const dataList = [{ mode: 4, data: '' }]
498
- // We'll set data again below
499
- }
500
-
501
- this.makeImpl(false, this.getBestMaskPattern())
502
- }
326
+ // Find minimum version
327
+ let typeNumber = 1
328
+ for (; typeNumber <= 40; typeNumber++) {
329
+ const rsBlocks = getRSBlocks(typeNumber, ecl)
330
+ let totalDataCount = 0
331
+ for (const block of rsBlocks) totalDataCount += block.dataCount
503
332
 
504
- private getBestMaskPattern(): number {
505
- let minLostPoint = 0
506
- let pattern = 0
333
+ // Byte mode: 4 (mode) + length bits + data bits
334
+ const lengthBits = typeNumber < 10 ? 8 : 16
335
+ const totalBits = 4 + lengthBits + (stringToBytes(text).length * 8)
507
336
 
508
- for (let i = 0; i < 8; i++) {
509
- this.makeImpl(true, i)
510
- const lostPoint = this.getLostPoint()
511
- if (i === 0 || minLostPoint > lostPoint) {
512
- minLostPoint = lostPoint
513
- pattern = i
514
- }
515
- }
516
- return pattern
337
+ if (totalBits <= totalDataCount * 8) break
517
338
  }
518
339
 
519
- private makeImpl(test: boolean, maskPattern: number) {
520
- this.moduleCount = this.typeNumber * 4 + 17
521
- this.modules = []
522
- for (let row = 0; row < this.moduleCount; row++) {
523
- this.modules[row] = new Array(this.moduleCount).fill(null)
524
- }
340
+ const size = typeNumber * 4 + 17
341
+ const data = createData(typeNumber, ecl, text)
525
342
 
526
- this.setupPositionProbePattern(0, 0)
527
- this.setupPositionProbePattern(this.moduleCount - 7, 0)
528
- this.setupPositionProbePattern(0, this.moduleCount - 7)
529
- this.setupPositionAdjustPattern()
530
- this.setupTimingPattern()
531
- this.setupTypeInfo(test, maskPattern)
343
+ // Initialize modules
344
+ const mod: (boolean | null)[][] = []
345
+ for (let i = 0; i < size; i++) mod[i] = new Array(size).fill(null)
532
346
 
533
- if (this.typeNumber >= 7) {
534
- this.setupTypeNumber(test)
535
- }
536
-
537
- if (this.dataCache === null) {
538
- throw new Error('dataCache is null')
539
- }
540
-
541
- this.mapData(this.dataCache, maskPattern)
542
- }
543
-
544
- private setupPositionProbePattern(row: number, col: number) {
347
+ // Setup finder patterns
348
+ const setupFinderPattern = (row: number, col: number) => {
545
349
  for (let r = -1; r <= 7; r++) {
546
- if (row + r <= -1 || this.moduleCount <= row + r) continue
547
350
  for (let c = -1; c <= 7; c++) {
548
- if (col + c <= -1 || this.moduleCount <= col + c) continue
549
-
550
- if (
551
- (0 <= r && r <= 6 && (c === 0 || c === 6)) ||
552
- (0 <= c && c <= 6 && (r === 0 || r === 6)) ||
553
- (2 <= r && r <= 4 && 2 <= c && c <= 4)
554
- ) {
555
- this.modules[row + r][col + c] = true
351
+ const nr = row + r, nc = col + c
352
+ if (nr < 0 || nr >= size || nc < 0 || nc >= size) continue
353
+
354
+ if ((r >= 0 && r <= 6 && (c === 0 || c === 6)) ||
355
+ (c >= 0 && c <= 6 && (r === 0 || r === 6)) ||
356
+ (r >= 2 && r <= 4 && c >= 2 && c <= 4)) {
357
+ mod[nr][nc] = true
556
358
  } else {
557
- this.modules[row + r][col + c] = false
359
+ mod[nr][nc] = false
558
360
  }
559
361
  }
560
362
  }
561
363
  }
562
364
 
563
- private setupTimingPattern() {
564
- for (let r = 8; r < this.moduleCount - 8; r++) {
565
- if (this.modules[r][6] !== null) continue
566
- this.modules[r][6] = r % 2 === 0
567
- }
568
- for (let c = 8; c < this.moduleCount - 8; c++) {
569
- if (this.modules[6][c] !== null) continue
570
- this.modules[6][c] = c % 2 === 0
571
- }
365
+ setupFinderPattern(0, 0)
366
+ setupFinderPattern(size - 7, 0)
367
+ setupFinderPattern(0, size - 7)
368
+
369
+ // Setup timing patterns
370
+ for (let i = 8; i < size - 8; i++) {
371
+ if (mod[i][6] === null) mod[i][6] = i % 2 === 0
372
+ if (mod[6][i] === null) mod[6][i] = i % 2 === 0
572
373
  }
573
374
 
574
- private setupPositionAdjustPattern() {
575
- const pos = getPatternPosition(this.typeNumber)
576
- for (let i = 0; i < pos.length; i++) {
577
- for (let j = 0; j < pos.length; j++) {
578
- const row = pos[i]
579
- const col = pos[j]
580
- if (this.modules[row][col] !== null) continue
581
-
582
- for (let r = -2; r <= 2; r++) {
583
- for (let c = -2; c <= 2; c++) {
584
- if (r === -2 || r === 2 || c === -2 || c === 2 || (r === 0 && c === 0)) {
585
- this.modules[row + r][col + c] = true
586
- } else {
587
- this.modules[row + r][col + c] = false
588
- }
589
- }
375
+ // Setup alignment patterns
376
+ const alignPos = getPatternPosition(typeNumber)
377
+ for (const row of alignPos) {
378
+ for (const col of alignPos) {
379
+ if (mod[row][col] !== null) continue
380
+ for (let r = -2; r <= 2; r++) {
381
+ for (let c = -2; c <= 2; c++) {
382
+ mod[row + r][col + c] = r === -2 || r === 2 || c === -2 || c === 2 || (r === 0 && c === 0)
590
383
  }
591
384
  }
592
385
  }
593
386
  }
594
387
 
595
- private setupTypeNumber(test: boolean) {
596
- const bits = getBCHTypeNumber(this.typeNumber)
597
- for (let i = 0; i < 18; i++) {
598
- const mod = !test && ((bits >> i) & 1) === 1
599
- this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod
600
- }
601
- for (let i = 0; i < 18; i++) {
602
- const mod = !test && ((bits >> i) & 1) === 1
603
- this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod
604
- }
388
+ // Setup format info area (reserved)
389
+ for (let i = 0; i < 9; i++) {
390
+ if (mod[8][i] === null) mod[8][i] = false
391
+ if (mod[i][8] === null) mod[i][8] = false
605
392
  }
393
+ for (let i = 0; i < 8; i++) {
394
+ if (mod[8][size - 1 - i] === null) mod[8][size - 1 - i] = false
395
+ if (mod[size - 1 - i][8] === null) mod[size - 1 - i][8] = false
396
+ }
397
+ // Dark module
398
+ mod[size - 8][8] = true
606
399
 
607
- private setupTypeInfo(test: boolean, maskPattern: number) {
608
- const data = (this.errorCorrectionLevel << 3) | maskPattern
609
- const bits = getBCHTypeInfo(data)
610
-
611
- // Vertical
612
- for (let i = 0; i < 15; i++) {
613
- const mod = !test && ((bits >> i) & 1) === 1
614
- if (i < 6) {
615
- this.modules[i][8] = mod
616
- } else if (i < 8) {
617
- this.modules[i + 1][8] = mod
618
- } else {
619
- this.modules[this.moduleCount - 15 + i][8] = mod
400
+ // Setup type number (version 7+)
401
+ if (typeNumber >= 7) {
402
+ const bits = typeNumber
403
+ for (let i = 0; i < 6; i++) {
404
+ for (let j = 0; j < 3; j++) {
405
+ mod[i][size - 11 + j] = ((bits >> (i * 3 + j)) & 1) === 1
406
+ mod[size - 11 + j][i] = ((bits >> (i * 3 + j)) & 1) === 1
620
407
  }
621
408
  }
409
+ }
622
410
 
623
- // Horizontal
624
- for (let i = 0; i < 15; i++) {
625
- const mod = !test && ((bits >> i) & 1) === 1
626
- if (i < 8) {
627
- this.modules[8][this.moduleCount - i - 1] = mod
628
- } else if (i < 9) {
629
- this.modules[8][15 - i - 1 + 1] = mod
630
- } else {
631
- this.modules[8][15 - i - 1] = mod
632
- }
633
- }
411
+ // Map data with best mask
412
+ let bestMask = 0
413
+ let bestPenalty = Infinity
634
414
 
635
- // Fixed module
636
- this.modules[this.moduleCount - 8][8] = !test
415
+ for (let mask = 0; mask < 8; mask++) {
416
+ const testMod = mod.map(row => [...row])
417
+ mapData(testMod, size, data, mask)
418
+ const penalty = calculatePenalty(testMod, size)
419
+ if (penalty < bestPenalty) {
420
+ bestPenalty = penalty
421
+ bestMask = mask
422
+ }
637
423
  }
638
424
 
639
- private mapData(data: number[], maskPattern: number) {
640
- let inc = -1
641
- let row = this.moduleCount - 1
642
- let bitIndex = 7
643
- let byteIndex = 0
644
- const maskFunc = getMaskFunction(maskPattern)
645
-
646
- for (let col = this.moduleCount - 1; col > 0; col -= 2) {
647
- if (col === 6) col -= 1
648
-
649
- while (true) {
650
- for (let c = 0; c < 2; c++) {
651
- if (this.modules[row][col - c] === null) {
652
- let dark = false
653
- if (byteIndex < data.length) {
654
- dark = ((data[byteIndex] >>> bitIndex) & 1) === 1
655
- }
656
-
657
- const mask = maskFunc(row, col - c)
658
- if (mask) dark = !dark
659
-
660
- this.modules[row][col - c] = dark
661
- bitIndex--
662
-
663
- if (bitIndex === -1) {
664
- byteIndex++
665
- bitIndex = 7
666
- }
667
- }
668
- }
425
+ // Apply best mask
426
+ mapData(mod, size, data, bestMask)
669
427
 
670
- row += inc
671
- if (row < 0 || this.moduleCount <= row) {
672
- row -= inc
673
- inc = -inc
674
- break
675
- }
676
- }
428
+ // Write format info
429
+ const formatData = (ecl << 3) | bestMask
430
+ const formatBits = getBCHTypeInfo(formatData)
431
+
432
+ // Write format bits
433
+ for (let i = 0; i < 6; i++) mod[8][i] = ((formatBits >> i) & 1) === 1
434
+ mod[8][7] = ((formatBits >> 6) & 1) === 1
435
+ mod[8][8] = ((formatBits >> 7) & 1) === 1
436
+ mod[7][8] = ((formatBits >> 8) & 1) === 1
437
+ for (let i = 9; i < 15; i++) mod[14 - i][8] = ((formatBits >> i) & 1) === 1
438
+
439
+ for (let i = 0; i < 8; i++) mod[8][size - 1 - i] = ((formatBits >> i) & 1) === 1
440
+ for (let i = 8; i < 15; i++) mod[size - 15 + i][8] = ((formatBits >> i) & 1) === 1
441
+
442
+ // Convert to boolean matrix
443
+ const result: boolean[][] = []
444
+ for (let r = 0; r < size; r++) {
445
+ result[r] = []
446
+ for (let c = 0; c < size; c++) {
447
+ result[r][c] = mod[r][c] === true
677
448
  }
678
449
  }
679
450
 
680
- private getLostPoint(): number {
681
- let lostPoint = 0
682
-
683
- // LEVEL1
684
- for (let row = 0; row < this.moduleCount; row++) {
685
- for (let col = 0; col < this.moduleCount; col++) {
686
- let sameCount = 0
687
- const dark = this.modules[row][col]
688
-
689
- for (let r = -1; r <= 1; r++) {
690
- if (row + r < 0 || this.moduleCount <= row + r) continue
691
- for (let c = -1; c <= 1; c++) {
692
- if (col + c < 0 || this.moduleCount <= col + c) continue
693
- if (r === 0 && c === 0) continue
694
- if (dark === this.modules[row + r][col + c]) sameCount++
451
+ return { modules: result, moduleCount: size }
452
+ }
453
+
454
+ // Map data to modules
455
+ function mapData(mod: (boolean | null)[][], size: number, data: number[], maskPattern: number) {
456
+ const maskFunc = getMaskFunction(maskPattern)
457
+ let bitIndex = 0
458
+ let inc = -1
459
+ let row = size - 1
460
+
461
+ for (let col = size - 1; col > 0; col -= 2) {
462
+ if (col === 6) col = 5
463
+
464
+ while (true) {
465
+ for (let c = 0; c < 2; c++) {
466
+ const tc = col - c
467
+ if (mod[row][tc] === null) {
468
+ let dark = false
469
+ if (bitIndex < data.length * 8) {
470
+ dark = ((data[Math.floor(bitIndex / 8)] >>> (7 - (bitIndex % 8))) & 1) === 1
695
471
  }
472
+ if (maskFunc(row, tc)) dark = !dark
473
+ mod[row][tc] = dark
474
+ bitIndex++
696
475
  }
697
- if (sameCount > 5) lostPoint += 3 + sameCount - 5
698
476
  }
699
- }
700
477
 
701
- // LEVEL2
702
- for (let row = 0; row < this.moduleCount - 1; row++) {
703
- for (let col = 0; col < this.moduleCount - 1; col++) {
704
- let count = 0
705
- if (this.modules[row][col]) count++
706
- if (this.modules[row + 1][col]) count++
707
- if (this.modules[row][col + 1]) count++
708
- if (this.modules[row + 1][col + 1]) count++
709
- if (count === 0 || count === 4) lostPoint += 3
478
+ row += inc
479
+ if (row < 0 || row >= size) {
480
+ row -= inc
481
+ inc = -inc
482
+ break
710
483
  }
711
484
  }
485
+ }
486
+ }
712
487
 
713
- // LEVEL3
714
- for (let row = 0; row < this.moduleCount; row++) {
715
- for (let col = 0; col < this.moduleCount - 6; col++) {
716
- if (
717
- this.modules[row][col] &&
718
- !this.modules[row][col + 1] &&
719
- this.modules[row][col + 2] &&
720
- this.modules[row][col + 3] &&
721
- this.modules[row][col + 4] &&
722
- !this.modules[row][col + 5] &&
723
- this.modules[row][col + 6]
724
- ) {
725
- lostPoint += 40
726
- }
727
- }
728
- }
729
- for (let col = 0; col < this.moduleCount; col++) {
730
- for (let row = 0; row < this.moduleCount - 6; row++) {
731
- if (
732
- this.modules[row][col] &&
733
- !this.modules[row + 1][col] &&
734
- this.modules[row + 2][col] &&
735
- this.modules[row + 3][col] &&
736
- this.modules[row + 4][col] &&
737
- !this.modules[row + 5][col] &&
738
- this.modules[row + 6][col]
739
- ) {
740
- lostPoint += 40
741
- }
742
- }
743
- }
488
+ // Calculate penalty score
489
+ function calculatePenalty(mod: (boolean | null)[][], size: number): number {
490
+ let penalty = 0
744
491
 
745
- // LEVEL4
746
- let darkCount = 0
747
- for (let col = 0; col < this.moduleCount; col++) {
748
- for (let row = 0; row < this.moduleCount; row++) {
749
- if (this.modules[row][col]) darkCount++
492
+ // Rule 1: Adjacent modules in row/column
493
+ for (let row = 0; row < size; row++) {
494
+ let count = 1
495
+ for (let col = 1; col < size; col++) {
496
+ if (mod[row][col] === mod[row][col - 1]) {
497
+ count++
498
+ } else {
499
+ if (count >= 5) penalty += 3 + (count - 5)
500
+ count = 1
750
501
  }
751
502
  }
752
- const ratio = Math.abs(100 * darkCount / this.moduleCount / this.moduleCount - 50) / 5
753
- lostPoint += ratio * 10
754
-
755
- return lostPoint
503
+ if (count >= 5) penalty += 3 + (count - 5)
756
504
  }
757
505
 
758
- getModuleCount(): number {
759
- return this.moduleCount
760
- }
761
-
762
- isDark(row: number, col: number): boolean {
763
- if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
764
- throw new Error(row + ',' + col)
506
+ for (let col = 0; col < size; col++) {
507
+ let count = 1
508
+ for (let row = 1; row < size; row++) {
509
+ if (mod[row][col] === mod[row - 1][col]) {
510
+ count++
511
+ } else {
512
+ if (count >= 5) penalty += 3 + (count - 5)
513
+ count = 1
514
+ }
765
515
  }
766
- return this.modules[row][col] === true
516
+ if (count >= 5) penalty += 3 + (count - 5)
767
517
  }
518
+
519
+ return penalty
768
520
  }
769
521
 
770
522
  // Generate QR code
@@ -776,22 +528,9 @@ const generate = () => {
776
528
  }
777
529
 
778
530
  try {
779
- const qr = new QRCode(0, props.errorCorrectLevel)
780
- qr.addData(props.value)
781
- qr.make()
782
-
783
- const count = qr.getModuleCount()
784
- moduleCount.value = count
785
-
786
- const result: boolean[][] = []
787
- for (let row = 0; row < count; row++) {
788
- result[row] = []
789
- for (let col = 0; col < count; col++) {
790
- result[row][col] = qr.isDark(row, col)
791
- }
792
- }
793
-
794
- modules.value = result
531
+ const qr = generateQRCode(props.value, props.errorCorrectLevel)
532
+ modules.value = qr.modules
533
+ moduleCount.value = qr.moduleCount
795
534
  } catch (e) {
796
535
  console.error('QR Code generation failed:', e)
797
536
  modules.value = []