bare-script 3.8.15 → 3.8.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/include/qrcode.bare +825 -0
- package/package.json +1 -1
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
# Licensed under the MIT License
|
|
2
|
+
# https://github.com/craigahobbs/bare-script/blob/main/LICENSE
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# $function: qrcodeDraw
|
|
6
|
+
# $group: qrcode.bare
|
|
7
|
+
# $doc: Draw a QR code at the specified position and size
|
|
8
|
+
# $arg message: The QR code message or the QR code matrix
|
|
9
|
+
# $arg x: The X-coordinate, in pixels, of the left-side of the QR code
|
|
10
|
+
# $arg y: The Y-coordinate, in pixels, of the top of the QR code
|
|
11
|
+
# $arg size: The size of the QR code, in pixels
|
|
12
|
+
# $arg level: Optional (default is 'low'). The error correction level: 'low', 'medium', 'quartile', or 'high'.
|
|
13
|
+
function qrcodeDraw(message, x, y, size, level):
|
|
14
|
+
# Get the QR code pixel matrix
|
|
15
|
+
matrix = if(systemType(message) == 'string', qrcodeMatrix(message, level), message)
|
|
16
|
+
qrcodeSize = arrayLength(matrix)
|
|
17
|
+
|
|
18
|
+
# Draw the white background
|
|
19
|
+
drawStyle('none', 0, 'white')
|
|
20
|
+
drawRect(x, y, size, size)
|
|
21
|
+
|
|
22
|
+
# Draw the black pixels
|
|
23
|
+
# Note: The standard recommendation is a 4-module quiet zone on all sides.
|
|
24
|
+
drawStyle('none', 0, 'black')
|
|
25
|
+
ixRow = 0
|
|
26
|
+
pixelSize = size / (qrcodeSize + 8)
|
|
27
|
+
borderSize = pixelSize * 4
|
|
28
|
+
precision = 6
|
|
29
|
+
pixelSizeRound = mathRound(pixelSize, precision)
|
|
30
|
+
while ixRow < qrcodeSize:
|
|
31
|
+
ixCol = 0
|
|
32
|
+
while ixCol < qrcodeSize:
|
|
33
|
+
pixelValue = arrayGet(arrayGet(matrix, ixRow), ixCol)
|
|
34
|
+
if pixelValue:
|
|
35
|
+
drawPathRect( \
|
|
36
|
+
mathRound(borderSize + x + ixCol * pixelSize, precision), \
|
|
37
|
+
mathRound(borderSize + y + ixRow * pixelSize, precision), \
|
|
38
|
+
pixelSizeRound, \
|
|
39
|
+
pixelSizeRound \
|
|
40
|
+
)
|
|
41
|
+
endif
|
|
42
|
+
ixCol = ixCol + 1
|
|
43
|
+
endwhile
|
|
44
|
+
ixRow = ixRow + 1
|
|
45
|
+
endwhile
|
|
46
|
+
endfunction
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# $function: qrcodeMatrix
|
|
50
|
+
# $group: qrcode.bare
|
|
51
|
+
# $doc: Generate a QR code pixel matrix
|
|
52
|
+
# $arg message: The QR code message
|
|
53
|
+
# $arg level: Optional (default is 'low'). The error correction level: 'low', 'medium', 'quartile', or 'high'.
|
|
54
|
+
# $return: The QR code pixel matrix
|
|
55
|
+
function qrcodeMatrix(message, level):
|
|
56
|
+
level = if(level != null, level, 'low')
|
|
57
|
+
|
|
58
|
+
# Find the QR code model
|
|
59
|
+
for qrcode in qrcodeVersions:
|
|
60
|
+
size = objectGet(qrcode, 'size')
|
|
61
|
+
alignmentPatterns = objectGet(qrcode, 'alignmentPatterns')
|
|
62
|
+
ecLevel = objectGet(qrcode, level)
|
|
63
|
+
totalCodewords = objectGet(ecLevel, 'totalCodewords')
|
|
64
|
+
|
|
65
|
+
# Create the pixel matrix
|
|
66
|
+
matrix = []
|
|
67
|
+
ixRow = 0
|
|
68
|
+
while ixRow < size:
|
|
69
|
+
arrayPush(matrix, arrayNewSize(size, null))
|
|
70
|
+
ixRow = ixRow + 1
|
|
71
|
+
endwhile
|
|
72
|
+
|
|
73
|
+
# Position squares
|
|
74
|
+
qrcodeMatrixPositionSquare(matrix, 3, 3)
|
|
75
|
+
qrcodeMatrixPositionSquare(matrix, 3, size - 4)
|
|
76
|
+
qrcodeMatrixPositionSquare(matrix, size - 4, 3)
|
|
77
|
+
|
|
78
|
+
# Alignment patterns
|
|
79
|
+
if alignmentPatterns:
|
|
80
|
+
ixLast = arrayLength(alignmentPatterns) - 1
|
|
81
|
+
for alignX, ixX in alignmentPatterns:
|
|
82
|
+
for alignY, ixY in alignmentPatterns:
|
|
83
|
+
if !(ixX == 0 && ixY == 0) && !(ixX == 0 && ixY == ixLast) && !(ixX == ixLast && ixY == 0):
|
|
84
|
+
qrcodeMatrixAlignmentPattern(matrix, alignX, alignY)
|
|
85
|
+
endif
|
|
86
|
+
endfor
|
|
87
|
+
endfor
|
|
88
|
+
endif
|
|
89
|
+
|
|
90
|
+
# Timing strip
|
|
91
|
+
qrcodeMatrixTimingStrips(matrix)
|
|
92
|
+
|
|
93
|
+
# Format strips - use mask 0 as placeholder bits for qrcodeDataPixels
|
|
94
|
+
qrcodeMatrixFormatStrips(matrix, level, 0)
|
|
95
|
+
|
|
96
|
+
# Version blocks
|
|
97
|
+
qrcodeMatrixVersionBlocks(matrix, qrcode)
|
|
98
|
+
|
|
99
|
+
# Get the message bits (mode, version, message, terminator) - non-null means we've found it
|
|
100
|
+
messageBits = qrcodeMessageBits(qrcode, level, message)
|
|
101
|
+
if messageBits != null:
|
|
102
|
+
# Compute the data pixels
|
|
103
|
+
dataPixels = qrcodeDataPixels(matrix)
|
|
104
|
+
|
|
105
|
+
# Compute the data bits
|
|
106
|
+
dataBits = qrcodeDataBits(qrcode, level, message)
|
|
107
|
+
|
|
108
|
+
# Add the data remainder bits
|
|
109
|
+
remainderLength = arrayLength(dataPixels) - totalCodewords * 8
|
|
110
|
+
if remainderLength > 0:
|
|
111
|
+
arrayExtend(dataBits, arrayNewSize(remainderLength, 0))
|
|
112
|
+
endif
|
|
113
|
+
|
|
114
|
+
# Compute the penalty for each mask
|
|
115
|
+
bestMatrix = null
|
|
116
|
+
bestPenalty = null
|
|
117
|
+
mask = 0
|
|
118
|
+
while mask < 8:
|
|
119
|
+
# Set full data bits with mask
|
|
120
|
+
maskMatrix = jsonParse(jsonStringify(matrix))
|
|
121
|
+
qrcodeMatrixPixels(maskMatrix, dataPixels, dataBits, mask)
|
|
122
|
+
|
|
123
|
+
# Set the format strips bits
|
|
124
|
+
qrcodeMatrixFormatStrips(maskMatrix, level, mask)
|
|
125
|
+
|
|
126
|
+
# Compute the mask's penalty
|
|
127
|
+
maskPenalty = qrcodeMatrixPenalty(maskMatrix)
|
|
128
|
+
if bestPenalty == null || maskPenalty < bestPenalty:
|
|
129
|
+
bestMatrix = maskMatrix
|
|
130
|
+
bestPenalty = maskPenalty
|
|
131
|
+
endif
|
|
132
|
+
mask = mask + 1
|
|
133
|
+
endwhile
|
|
134
|
+
|
|
135
|
+
return bestMatrix
|
|
136
|
+
endif
|
|
137
|
+
endfor
|
|
138
|
+
|
|
139
|
+
# Message too long
|
|
140
|
+
systemLogDebug('qrcode.bare: qrcodeMatrix - Message too long (' + stringLength(message) + ')')
|
|
141
|
+
return null
|
|
142
|
+
endfunction
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# Set a position square's pixels
|
|
146
|
+
function qrcodeMatrixPositionSquare(matrix, x, y):
|
|
147
|
+
qrcodeMatrixSquare(matrix, x - 4, y - 4, 9, 0)
|
|
148
|
+
qrcodeMatrixSquare(matrix, x - 3, y - 3, 7, 1)
|
|
149
|
+
qrcodeMatrixSquare(matrix, x - 2, y - 2, 5, 0)
|
|
150
|
+
qrcodeMatrixSquare(matrix, x - 1, y - 1, 3, 1)
|
|
151
|
+
endfunction
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# Set an alignment pattern's pixels
|
|
155
|
+
function qrcodeMatrixAlignmentPattern(matrix, x, y):
|
|
156
|
+
qrcodeMatrixSquare(matrix, x - 2, y - 2, 5, 1)
|
|
157
|
+
qrcodeMatrixSquare(matrix, x - 1, y - 1, 3, 0)
|
|
158
|
+
qrcodeMatrixSquare(matrix, x, y, 1, 1)
|
|
159
|
+
endfunction
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# Set a solid square's pixels
|
|
163
|
+
function qrcodeMatrixSquare(matrix, x, y, size, colorValue):
|
|
164
|
+
xEnd = mathMin(arrayLength(matrix), x + size)
|
|
165
|
+
yEnd = mathMin(arrayLength(matrix), y + size)
|
|
166
|
+
iy = mathMax(0, y)
|
|
167
|
+
while iy < yEnd:
|
|
168
|
+
ix = mathMax(0, x)
|
|
169
|
+
while ix < xEnd:
|
|
170
|
+
arraySet(arrayGet(matrix, iy), ix, colorValue)
|
|
171
|
+
ix = ix + 1
|
|
172
|
+
endwhile
|
|
173
|
+
iy = iy + 1
|
|
174
|
+
endwhile
|
|
175
|
+
endfunction
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# Set the timing strip pixels
|
|
179
|
+
function qrcodeMatrixTimingStrips(matrix):
|
|
180
|
+
size = arrayLength(matrix)
|
|
181
|
+
ix = 8
|
|
182
|
+
ixEnd = size - 8
|
|
183
|
+
while ix < ixEnd:
|
|
184
|
+
arraySet(arrayGet(matrix, 6), ix, if(ix % 2, 0, 1))
|
|
185
|
+
arraySet(arrayGet(matrix, ix), 6, if(ix % 2, 0, 1))
|
|
186
|
+
ix = ix + 1
|
|
187
|
+
endwhile
|
|
188
|
+
endfunction
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# Set the format strips pixels
|
|
192
|
+
function qrcodeMatrixFormatStrips(matrix, level, mask):
|
|
193
|
+
# The "dark pixel"
|
|
194
|
+
size = arrayLength(matrix)
|
|
195
|
+
arraySet(arrayGet(matrix, size - 8), 8, 1)
|
|
196
|
+
|
|
197
|
+
# Set the format strip bits
|
|
198
|
+
formatStripBits = objectGet(objectGet(qrcodeMatrixFormatStripsBitsLookup, level), stringNew(mask))
|
|
199
|
+
pixelsTopLeft = [ \
|
|
200
|
+
[0, 8], [1, 8], [2, 8], [3, 8], [4, 8], [5, 8], [7, 8], [8, 8], \
|
|
201
|
+
[8, 7], [8, 5], [8, 4], [8, 3], [8, 2], [8, 1], [8, 0] \
|
|
202
|
+
]
|
|
203
|
+
pixelsNonTopLeft = [ \
|
|
204
|
+
[8, size - 1], [8, size - 2], [8, size - 3], [8, size - 4], [8, size - 5], [8, size - 6], [8, size - 7], [size - 8, 8], \
|
|
205
|
+
[size - 7, 8], [size - 6, 8], [size - 5, 8], [size - 4, 8], [size - 3, 8], [size - 2, 8], [size - 1, 8] \
|
|
206
|
+
]
|
|
207
|
+
qrcodeMatrixPixels(matrix, pixelsTopLeft, formatStripBits)
|
|
208
|
+
qrcodeMatrixPixels(matrix, pixelsNonTopLeft, formatStripBits)
|
|
209
|
+
endfunction
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# Format strip bits lookup table
|
|
213
|
+
qrcodeMatrixFormatStripsBitsLookup = { \
|
|
214
|
+
'low': { \
|
|
215
|
+
'0': [1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0], \
|
|
216
|
+
'1': [1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1], \
|
|
217
|
+
'2': [1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0], \
|
|
218
|
+
'3': [1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1], \
|
|
219
|
+
'4': [1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1], \
|
|
220
|
+
'5': [1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0], \
|
|
221
|
+
'6': [1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1], \
|
|
222
|
+
'7': [1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0] \
|
|
223
|
+
}, \
|
|
224
|
+
'medium': { \
|
|
225
|
+
'0': [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0], \
|
|
226
|
+
'1': [1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1], \
|
|
227
|
+
'2': [1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0], \
|
|
228
|
+
'3': [1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1], \
|
|
229
|
+
'4': [1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1], \
|
|
230
|
+
'5': [1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0], \
|
|
231
|
+
'6': [1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1], \
|
|
232
|
+
'7': [1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0] \
|
|
233
|
+
}, \
|
|
234
|
+
'quartile': { \
|
|
235
|
+
'0': [0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1], \
|
|
236
|
+
'1': [0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0], \
|
|
237
|
+
'2': [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1], \
|
|
238
|
+
'3': [0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0], \
|
|
239
|
+
'4': [0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0], \
|
|
240
|
+
'5': [0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1], \
|
|
241
|
+
'6': [0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0], \
|
|
242
|
+
'7': [0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1] \
|
|
243
|
+
}, \
|
|
244
|
+
'high': { \
|
|
245
|
+
'0': [0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1], \
|
|
246
|
+
'1': [0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0], \
|
|
247
|
+
'2': [0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1], \
|
|
248
|
+
'3': [0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0], \
|
|
249
|
+
'4': [0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0], \
|
|
250
|
+
'5': [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1], \
|
|
251
|
+
'6': [0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0], \
|
|
252
|
+
'7': [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1] \
|
|
253
|
+
} \
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# Set the version blocks pixels
|
|
258
|
+
function qrcodeMatrixVersionBlocks(matrix, qrcode):
|
|
259
|
+
version = objectGet(qrcode, 'version')
|
|
260
|
+
versionBits = objectGet(qrcodeMatrixVersionBitsLookup, stringNew(version))
|
|
261
|
+
if versionBits:
|
|
262
|
+
size = arrayLength(matrix)
|
|
263
|
+
bottomLeftPixels = [ \
|
|
264
|
+
[5, size - 9], [5, size - 10], [5, size - 11], [4, size - 9], [4, size - 10], [4, size - 11], \
|
|
265
|
+
[3, size - 9], [3, size - 10], [3, size - 11], [2, size - 9], [2, size - 10], [2, size - 11], \
|
|
266
|
+
[1, size - 9], [1, size - 10], [1, size - 11], [0, size - 9], [0, size - 10], [0, size - 11] \
|
|
267
|
+
]
|
|
268
|
+
topRightPixels = [ \
|
|
269
|
+
[size - 9, 5], [size - 10, 5], [size - 11, 5], [size - 9, 4], [size - 10, 4], [size - 11, 4], \
|
|
270
|
+
[size - 9, 3], [size - 10, 3], [size - 11, 3], [size - 9, 2], [size - 10, 2], [size - 11, 2], \
|
|
271
|
+
[size - 9, 1], [size - 10, 1], [size - 11, 1], [size - 9, 0], [size - 10, 0], [size - 11, 0] \
|
|
272
|
+
]
|
|
273
|
+
qrcodeMatrixPixels(matrix, bottomLeftPixels, versionBits)
|
|
274
|
+
qrcodeMatrixPixels(matrix, topRightPixels, versionBits)
|
|
275
|
+
endif
|
|
276
|
+
endfunction
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
# Version bits lookup table
|
|
280
|
+
qrcodeMatrixVersionBitsLookup = { \
|
|
281
|
+
'7': [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0], \
|
|
282
|
+
'10': [0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1], \
|
|
283
|
+
'15': [0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0] \
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# Set a pixel array's pixels
|
|
288
|
+
function qrcodeMatrixPixels(matrix, pixels, bits, mask):
|
|
289
|
+
for bitValue, ixBit in bits:
|
|
290
|
+
pixel = arrayGet(pixels, ixBit)
|
|
291
|
+
ix = arrayGet(pixel, 0)
|
|
292
|
+
iy = arrayGet(pixel, 1)
|
|
293
|
+
|
|
294
|
+
# Apply an XOR mask, if requested
|
|
295
|
+
if mask != null:
|
|
296
|
+
if mask == 0:
|
|
297
|
+
maskBit = if((iy + ix) % 2, 0, 1)
|
|
298
|
+
elif mask == 1:
|
|
299
|
+
maskBit = if(iy % 2, 0, 1)
|
|
300
|
+
elif mask == 2:
|
|
301
|
+
maskBit = if(ix % 3, 0, 1)
|
|
302
|
+
elif mask == 3:
|
|
303
|
+
maskBit = if((iy + ix) % 3, 0, 1)
|
|
304
|
+
elif mask == 4:
|
|
305
|
+
maskBit = if((mathFloor(iy / 2) + mathFloor(ix / 3)) % 2, 0, 1)
|
|
306
|
+
elif mask == 5:
|
|
307
|
+
maskBit = if((iy * ix) % 2 + (iy * ix) % 3, 0, 1)
|
|
308
|
+
elif mask == 6:
|
|
309
|
+
maskBit = if((((iy * ix) % 2) + ((iy * ix) % 3)) % 2, 0, 1)
|
|
310
|
+
else: # mask == 7
|
|
311
|
+
maskBit = if((((iy + ix) % 2) + ((iy * ix) % 3)) % 2, 0, 1)
|
|
312
|
+
endif
|
|
313
|
+
bitValue = bitValue ^ maskBit
|
|
314
|
+
endif
|
|
315
|
+
|
|
316
|
+
# Set the pixel
|
|
317
|
+
arraySet(arrayGet(matrix, iy), ix, bitValue)
|
|
318
|
+
endfor
|
|
319
|
+
endfunction
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
# Get the list of available data pixel positions in the QR code
|
|
323
|
+
function qrcodeDataPixels(matrix):
|
|
324
|
+
dataPixels = []
|
|
325
|
+
size = arrayLength(matrix)
|
|
326
|
+
|
|
327
|
+
# Iterate pixel pairs - up and down, right to left
|
|
328
|
+
x = size - 1
|
|
329
|
+
y = size - 1
|
|
330
|
+
up = true
|
|
331
|
+
while true:
|
|
332
|
+
# Add the pixel pair (right then left) if its unused
|
|
333
|
+
if arrayGet(arrayGet(matrix, y), x) == null:
|
|
334
|
+
arrayPush(dataPixels, [x, y])
|
|
335
|
+
endif
|
|
336
|
+
if x > 0 && arrayGet(arrayGet(matrix, y), x - 1) == null:
|
|
337
|
+
arrayPush(dataPixels, [x - 1, y])
|
|
338
|
+
endif
|
|
339
|
+
|
|
340
|
+
# Move the pixel pair
|
|
341
|
+
if (up && y == 0) || (!up && y == size - 1):
|
|
342
|
+
up = !up
|
|
343
|
+
x = x - 2
|
|
344
|
+
if x == 6:
|
|
345
|
+
x = 5
|
|
346
|
+
elif x < 0:
|
|
347
|
+
break
|
|
348
|
+
endif
|
|
349
|
+
else:
|
|
350
|
+
y = y + if(up, -1, 1)
|
|
351
|
+
endif
|
|
352
|
+
endwhile
|
|
353
|
+
|
|
354
|
+
return dataPixels
|
|
355
|
+
endfunction
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
# Get the full data bits (includes mode, size, message, terminator, padding, ec)
|
|
359
|
+
function qrcodeDataBits(qrcode, level, message):
|
|
360
|
+
ecLevel = objectGet(qrcode, level)
|
|
361
|
+
totalMessageCodewords = objectGet(ecLevel, 'totalMessageCodewords')
|
|
362
|
+
errorCodewordsPerBlock = objectGet(ecLevel, 'errorCodewordsPerBlock')
|
|
363
|
+
blockGroups = objectGet(ecLevel, 'blockGroups')
|
|
364
|
+
|
|
365
|
+
# Compute the message bits
|
|
366
|
+
messageBits = qrcodeMessageBits(qrcode, level, message)
|
|
367
|
+
|
|
368
|
+
# Padding bits
|
|
369
|
+
paddingBits = []
|
|
370
|
+
remainingBitCount = totalMessageCodewords * 8 - arrayLength(messageBits)
|
|
371
|
+
byteCount = mathFloor(remainingBitCount / 8)
|
|
372
|
+
ix = 0
|
|
373
|
+
while ix < byteCount:
|
|
374
|
+
padValue = if(ix % 2, 17, 236)
|
|
375
|
+
padBits = qrcodeBytesToBits([padValue])
|
|
376
|
+
arrayExtend(paddingBits, padBits)
|
|
377
|
+
ix = ix + 1
|
|
378
|
+
endwhile
|
|
379
|
+
|
|
380
|
+
# Compute the full data bytes
|
|
381
|
+
fullMessageBits = []
|
|
382
|
+
arrayExtend(fullMessageBits, messageBits)
|
|
383
|
+
arrayExtend(fullMessageBits, paddingBits)
|
|
384
|
+
fullMessageBytes = qrcodeBitsToBytes(fullMessageBits)
|
|
385
|
+
|
|
386
|
+
# Data group/block interleaving and error correction codeword generation
|
|
387
|
+
allDataBlocks = []
|
|
388
|
+
allECBlocks = []
|
|
389
|
+
maxDataBlocks = 0
|
|
390
|
+
byteIndex = 0
|
|
391
|
+
for blockGroup in blockGroups:
|
|
392
|
+
blockCount = objectGet(blockGroup, 'blockCount')
|
|
393
|
+
dataCodewordsPerBlock = objectGet(blockGroup, 'dataCodewordsPerBlock')
|
|
394
|
+
ixBlock = 0
|
|
395
|
+
while ixBlock < blockCount:
|
|
396
|
+
# Extract data bytes for this block
|
|
397
|
+
blockDataBytes = arraySlice(fullMessageBytes, byteIndex, byteIndex + dataCodewordsPerBlock)
|
|
398
|
+
maxDataBlocks = mathMax(maxDataBlocks, arrayLength(blockDataBytes))
|
|
399
|
+
arrayPush(allDataBlocks, blockDataBytes)
|
|
400
|
+
|
|
401
|
+
# Generate error correction codewords
|
|
402
|
+
ecCodewords = qrcodeRSEncode(blockDataBytes, errorCodewordsPerBlock)
|
|
403
|
+
arrayPush(allECBlocks, ecCodewords)
|
|
404
|
+
|
|
405
|
+
byteIndex = byteIndex + dataCodewordsPerBlock
|
|
406
|
+
ixBlock = ixBlock + 1
|
|
407
|
+
endwhile
|
|
408
|
+
endfor
|
|
409
|
+
|
|
410
|
+
# Interleave data blocks
|
|
411
|
+
interleaved = []
|
|
412
|
+
ixByte = 0
|
|
413
|
+
while ixByte < maxDataBlocks:
|
|
414
|
+
for dataBlock in allDataBlocks:
|
|
415
|
+
if ixByte < arrayLength(dataBlock):
|
|
416
|
+
arrayPush(interleaved, arrayGet(dataBlock, ixByte))
|
|
417
|
+
endif
|
|
418
|
+
endfor
|
|
419
|
+
ixByte = ixByte + 1
|
|
420
|
+
endwhile
|
|
421
|
+
|
|
422
|
+
# Interleave error correction blocks
|
|
423
|
+
ixByte = 0
|
|
424
|
+
while ixByte < errorCodewordsPerBlock:
|
|
425
|
+
for ecBlock in allECBlocks:
|
|
426
|
+
if ixByte < arrayLength(ecBlock):
|
|
427
|
+
arrayPush(interleaved, arrayGet(ecBlock, ixByte))
|
|
428
|
+
endif
|
|
429
|
+
endfor
|
|
430
|
+
ixByte = ixByte + 1
|
|
431
|
+
endwhile
|
|
432
|
+
|
|
433
|
+
# Convert back to bits
|
|
434
|
+
return qrcodeBytesToBits(interleaved)
|
|
435
|
+
endfunction
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
# Get the terminated message bits (mode, size, message, terminator)
|
|
439
|
+
function qrcodeMessageBits(qrcode, level, message):
|
|
440
|
+
version = objectGet(qrcode, 'version')
|
|
441
|
+
ecLevel = objectGet(qrcode, level)
|
|
442
|
+
totalMessageCodewords = objectGet(ecLevel, 'totalMessageCodewords')
|
|
443
|
+
|
|
444
|
+
# Determine the message mode
|
|
445
|
+
# Note: Kanji and ECI modes not supported
|
|
446
|
+
if regexMatch(qrcodeModeNumericRegex, message):
|
|
447
|
+
modeBits = [0, 0, 0, 1]
|
|
448
|
+
messageBits = qrcodeModeNumericBits(message)
|
|
449
|
+
messageSize = stringLength(message)
|
|
450
|
+
sizeBitsLength = if(version < 10, 10, if(version < 27, 12, 14))
|
|
451
|
+
elif regexMatch(qrcodeModeAlphanumericRegex, message):
|
|
452
|
+
modeBits = [0, 0, 1, 0]
|
|
453
|
+
messageBits = qrcodeModeAlphanumericBits(message)
|
|
454
|
+
messageSize = stringLength(message)
|
|
455
|
+
sizeBitsLength = if(version < 10, 9, if(version < 27, 11, 13))
|
|
456
|
+
else:
|
|
457
|
+
modeBits = [0, 1, 0, 0]
|
|
458
|
+
messageBytes = stringEncode(message)
|
|
459
|
+
messageBits = qrcodeBytesToBits(messageBytes)
|
|
460
|
+
messageSize = arrayLength(messageBytes)
|
|
461
|
+
sizeBitsLength = if(version < 10, 8, 16)
|
|
462
|
+
endif
|
|
463
|
+
|
|
464
|
+
# Add terminator (min(4, remaining) zeros)
|
|
465
|
+
usedBitCount = arrayLength(modeBits) + sizeBitsLength + arrayLength(messageBits)
|
|
466
|
+
remainingBitCount = totalMessageCodewords * 8 - usedBitCount
|
|
467
|
+
if remainingBitCount < 0:
|
|
468
|
+
return null
|
|
469
|
+
endif
|
|
470
|
+
termLen = mathMin(4, remainingBitCount)
|
|
471
|
+
terminatorBits = arrayNewSize(termLen, 0)
|
|
472
|
+
|
|
473
|
+
# Add bit padding (zeros to align to multiple of 8)
|
|
474
|
+
usedBitCount = usedBitCount + termLen
|
|
475
|
+
alignZeros = (8 - (usedBitCount % 8)) % 8
|
|
476
|
+
alignBits = arrayNewSize(alignZeros, 0)
|
|
477
|
+
|
|
478
|
+
# Compute the total message bits
|
|
479
|
+
totalMessageBits = []
|
|
480
|
+
arrayExtend(totalMessageBits, modeBits)
|
|
481
|
+
arrayExtend(totalMessageBits, qrcodeBytesToBits([messageSize], sizeBitsLength))
|
|
482
|
+
arrayExtend(totalMessageBits, messageBits)
|
|
483
|
+
arrayExtend(totalMessageBits, terminatorBits)
|
|
484
|
+
arrayExtend(totalMessageBits, alignBits)
|
|
485
|
+
return totalMessageBits
|
|
486
|
+
endfunction
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
# Mode determination regex
|
|
490
|
+
qrcodeModeNumericRegex = regexNew('^[0-9]+$')
|
|
491
|
+
qrcodeModeAlphanumericRegex = regexNew('^[0-9A-Z $%*+-./:]+$')
|
|
492
|
+
qrcodeModeAlphanumericStr = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
# Numeric mode message encoding
|
|
496
|
+
function qrcodeModeNumericBits(message):
|
|
497
|
+
messageBits = []
|
|
498
|
+
messageLength = stringLength(message)
|
|
499
|
+
ixChar = 0
|
|
500
|
+
while ixChar < messageLength:
|
|
501
|
+
chunk = stringSlice(message, ixChar, mathMin(messageLength, ixChar + 3))
|
|
502
|
+
chunkLength = stringLength(chunk)
|
|
503
|
+
chunkValue = numberParseInt(chunk)
|
|
504
|
+
if chunkLength == 1:
|
|
505
|
+
arrayExtend(messageBits, qrcodeBytesToBits([chunkValue], 4))
|
|
506
|
+
elif chunkLength == 2:
|
|
507
|
+
arrayExtend(messageBits, qrcodeBytesToBits([chunkValue], 7))
|
|
508
|
+
else: # chunkLength == 3
|
|
509
|
+
arrayExtend(messageBits, qrcodeBytesToBits([chunkValue], 10))
|
|
510
|
+
endif
|
|
511
|
+
ixChar = ixChar + 3
|
|
512
|
+
endwhile
|
|
513
|
+
return messageBits
|
|
514
|
+
endfunction
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
# Numeric mode message encoding
|
|
518
|
+
function qrcodeModeAlphanumericBits(message):
|
|
519
|
+
messageBits = []
|
|
520
|
+
messageLength = stringLength(message)
|
|
521
|
+
messageLengthMinusOne = messageLength - 1
|
|
522
|
+
ixChar = 0
|
|
523
|
+
while ixChar < messageLength:
|
|
524
|
+
value1 = stringIndexOf(qrcodeModeAlphanumericStr, stringSlice(message, ixChar, ixChar + 1))
|
|
525
|
+
value2 = if(ixChar < messageLengthMinusOne, stringIndexOf(qrcodeModeAlphanumericStr, stringSlice(message, ixChar + 1, ixChar + 2)))
|
|
526
|
+
if value2 == null:
|
|
527
|
+
arrayExtend(messageBits, qrcodeBytesToBits([value1], 6))
|
|
528
|
+
else:
|
|
529
|
+
arrayExtend(messageBits, qrcodeBytesToBits([45 * value1 + value2], 11))
|
|
530
|
+
endif
|
|
531
|
+
ixChar = ixChar + 2
|
|
532
|
+
endwhile
|
|
533
|
+
return messageBits
|
|
534
|
+
endfunction
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
# Convert a byte value array to bit array
|
|
538
|
+
function qrcodeBytesToBits(bytes, bitsLength):
|
|
539
|
+
bitsLength = if(bitsLength, bitsLength, 8)
|
|
540
|
+
bits = []
|
|
541
|
+
for byteValue in bytes:
|
|
542
|
+
reversedBits = []
|
|
543
|
+
ixBit = 0
|
|
544
|
+
while ixBit < bitsLength:
|
|
545
|
+
arrayPush(reversedBits, byteValue & 1)
|
|
546
|
+
byteValue = byteValue >> 1
|
|
547
|
+
ixBit = ixBit + 1
|
|
548
|
+
endwhile
|
|
549
|
+
arrayExtend(bits, arrayReverse(reversedBits))
|
|
550
|
+
endfor
|
|
551
|
+
return bits
|
|
552
|
+
endfunction
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
# Convert bit array to byte array
|
|
556
|
+
function qrcodeBitsToBytes(bits):
|
|
557
|
+
bytes = []
|
|
558
|
+
ixBit = 0
|
|
559
|
+
bitsLength = arrayLength(bits)
|
|
560
|
+
while ixBit < bitsLength:
|
|
561
|
+
byteBits = arraySlice(bits, ixBit, mathMin(ixBit + 8, bitsLength))
|
|
562
|
+
arrayExtend(byteBits, arrayNewSize(8 - arrayLength(byteBits), 0))
|
|
563
|
+
byteValue = numberParseInt(arrayJoin(byteBits, ''), 2)
|
|
564
|
+
arrayPush(bytes, byteValue)
|
|
565
|
+
ixBit = ixBit + 8
|
|
566
|
+
endwhile
|
|
567
|
+
return bytes
|
|
568
|
+
endfunction
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
# Compute the masked matrix penalty
|
|
572
|
+
function qrcodeMatrixPenalty(matrix):
|
|
573
|
+
size = arrayLength(matrix)
|
|
574
|
+
penalty = 0
|
|
575
|
+
|
|
576
|
+
# Add the string rows
|
|
577
|
+
stringRowsAndColumns = []
|
|
578
|
+
for row in matrix:
|
|
579
|
+
arrayPush(stringRowsAndColumns, arrayJoin(row, ''))
|
|
580
|
+
endfor
|
|
581
|
+
|
|
582
|
+
# Add the string columns - also compute the 2x2 count and "dark" count
|
|
583
|
+
twoByCount = 0
|
|
584
|
+
twoBySize = size - 1
|
|
585
|
+
darkCount = 0
|
|
586
|
+
ix = 0
|
|
587
|
+
while ix < size:
|
|
588
|
+
column = []
|
|
589
|
+
iy = 0
|
|
590
|
+
while iy < size:
|
|
591
|
+
row = arrayGet(matrix, iy)
|
|
592
|
+
row2 = if(ix < twoBySize && iy < twoBySize, arrayGet(matrix, iy + 1))
|
|
593
|
+
bit = arrayGet(row, ix)
|
|
594
|
+
|
|
595
|
+
# Add the column bit
|
|
596
|
+
arrayPush(column, bit)
|
|
597
|
+
|
|
598
|
+
# Count the 2x2 blocks
|
|
599
|
+
if row2 && bit == arrayGet(row, ix + 1) && bit == arrayGet(row2, ix) && bit == arrayGet(row2, ix + 1):
|
|
600
|
+
twoByCount = twoByCount + 1
|
|
601
|
+
endif
|
|
602
|
+
|
|
603
|
+
# Count "dark" pixels
|
|
604
|
+
if bit:
|
|
605
|
+
darkCount = darkCount + 1
|
|
606
|
+
endif
|
|
607
|
+
|
|
608
|
+
iy = iy + 1
|
|
609
|
+
endwhile
|
|
610
|
+
|
|
611
|
+
# Add the column string
|
|
612
|
+
arrayPush(stringRowsAndColumns, arrayJoin(column, ''))
|
|
613
|
+
ix = ix + 1
|
|
614
|
+
endwhile
|
|
615
|
+
|
|
616
|
+
# Penalty 1: Five or more same-colored modules in a row/column
|
|
617
|
+
for stringRow in stringRowsAndColumns:
|
|
618
|
+
for runMatch in regexMatchAll(qrcodeMatrixPenaltyRunRegex, stringRow):
|
|
619
|
+
penalty = penalty + 3 + (stringLength(objectGet(objectGet(runMatch, 'groups'), '0')) - 5)
|
|
620
|
+
endfor
|
|
621
|
+
endfor
|
|
622
|
+
|
|
623
|
+
# Penalty 2: 2x2 blocks of same color
|
|
624
|
+
penalty = penalty + 3 * twoByCount
|
|
625
|
+
|
|
626
|
+
# Penalty 3: Finder-like patterns (1011101 with 4 white modules on either side)
|
|
627
|
+
for stringRow in stringRowsAndColumns:
|
|
628
|
+
penalty = penalty + 40 * arrayLength(regexMatchAll(qrcodeMatrixPenaltyPatternRegex, stringRow))
|
|
629
|
+
endfor
|
|
630
|
+
|
|
631
|
+
# Penalty 4: Proportion of dark modules
|
|
632
|
+
darkPercent = (darkCount * 100) / (size * size)
|
|
633
|
+
penalty = penalty + mathFloor(mathAbs(darkPercent - 50) / 5) * 10
|
|
634
|
+
|
|
635
|
+
return penalty
|
|
636
|
+
endfunction
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
# QR matrix scorinn pattern regular expressions
|
|
640
|
+
qrcodeMatrixPenaltyRunRegex = regexNew('(?:0{5,}|1{5,})')
|
|
641
|
+
qrcodeMatrixPenaltyPatternRegex = regexNew('(?:10111010000|00001011101)')
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
# Generate Reed-Solomon error correction codewords
|
|
645
|
+
function qrcodeRSEncode(bytesIn, numCodewords):
|
|
646
|
+
# Copy input message and append zero bytes for EC codewords
|
|
647
|
+
bytesOut = arrayCopy(bytesIn)
|
|
648
|
+
arrayExtend(bytesOut, arrayNewSize(numCodewords, 0))
|
|
649
|
+
|
|
650
|
+
# Get the generator polynomial coefficients (stored as alpha exponents)
|
|
651
|
+
genExponents = objectGet(qrcodeRSGeneratorPolynomials, stringNew(numCodewords))
|
|
652
|
+
lenGen = arrayLength(genExponents)
|
|
653
|
+
lenMsg = arrayLength(bytesIn)
|
|
654
|
+
|
|
655
|
+
# Perform Reed-Solomon division (long division in GF(256))
|
|
656
|
+
i = 0
|
|
657
|
+
while i < lenMsg:
|
|
658
|
+
leadCoef = arrayGet(bytesOut, i)
|
|
659
|
+
if leadCoef != 0:
|
|
660
|
+
# Precompute log of leading coefficient for faster GF multiplication
|
|
661
|
+
logLead = arrayGet(qrcodeGaloisFieldLogTable, leadCoef)
|
|
662
|
+
|
|
663
|
+
# Apply generator polynomial (skip j=0 term — it's always 1)
|
|
664
|
+
j = 1
|
|
665
|
+
while j < lenGen:
|
|
666
|
+
oldVal = arrayGet(bytesOut, i + j)
|
|
667
|
+
|
|
668
|
+
# Convert generator coefficient from alpha exponent to field element
|
|
669
|
+
genCoefExp = arrayGet(genExponents, j)
|
|
670
|
+
genCoef = if(genCoefExp == 0, 1, arrayGet(qrcodeGaloisFieldExpTable, genCoefExp))
|
|
671
|
+
|
|
672
|
+
# GF multiply: leadCoef * genCoef → using log tables
|
|
673
|
+
productExp = (logLead + arrayGet(qrcodeGaloisFieldLogTable, genCoef)) % 255
|
|
674
|
+
product = arrayGet(qrcodeGaloisFieldExpTable, productExp)
|
|
675
|
+
|
|
676
|
+
# XOR with existing value (subtraction in GF(2^8))
|
|
677
|
+
arraySet(bytesOut, i + j, oldVal ^ product)
|
|
678
|
+
j = j + 1
|
|
679
|
+
endwhile
|
|
680
|
+
endif
|
|
681
|
+
i = i + 1
|
|
682
|
+
endwhile
|
|
683
|
+
|
|
684
|
+
# Return only the error correction codewords (the remainder)
|
|
685
|
+
return arraySlice(bytesOut, lenMsg)
|
|
686
|
+
endfunction
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
# Reed-Solomon error correction codeword count (string) to generator polynomial exponents
|
|
690
|
+
qrcodeRSGeneratorPolynomials = { \
|
|
691
|
+
'10': [1, 251, 67, 46, 61, 118, 70, 64, 94, 32, 45], \
|
|
692
|
+
'15': [1, 8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105], \
|
|
693
|
+
'16': [1, 120, 104, 107, 109, 102, 161, 76, 3, 91, 191, 147, 169, 182, 194, 225, 120], \
|
|
694
|
+
'18': [1, 215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, 5, 98, 96, 153], \
|
|
695
|
+
'20': [1, 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, 164, 212, 212, 188, 190], \
|
|
696
|
+
'22': [1, 210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, 98, 80, 219, 134, 160, 105, 165, 231], \
|
|
697
|
+
'24': [1, 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 228, 218, 111, 0, 117, 232, 87, 96, 227, 21], \
|
|
698
|
+
'26': [1, 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, 161, 21, 245, 142, 13, 102, 48, 227, 153, 145, 218, 70], \
|
|
699
|
+
'28': [1, 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, 232, 201, 21, 43, 245, 87, 42, 195, 212, 119, 242, 37, 9, 123], \
|
|
700
|
+
'30': [1, 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 125, 42, 173, 226, 193, 224, 130, 156, 37, 251, 216, 238, 40, 192, 180] \
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
# GF(256) EXP table
|
|
705
|
+
qrcodeGaloisFieldExpTable = [ \
|
|
706
|
+
1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, \
|
|
707
|
+
24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, \
|
|
708
|
+
160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, \
|
|
709
|
+
253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, \
|
|
710
|
+
26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, \
|
|
711
|
+
46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, \
|
|
712
|
+
228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, \
|
|
713
|
+
150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, \
|
|
714
|
+
83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, \
|
|
715
|
+
247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1 \
|
|
716
|
+
]
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
# GF(256) LOG table
|
|
720
|
+
qrcodeGaloisFieldLogTable = [ \
|
|
721
|
+
null, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, \
|
|
722
|
+
248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, \
|
|
723
|
+
39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, \
|
|
724
|
+
136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, \
|
|
725
|
+
107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, \
|
|
726
|
+
192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, \
|
|
727
|
+
217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, \
|
|
728
|
+
20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, \
|
|
729
|
+
111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, \
|
|
730
|
+
81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175 \
|
|
731
|
+
]
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
# QR Code version information table
|
|
735
|
+
# 0 - Version
|
|
736
|
+
# 1 - Size
|
|
737
|
+
# 2 - Alignment Pattern Center Positions
|
|
738
|
+
qrcodeVersionTable = [ \
|
|
739
|
+
[2, 25, 6, 18], \
|
|
740
|
+
[3, 29, 6, 22], \
|
|
741
|
+
[5, 37, 6, 30], \
|
|
742
|
+
[7, 45, 6, 22, 38], \
|
|
743
|
+
[10, 57, 6, 28, 50], \
|
|
744
|
+
[15, 77, 6, 26, 48, 70] \
|
|
745
|
+
]
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
# Error correction configuration table
|
|
749
|
+
# 0 - Version
|
|
750
|
+
# 1 - EC Level
|
|
751
|
+
# 2 - Total Number of Data Codewords for this Version and EC Level
|
|
752
|
+
# 3 - EC Codewords Per Block
|
|
753
|
+
# 4 - Number of Blocks in Group 1
|
|
754
|
+
# 5 - Number of Data Codewords in Each of Group 1's Blocks
|
|
755
|
+
# 6 - Number of Blocks in Group 2
|
|
756
|
+
# 7 - Number of Data Codewords in Each of Group 2's Blocks
|
|
757
|
+
qrcodeErrorCorrectionTable = [ \
|
|
758
|
+
[2, 'low', 34, 10, 1, 34, null, null], \
|
|
759
|
+
[2, 'medium', 28, 16, 1, 28, null, null], \
|
|
760
|
+
[2, 'quartile', 22, 22, 1, 22, null, null], \
|
|
761
|
+
[2, 'high', 16, 28, 1, 16, null, null], \
|
|
762
|
+
[3, 'low', 55, 15, 1, 55, null, null], \
|
|
763
|
+
[3, 'medium', 44, 26, 1, 44, null, null], \
|
|
764
|
+
[3, 'quartile', 34, 18, 2, 17, null, null], \
|
|
765
|
+
[3, 'high', 26, 22, 2, 13, null, null], \
|
|
766
|
+
[5, 'low', 108, 26, 1, 108, null, null], \
|
|
767
|
+
[5, 'medium', 86, 24, 2, 43, null, null], \
|
|
768
|
+
[5, 'quartile', 62, 18, 2, 15, 2, 16], \
|
|
769
|
+
[5, 'high', 46, 22, 2, 11, 2, 12], \
|
|
770
|
+
[7, 'low', 156, 20, 2, 78, null, null], \
|
|
771
|
+
[7, 'medium', 124, 18, 4, 31, null, null], \
|
|
772
|
+
[7, 'quartile', 88, 18, 2, 14, 4, 15], \
|
|
773
|
+
[7, 'high', 66, 26, 4, 13, 1, 14], \
|
|
774
|
+
[10, 'low', 274, 18, 2, 68, 2, 69], \
|
|
775
|
+
[10, 'medium', 216, 26, 4, 43, 1, 44], \
|
|
776
|
+
[10, 'quartile', 154, 24, 6, 19, 2, 20], \
|
|
777
|
+
[10, 'high', 122, 28, 6, 15, 2, 16], \
|
|
778
|
+
[15, 'low', 523, 22, 5, 87, 1, 88], \
|
|
779
|
+
[15, 'medium', 415, 24, 5, 41, 5, 42], \
|
|
780
|
+
[15, 'quartile', 295, 30, 5, 24, 7, 25], \
|
|
781
|
+
[15, 'high', 223, 24, 11, 12, 7, 13] \
|
|
782
|
+
]
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
# QR code models sorted ascending by version
|
|
786
|
+
function qrcodeVersionsCreate():
|
|
787
|
+
versions = []
|
|
788
|
+
for row in qrcodeVersionTable:
|
|
789
|
+
# Create the QR code model
|
|
790
|
+
version = arrayGet(row, 0)
|
|
791
|
+
qrcode = { \
|
|
792
|
+
'version': version, \
|
|
793
|
+
'size': arrayGet(row, 1), \
|
|
794
|
+
'alignmentPatterns': arraySlice(row, 2) \
|
|
795
|
+
}
|
|
796
|
+
arrayPush(versions, qrcode)
|
|
797
|
+
|
|
798
|
+
# Add the error correction configurations
|
|
799
|
+
for ecRow in qrcodeErrorCorrectionTable:
|
|
800
|
+
ecVersion = arrayGet(ecRow, 0)
|
|
801
|
+
if ecVersion == version:
|
|
802
|
+
level = arrayGet(ecRow, 1)
|
|
803
|
+
totalDataCodewords = arrayGet(ecRow, 2)
|
|
804
|
+
ecCodewordsPerBlock = arrayGet(ecRow, 3)
|
|
805
|
+
numBlocksGroup1 = arrayGet(ecRow, 4)
|
|
806
|
+
numBlocksGroup2 = arrayGet(ecRow, 6)
|
|
807
|
+
totalECCodewords = ecCodewordsPerBlock * (numBlocksGroup1 + (numBlocksGroup2 || 0))
|
|
808
|
+
blockGroups = [ \
|
|
809
|
+
{'blockCount': numBlocksGroup1, 'dataCodewordsPerBlock': arrayGet(ecRow, 5)} \
|
|
810
|
+
]
|
|
811
|
+
if numBlocksGroup2 != null:
|
|
812
|
+
arrayPush(blockGroups, {'blockCount': numBlocksGroup2, 'dataCodewordsPerBlock': arrayGet(ecRow, 7)})
|
|
813
|
+
endif
|
|
814
|
+
objectSet(qrcode, level, { \
|
|
815
|
+
'totalCodewords': totalDataCodewords + totalECCodewords, \
|
|
816
|
+
'totalMessageCodewords': totalDataCodewords, \
|
|
817
|
+
'errorCodewordsPerBlock': ecCodewordsPerBlock, \
|
|
818
|
+
'blockGroups': blockGroups \
|
|
819
|
+
})
|
|
820
|
+
endif
|
|
821
|
+
endfor
|
|
822
|
+
endfor
|
|
823
|
+
return versions
|
|
824
|
+
endfunction
|
|
825
|
+
qrcodeVersions = qrcodeVersionsCreate()
|