qrest 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/qrest/base.rb +13 -0
- data/lib/qrest/bch.rb +57 -0
- data/lib/qrest/bitbuffer.rb +85 -0
- data/lib/qrest/bitstream.rb +27 -0
- data/lib/qrest/demerits.rb +118 -0
- data/lib/qrest/foreign/supplement.rb +51 -0
- data/lib/qrest/formats/eps.rb +65 -0
- data/lib/qrest/formats/svg.rb +77 -0
- data/lib/qrest/formats/xpm.rb +48 -0
- data/lib/qrest/modules.rb +300 -0
- data/lib/qrest/polynomial.rb +136 -0
- data/lib/qrest/rsblocks.rb +143 -0
- data/lib/qrest/segment.rb +210 -0
- data/lib/qrest/version.rb +12 -0
- data/lib/qrest.rb +67 -0
- metadata +56 -0
@@ -0,0 +1,300 @@
|
|
1
|
+
#
|
2
|
+
# qrest/modules.rb -- Modules
|
3
|
+
#
|
4
|
+
|
5
|
+
require "qrest/base"
|
6
|
+
require "qrest/bitstream"
|
7
|
+
require "qrest/demerits"
|
8
|
+
require "qrest/bch"
|
9
|
+
|
10
|
+
|
11
|
+
module QRest
|
12
|
+
|
13
|
+
class Modules
|
14
|
+
|
15
|
+
POSITIONPATTERNLENGTH = (7 + 1) * 2 + 1
|
16
|
+
|
17
|
+
ERRORCORRECTLEVEL = { l: 1, m: 0, q: 3, h: 2 }
|
18
|
+
|
19
|
+
# http://web.archive.org/web/20110710094955/http://www.denso-wave.com/qrcode/vertable1-e.html
|
20
|
+
# http://web.archive.org/web/20110710094955/http://www.denso-wave.com/qrcode/vertable2-e.html
|
21
|
+
# http://web.archive.org/web/20110710094955/http://www.denso-wave.com/qrcode/vertable3-e.html
|
22
|
+
# http://web.archive.org/web/20110710094955/http://www.denso-wave.com/qrcode/vertable4-e.html
|
23
|
+
MAXBITS = {
|
24
|
+
l: [152, 272, 440, 640, 864, 1088, 1248, 1552, 1856, 2192, 2592, 2960, 3424, 3688, 4184, 4712, 5176, 5768, 6360, 6888, 7456, 8048, 8752, 9392, 10_208, 10_960, 11_744, 12_248, 13_048, 13_880, 14_744, 15_640, 16_568, 17_528, 18_448, 19_472, 20_528, 21_616, 22_496, 23_648],
|
25
|
+
m: [128, 224, 352, 512, 688, 864, 992, 1232, 1456, 1728, 2032, 2320, 2672, 2920, 3320, 3624, 4056, 4504, 5016, 5352, 5712, 6256, 6880, 7312, 8000, 8496, 9024, 9544, 10_136, 10_984, 11_640, 12_328, 13_048, 13_800, 14_496, 15_312, 15_936, 16_816, 17_728, 18_672],
|
26
|
+
q: [104, 176, 272, 384, 496, 608, 704, 880, 1056, 1232, 1440, 1648, 1952, 2088, 2360, 2600, 2936, 3176, 3560, 3880, 4096, 4544, 4912, 5312, 5744, 6032, 6464, 6968, 7288, 7880, 8264, 8920, 9368, 9848, 10288, 10832, 11408, 12016, 12656, 13328],
|
27
|
+
h: [72, 128, 208, 288, 368, 480, 528, 688, 800, 976, 1120, 1264, 1440, 1576, 1784, 2024, 2264, 2504, 2728, 3080, 3248, 3536, 3712, 4112, 4304, 4768, 5024, 5288, 5608, 5960, 6344, 6760, 7208, 7688, 7888, 8432, 8768, 9136, 9776, 10_208],
|
28
|
+
}
|
29
|
+
|
30
|
+
MASK_PATTERNS = [
|
31
|
+
proc { |i,j| (i + j) % 2 },
|
32
|
+
proc { |i,j| i % 2 },
|
33
|
+
proc { |i,j| j % 3 },
|
34
|
+
proc { |i,j| (i + j) % 3 },
|
35
|
+
proc { |i,j| ((i / 2) + (j / 3)) % 2 },
|
36
|
+
proc { |i,j| (i * j) % 2 + (i * j) % 3 },
|
37
|
+
proc { |i,j| ((i * j) % 2 + (i * j) % 3) % 2 },
|
38
|
+
proc { |i,j| ((i * j) % 3 + (i + j) % 2) % 2 },
|
39
|
+
]
|
40
|
+
|
41
|
+
attr_reader :version, :count, :fields, :demerits
|
42
|
+
|
43
|
+
class <<self
|
44
|
+
|
45
|
+
WEIGHTS = {
|
46
|
+
same_color: [ 3, 1],
|
47
|
+
full_blocks: 3,
|
48
|
+
dangerous: 40,
|
49
|
+
dark_ratio: 2,
|
50
|
+
}
|
51
|
+
|
52
|
+
def create_best data, version, error_correct_level
|
53
|
+
demerits, pattern = nil, 0
|
54
|
+
MASK_PATTERNS.length.times { |i|
|
55
|
+
test = new data, version, error_correct_level, true, i
|
56
|
+
d = test.demerits.total **WEIGHTS
|
57
|
+
demerits, pattern = d, i if not demerits or demerits > d
|
58
|
+
}
|
59
|
+
new data, version, error_correct_level, false, pattern
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
def initialize data, version, ecl, test, mask_pattern
|
65
|
+
count = version * 4 + POSITIONPATTERNLENGTH
|
66
|
+
@fields = Array.new count do Array.new count end
|
67
|
+
place_position_probe_pattern 0, 0
|
68
|
+
place_position_probe_pattern @fields.size - 7, 0
|
69
|
+
place_position_probe_pattern 0, @fields.size - 7
|
70
|
+
place_position_adjust_pattern version
|
71
|
+
place_timing_pattern
|
72
|
+
place_format_info test, (ERRORCORRECTLEVEL[ecl]<<3) | mask_pattern
|
73
|
+
place_version_info version, test if version >= 7
|
74
|
+
bs = BitStream.new data
|
75
|
+
walk_fields mask_pattern do bs.get end
|
76
|
+
@demerits = Demerits.new @fields if test
|
77
|
+
end
|
78
|
+
|
79
|
+
def inspect
|
80
|
+
d = [
|
81
|
+
"#{self.class}:",
|
82
|
+
(@fields.map { |row| row.map { |x| x.nil? ? "?" : x ? "X" : "." }.join }.join " "),
|
83
|
+
].join " "
|
84
|
+
"#<#{d}>"
|
85
|
+
end
|
86
|
+
|
87
|
+
def size ; @fields.size ; end
|
88
|
+
def range ; @range ||= 0...size ; end
|
89
|
+
|
90
|
+
def check? row, col
|
91
|
+
range === row or raise Error, "Invalid row: #{row}/#{col}"
|
92
|
+
range === col or raise Error, "Invalid column: #{row}/#{col}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def [] row, col ; check? roq, col ; @fields[ row][col] ; end
|
96
|
+
def []= row, col, val ; check? row, col ; @fields[ row][col] = val ; end
|
97
|
+
|
98
|
+
def each_row &block
|
99
|
+
@fields.each &block
|
100
|
+
end
|
101
|
+
|
102
|
+
def each_field quiet, start = 0
|
103
|
+
ri = start + quiet
|
104
|
+
each_row { |row|
|
105
|
+
ci = quiet
|
106
|
+
row.each { |field|
|
107
|
+
yield ri, ci if field
|
108
|
+
ci += 1
|
109
|
+
}
|
110
|
+
ri += 1
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def each_field_neg quiet, start = 0
|
115
|
+
ri = start - quiet
|
116
|
+
each_row { |row|
|
117
|
+
ci = quiet
|
118
|
+
row.each { |field|
|
119
|
+
yield ri, ci if field
|
120
|
+
ci += 1
|
121
|
+
}
|
122
|
+
ri -= 1
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_s dark: nil, light: nil, quiet_size: nil
|
127
|
+
r = []
|
128
|
+
lines dark: dark, light: light, quiet_size: quiet_size do |l| r.push l end
|
129
|
+
r.join "\n"
|
130
|
+
end
|
131
|
+
|
132
|
+
def lines dark: nil, light: nil, quiet_size: nil
|
133
|
+
dark ||= "X"
|
134
|
+
light ||= " "
|
135
|
+
quiet_size ||= 0
|
136
|
+
qr = light * (@fields.size + 2*quiet_size)
|
137
|
+
quiet_size.times do yield qr end
|
138
|
+
qc = light * quiet_size
|
139
|
+
dl = { true => dark, false =>light, nil => "?"}
|
140
|
+
@fields.each do |row|
|
141
|
+
yield "" << qc << (row.map do |col| dl[ col] end.join) << qc
|
142
|
+
end
|
143
|
+
quiet_size.times do yield qr end
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
R08 = 0..8
|
149
|
+
R17 = 1..7
|
150
|
+
R35 = 3..5
|
151
|
+
|
152
|
+
def place_position_probe_pattern row, col
|
153
|
+
R08.each do |i|
|
154
|
+
r = row + i - 1
|
155
|
+
next unless range === r
|
156
|
+
iv = R17 === i
|
157
|
+
ih = R17.minmax.include? i
|
158
|
+
R08.each do |j|
|
159
|
+
c = col + j - 1
|
160
|
+
next unless range === c
|
161
|
+
@fields[ r][ c] = (iv && (R17.minmax.include? j)) ||
|
162
|
+
(ih && R17 === j ) ||
|
163
|
+
(R35 === i && R35 === j)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
PATTERN_POSITION_TABLE = [
|
169
|
+
[],
|
170
|
+
[6, 18],
|
171
|
+
[6, 22],
|
172
|
+
[6, 26],
|
173
|
+
[6, 30],
|
174
|
+
[6, 34],
|
175
|
+
[6, 22, 38],
|
176
|
+
[6, 24, 42],
|
177
|
+
[6, 26, 46],
|
178
|
+
[6, 28, 50],
|
179
|
+
[6, 30, 54],
|
180
|
+
[6, 32, 58],
|
181
|
+
[6, 34, 62],
|
182
|
+
[6, 26, 46, 66],
|
183
|
+
[6, 26, 48, 70],
|
184
|
+
[6, 26, 50, 74],
|
185
|
+
[6, 30, 54, 78],
|
186
|
+
[6, 30, 56, 82],
|
187
|
+
[6, 30, 58, 86],
|
188
|
+
[6, 34, 62, 90],
|
189
|
+
[6, 28, 50, 72, 94],
|
190
|
+
[6, 26, 50, 74, 98],
|
191
|
+
[6, 30, 54, 78, 102],
|
192
|
+
[6, 28, 54, 80, 106],
|
193
|
+
[6, 32, 58, 84, 110],
|
194
|
+
[6, 30, 58, 86, 114],
|
195
|
+
[6, 34, 62, 90, 118],
|
196
|
+
[6, 26, 50, 74, 98, 122],
|
197
|
+
[6, 30, 54, 78, 102, 126],
|
198
|
+
[6, 26, 52, 78, 104, 130],
|
199
|
+
[6, 30, 56, 82, 108, 134],
|
200
|
+
[6, 34, 60, 86, 112, 138],
|
201
|
+
[6, 30, 58, 86, 114, 142],
|
202
|
+
[6, 34, 62, 90, 118, 146],
|
203
|
+
[6, 30, 54, 78, 102, 126, 150],
|
204
|
+
[6, 24, 50, 76, 102, 128, 154],
|
205
|
+
[6, 28, 54, 80, 106, 132, 158],
|
206
|
+
[6, 32, 58, 84, 110, 136, 162],
|
207
|
+
[6, 26, 54, 82, 110, 138, 166],
|
208
|
+
[6, 30, 58, 86, 114, 142, 170]
|
209
|
+
]
|
210
|
+
|
211
|
+
MAX_VERSION = PATTERN_POSITION_TABLE.size
|
212
|
+
|
213
|
+
R_22 = -2..2
|
214
|
+
|
215
|
+
def place_position_adjust_pattern version
|
216
|
+
positions = PATTERN_POSITION_TABLE[ version - 1]
|
217
|
+
rd = R_22.minmax
|
218
|
+
positions.each do |row|
|
219
|
+
positions.each do |col|
|
220
|
+
next unless @fields[ row][ col].nil?
|
221
|
+
R_22.each do |r|
|
222
|
+
pr = row + r
|
223
|
+
R_22.each do |c|
|
224
|
+
pc = col + c
|
225
|
+
@fields[ pr][ pc] = (rd.include? r) || (rd.include? c) || (r == 0 && c == 0)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def place_timing_pattern
|
233
|
+
(8...@fields.size-8).each do |i|
|
234
|
+
@fields[ i][ 6] = @fields[ 6][ i] = i.even?
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def place_format_info test, ecl
|
239
|
+
bits = Bch.format_info ecl
|
240
|
+
15.times do |i|
|
241
|
+
mod = !test && bits.odd?
|
242
|
+
r =
|
243
|
+
case i
|
244
|
+
when ...6 then 0
|
245
|
+
when ...8 then 1
|
246
|
+
else @fields.size - 15
|
247
|
+
end
|
248
|
+
@fields[ r+i][ 8] = mod
|
249
|
+
c =
|
250
|
+
case i
|
251
|
+
when ...8 then @fields.size
|
252
|
+
when ...9 then 16
|
253
|
+
else 15
|
254
|
+
end
|
255
|
+
@fields[ 8][ c - i - 1] = mod
|
256
|
+
bits >>= 1
|
257
|
+
end
|
258
|
+
@fields[ @fields.size - 8][ 8] = !test
|
259
|
+
end
|
260
|
+
|
261
|
+
def place_version_info version, test
|
262
|
+
bits = Bch.version version
|
263
|
+
18.times do |i|
|
264
|
+
id, im = i.divmod 3
|
265
|
+
im += @fields.size - 8 - 3
|
266
|
+
@fields[ id][ im] = @fields[ im][ id] = !test && (bits & 1) == 1
|
267
|
+
bits >>= 1
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def walk_fields mask_pattern
|
272
|
+
# An image says more than a thousand statements:
|
273
|
+
# <https://en.wikipedia.org/wiki/QR_code#Message_placement>
|
274
|
+
mp = MASK_PATTERNS[ mask_pattern] or raise ArgumentError, "Bad mask_pattern: #{mask_pattern}"
|
275
|
+
row = c2 = @fields.size - 1
|
276
|
+
inc = -1
|
277
|
+
loop do
|
278
|
+
loop do
|
279
|
+
2.times do |c|
|
280
|
+
col = c2 - c
|
281
|
+
next unless @fields[ row][ col].nil?
|
282
|
+
@fields[ row][ col] = (mp.call row, col).zero? ^ yield
|
283
|
+
end
|
284
|
+
ri = row + inc
|
285
|
+
unless range === ri then
|
286
|
+
inc = -inc
|
287
|
+
break
|
288
|
+
end
|
289
|
+
row = ri
|
290
|
+
end
|
291
|
+
break if c2 == 1
|
292
|
+
c2 -= 2
|
293
|
+
c2 -= 1 if c2 == 6
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
300
|
+
|
@@ -0,0 +1,136 @@
|
|
1
|
+
#
|
2
|
+
# qrest/polynomial.rb -- Polynomes
|
3
|
+
#
|
4
|
+
|
5
|
+
require "qrest/base"
|
6
|
+
|
7
|
+
|
8
|
+
module QRest
|
9
|
+
|
10
|
+
class Polynomial
|
11
|
+
|
12
|
+
@ec = {}
|
13
|
+
|
14
|
+
class <<self
|
15
|
+
|
16
|
+
def error_correct error_correct_length
|
17
|
+
@ec[ error_correct_length] ||=
|
18
|
+
if error_correct_length > 0 then
|
19
|
+
e = error_correct_length - 1
|
20
|
+
ge = new [1, 0]
|
21
|
+
ge.gexp! 1, e
|
22
|
+
(error_correct e).multiply ge
|
23
|
+
else
|
24
|
+
new [1]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def zeroes len
|
29
|
+
new [ 0] * len
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :num
|
35
|
+
|
36
|
+
def initialize num
|
37
|
+
num.empty? and raise ArgumentError, "Empty polynomial."
|
38
|
+
@num = num
|
39
|
+
end
|
40
|
+
|
41
|
+
def dup ; Polynomial.new @num.dup ; end
|
42
|
+
|
43
|
+
def [] index ; @num[index] ; end
|
44
|
+
def size ; @num.size ; end
|
45
|
+
alias length size
|
46
|
+
|
47
|
+
def norm!
|
48
|
+
@num.shift while @num.first == 0
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def extend! shift
|
53
|
+
shift.times { @num.push 0 }
|
54
|
+
end
|
55
|
+
|
56
|
+
def grow! n
|
57
|
+
@num.unshift 0 until @num.length >= n
|
58
|
+
end
|
59
|
+
|
60
|
+
def first_glog
|
61
|
+
Polynomial.glog @num.first
|
62
|
+
end
|
63
|
+
|
64
|
+
def each_glog
|
65
|
+
@num.each_with_index { |n,i|
|
66
|
+
yield (Polynomial.glog n), i
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def gexp! i, n
|
71
|
+
@num[ i] ^= Polynomial.gexp n
|
72
|
+
end
|
73
|
+
|
74
|
+
def multiply e
|
75
|
+
r = Polynomial.zeroes length + e.length - 1
|
76
|
+
each_glog { |gi,i|
|
77
|
+
e.each_glog { |gj,j|
|
78
|
+
r.gexp! i + j, gi + gj
|
79
|
+
}
|
80
|
+
}
|
81
|
+
r
|
82
|
+
end
|
83
|
+
|
84
|
+
def error_mod error_count
|
85
|
+
e = Polynomial.error_correct error_count
|
86
|
+
ef = e.first_glog
|
87
|
+
p = dup
|
88
|
+
p.extend! error_count
|
89
|
+
loop do
|
90
|
+
p.norm!
|
91
|
+
break if p.length < e.length
|
92
|
+
ratio = p.first_glog - ef
|
93
|
+
e.each_glog { |gi,i|
|
94
|
+
p.gexp! i, ratio + gi
|
95
|
+
}
|
96
|
+
end
|
97
|
+
p.grow! error_count
|
98
|
+
p
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
EXP_TABLE = []
|
103
|
+
(0...8).each do |i|
|
104
|
+
EXP_TABLE.push 1 << i
|
105
|
+
end
|
106
|
+
(8...256).each do |i|
|
107
|
+
EXP_TABLE.push EXP_TABLE[i-4] ^ EXP_TABLE[i-5] ^ EXP_TABLE[i-6] ^ EXP_TABLE[i-8]
|
108
|
+
end
|
109
|
+
|
110
|
+
LOG_TABLE = [nil] * 256
|
111
|
+
255.times do |i|
|
112
|
+
LOG_TABLE[EXP_TABLE[i]] = i
|
113
|
+
end
|
114
|
+
|
115
|
+
EXP_TABLE.freeze
|
116
|
+
LOG_TABLE.freeze
|
117
|
+
|
118
|
+
class <<self
|
119
|
+
|
120
|
+
def glog n
|
121
|
+
n >= 1 or raise Error, "Internal error: glog(#{n})."
|
122
|
+
LOG_TABLE[n]
|
123
|
+
end
|
124
|
+
|
125
|
+
def gexp n
|
126
|
+
while n < 0 do n += 255 end
|
127
|
+
while n >= 256 do n -= 255 end
|
128
|
+
EXP_TABLE[n]
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
@@ -0,0 +1,143 @@
|
|
1
|
+
#
|
2
|
+
# qrest/rsblocks.rb -- Reed-Solomon-Code blocks
|
3
|
+
#
|
4
|
+
|
5
|
+
require "qrest/base"
|
6
|
+
require "qrest/bitbuffer"
|
7
|
+
require "qrest/polynomial"
|
8
|
+
|
9
|
+
|
10
|
+
module QRest
|
11
|
+
|
12
|
+
class RSBlocks
|
13
|
+
|
14
|
+
class Block
|
15
|
+
|
16
|
+
attr_reader :data_count, :total_count
|
17
|
+
|
18
|
+
def initialize total_count, data_count
|
19
|
+
@total_count, @data_count = total_count, data_count
|
20
|
+
end
|
21
|
+
|
22
|
+
def error_count ; total_count - data_count ; end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize version, bs
|
27
|
+
@version = version
|
28
|
+
@list = bs.map { |num,total,data| [ num, (Block.new total, data)] }
|
29
|
+
end
|
30
|
+
|
31
|
+
def each ; @list.each { |(n,b)| n.times { yield b } } ; end
|
32
|
+
def sum sym ; @list.map { |(n,b)| n * (b.send sym) }.sum ; end
|
33
|
+
def max sym ; @list.map { |(_,b)| (b.send sym) }.max ; end
|
34
|
+
|
35
|
+
def create_data data
|
36
|
+
l = sum :data_count
|
37
|
+
b = BitBuffer.build l do |buf| data.write buf, @version end
|
38
|
+
create_bytes b
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def create_bytes buffer
|
44
|
+
dcdata, ecdata = [], []
|
45
|
+
i = 0
|
46
|
+
each do |rs|
|
47
|
+
p = Polynomial.new buffer[ i, rs.data_count]
|
48
|
+
i += rs.data_count
|
49
|
+
dcdata.push p.num
|
50
|
+
ecdata.push (p.error_mod rs.error_count).num
|
51
|
+
end
|
52
|
+
data = []
|
53
|
+
(max :data_count).times do |j|
|
54
|
+
dcdata.each do |dc|
|
55
|
+
data.push dc[j] if j < dc.size
|
56
|
+
end
|
57
|
+
end
|
58
|
+
(max :error_count).times do |j|
|
59
|
+
ecdata.each do |ec|
|
60
|
+
data.push ec[j] if j < ec.size
|
61
|
+
end
|
62
|
+
end
|
63
|
+
data.length == (sum :total_count) or
|
64
|
+
raise Error, "Internal error. Please consider a report."
|
65
|
+
data
|
66
|
+
end
|
67
|
+
|
68
|
+
@blocks = Hash.new { |h,k|
|
69
|
+
v = nil
|
70
|
+
RS_BLOCK_TABLE.each_line { |l|
|
71
|
+
n, l = l.split nil, 2
|
72
|
+
if n.to_i == k then
|
73
|
+
v = {}
|
74
|
+
while l =~ /([a-z])\b([ 0-9,]+)/ do
|
75
|
+
n, b, l = $1, $2, $'
|
76
|
+
b.rstrip!
|
77
|
+
v[ n.to_sym] = (b.split /,/).map { |t| t.split.map { |i| i.to_i } }
|
78
|
+
end
|
79
|
+
break
|
80
|
+
end
|
81
|
+
}
|
82
|
+
h[k] = v
|
83
|
+
}
|
84
|
+
|
85
|
+
class <<self
|
86
|
+
|
87
|
+
private :new
|
88
|
+
|
89
|
+
def get version, error_correct_level
|
90
|
+
v = @blocks[version] or raise ArgumentError, "No RS block for version #{version}"
|
91
|
+
new version, v[error_correct_level]
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
# http://www.thonky.com/qr-code-tutorial/error-correction-table/
|
97
|
+
RS_BLOCK_TABLE = <<~EOT
|
98
|
+
1 l 1 26 19, m 1 26 16, q 1 26 13, h 1 26 9,
|
99
|
+
2 l 1 44 34, m 1 44 28, q 1 44 22, h 1 44 16,
|
100
|
+
3 l 1 70 55, m 1 70 44, q 2 35 17, h 2 35 13,
|
101
|
+
4 l 1 100 80, m 2 50 32, q 2 50 24, h 4 25 9,
|
102
|
+
5 l 1 134 108, m 2 67 43, q 2 33 15, 2 34 16, h 2 33 11, 2 34 12,
|
103
|
+
6 l 2 86 68, m 4 43 27, q 4 43 19, h 4 43 15,
|
104
|
+
7 l 2 98 78, m 4 49 31, q 2 32 14, 4 33 15, h 4 39 13, 1 40 14,
|
105
|
+
8 l 2 121 97, m 2 60 38, 2 61 39, q 4 40 18, 2 41 19, h 4 40 14, 2 41 15,
|
106
|
+
9 l 2 146 116, m 3 58 36, 2 59 37, q 4 36 16, 4 37 17, h 4 36 12, 4 37 13,
|
107
|
+
10 l 2 86 68, 2 87 69, m 4 69 43, 1 70 44, q 6 43 19, 2 44 20, h 6 43 15, 2 44 16,
|
108
|
+
11 l 4 101 81, m 1 80 50, 4 81 51, q 4 50 22, 4 51 23, h 3 36 12, 8 37 13,
|
109
|
+
12 l 2 116 92, 2 117 93, m 6 58 36, 2 59 37, q 4 46 20, 6 47 21, h 7 42 14, 4 43 15,
|
110
|
+
13 l 4 133 107, m 8 59 37, 1 60 38, q 8 44 20, 4 45 21, h 12 33 11, 4 34 12,
|
111
|
+
14 l 3 145 115, 1 146 116, m 4 64 40, 5 65 41, q 11 36 16, 5 37 17, h 11 36 12, 5 37 13,
|
112
|
+
15 l 5 109 87, 1 110 88, m 5 65 41, 5 66 42, q 5 54 24, 7 55 25, h 11 36 12, 7 37 13,
|
113
|
+
16 l 5 122 98, 1 123 99, m 7 73 45, 3 74 46, q 15 43 19, 2 44 20, h 3 45 15, 13 46 16,
|
114
|
+
17 l 1 135 107, 5 136 108, m 10 74 46, 1 75 47, q 1 50 22, 15 51 23, h 2 42 14, 17 43 15,
|
115
|
+
18 l 5 150 120, 1 151 121, m 9 69 43, 4 70 44, q 17 50 22, 1 51 23, h 2 42 14, 19 43 15,
|
116
|
+
19 l 3 141 113, 4 142 114, m 3 70 44, 11 71 45, q 17 47 21, 4 48 22, h 9 39 13, 16 40 14,
|
117
|
+
20 l 3 135 107, 5 136 108, m 3 67 41, 13 68 42, q 15 54 24, 5 55 25, h 15 43 15, 10 44 16,
|
118
|
+
21 l 4 144 116, 4 145 117, m 17 68 42, q 17 50 22, 6 51 23, h 19 46 16, 6 47 17,
|
119
|
+
22 l 2 139 111, 7 140 112, m 17 74 46, q 7 54 24, 16 55 25, h 34 37 13,
|
120
|
+
23 l 4 151 121, 5 152 122, m 4 75 47, 14 76 48, q 11 54 24, 14 55 25, h 16 45 15, 14 46 16,
|
121
|
+
24 l 6 147 117, 4 148 118, m 6 73 45, 14 74 46, q 11 54 24, 16 55 25, h 30 46 16, 2 47 17,
|
122
|
+
25 l 8 132 106, 4 133 107, m 8 75 47, 13 76 48, q 7 54 24, 22 55 25, h 22 45 15, 13 46 16,
|
123
|
+
26 l 10 142 114, 2 143 115, m 19 74 46, 4 75 47, q 28 50 22, 6 51 23, h 33 46 16, 4 47 17,
|
124
|
+
27 l 8 152 122, 4 153 123, m 22 73 45, 3 74 46, q 8 53 23, 26 54 24, h 12 45 15, 28 46 16,
|
125
|
+
28 l 3 147 117, 10 148 118, m 3 73 45, 23 74 46, q 4 54 24, 31 55 25, h 11 45 15, 31 46 16,
|
126
|
+
29 l 7 146 116, 7 147 117, m 21 73 45, 7 74 46, q 1 53 23, 37 54 24, h 19 45 15, 26 46 16,
|
127
|
+
30 l 5 145 115, 10 146 116, m 19 75 47, 10 76 48, q 15 54 24, 25 55 25, h 23 45 15, 25 46 16,
|
128
|
+
31 l 13 145 115, 3 146 116, m 2 74 46, 29 75 47, q 42 54 24, 1 55 25, h 23 45 15, 28 46 16,
|
129
|
+
32 l 17 145 115, m 10 74 46, 23 75 47, q 10 54 24, 35 55 25, h 19 45 15, 35 46 16,
|
130
|
+
33 l 17 145 115, 1 146 116, m 14 74 46, 21 75 47, q 29 54 24, 19 55 25, h 11 45 15, 46 46 16,
|
131
|
+
34 l 13 145 115, 6 146 116, m 14 74 46, 23 75 47, q 44 54 24, 7 55 25, h 59 46 16, 1 47 17,
|
132
|
+
35 l 12 151 121, 7 152 122, m 12 75 47, 26 76 48, q 39 54 24, 14 55 25, h 22 45 15, 41 46 16,
|
133
|
+
36 l 6 151 121, 14 152 122, m 6 75 47, 34 76 48, q 46 54 24, 10 55 25, h 2 45 15, 64 46 16,
|
134
|
+
37 l 17 152 122, 4 153 123, m 29 74 46, 14 75 47, q 49 54 24, 10 55 25, h 24 45 15, 46 46 16,
|
135
|
+
38 l 4 152 122, 18 153 123, m 13 74 46, 32 75 47, q 48 54 24, 14 55 25, h 42 45 15, 32 46 16,
|
136
|
+
39 l 20 147 117, 4 148 118, m 40 75 47, 7 76 48, q 43 54 24, 22 55 25, h 10 45 15, 67 46 16,
|
137
|
+
40 l 19 148 118, 6 149 119, m 18 75 47, 31 76 48, q 34 54 24, 34 55 25, h 20 45 15, 61 46 16,
|
138
|
+
EOT
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|