qrcode 0.0.1 → 0.1.1
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
- checksums.yaml.gz.sig +0 -0
- data/.github/copilot-instructions.md +20 -0
- data/.github/workflows/documentation-coverage.yaml +24 -0
- data/.github/workflows/documentation.yaml +57 -0
- data/.github/workflows/rubocop.yaml +21 -0
- data/.github/workflows/test-coverage.yaml +58 -0
- data/.github/workflows/test-external.yaml +33 -0
- data/.github/workflows/test.yaml +47 -0
- data/fixtures/qrcode/sample_data.rb +31 -0
- data/lib/qrcode/encoder/bit_buffer.rb +90 -0
- data/lib/qrcode/encoder/code.rb +436 -0
- data/lib/qrcode/encoder/constants.rb +75 -0
- data/lib/qrcode/encoder/error_correction_block.rb +318 -0
- data/lib/qrcode/encoder/math.rb +50 -0
- data/lib/qrcode/encoder/polynomial.rb +69 -0
- data/lib/qrcode/encoder/segment.rb +190 -0
- data/lib/qrcode/encoder/util.rb +269 -0
- data/lib/qrcode/encoder.rb +13 -0
- data/lib/qrcode/output/svg.rb +46 -0
- data/lib/qrcode/output/text.rb +62 -0
- data/lib/qrcode/version.rb +13 -2
- data/lib/qrcode.rb +23 -3
- data/license.md +54 -0
- data/readme.md +58 -0
- data/releases.md +14 -0
- data.tar.gz.sig +0 -0
- metadata +101 -63
- metadata.gz.sig +0 -0
- data/.gitignore +0 -17
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -22
- data/README.md +0 -29
- data/Rakefile +0 -1
- data/qrcode.gemspec +0 -35
@@ -0,0 +1,436 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2008-2025, by Duncan Robertson.
|
5
|
+
# Copyright, 2011, by Daniel Schierbeck.
|
6
|
+
# Copyright, 2011, by Gioele Barabucci.
|
7
|
+
# Copyright, 2012-2015, by Björn Blomqvist.
|
8
|
+
# Copyright, 2012, by xn.
|
9
|
+
# Copyright, 2013, by Yauhen Kharuzhy.
|
10
|
+
# Copyright, 2014, by Sean Doig.
|
11
|
+
# Copyright, 2015, by Tonči Damjanić.
|
12
|
+
# Copyright, 2015-2016, by Bjorn Blomqvist.
|
13
|
+
# Copyright, 2015-2016, by Fabio Napoleoni.
|
14
|
+
# Copyright, 2020, by Nathaniel Roman.
|
15
|
+
# Copyright, 2021, by Simon Schrape.
|
16
|
+
# Copyright, 2021, by Sam Sayer.
|
17
|
+
# Copyright, 2025, by Samuel Williams.
|
18
|
+
|
19
|
+
require_relative "constants"
|
20
|
+
require_relative "math"
|
21
|
+
require_relative "polynomial"
|
22
|
+
require_relative "util"
|
23
|
+
require_relative "bit_buffer"
|
24
|
+
require_relative "error_correction_block"
|
25
|
+
require_relative "segment"
|
26
|
+
|
27
|
+
module QRCode
|
28
|
+
module Encoder
|
29
|
+
# == Creation
|
30
|
+
#
|
31
|
+
# QRCode objects expect only one required constructor parameter
|
32
|
+
# and an optional hash of any other. Here's a few examples:
|
33
|
+
#
|
34
|
+
# qr = QRCode::Encoder::Code.new('hello world')
|
35
|
+
# qr = QRCode::Encoder::Code.new('hello world', size: 1, level: :m, mode: :alphanumeric)
|
36
|
+
#
|
37
|
+
class Code
|
38
|
+
attr_reader :modules, :module_count, :version, :segments
|
39
|
+
|
40
|
+
# Alias for module_count - the width/height of the QR code square
|
41
|
+
alias_method :size, :module_count
|
42
|
+
|
43
|
+
# Factory method to build QR code from data
|
44
|
+
# @parameter data [String, Array] The data to encode
|
45
|
+
# @parameter level [Symbol] Error correction level (:l, :m, :q, :h)
|
46
|
+
# @parameter mode [Symbol] Encoding mode (:auto, :number, :alphanumeric, :byte_8bit)
|
47
|
+
# @parameter size [Integer] QR code version (auto-detected if not specified)
|
48
|
+
# @parameter max_size [Integer] Maximum allowed version
|
49
|
+
def self.build(data, level: :h, mode: :auto, size: nil, max_size: nil)
|
50
|
+
segments = Segment.build(data, mode: mode)
|
51
|
+
new(segments, level: level, size: size, max_size: max_size)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Simple constructor that takes an array of segments
|
55
|
+
# @parameter segments [Array<Segment>] Array of segments to encode
|
56
|
+
# @parameter level [Symbol] Error correction level (:l, :m, :q, :h)
|
57
|
+
# @parameter size [Integer] QR code version (auto-detected if not specified)
|
58
|
+
# @parameter max_size [Integer] Maximum allowed version
|
59
|
+
def initialize(segments, level: :h, size: nil, max_size: nil)
|
60
|
+
@segments = Array(segments)
|
61
|
+
@error_correction_level = ERROR_CORRECTION_LEVEL[level]
|
62
|
+
|
63
|
+
unless @error_correction_level
|
64
|
+
raise ArgumentError, "Unknown error correction level `#{level.inspect}`"
|
65
|
+
end
|
66
|
+
|
67
|
+
max_size ||= Encoder::Util.max_size
|
68
|
+
calculated_size = size || minimum_version(limit: max_size)
|
69
|
+
|
70
|
+
if calculated_size > max_size
|
71
|
+
raise ArgumentError, "Given size greater than maximum possible size of #{max_size}"
|
72
|
+
end
|
73
|
+
|
74
|
+
@version = calculated_size
|
75
|
+
@module_count = @version * 4 + POSITION_PATTERN_LENGTH
|
76
|
+
@modules = Array.new(@module_count)
|
77
|
+
@data_cache = nil
|
78
|
+
make
|
79
|
+
end
|
80
|
+
|
81
|
+
# <tt>checked?</tt> is called with a +col+ and +row+ parameter. This will
|
82
|
+
# return true or false based on whether that coordinate exists in the
|
83
|
+
# matrix returned. It would normally be called while iterating through
|
84
|
+
# <tt>modules</tt>. A simple example would be:
|
85
|
+
#
|
86
|
+
# instance.checked?( 10, 10 ) => true
|
87
|
+
#
|
88
|
+
def checked?(row, col)
|
89
|
+
if !row.between?(0, @module_count - 1) || !col.between?(0, @module_count - 1)
|
90
|
+
raise RuntimeError, "Invalid row/column pair: #{row}, #{col}"
|
91
|
+
end
|
92
|
+
@modules[row][col]
|
93
|
+
end
|
94
|
+
|
95
|
+
# This is a public method that returns the QR Code you have
|
96
|
+
# generated as a string. It will not be able to be read
|
97
|
+
# in this format by a QR Code reader, but will give you an
|
98
|
+
# idea if the final outout. It takes two optional args
|
99
|
+
# +:dark+ and +:light+ which are there for you to choose
|
100
|
+
# how the output looks. Here's an example of it's use:
|
101
|
+
#
|
102
|
+
# instance.to_s =>
|
103
|
+
# xxxxxxx x x x x x xx xxxxxxx
|
104
|
+
# x x xxx xxxxxx xxx x x
|
105
|
+
# x xxx x xxxxx x xx x xxx x
|
106
|
+
#
|
107
|
+
# instance.to_s( dark: 'E', light: 'Q' ) =>
|
108
|
+
# EEEEEEEQEQQEQEQQQEQEQQEEQQEEEEEEE
|
109
|
+
# EQQQQQEQQEEEQQEEEEEEQEEEQQEQQQQQE
|
110
|
+
# EQEEEQEQQEEEEEQEQQQQQQQEEQEQEEEQE
|
111
|
+
#
|
112
|
+
def to_s(*args)
|
113
|
+
options = extract_options!(args)
|
114
|
+
dark = options[:dark] || "x"
|
115
|
+
light = options[:light] || " "
|
116
|
+
quiet_zone_size = options[:quiet_zone_size] || 0
|
117
|
+
|
118
|
+
rows = []
|
119
|
+
|
120
|
+
@modules.each do |row|
|
121
|
+
cols = light * quiet_zone_size
|
122
|
+
row.each do |col|
|
123
|
+
cols += (col ? dark : light)
|
124
|
+
end
|
125
|
+
rows << cols
|
126
|
+
end
|
127
|
+
|
128
|
+
quiet_zone_size.times do
|
129
|
+
rows.unshift(light * (rows.first.length / light.size))
|
130
|
+
rows << light * (rows.first.length / light.size)
|
131
|
+
end
|
132
|
+
rows.join("\n")
|
133
|
+
end
|
134
|
+
|
135
|
+
# Public overide as default inspect is very verbose
|
136
|
+
#
|
137
|
+
# QRCode::Encoder::Code.new('my string to generate', size: 4, level: :h)
|
138
|
+
# => QRCodeCore: @data='my string to generate', @error_correction_level=2, @version=4, @module_count=33
|
139
|
+
#
|
140
|
+
def inspect
|
141
|
+
"QRCodeCore: @segments=#{@segments.size} segments, @error_correction_level=#{@error_correction_level}, @version=#{@version}, @module_count=#{@module_count}"
|
142
|
+
end
|
143
|
+
|
144
|
+
# Return a symbol for current error connection level
|
145
|
+
def error_correction_level
|
146
|
+
ERROR_CORRECTION_LEVEL.invert[@error_correction_level]
|
147
|
+
end
|
148
|
+
|
149
|
+
# Return true if this QR Code includes multiple encoded segments
|
150
|
+
def multi_segment?
|
151
|
+
@segments.size > 1
|
152
|
+
end
|
153
|
+
|
154
|
+
# Return the primary mode used (first segment's mode)
|
155
|
+
def mode
|
156
|
+
@segments.first&.mode || :mode_8bit_byte
|
157
|
+
end
|
158
|
+
|
159
|
+
protected
|
160
|
+
|
161
|
+
def make # :nodoc:
|
162
|
+
prepare_common_patterns
|
163
|
+
make_impl(false, get_best_mask_pattern)
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def prepare_common_patterns # :nodoc:
|
169
|
+
@modules.map! {|row| Array.new(@module_count)}
|
170
|
+
|
171
|
+
place_position_probe_pattern(0, 0)
|
172
|
+
place_position_probe_pattern(@module_count - 7, 0)
|
173
|
+
place_position_probe_pattern(0, @module_count - 7)
|
174
|
+
place_position_adjust_pattern
|
175
|
+
place_timing_pattern
|
176
|
+
|
177
|
+
@common_patterns = @modules.map(&:clone)
|
178
|
+
end
|
179
|
+
|
180
|
+
def make_impl(test, mask_pattern) # :nodoc:
|
181
|
+
@modules = @common_patterns.map(&:clone)
|
182
|
+
|
183
|
+
place_format_info(test, mask_pattern)
|
184
|
+
place_version_info(test) if @version >= 7
|
185
|
+
|
186
|
+
if @data_cache.nil?
|
187
|
+
@data_cache = Code.create_data(
|
188
|
+
@version, @error_correction_level, @segments
|
189
|
+
)
|
190
|
+
end
|
191
|
+
|
192
|
+
map_data(@data_cache, mask_pattern)
|
193
|
+
end
|
194
|
+
|
195
|
+
def place_position_probe_pattern(row, col) # :nodoc:
|
196
|
+
(-1..7).each do |r|
|
197
|
+
next unless (row + r).between?(0, @module_count - 1)
|
198
|
+
|
199
|
+
(-1..7).each do |c|
|
200
|
+
next unless (col + c).between?(0, @module_count - 1)
|
201
|
+
|
202
|
+
is_vert_line = r.between?(0, 6) && (c == 0 || c == 6)
|
203
|
+
is_horiz_line = c.between?(0, 6) && (r == 0 || r == 6)
|
204
|
+
is_square = r.between?(2, 4) && c.between?(2, 4)
|
205
|
+
|
206
|
+
is_part_of_probe = is_vert_line || is_horiz_line || is_square
|
207
|
+
@modules[row + r][col + c] = is_part_of_probe
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def get_best_mask_pattern # :nodoc:
|
213
|
+
min_lost_point = 0
|
214
|
+
pattern = 0
|
215
|
+
|
216
|
+
8.times do |i|
|
217
|
+
make_impl(true, i)
|
218
|
+
lost_point = Encoder::Util.get_lost_points(modules)
|
219
|
+
|
220
|
+
if i == 0 || min_lost_point > lost_point
|
221
|
+
min_lost_point = lost_point
|
222
|
+
pattern = i
|
223
|
+
end
|
224
|
+
end
|
225
|
+
pattern
|
226
|
+
end
|
227
|
+
|
228
|
+
def place_timing_pattern # :nodoc:
|
229
|
+
(8...@module_count - 8).each do |i|
|
230
|
+
@modules[i][6] = @modules[6][i] = i % 2 == 0
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def place_position_adjust_pattern # :nodoc:
|
235
|
+
positions = Encoder::Util.get_pattern_positions(@version)
|
236
|
+
|
237
|
+
positions.each do |row|
|
238
|
+
positions.each do |col|
|
239
|
+
next unless @modules[row][col].nil?
|
240
|
+
|
241
|
+
(-2..2).each do |r|
|
242
|
+
(-2..2).each do |c|
|
243
|
+
is_part_of_pattern = r.abs == 2 || c.abs == 2 || (r == 0 && c == 0)
|
244
|
+
@modules[row + r][col + c] = is_part_of_pattern
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def place_version_info(test) # :nodoc:
|
252
|
+
bits = Encoder::Util.get_bch_version(@version)
|
253
|
+
|
254
|
+
18.times do |i|
|
255
|
+
mod = !test && ((bits >> i) & 1) == 1
|
256
|
+
@modules[(i / 3).floor][ i % 3 + @module_count - 8 - 3 ] = mod
|
257
|
+
@modules[i % 3 + @module_count - 8 - 3][ (i / 3).floor ] = mod
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def place_format_info(test, mask_pattern) # :nodoc:
|
262
|
+
data = (@error_correction_level << 3 | mask_pattern)
|
263
|
+
bits = Encoder::Util.get_bch_format_info(data)
|
264
|
+
|
265
|
+
FORMAT_INFO_LENGTH.times do |i|
|
266
|
+
mod = !test && ((bits >> i) & 1) == 1
|
267
|
+
|
268
|
+
# vertical
|
269
|
+
row = if i < 6
|
270
|
+
i
|
271
|
+
elsif i < 8
|
272
|
+
i + 1
|
273
|
+
else
|
274
|
+
@module_count - 15 + i
|
275
|
+
end
|
276
|
+
@modules[row][8] = mod
|
277
|
+
|
278
|
+
# horizontal
|
279
|
+
col = if i < 8
|
280
|
+
@module_count - i - 1
|
281
|
+
elsif i < 9
|
282
|
+
15 - i - 1 + 1
|
283
|
+
else
|
284
|
+
15 - i - 1
|
285
|
+
end
|
286
|
+
@modules[8][col] = mod
|
287
|
+
end
|
288
|
+
|
289
|
+
# fixed module
|
290
|
+
@modules[@module_count - 8][8] = !test
|
291
|
+
end
|
292
|
+
|
293
|
+
def map_data(data, mask_pattern) # :nodoc:
|
294
|
+
inc = -1
|
295
|
+
row = @module_count - 1
|
296
|
+
bit_index = 7
|
297
|
+
byte_index = 0
|
298
|
+
|
299
|
+
(@module_count - 1).step(1, -2) do |col|
|
300
|
+
col -= 1 if col <= 6
|
301
|
+
|
302
|
+
loop do
|
303
|
+
2.times do |c|
|
304
|
+
if @modules[row][col - c].nil?
|
305
|
+
dark = false
|
306
|
+
if byte_index < data.size && !data[byte_index].nil?
|
307
|
+
dark = ((Encoder::Util.rszf(data[byte_index], bit_index) & 1) == 1)
|
308
|
+
end
|
309
|
+
mask = Encoder::Util.get_mask(mask_pattern, row, col - c)
|
310
|
+
dark = !dark if mask
|
311
|
+
@modules[row][ col - c ] = dark
|
312
|
+
bit_index -= 1
|
313
|
+
|
314
|
+
if bit_index == -1
|
315
|
+
byte_index += 1
|
316
|
+
bit_index = 7
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
row += inc
|
322
|
+
|
323
|
+
if row < 0 || @module_count <= row
|
324
|
+
row -= inc
|
325
|
+
inc = -inc
|
326
|
+
break
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def minimum_version(limit: Encoder::Util.max_size, version: 1)
|
333
|
+
raise RuntimeError, "Data length exceed maximum capacity of version #{limit}" if version > limit
|
334
|
+
|
335
|
+
max_size_bits = MAX_BITS[error_correction_level][version - 1]
|
336
|
+
|
337
|
+
size_bits = @segments.sum {|segment| segment.bit_size(version)}
|
338
|
+
|
339
|
+
return version if size_bits < max_size_bits
|
340
|
+
|
341
|
+
minimum_version(limit: limit, version: version + 1)
|
342
|
+
end
|
343
|
+
|
344
|
+
def extract_options!(arr) # :nodoc:
|
345
|
+
arr.last.is_a?(::Hash) ? arr.pop : {}
|
346
|
+
end
|
347
|
+
|
348
|
+
class << self
|
349
|
+
def count_max_data_bits(rs_blocks) # :nodoc:
|
350
|
+
max_data_bytes = rs_blocks.reduce(0) do |sum, rs_block|
|
351
|
+
sum + rs_block.data_count
|
352
|
+
end
|
353
|
+
|
354
|
+
max_data_bytes * 8
|
355
|
+
end
|
356
|
+
|
357
|
+
def create_data(version, error_correction_level, segments) # :nodoc:
|
358
|
+
rs_blocks = Encoder::ErrorCorrectionBlock.for(version, error_correction_level)
|
359
|
+
max_data_bits = Code.count_max_data_bits(rs_blocks)
|
360
|
+
buffer = Encoder::BitBuffer.new(version)
|
361
|
+
|
362
|
+
segments.each {|segment| segment.write(buffer)}
|
363
|
+
buffer.end_of_message(max_data_bits)
|
364
|
+
|
365
|
+
if buffer.get_length_in_bits > max_data_bits
|
366
|
+
raise RuntimeError, "code length overflow. (#{buffer.get_length_in_bits}>#{max_data_bits}). (Try a larger size!)"
|
367
|
+
end
|
368
|
+
|
369
|
+
buffer.pad_until(max_data_bits)
|
370
|
+
|
371
|
+
Code.create_bytes(buffer, rs_blocks)
|
372
|
+
end
|
373
|
+
|
374
|
+
def create_bytes(buffer, rs_blocks) # :nodoc:
|
375
|
+
offset = 0
|
376
|
+
max_dc_count = 0
|
377
|
+
max_ec_count = 0
|
378
|
+
dcdata = Array.new(rs_blocks.size)
|
379
|
+
ecdata = Array.new(rs_blocks.size)
|
380
|
+
|
381
|
+
rs_blocks.each_with_index do |rs_block, r|
|
382
|
+
dc_count = rs_block.data_count
|
383
|
+
ec_count = rs_block.total_count - dc_count
|
384
|
+
max_dc_count = [max_dc_count, dc_count].max
|
385
|
+
max_ec_count = [max_ec_count, ec_count].max
|
386
|
+
|
387
|
+
dcdata_block = Array.new(dc_count)
|
388
|
+
dcdata_block.size.times do |i|
|
389
|
+
dcdata_block[i] = 0xff & buffer.buffer[i + offset]
|
390
|
+
end
|
391
|
+
dcdata[r] = dcdata_block
|
392
|
+
|
393
|
+
offset += dc_count
|
394
|
+
rs_poly = Encoder::Util.get_error_correct_polynomial(ec_count)
|
395
|
+
raw_poly = Encoder::Polynomial.new(dcdata[r], rs_poly.get_length - 1)
|
396
|
+
mod_poly = raw_poly.mod(rs_poly)
|
397
|
+
|
398
|
+
ecdata_block = Array.new(rs_poly.get_length - 1)
|
399
|
+
ecdata_block.size.times do |i|
|
400
|
+
mod_index = i + mod_poly.get_length - ecdata_block.size
|
401
|
+
ecdata_block[i] = (mod_index >= 0) ? mod_poly.get(mod_index) : 0
|
402
|
+
end
|
403
|
+
ecdata[r] = ecdata_block
|
404
|
+
end
|
405
|
+
|
406
|
+
total_code_count = rs_blocks.reduce(0) do |sum, rs_block|
|
407
|
+
sum + rs_block.total_count
|
408
|
+
end
|
409
|
+
|
410
|
+
data = Array.new(total_code_count)
|
411
|
+
index = 0
|
412
|
+
|
413
|
+
max_dc_count.times do |i|
|
414
|
+
rs_blocks.size.times do |r|
|
415
|
+
if i < dcdata[r].size
|
416
|
+
data[index] = dcdata[r][i]
|
417
|
+
index += 1
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
max_ec_count.times do |i|
|
423
|
+
rs_blocks.size.times do |r|
|
424
|
+
if i < ecdata[r].size
|
425
|
+
data[index] = ecdata[r][i]
|
426
|
+
index += 1
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
data
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
module QRCode
|
7
|
+
module Encoder
|
8
|
+
MODE = {
|
9
|
+
mode_number: 1 << 0, # 1 (binary: 0001)
|
10
|
+
mode_alpha_numk: 1 << 1, # 2 (binary: 0010)
|
11
|
+
mode_8bit_byte: 1 << 2 # 4 (binary: 0100)
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
MODE_NAME = {
|
15
|
+
number: :mode_number,
|
16
|
+
alphanumeric: :mode_alpha_numk,
|
17
|
+
byte_8bit: :mode_8bit_byte,
|
18
|
+
multi: :mode_multi
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
# Error correction levels as defined in ISO/IEC 18004 QR Code specification
|
22
|
+
# These exact numeric values are encoded in the format information of every QR code
|
23
|
+
# and are standardized across all QR code implementations worldwide.
|
24
|
+
#
|
25
|
+
# The seemingly random order (1,0,3,2) represents the official 2-bit binary encoding:
|
26
|
+
# L = 01 binary = 1 decimal (~7% error correction)
|
27
|
+
# M = 00 binary = 0 decimal (~15% error correction)
|
28
|
+
# Q = 11 binary = 3 decimal (~25% error correction)
|
29
|
+
# H = 10 binary = 2 decimal (~30% error correction)
|
30
|
+
#
|
31
|
+
# These values MUST NOT be changed as they are part of the global QR Code standard.
|
32
|
+
ERROR_CORRECTION_LEVEL = {
|
33
|
+
l: 1, # Low - ~7% of codewords can be restored
|
34
|
+
m: 0, # Medium - ~15% of codewords can be restored
|
35
|
+
q: 3, # Quartile - ~25% of codewords can be restored
|
36
|
+
h: 2 # High - ~30% of codewords can be restored
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
MASK_PATTERN = {
|
40
|
+
pattern000: 0,
|
41
|
+
pattern001: 1,
|
42
|
+
pattern010: 2,
|
43
|
+
pattern011: 3,
|
44
|
+
pattern100: 4,
|
45
|
+
pattern101: 5,
|
46
|
+
pattern110: 6,
|
47
|
+
pattern111: 7
|
48
|
+
}.freeze
|
49
|
+
|
50
|
+
MASK_COMPUTATIONS = [
|
51
|
+
proc {|i, j| (i + j) % 2 == 0},
|
52
|
+
proc {|i, j| i % 2 == 0},
|
53
|
+
proc {|i, j| j % 3 == 0},
|
54
|
+
proc {|i, j| (i + j) % 3 == 0},
|
55
|
+
proc {|i, j| ((i / 2) + (j / 3)) % 2 == 0},
|
56
|
+
proc {|i, j| ((i * j) % 2) + ((i * j) % 3) == 0},
|
57
|
+
proc {|i, j| (((i * j) % 2) + ((i * j) % 3)) % 2 == 0},
|
58
|
+
proc {|i, j| (((i * j) % 3) + ((i + j) % 2)) % 2 == 0}
|
59
|
+
].freeze
|
60
|
+
|
61
|
+
POSITION_PATTERN_LENGTH = (7 + 1) * 2 + 1
|
62
|
+
FORMAT_INFO_LENGTH = 15
|
63
|
+
|
64
|
+
# max bits by version
|
65
|
+
# version 1: 26 x 26 = 676
|
66
|
+
# version 40: 177 x 177 = 31329
|
67
|
+
# 31329 / 8 = 3916 bytes
|
68
|
+
MAX_BITS = {
|
69
|
+
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, 10208, 10960, 11744, 12248, 13048, 13880, 14744, 15640, 16568, 17528, 18448, 19472, 20528, 21616, 22496, 23648],
|
70
|
+
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, 10136, 10984, 11640, 12328, 13048, 13800, 14496, 15312, 15936, 16816, 17728, 18672],
|
71
|
+
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],
|
72
|
+
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, 10208]
|
73
|
+
}.freeze
|
74
|
+
end
|
75
|
+
end
|