qr_reader 0.0.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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +16 -0
  3. data/LICENSE.txt +21 -0
  4. data/NOTICE +14 -0
  5. data/README.md +222 -0
  6. data/exe/qr_reader +66 -0
  7. data/lib/qr_reader/binarizer.rb +170 -0
  8. data/lib/qr_reader/bit_matrix.rb +120 -0
  9. data/lib/qr_reader/decoder/bitstream_parser.rb +307 -0
  10. data/lib/qr_reader/decoder/character_set.rb +163 -0
  11. data/lib/qr_reader/decoder/codewords.rb +164 -0
  12. data/lib/qr_reader/decoder/data_mask.rb +57 -0
  13. data/lib/qr_reader/decoder/format_reader.rb +204 -0
  14. data/lib/qr_reader/decoder/function_patterns.rb +62 -0
  15. data/lib/qr_reader/decoder/galois_field.rb +69 -0
  16. data/lib/qr_reader/decoder/reed_solomon.rb +312 -0
  17. data/lib/qr_reader/decoder.rb +241 -0
  18. data/lib/qr_reader/detector/alignment_grid.rb +127 -0
  19. data/lib/qr_reader/detector/alignment_pattern_finder.rb +191 -0
  20. data/lib/qr_reader/detector/candidate/dimension.rb +237 -0
  21. data/lib/qr_reader/detector/candidate/strokes.rb +126 -0
  22. data/lib/qr_reader/detector/candidate.rb +436 -0
  23. data/lib/qr_reader/detector/finder_pattern.rb +63 -0
  24. data/lib/qr_reader/detector/finder_pattern_finder.rb +199 -0
  25. data/lib/qr_reader/detector/geometry.rb +118 -0
  26. data/lib/qr_reader/detector/grid_sampler.rb +350 -0
  27. data/lib/qr_reader/detector/pattern_sets.rb +235 -0
  28. data/lib/qr_reader/detector/perspective_transform.rb +117 -0
  29. data/lib/qr_reader/detector/pure_detector.rb +197 -0
  30. data/lib/qr_reader/detector.rb +215 -0
  31. data/lib/qr_reader/errors.rb +14 -0
  32. data/lib/qr_reader/format_information.rb +79 -0
  33. data/lib/qr_reader/formats/bmp.rb +434 -0
  34. data/lib/qr_reader/formats/ccitt.rb +81 -0
  35. data/lib/qr_reader/formats/gif.rb +272 -0
  36. data/lib/qr_reader/formats/jpeg.rb +152 -0
  37. data/lib/qr_reader/formats/pdf.rb +2935 -0
  38. data/lib/qr_reader/formats/png.rb +311 -0
  39. data/lib/qr_reader/formats/pnm.rb +286 -0
  40. data/lib/qr_reader/formats/tiff.rb +1345 -0
  41. data/lib/qr_reader/formats.rb +279 -0
  42. data/lib/qr_reader/image.rb +231 -0
  43. data/lib/qr_reader/kernels/binarizer.rb +475 -0
  44. data/lib/qr_reader/kernels/ccitt.rb +554 -0
  45. data/lib/qr_reader/kernels/detector.rb +587 -0
  46. data/lib/qr_reader/kernels/image.rb +65 -0
  47. data/lib/qr_reader/kernels/jpeg.rb +3421 -0
  48. data/lib/qr_reader/kernels/lzw.rb +173 -0
  49. data/lib/qr_reader/kernels/pixels.rb +116 -0
  50. data/lib/qr_reader/kernels/png.rb +1138 -0
  51. data/lib/qr_reader/kernels.rb +14 -0
  52. data/lib/qr_reader/native_kernels.rb +130 -0
  53. data/lib/qr_reader/reader.rb +348 -0
  54. data/lib/qr_reader/result.rb +49 -0
  55. data/lib/qr_reader/symbol_version.rb +173 -0
  56. data/lib/qr_reader/version.rb +5 -0
  57. data/lib/qr_reader.rb +77 -0
  58. metadata +103 -0
@@ -0,0 +1,307 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QrReader
4
+ module Decoder
5
+ # Raised by BitstreamParser for data that violates the bit stream syntax.
6
+ # Decoder.decode turns it into a nil result.
7
+ class FormatError < Error; end
8
+
9
+ # Decoded data of one symbol, before it is wrapped into a Result (same
10
+ # meaning as the Result attributes of the same names).
11
+ Content = Data.define(:text, :raw_bytes, :segments, :eci, :fnc1, :structured_append, :symbology_identifier)
12
+
13
+ # Parses the data codewords of a symbol (ISO/IEC 18004 §7.4): a sequence
14
+ # of segments, each a 4-bit mode indicator followed by mode-specific data,
15
+ # ended by the terminator 0000 or by running out of bits (fewer than 4 left).
16
+ #
17
+ # BitstreamParser.parse("\x40\x24\x86\x90\xec".b, 1).text # => "Hi" (byte mode, 2 chars)
18
+ class BitstreamParser
19
+ TERMINATOR = 0x0
20
+ NUMERIC = 0x1
21
+ ALPHANUMERIC = 0x2
22
+ STRUCTURED_APPEND = 0x3
23
+ BYTE = 0x4
24
+ FNC1_FIRST = 0x5
25
+ ECI = 0x7
26
+ KANJI = 0x8
27
+ FNC1_SECOND = 0x9
28
+ HANZI = 0xD
29
+
30
+ # Character count indicator lengths for versions 1-9, 10-26 and 27-40.
31
+ COUNT_BITS = {
32
+ NUMERIC => [10, 12, 14], ALPHANUMERIC => [9, 11, 13], BYTE => [8, 16, 16],
33
+ KANJI => [8, 10, 12], HANZI => [8, 10, 12]
34
+ }.freeze
35
+
36
+ ALPHANUMERIC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".b.freeze
37
+ # Decoded text for every valid 10-bit numeric group ("000".."999").
38
+ DIGIT_TRIPLES = ("000".."999").map(&:freeze).freeze
39
+ GB2312_SUBSET = 1
40
+ GROUP_SEPARATOR = "\x1D".b.freeze
41
+ ASCII = Encoding::US_ASCII
42
+
43
+ # Parses +data+ (binary String of data codewords) of a symbol of version
44
+ # +version+ (1..40). Returns a Content; raises FormatError.
45
+ def self.parse(data, version)
46
+ new(data, version).parse
47
+ end
48
+
49
+ def initialize(data, version)
50
+ raise ArgumentError, "version must be 1..40" unless version.is_a?(Integer) && version.between?(1, 40)
51
+
52
+ @data = data.b + "\0\0" # padding so a 24-bit window never reads past the end
53
+ @limit = data.bytesize * 8
54
+ @pos = 0
55
+ @size_class = if version <= 9 then 0
56
+ elsif version <= 26 then 1
57
+ else 2
58
+ end
59
+ end
60
+
61
+ def parse
62
+ segments = [] # [mode, bytes, eci designator or nil]
63
+ eci = nil
64
+ first_eci = nil
65
+ fnc1 = nil
66
+ structured_append = nil
67
+ while @limit - @pos >= 4
68
+ mode = read_bits(4)
69
+ case mode
70
+ when TERMINATOR
71
+ break
72
+ when NUMERIC
73
+ segments << [:numeric, numeric(read_count(mode)), eci]
74
+ when ALPHANUMERIC
75
+ segments << [:alphanumeric, alphanumeric(read_count(mode), fnc1), eci]
76
+ when BYTE
77
+ segments << [:byte, byte(read_count(mode)), eci]
78
+ when KANJI
79
+ segments << [:kanji, kanji(read_count(mode)), eci]
80
+ when HANZI
81
+ subset = read_bits(4)
82
+ count = read_count(mode)
83
+ raise FormatError, "unsupported Hanzi subset #{subset}" unless subset == GB2312_SUBSET
84
+
85
+ segments << [:hanzi, hanzi(count), eci]
86
+ when ECI
87
+ eci = read_eci
88
+ first_eci ||= eci
89
+ when FNC1_FIRST
90
+ fnc1 = :first
91
+ when FNC1_SECOND
92
+ fnc1 = [:second, application_indicator]
93
+ when STRUCTURED_APPEND
94
+ sequence = read_bits(8)
95
+ structured_append = StructuredAppend.new(index: sequence >> 4, count: (sequence & 0x0F) + 1,
96
+ parity: read_bits(8))
97
+ else
98
+ raise FormatError, format("invalid mode indicator %04b", mode)
99
+ end
100
+ end
101
+ build_content(segments, first_eci, fnc1, structured_append)
102
+ end
103
+
104
+ private
105
+
106
+ def available
107
+ @limit - @pos
108
+ end
109
+
110
+ # Next +count+ (<= 17) bits as an Integer, most significant first.
111
+ def read_bits(count)
112
+ pos = @pos
113
+ raise FormatError, "bit stream ends early" if pos + count > @limit
114
+
115
+ data = @data
116
+ index = pos >> 3
117
+ window = (data.getbyte(index) << 16) | (data.getbyte(index + 1) << 8) | data.getbyte(index + 2)
118
+ @pos = pos + count
119
+ (window >> (24 - (pos & 7) - count)) & ((1 << count) - 1)
120
+ end
121
+
122
+ def read_count(mode)
123
+ read_bits(COUNT_BITS[mode][@size_class])
124
+ end
125
+
126
+ def require_bits(count)
127
+ raise FormatError, "segment longer than the remaining data" if count > available
128
+ end
129
+
130
+ # 3 digits per 10 bits; a remainder of 2 digits takes 7 bits, 1 digit 4 bits.
131
+ def numeric(count)
132
+ require_bits(count / 3 * 10 + [0, 4, 7][count % 3])
133
+ out = "".b
134
+ while count >= 3
135
+ value = read_bits(10)
136
+ raise FormatError, "invalid numeric group #{value}" if value >= 1000
137
+
138
+ out << DIGIT_TRIPLES[value]
139
+ count -= 3
140
+ end
141
+ if count == 2
142
+ value = read_bits(7)
143
+ raise FormatError, "invalid numeric group #{value}" if value >= 100
144
+
145
+ out << DIGIT_TRIPLES[value].byteslice(1, 2)
146
+ elsif count == 1
147
+ value = read_bits(4)
148
+ raise FormatError, "invalid numeric digit #{value}" if value >= 10
149
+
150
+ out << DIGIT_TRIPLES[value].byteslice(2, 1)
151
+ end
152
+ out
153
+ end
154
+
155
+ # 2 characters per 11 bits, a final odd character in 6 bits. With FNC1
156
+ # in effect, "%%" stands for "%" and a lone "%" for the GS separator.
157
+ def alphanumeric(count, fnc1)
158
+ require_bits(count / 2 * 11 + (count.odd? ? 6 : 0))
159
+ out = "".b
160
+ while count >= 2
161
+ value = read_bits(11)
162
+ raise FormatError, "invalid alphanumeric pair #{value}" if value >= 2025
163
+
164
+ out << ALPHANUMERIC_CHARS.getbyte(value / 45) << ALPHANUMERIC_CHARS.getbyte(value % 45)
165
+ count -= 2
166
+ end
167
+ if count == 1
168
+ value = read_bits(6)
169
+ raise FormatError, "invalid alphanumeric character #{value}" if value >= 45
170
+
171
+ out << ALPHANUMERIC_CHARS.getbyte(value)
172
+ end
173
+ out = out.gsub(/%%?/n) { |m| m.bytesize == 2 ? "%" : GROUP_SEPARATOR } if fnc1 && out.include?("%")
174
+ out
175
+ end
176
+
177
+ def byte(count)
178
+ require_bits(count * 8)
179
+ start = @pos
180
+ shift = start & 7
181
+ @pos = start + count * 8
182
+ return @data.byteslice(start >> 3, count) if shift.zero?
183
+
184
+ # Unaligned: re-pack through a "0101..." string, at C speed.
185
+ [@data.byteslice(start >> 3, count + 1).unpack1("B*").byteslice(shift, count * 8)].pack("B*")
186
+ end
187
+
188
+ # 13 bits per character: the Shift_JIS code minus 0x8140 or 0xC140,
189
+ # high byte times 0xC0 plus low byte.
190
+ def kanji(count)
191
+ require_bits(count * 13)
192
+ out = "".b
193
+ while count.positive?
194
+ value = read_bits(13)
195
+ code = ((value / 0xC0) << 8) | (value % 0xC0)
196
+ code += code < 0x1F00 ? 0x8140 : 0xC140
197
+ out << (code >> 8) << (code & 0xFF)
198
+ count -= 1
199
+ end
200
+ out
201
+ end
202
+
203
+ # 13 bits per character: the GB 2312 code minus 0xA1A1 or 0xA6A1,
204
+ # high byte times 0x60 plus low byte.
205
+ def hanzi(count)
206
+ require_bits(count * 13)
207
+ out = "".b
208
+ while count.positive?
209
+ value = read_bits(13)
210
+ code = ((value / 0x60) << 8) | (value % 0x60)
211
+ code += code < 0x0A00 ? 0xA1A1 : 0xA6A1
212
+ out << ((code >> 8) & 0xFF) << (code & 0xFF)
213
+ count -= 1
214
+ end
215
+ out
216
+ end
217
+
218
+ # ECI designator in 1, 2 or 3 bytes (0xxxxxxx, 10xxxxxx +8, 110xxxxx +16).
219
+ def read_eci
220
+ first = read_bits(8)
221
+ value = if (first & 0x80).zero?
222
+ first
223
+ elsif (first & 0xC0) == 0x80
224
+ ((first & 0x3F) << 8) | read_bits(8)
225
+ elsif (first & 0xE0) == 0xC0
226
+ ((first & 0x1F) << 16) | read_bits(16)
227
+ else
228
+ raise FormatError, "invalid ECI designator"
229
+ end
230
+ raise FormatError, "ECI designator #{value} out of range" if value > 999_999
231
+
232
+ value
233
+ end
234
+
235
+ # FNC1 in second position is followed by an application indicator:
236
+ # two digits (00-99) or one letter (encoded as its ASCII value + 100).
237
+ def application_indicator
238
+ value = read_bits(8)
239
+ return format("%02d", value) if value < 100
240
+ return (value - 100).chr if value.between?(165, 190) || value.between?(197, 222)
241
+
242
+ raise FormatError, "invalid FNC1 application indicator #{value}"
243
+ end
244
+
245
+ def build_content(segments, first_eci, fnc1, structured_append)
246
+ guessed = guess_encoding(segments) unless first_eci
247
+ resolved = segments.map do |mode, bytes, eci|
248
+ [mode, bytes, eci, segment_encoding(mode, eci, first_eci, guessed)]
249
+ end
250
+ text = +""
251
+ bytes = "".b
252
+ segment_list = []
253
+ run_start = 0
254
+ resolved.each_with_index do |(mode, data, eci, encoding), i|
255
+ bytes << data
256
+ segment_list << Segment.new(mode: mode, text: CharacterSet.to_utf8(data, encoding), eci: eci)
257
+ next_encoding = resolved[i + 1]&.last
258
+ next if next_encoding == encoding
259
+
260
+ # Convert runs of segments in the same encoding together, so a
261
+ # character split across segments survives.
262
+ text << if run_start == i
263
+ segment_list[i].text
264
+ else
265
+ CharacterSet.to_utf8(resolved[run_start..i].map { |s| s[1] }.join, encoding)
266
+ end
267
+ run_start = i + 1
268
+ end
269
+ Content.new(text: text, raw_bytes: bytes, segments: segment_list.freeze, eci: first_eci, fnc1: fnc1,
270
+ structured_append: structured_append,
271
+ symbology_identifier: "]Q#{symbology_modifier(first_eci, fnc1)}")
272
+ end
273
+
274
+ # ISO/IEC 18004 Table F.1: 1 plain, 3 FNC1 first, 5 FNC1 second;
275
+ # one more when an ECI is present.
276
+ def symbology_modifier(eci, fnc1)
277
+ base = if fnc1 == :first then 3
278
+ elsif fnc1 then 5
279
+ else 1
280
+ end
281
+ eci ? base + 1 : base
282
+ end
283
+
284
+ # Without any ECI, all byte segments share one guessed encoding.
285
+ def guess_encoding(segments)
286
+ data = segments.filter_map { |mode, bytes, _| bytes if mode == :byte }
287
+ data.empty? ? nil : CharacterSet.guess(data.join)
288
+ end
289
+
290
+ def segment_encoding(mode, eci, first_eci, guessed)
291
+ case mode
292
+ when :numeric, :alphanumeric then ASCII
293
+ when :kanji then CharacterSet::SHIFT_JIS
294
+ when :hanzi then CharacterSet::GB18030
295
+ else
296
+ if eci
297
+ CharacterSet.for_eci(eci) || CharacterSet::ISO_8859_1
298
+ elsif first_eci
299
+ CharacterSet::ISO_8859_1 # default ECI 000003 before the first ECI
300
+ else
301
+ guessed
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QrReader
4
+ module Decoder
5
+ # Character sets: ECI designators to Ruby encodings, the encoding guess
6
+ # for byte segments without ECI, and conversion to valid UTF-8.
7
+ module CharacterSet
8
+ UTF_8 = Encoding::UTF_8
9
+ BINARY = Encoding::BINARY
10
+ ISO_8859_1 = Encoding::ISO_8859_1
11
+ SHIFT_JIS = Encoding::Shift_JIS
12
+ # Superset of Shift_JIS (NEC/IBM extensions); used when strict Shift_JIS
13
+ # cannot represent a character.
14
+ WINDOWS_31J = Encoding::Windows_31J
15
+ # Superset of GB 2312 (EUC-CN), used for Hanzi mode.
16
+ GB18030 = Encoding::GB18030
17
+
18
+ # ECI designator => Encoding (ISO/IEC 18004 / AIM ECI assignments as used
19
+ # by ZXing). 899 is "binary": the bytes are shown as ISO-8859-1.
20
+ ECI_ENCODINGS = {
21
+ 0 => Encoding::IBM437, 2 => Encoding::IBM437,
22
+ 1 => ISO_8859_1, 3 => ISO_8859_1,
23
+ 4 => Encoding::ISO_8859_2, 5 => Encoding::ISO_8859_3, 6 => Encoding::ISO_8859_4,
24
+ 7 => Encoding::ISO_8859_5, 8 => Encoding::ISO_8859_6, 9 => Encoding::ISO_8859_7,
25
+ 10 => Encoding::ISO_8859_8, 11 => Encoding::ISO_8859_9, 12 => Encoding::ISO_8859_10,
26
+ 13 => Encoding::ISO_8859_11, 15 => Encoding::ISO_8859_13, 16 => Encoding::ISO_8859_14,
27
+ 17 => Encoding::ISO_8859_15, 18 => Encoding::ISO_8859_16,
28
+ 20 => SHIFT_JIS,
29
+ 21 => Encoding::Windows_1250, 22 => Encoding::Windows_1251,
30
+ 23 => Encoding::Windows_1252, 24 => Encoding::Windows_1256,
31
+ 25 => Encoding::UTF_16BE, 26 => UTF_8,
32
+ 27 => Encoding::US_ASCII, 170 => Encoding::US_ASCII,
33
+ 28 => Encoding::Big5, 29 => GB18030, 30 => Encoding::EUC_KR,
34
+ 899 => BINARY
35
+ }.freeze
36
+
37
+ # Decodes one character that Shift_JIS cannot map, via Windows-31J.
38
+ WINDOWS_31J_FALLBACK = lambda do |char|
39
+ char.dup.force_encoding(WINDOWS_31J).encode(UTF_8, invalid: :replace, undef: :replace)
40
+ end
41
+
42
+ # GB 18030-2000 private-use code points (from the 2-byte codes A6D9..A6DF,
43
+ # A6EC, A6ED, A6F3 and A8BC) and their GB 18030-2005 replacements.
44
+ GB18030_PUA = "\uE78D\uE78E\uE78F\uE790\uE791\uE792\uE793\uE794\uE795\uE796\uE7C7"
45
+ GB18030_2005 = "\uFE10\uFE12\uFE11\uFE13\uFE14\uFE15\uFE16\uFE17\uFE18\uFE19\u1E3F"
46
+
47
+ module_function
48
+
49
+ # Encoding for an ECI designator, or nil if it names no known character set.
50
+ def for_eci(designator)
51
+ ECI_ENCODINGS[designator]
52
+ end
53
+
54
+ # Guesses the encoding of byte-mode data without ECI, following ZXing's
55
+ # StringUtils.guessCharset: UTF-16 with BOM; UTF-8 if the bytes are valid
56
+ # UTF-8 (strictly) containing multibyte characters; Shift_JIS if the bytes
57
+ # look like it (runs of 3+ double-byte or half-width katakana characters,
58
+ # or many Latin-1 symbols); else ISO-8859-1. Like zxing-cpp, control
59
+ # characters other than CR/LF mark the data as binary, not Shift_JIS,
60
+ # so that it decodes losslessly as ISO-8859-1.
61
+ def guess(bytes)
62
+ return ISO_8859_1 if bytes.ascii_only?
63
+
64
+ if bytes.bytesize > 2
65
+ b0 = bytes.getbyte(0)
66
+ b1 = bytes.getbyte(1)
67
+ return Encoding::UTF_16BE if b0 == 0xFE && b1 == 0xFF
68
+ return Encoding::UTF_16LE if b0 == 0xFF && b1 == 0xFE
69
+ end
70
+ return UTF_8 if bytes.dup.force_encoding(UTF_8).valid_encoding?
71
+
72
+ guess_latin1_or_shift_jis(bytes)
73
+ end
74
+
75
+ # Converts +bytes+ in +encoding+ to a valid UTF-8 String (invalid or
76
+ # unmappable sequences become U+FFFD).
77
+ def to_utf8(bytes, encoding)
78
+ return bytes.dup.force_encoding(UTF_8) if bytes.ascii_only? && encoding.ascii_compatible?
79
+
80
+ case encoding
81
+ when UTF_8
82
+ bytes.dup.force_encoding(UTF_8).scrub
83
+ when BINARY
84
+ bytes.encode(UTF_8, ISO_8859_1)
85
+ when SHIFT_JIS
86
+ # Characters Shift_JIS lacks (NEC/IBM extensions) are taken from
87
+ # Windows-31J one by one, so the rest keep their JIS X 0208 mapping.
88
+ # Ruby maps 0x815C to U+2014 EM DASH; ZXing and zxing-cpp (like
89
+ # Unicode's SHIFTJIS.TXT and Windows-31J) to U+2015 HORIZONTAL BAR.
90
+ bytes.encode(UTF_8, SHIFT_JIS, invalid: :replace, fallback: WINDOWS_31J_FALLBACK).tr("\u2014", "\u2015")
91
+ when GB18030
92
+ # Ruby uses the GB 18030-2000 private-use mappings for 11 GBK codes
93
+ # that GB 18030-2005 (and zxing-cpp) map to standard characters.
94
+ bytes.encode(UTF_8, GB18030, invalid: :replace, undef: :replace).tr(GB18030_PUA, GB18030_2005)
95
+ when Encoding::UTF_16BE, Encoding::UTF_16LE
96
+ text = bytes.encode(UTF_8, encoding, invalid: :replace, undef: :replace)
97
+ text.delete_prefix("\uFEFF")
98
+ else
99
+ bytes.encode(UTF_8, encoding, invalid: :replace, undef: :replace)
100
+ end
101
+ end
102
+
103
+ # ZXing's Latin-1 vs. Shift_JIS heuristics (bytes known not to be UTF-8).
104
+ def guess_latin1_or_shift_jis(bytes)
105
+ can_be_latin1 = true
106
+ can_be_sjis = true
107
+ sjis_bytes_left = 0
108
+ katakana_chars = 0
109
+ katakana_run = 0
110
+ double_byte_run = 0
111
+ max_katakana_run = 0
112
+ max_double_byte_run = 0
113
+ latin1_high_other = 0
114
+ bytes.each_byte do |v|
115
+ if can_be_latin1
116
+ if v > 0x7F && v < 0xA0
117
+ can_be_latin1 = false
118
+ elsif v > 0x9F && (v < 0xC0 || v == 0xD7 || v == 0xF7)
119
+ latin1_high_other += 1
120
+ end
121
+ end
122
+ if can_be_sjis
123
+ if sjis_bytes_left.positive?
124
+ if v < 0x40 || v == 0x7F || v > 0xFC
125
+ can_be_sjis = false
126
+ else
127
+ sjis_bytes_left -= 1
128
+ end
129
+ elsif v == 0x80 || v == 0xA0 || v > 0xEF || (v < 0x20 && v != 0x0A && v != 0x0D)
130
+ can_be_sjis = false
131
+ elsif v > 0xA0 && v < 0xE0
132
+ katakana_chars += 1
133
+ double_byte_run = 0
134
+ katakana_run += 1
135
+ max_katakana_run = katakana_run if katakana_run > max_katakana_run
136
+ elsif v > 0x7F
137
+ sjis_bytes_left += 1
138
+ katakana_run = 0
139
+ double_byte_run += 1
140
+ max_double_byte_run = double_byte_run if double_byte_run > max_double_byte_run
141
+ else
142
+ katakana_run = 0
143
+ double_byte_run = 0
144
+ end
145
+ end
146
+ break unless can_be_latin1 || can_be_sjis
147
+ end
148
+ can_be_sjis = false if sjis_bytes_left.positive?
149
+
150
+ return SHIFT_JIS if can_be_sjis && (max_katakana_run >= 3 || max_double_byte_run >= 3)
151
+
152
+ if can_be_latin1 && can_be_sjis
153
+ return SHIFT_JIS if (max_katakana_run == 2 && katakana_chars == 2) || latin1_high_other * 10 >= bytes.bytesize
154
+
155
+ return ISO_8859_1
156
+ end
157
+ return SHIFT_JIS if can_be_sjis && !can_be_latin1
158
+
159
+ ISO_8859_1
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QrReader
4
+ module Decoder
5
+ # Codeword placement (ISO/IEC 18004 §7.7.3) and block structure.
6
+ #
7
+ # Everything that depends only on the version is computed once and cached
8
+ # in a Layout: the module indices of all data bits in zig-zag placement
9
+ # order, the data mask as one XOR value per codeword, and per EC level
10
+ # the raw codeword indices of every RS block. Reading a block is then a
11
+ # tight loop of getbyte calls on the module grid.
12
+ module Codewords
13
+ # Precomputed tables for one symbol version.
14
+ class Layout
15
+ # +order+: module indices of the data bits in placement order, 8 per
16
+ # codeword. +mirrored_order+: the same for a symbol stored transposed
17
+ # (mirrored), where module (x, y) lives at x * n + y.
18
+ attr_reader :version, :dimension, :order, :mirrored_order
19
+
20
+ def initialize(version)
21
+ @version = version
22
+ @dimension = version.dimension
23
+ @order, @mirrored_order = build_orders
24
+ @mask_codewords = Array.new(8)
25
+ @blocks = {}
26
+ end
27
+
28
+ # One Integer per codeword: the bits that data mask +mask+ flips,
29
+ # read from the mask's module grid like the data itself.
30
+ def mask_codewords(mask)
31
+ @mask_codewords[mask] ||= begin
32
+ count = @order.size / 8
33
+ Codewords.read_block(DataMask.grid(mask, @dimension), @order, Array.new(count, 0),
34
+ Array.new(count) { |k| k }).freeze
35
+ end
36
+ end
37
+
38
+ # RS blocks for EC level +level+ in block order: Array of
39
+ # [data_codeword_count, raw_codeword_indices] (data then EC codewords).
40
+ # Undoes the interleaving of §7.6: data codewords are taken from every
41
+ # block in turn (shorter blocks run out first), then EC codewords.
42
+ def blocks(level)
43
+ @blocks[level] ||= build_blocks(@version.ec_blocks(level))
44
+ end
45
+
46
+ private
47
+
48
+ # Undoes the interleaving of §7.6 (plain loops: runs once per level).
49
+ def build_blocks(ec_blocks)
50
+ data_counts = ec_blocks.data_codewords_per_block
51
+ count = data_counts.size
52
+ indices = Array.new(count) { [] }
53
+ raw = 0
54
+ longest = data_counts.max
55
+ i = 0
56
+ while i < longest
57
+ b = 0
58
+ while b < count
59
+ if i < data_counts[b]
60
+ indices[b] << raw
61
+ raw += 1
62
+ end
63
+ b += 1
64
+ end
65
+ i += 1
66
+ end
67
+ i = 0
68
+ while i < ec_blocks.ec_codewords_per_block
69
+ b = 0
70
+ while b < count
71
+ indices[b] << raw
72
+ raw += 1
73
+ b += 1
74
+ end
75
+ i += 1
76
+ end
77
+ blocks = []
78
+ b = 0
79
+ while b < count
80
+ blocks << [data_counts[b], indices[b].freeze].freeze
81
+ b += 1
82
+ end
83
+ blocks.freeze
84
+ end
85
+
86
+ # Zig-zag over column pairs from the right, alternately upwards and
87
+ # downwards, skipping the vertical timing column and function modules
88
+ # (same traversal as ZXing's BitMatrixParser#readCodewords). Returns
89
+ # the normal and the mirrored order; remainder bits are dropped.
90
+ def build_orders
91
+ n = @dimension
92
+ function = FunctionPatterns.for_version(@version.number)
93
+ order = []
94
+ mirrored = []
95
+ upward = true
96
+ x = n - 1
97
+ while x.positive?
98
+ x -= 1 if x == 6
99
+ count = 0
100
+ while count < n
101
+ y = upward ? n - 1 - count : count
102
+ index = y * n + x
103
+ if function.getbyte(index).zero?
104
+ order << index
105
+ mirrored << x * n + y
106
+ end
107
+ if function.getbyte(index - 1).zero?
108
+ order << index - 1
109
+ mirrored << (x - 1) * n + y
110
+ end
111
+ count += 1
112
+ end
113
+ upward = !upward
114
+ x -= 2
115
+ end
116
+ limit = @version.total_codewords * 8
117
+ [order.first(limit).freeze, mirrored.first(limit).freeze]
118
+ end
119
+ end
120
+
121
+ @layouts = Array.new(41)
122
+
123
+ class << self
124
+ # Layout for version +number+ (1..40), built on first use.
125
+ def layout(number)
126
+ @layouts[number] ||= Layout.new(SymbolVersion[number])
127
+ end
128
+
129
+ # Reads and unmasks the codewords with the given raw indices from
130
+ # +bits+ (a symbol's module String with only 0/1 bytes).
131
+ def read_block(bits, order, mask_codewords, indices)
132
+ count = indices.size
133
+ out = Array.new(count)
134
+ k = 0
135
+ while k < count
136
+ raw = indices[k]
137
+ i = raw << 3
138
+ out[k] = ((bits.getbyte(order[i]) << 7) | (bits.getbyte(order[i + 1]) << 6) |
139
+ (bits.getbyte(order[i + 2]) << 5) | (bits.getbyte(order[i + 3]) << 4) |
140
+ (bits.getbyte(order[i + 4]) << 3) | (bits.getbyte(order[i + 5]) << 2) |
141
+ (bits.getbyte(order[i + 6]) << 1) | bits.getbyte(order[i + 7])) ^ mask_codewords[raw]
142
+ k += 1
143
+ end
144
+ out
145
+ end
146
+
147
+ # All codewords of a symbol in transmission (interleaved) order,
148
+ # unmasked. Mainly for tests and diagnostics; the decoder reads block
149
+ # by block so that garbage fails after the first block.
150
+ def read(bit_matrix, version, mask, mirrored: false)
151
+ layout = layout(version)
152
+ order = mirrored ? layout.mirrored_order : layout.order
153
+ mask_codewords = layout.mask_codewords(mask)
154
+ read_block(bit_matrix.bits, order, mask_codewords, (0...mask_codewords.size).to_a)
155
+ end
156
+
157
+ # Splits interleaved codewords into RS blocks (Arrays of Integers).
158
+ def deinterleave(codewords, version, level)
159
+ layout(version).blocks(level).map { |_, indices| codewords.values_at(*indices) }
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QrReader
4
+ module Decoder
5
+ # The eight data mask patterns of ISO/IEC 18004 Table 10. Only used to
6
+ # precompute per-version tables, so clarity beats speed in masked?.
7
+ module DataMask
8
+ # Every pattern repeats after 12 rows and 12 columns (lcm of the 2-, 3-,
9
+ # 4- and 6-periodic conditions).
10
+ PERIOD = 12
11
+
12
+ module_function
13
+
14
+ # True if mask pattern +mask+ (0..7) inverts the module in column +x+,
15
+ # row +y+ (ISO's j and i).
16
+ def masked?(mask, x, y)
17
+ case mask
18
+ when 0 then (y + x).even?
19
+ when 1 then y.even?
20
+ when 2 then (x % 3).zero?
21
+ when 3 then ((y + x) % 3).zero?
22
+ when 4 then ((y / 2) + (x / 3)).even?
23
+ when 5 then ((y * x) % 6).zero? # (ij mod 2) + (ij mod 3) == 0
24
+ when 6 then ((y * x) % 6) < 3 # ((ij mod 2) + (ij mod 3)) mod 2 == 0
25
+ when 7 then (((y + x) % 2) + ((y * x) % 3)).even?
26
+ else raise ArgumentError, "mask must be 0..7, got #{mask.inspect}"
27
+ end
28
+ end
29
+
30
+ # The whole pattern for an n x n symbol as a binary String of n * n
31
+ # bytes, row-major, "\x01" where the mask inverts the module. Built
32
+ # from the 12 distinct rows, so it costs O(n) String operations.
33
+ def grid(mask, n)
34
+ repeat = n / PERIOD + 1
35
+ rows = Array.new(PERIOD)
36
+ y = 0
37
+ while y < PERIOD
38
+ unit = "\0".b * PERIOD
39
+ x = 0
40
+ while x < PERIOD
41
+ unit.setbyte(x, 1) if masked?(mask, x, y)
42
+ x += 1
43
+ end
44
+ rows[y] = (unit * repeat).byteslice(0, n)
45
+ y += 1
46
+ end
47
+ out = "".b
48
+ y = 0
49
+ while y < n
50
+ out << rows[y % PERIOD]
51
+ y += 1
52
+ end
53
+ out
54
+ end
55
+ end
56
+ end
57
+ end