rqrcode_core 0.1.2 → 1.2.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 +4 -4
- data/.github/workflows/ruby.yml +12 -14
- data/.gitignore +1 -0
- data/CHANGELOG.md +44 -0
- data/Gemfile.lock +33 -4
- data/README.md +56 -21
- data/Rakefile +17 -8
- data/lib/rqrcode_core/qrcode/qr_8bit_byte.rb +4 -12
- data/lib/rqrcode_core/qrcode/qr_alphanumeric.rb +18 -25
- data/lib/rqrcode_core/qrcode/qr_bit_buffer.rb +14 -24
- data/lib/rqrcode_core/qrcode/qr_code.rb +210 -286
- data/lib/rqrcode_core/qrcode/qr_math.rb +9 -10
- data/lib/rqrcode_core/qrcode/qr_multi.rb +13 -0
- data/lib/rqrcode_core/qrcode/qr_numeric.rb +9 -20
- data/lib/rqrcode_core/qrcode/qr_polynomial.rb +15 -15
- data/lib/rqrcode_core/qrcode/qr_rs_block.rb +8 -10
- data/lib/rqrcode_core/qrcode/qr_segment.rb +61 -0
- data/lib/rqrcode_core/qrcode/qr_util.rb +48 -49
- data/lib/rqrcode_core/qrcode.rb +11 -9
- data/lib/rqrcode_core/version.rb +1 -1
- data/rqrcode_core.gemspec +17 -17
- metadata +21 -4
@@ -6,39 +6,38 @@ module RQRCodeCore
|
|
6
6
|
exp_table = Array.new(256)
|
7
7
|
log_table = Array.new(256)
|
8
8
|
|
9
|
-
(
|
9
|
+
(0...8).each do |i|
|
10
10
|
exp_table[i] = 1 << i
|
11
11
|
end
|
12
12
|
|
13
|
-
(
|
13
|
+
(8...256).each do |i|
|
14
14
|
exp_table[i] = exp_table[i - 4] \
|
15
15
|
^ exp_table[i - 5] \
|
16
16
|
^ exp_table[i - 6] \
|
17
17
|
^ exp_table[i - 8]
|
18
18
|
end
|
19
19
|
|
20
|
-
(
|
21
|
-
log_table[exp_table[i]
|
20
|
+
(0...255).each do |i|
|
21
|
+
log_table[exp_table[i]] = i
|
22
22
|
end
|
23
23
|
|
24
|
-
EXP_TABLE
|
25
|
-
LOG_TABLE
|
24
|
+
const_set(:EXP_TABLE, exp_table).freeze
|
25
|
+
const_set(:LOG_TABLE, log_table).freeze
|
26
26
|
}
|
27
27
|
|
28
28
|
class << self
|
29
29
|
def glog(n)
|
30
|
-
raise QRCodeRunTimeError, "glog(#{n})" if
|
30
|
+
raise QRCodeRunTimeError, "glog(#{n})" if n < 1
|
31
31
|
LOG_TABLE[n]
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
34
|
def gexp(n)
|
36
35
|
while n < 0
|
37
|
-
n
|
36
|
+
n += 255
|
38
37
|
end
|
39
38
|
|
40
39
|
while n >= 256
|
41
|
-
n
|
40
|
+
n -= 255
|
42
41
|
end
|
43
42
|
|
44
43
|
EXP_TABLE[n]
|
@@ -1,38 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RQRCodeCore
|
4
|
-
NUMERIC = [
|
4
|
+
NUMERIC = %w[0 1 2 3 4 5 6 7 8 9].freeze
|
5
5
|
|
6
6
|
class QRNumeric
|
7
|
-
|
8
|
-
|
9
|
-
def initialize( data )
|
10
|
-
@mode = QRMODE[:mode_number]
|
11
|
-
|
7
|
+
def initialize(data)
|
12
8
|
raise QRCodeArgumentError, "Not a numeric string `#{data}`" unless QRNumeric.valid_data?(data)
|
13
9
|
|
14
|
-
@data = data
|
10
|
+
@data = data
|
15
11
|
end
|
16
12
|
|
17
|
-
def
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.valid_data? data
|
22
|
-
data.each_char do |s|
|
23
|
-
return false if NUMERIC.index(s).nil?
|
24
|
-
end
|
25
|
-
true
|
13
|
+
def self.valid_data?(data)
|
14
|
+
(data.chars - NUMERIC).empty?
|
26
15
|
end
|
27
16
|
|
28
|
-
def write(
|
29
|
-
buffer.numeric_encoding_start(
|
17
|
+
def write(buffer)
|
18
|
+
buffer.numeric_encoding_start(@data.size)
|
30
19
|
|
31
|
-
|
20
|
+
@data.size.times do |i|
|
32
21
|
if i % 3 == 0
|
33
22
|
chars = @data[i, 3]
|
34
23
|
bit_length = get_bit_length(chars.length)
|
35
|
-
buffer.put(
|
24
|
+
buffer.put(get_code(chars), bit_length)
|
36
25
|
end
|
37
26
|
end
|
38
27
|
end
|
@@ -2,22 +2,22 @@
|
|
2
2
|
|
3
3
|
module RQRCodeCore
|
4
4
|
class QRPolynomial
|
5
|
-
def initialize(
|
5
|
+
def initialize(num, shift)
|
6
6
|
raise QRCodeRunTimeError, "#{num.size}/#{shift}" if num.empty?
|
7
7
|
offset = 0
|
8
8
|
|
9
9
|
while offset < num.size && num[offset] == 0
|
10
|
-
offset
|
10
|
+
offset += 1
|
11
11
|
end
|
12
12
|
|
13
|
-
@num = Array.new(
|
13
|
+
@num = Array.new(num.size - offset + shift)
|
14
14
|
|
15
|
-
(
|
15
|
+
(0...num.size - offset).each do |i|
|
16
16
|
@num[i] = num[i + offset]
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def get(
|
20
|
+
def get(index)
|
21
21
|
@num[index]
|
22
22
|
end
|
23
23
|
|
@@ -25,20 +25,20 @@ module RQRCodeCore
|
|
25
25
|
@num.size
|
26
26
|
end
|
27
27
|
|
28
|
-
def multiply(
|
29
|
-
num = Array.new(
|
28
|
+
def multiply(e)
|
29
|
+
num = Array.new(get_length + e.get_length - 1)
|
30
30
|
|
31
|
-
(
|
32
|
-
(
|
31
|
+
(0...get_length).each do |i|
|
32
|
+
(0...e.get_length).each do |j|
|
33
33
|
tmp = num[i + j].nil? ? 0 : num[i + j]
|
34
|
-
num[i + j] = tmp ^ QRMath.gexp(QRMath.glog(
|
34
|
+
num[i + j] = tmp ^ QRMath.gexp(QRMath.glog(get(i)) + QRMath.glog(e.get(j)))
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
QRPolynomial.new(num, 0)
|
39
39
|
end
|
40
40
|
|
41
|
-
def mod(
|
41
|
+
def mod(e)
|
42
42
|
if get_length - e.get_length < 0
|
43
43
|
return self
|
44
44
|
end
|
@@ -46,16 +46,16 @@ module RQRCodeCore
|
|
46
46
|
ratio = QRMath.glog(get(0)) - QRMath.glog(e.get(0))
|
47
47
|
num = Array.new(get_length)
|
48
48
|
|
49
|
-
(
|
49
|
+
(0...get_length).each do |i|
|
50
50
|
num[i] = get(i)
|
51
51
|
end
|
52
52
|
|
53
|
-
(
|
53
|
+
(0...e.get_length).each do |i|
|
54
54
|
tmp = num[i].nil? ? 0 : num[i]
|
55
55
|
num[i] = tmp ^ QRMath.gexp(QRMath.glog(e.get(i)) + ratio)
|
56
56
|
end
|
57
57
|
|
58
|
-
|
58
|
+
QRPolynomial.new(num, 0).mod(e)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -4,13 +4,13 @@ module RQRCodeCore
|
|
4
4
|
class QRRSBlock
|
5
5
|
attr_reader :data_count, :total_count
|
6
6
|
|
7
|
-
def initialize(
|
7
|
+
def initialize(total_count, data_count)
|
8
8
|
@total_count = total_count
|
9
9
|
@data_count = data_count
|
10
10
|
end
|
11
11
|
|
12
12
|
# http://www.thonky.com/qr-code-tutorial/error-correction-table/
|
13
|
-
|
13
|
+
RS_BLOCK_TABLE = [
|
14
14
|
# L
|
15
15
|
# M
|
16
16
|
# Q
|
@@ -256,9 +256,9 @@ module RQRCodeCore
|
|
256
256
|
[34, 54, 24, 34, 55, 25],
|
257
257
|
[20, 45, 15, 61, 46, 16]
|
258
258
|
|
259
|
-
]
|
259
|
+
].freeze
|
260
260
|
|
261
|
-
def
|
261
|
+
def self.get_rs_blocks(version, error_correct_level)
|
262
262
|
rs_block = QRRSBlock.get_rs_block_table(version, error_correct_level)
|
263
263
|
|
264
264
|
if rs_block.nil?
|
@@ -269,20 +269,20 @@ module RQRCodeCore
|
|
269
269
|
length = rs_block.size / 3
|
270
270
|
list = []
|
271
271
|
|
272
|
-
(
|
272
|
+
(0...length).each do |i|
|
273
273
|
count = rs_block[i * 3 + 0]
|
274
274
|
total_count = rs_block[i * 3 + 1]
|
275
275
|
data_count = rs_block[i * 3 + 2]
|
276
276
|
|
277
|
-
(
|
278
|
-
list << QRRSBlock.new(
|
277
|
+
(0...count).each do |j|
|
278
|
+
list << QRRSBlock.new(total_count, data_count)
|
279
279
|
end
|
280
280
|
end
|
281
281
|
|
282
282
|
list
|
283
283
|
end
|
284
284
|
|
285
|
-
def
|
285
|
+
def self.get_rs_block_table(version, error_correct_level)
|
286
286
|
case error_correct_level
|
287
287
|
when QRERRORCORRECTLEVEL[:l]
|
288
288
|
QRRSBlock::RS_BLOCK_TABLE[(version - 1) * 4 + 0]
|
@@ -292,8 +292,6 @@ module RQRCodeCore
|
|
292
292
|
QRRSBlock::RS_BLOCK_TABLE[(version - 1) * 4 + 2]
|
293
293
|
when QRERRORCORRECTLEVEL[:h]
|
294
294
|
QRRSBlock::RS_BLOCK_TABLE[(version - 1) * 4 + 3]
|
295
|
-
else
|
296
|
-
nil
|
297
295
|
end
|
298
296
|
end
|
299
297
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RQRCodeCore
|
4
|
+
class QRSegment
|
5
|
+
attr_reader :data, :mode
|
6
|
+
|
7
|
+
def initialize(data:, mode: nil)
|
8
|
+
@data = data
|
9
|
+
@mode = QRMODE_NAME.dig(mode&.to_sym)
|
10
|
+
|
11
|
+
# If mode is not explicitely found choose mode according to data type
|
12
|
+
@mode ||= if RQRCodeCore::QRNumeric.valid_data?(@data)
|
13
|
+
QRMODE_NAME[:number]
|
14
|
+
elsif QRAlphanumeric.valid_data?(@data)
|
15
|
+
QRMODE_NAME[:alphanumeric]
|
16
|
+
else
|
17
|
+
QRMODE_NAME[:byte_8bit]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def size(version)
|
22
|
+
4 + header_size(version) + content_size
|
23
|
+
end
|
24
|
+
|
25
|
+
def header_size(version)
|
26
|
+
QRUtil.get_length_in_bits(QRMODE[mode], version)
|
27
|
+
end
|
28
|
+
|
29
|
+
def content_size
|
30
|
+
chunk_size, bit_length, extra = case mode
|
31
|
+
when :mode_number
|
32
|
+
[3, QRNumeric::NUMBER_LENGTH[3], QRNumeric::NUMBER_LENGTH[data_length % 3] || 0]
|
33
|
+
when :mode_alpha_numk
|
34
|
+
[2, 11, 6]
|
35
|
+
when :mode_8bit_byte
|
36
|
+
[1, 8, 0]
|
37
|
+
end
|
38
|
+
|
39
|
+
(data_length / chunk_size) * bit_length + ((data_length % chunk_size) == 0 ? 0 : extra)
|
40
|
+
end
|
41
|
+
|
42
|
+
def writer
|
43
|
+
case mode
|
44
|
+
when :mode_number
|
45
|
+
QRNumeric.new(data)
|
46
|
+
when :mode_alpha_numk
|
47
|
+
QRAlphanumeric.new(data)
|
48
|
+
when :mode_multi
|
49
|
+
QRMulti.new(data)
|
50
|
+
else
|
51
|
+
QR8bitByte.new(data)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def data_length
|
58
|
+
data.bytesize
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -43,7 +43,7 @@ module RQRCodeCore
|
|
43
43
|
[6, 32, 58, 84, 110, 136, 162],
|
44
44
|
[6, 26, 54, 82, 110, 138, 166],
|
45
45
|
[6, 30, 58, 86, 114, 142, 170]
|
46
|
-
]
|
46
|
+
].freeze
|
47
47
|
|
48
48
|
G15 = 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0
|
49
49
|
G18 = 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0
|
@@ -58,70 +58,70 @@ module RQRCodeCore
|
|
58
58
|
QRMODE[:mode_number] => [10, 12, 14],
|
59
59
|
QRMODE[:mode_alpha_numk] => [9, 11, 13],
|
60
60
|
QRMODE[:mode_8bit_byte] => [8, 16, 16],
|
61
|
-
QRMODE[:mode_kanji] => [8, 10, 12]
|
62
|
-
}
|
61
|
+
QRMODE[:mode_kanji] => [8, 10, 12]
|
62
|
+
}.freeze
|
63
63
|
|
64
|
-
def
|
64
|
+
def self.max_size
|
65
65
|
PATTERN_POSITION_TABLE.count
|
66
66
|
end
|
67
67
|
|
68
|
-
def
|
68
|
+
def self.get_bch_format_info(data)
|
69
69
|
d = data << 10
|
70
70
|
while QRUtil.get_bch_digit(d) - QRUtil.get_bch_digit(G15) >= 0
|
71
71
|
d ^= (G15 << (QRUtil.get_bch_digit(d) - QRUtil.get_bch_digit(G15)))
|
72
72
|
end
|
73
|
-
((
|
73
|
+
((data << 10) | d) ^ G15_MASK
|
74
74
|
end
|
75
75
|
|
76
|
-
def
|
76
|
+
def self.rszf(num, count)
|
77
77
|
# zero fill right shift
|
78
|
-
(num >> count) & ((2
|
78
|
+
(num >> count) & ((2**((num.size * 8) - count)) - 1)
|
79
79
|
end
|
80
80
|
|
81
|
-
def
|
81
|
+
def self.get_bch_version(data)
|
82
82
|
d = data << 12
|
83
83
|
while QRUtil.get_bch_digit(d) - QRUtil.get_bch_digit(G18) >= 0
|
84
84
|
d ^= (G18 << (QRUtil.get_bch_digit(d) - QRUtil.get_bch_digit(G18)))
|
85
85
|
end
|
86
|
-
(
|
86
|
+
(data << 12) | d
|
87
87
|
end
|
88
88
|
|
89
|
-
def
|
89
|
+
def self.get_bch_digit(data)
|
90
90
|
digit = 0
|
91
91
|
|
92
92
|
while data != 0
|
93
|
-
digit
|
94
|
-
data = QRUtil.rszf(
|
93
|
+
digit += 1
|
94
|
+
data = QRUtil.rszf(data, 1)
|
95
95
|
end
|
96
96
|
|
97
97
|
digit
|
98
98
|
end
|
99
99
|
|
100
|
-
def
|
100
|
+
def self.get_pattern_positions(version)
|
101
101
|
PATTERN_POSITION_TABLE[version - 1]
|
102
102
|
end
|
103
103
|
|
104
|
-
def
|
104
|
+
def self.get_mask(mask_pattern, i, j)
|
105
105
|
if mask_pattern > QRMASKCOMPUTATIONS.size
|
106
106
|
raise QRCodeRunTimeError, "bad mask_pattern: #{mask_pattern}"
|
107
107
|
end
|
108
108
|
|
109
|
-
|
109
|
+
QRMASKCOMPUTATIONS[mask_pattern].call(i, j)
|
110
110
|
end
|
111
111
|
|
112
|
-
def
|
113
|
-
a = QRPolynomial.new(
|
112
|
+
def self.get_error_correct_polynomial(error_correct_length)
|
113
|
+
a = QRPolynomial.new([1], 0)
|
114
114
|
|
115
|
-
(
|
116
|
-
a = a.multiply(
|
115
|
+
(0...error_correct_length).each do |i|
|
116
|
+
a = a.multiply(QRPolynomial.new([1, QRMath.gexp(i)], 0))
|
117
117
|
end
|
118
118
|
|
119
119
|
a
|
120
120
|
end
|
121
121
|
|
122
|
-
def
|
122
|
+
def self.get_length_in_bits(mode, version)
|
123
123
|
if !QRMODE.value?(mode)
|
124
|
-
|
124
|
+
raise QRCodeRunTimeError, "Unknown mode: #{mode}"
|
125
125
|
end
|
126
126
|
|
127
127
|
if version > 40
|
@@ -139,10 +139,10 @@ module RQRCodeCore
|
|
139
139
|
macro_version = 2
|
140
140
|
end
|
141
141
|
|
142
|
-
|
142
|
+
BITS_FOR_MODE[mode][macro_version]
|
143
143
|
end
|
144
144
|
|
145
|
-
def
|
145
|
+
def self.get_lost_points(modules)
|
146
146
|
demerit_points = 0
|
147
147
|
|
148
148
|
demerit_points += QRUtil.demerit_points_1_same_color(modules)
|
@@ -150,10 +150,10 @@ module RQRCodeCore
|
|
150
150
|
demerit_points += QRUtil.demerit_points_3_dangerous_patterns(modules)
|
151
151
|
demerit_points += QRUtil.demerit_points_4_dark_ratio(modules)
|
152
152
|
|
153
|
-
|
153
|
+
demerit_points
|
154
154
|
end
|
155
155
|
|
156
|
-
def
|
156
|
+
def self.demerit_points_1_same_color(modules)
|
157
157
|
demerit_points = 0
|
158
158
|
module_count = modules.size
|
159
159
|
|
@@ -163,10 +163,10 @@ module RQRCodeCore
|
|
163
163
|
same_count = 0
|
164
164
|
dark = modules[row][col]
|
165
165
|
|
166
|
-
(
|
166
|
+
(-1..1).each do |r|
|
167
167
|
next if row + r < 0 || module_count <= row + r
|
168
168
|
|
169
|
-
(
|
169
|
+
(-1..1).each do |c|
|
170
170
|
next if col + c < 0 || module_count <= col + c
|
171
171
|
next if r == 0 && c == 0
|
172
172
|
if dark == modules[row + r][col + c]
|
@@ -181,10 +181,10 @@ module RQRCodeCore
|
|
181
181
|
end
|
182
182
|
end
|
183
183
|
|
184
|
-
|
184
|
+
demerit_points
|
185
185
|
end
|
186
186
|
|
187
|
-
def
|
187
|
+
def self.demerit_points_2_full_blocks(modules)
|
188
188
|
demerit_points = 0
|
189
189
|
module_count = modules.size
|
190
190
|
|
@@ -196,16 +196,16 @@ module RQRCodeCore
|
|
196
196
|
count += 1 if modules[row + 1][col]
|
197
197
|
count += 1 if modules[row][col + 1]
|
198
198
|
count += 1 if modules[row + 1][col + 1]
|
199
|
-
if
|
199
|
+
if count == 0 || count == 4
|
200
200
|
demerit_points += DEMERIT_POINTS_2
|
201
201
|
end
|
202
202
|
end
|
203
203
|
end
|
204
204
|
|
205
|
-
|
205
|
+
demerit_points
|
206
206
|
end
|
207
207
|
|
208
|
-
def
|
208
|
+
def self.demerit_points_3_dangerous_patterns(modules)
|
209
209
|
demerit_points = 0
|
210
210
|
module_count = modules.size
|
211
211
|
|
@@ -213,12 +213,12 @@ module RQRCodeCore
|
|
213
213
|
modules.each do |row|
|
214
214
|
(module_count - 6).times do |col_idx|
|
215
215
|
if row[col_idx] &&
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
216
|
+
!row[col_idx + 1] &&
|
217
|
+
row[col_idx + 2] &&
|
218
|
+
row[col_idx + 3] &&
|
219
|
+
row[col_idx + 4] &&
|
220
|
+
!row[col_idx + 5] &&
|
221
|
+
row[col_idx + 6]
|
222
222
|
demerit_points += DEMERIT_POINTS_3
|
223
223
|
end
|
224
224
|
end
|
@@ -227,21 +227,21 @@ module RQRCodeCore
|
|
227
227
|
(0...module_count).each do |col|
|
228
228
|
(0...(module_count - 6)).each do |row|
|
229
229
|
if modules[row][col] &&
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
230
|
+
!modules[row + 1][col] &&
|
231
|
+
modules[row + 2][col] &&
|
232
|
+
modules[row + 3][col] &&
|
233
|
+
modules[row + 4][col] &&
|
234
|
+
!modules[row + 5][col] &&
|
235
|
+
modules[row + 6][col]
|
236
236
|
demerit_points += DEMERIT_POINTS_3
|
237
237
|
end
|
238
238
|
end
|
239
239
|
end
|
240
240
|
|
241
|
-
|
241
|
+
demerit_points
|
242
242
|
end
|
243
243
|
|
244
|
-
def
|
244
|
+
def self.demerit_points_4_dark_ratio(modules)
|
245
245
|
# level 4
|
246
246
|
dark_count = modules.reduce(0) do |sum, col|
|
247
247
|
sum + col.count(true)
|
@@ -250,8 +250,7 @@ module RQRCodeCore
|
|
250
250
|
ratio = dark_count / (modules.size * modules.size)
|
251
251
|
ratio_delta = (100 * ratio - 50).abs / 5
|
252
252
|
|
253
|
-
|
254
|
-
return demerit_points
|
253
|
+
ratio_delta * DEMERIT_POINTS_4
|
255
254
|
end
|
256
255
|
end
|
257
256
|
end
|