rqrcode 0.1.0 → 0.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.
- data/CHANGELOG +6 -1
- data/COPYING +19 -0
- data/README +44 -44
- data/lib/rqrcode/core_ext/array.rb +1 -1
- data/lib/rqrcode/core_ext/array/behavior.rb +2 -2
- data/lib/rqrcode/core_ext/integer.rb +1 -1
- data/lib/rqrcode/core_ext/integer/bitwise.rb +2 -2
- data/lib/rqrcode/extensions.rb +0 -0
- data/lib/rqrcode/extensions/image.rb +1006 -0
- data/lib/rqrcode/qrcode.rb +3 -923
- data/lib/rqrcode/qrcode/qr_8bit_byte.rb +35 -0
- data/lib/rqrcode/qrcode/qr_bit_buffer.rb +56 -0
- data/lib/rqrcode/qrcode/qr_code.rb +394 -0
- data/lib/rqrcode/qrcode/qr_math.rb +63 -0
- data/lib/rqrcode/qrcode/qr_polynomial.rb +78 -0
- data/lib/rqrcode/qrcode/qr_rs_block.rb +134 -0
- data/lib/rqrcode/qrcode/qr_util.rb +254 -0
- data/test/{unit/qrcode_test.rb → runtest.rb} +2 -1
- metadata +23 -22
- data/test/test_helper.rb +0 -2
- data/test/unit/test_data.rb +0 -21
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright 2004 by Duncan Robertson (duncan@whomwah.com).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#++
|
11
|
+
|
12
|
+
module RQRCode
|
13
|
+
|
14
|
+
class QR8bitByte
|
15
|
+
attr_reader :mode
|
16
|
+
|
17
|
+
def initialize( data )
|
18
|
+
@mode = QRMODE[:mode_8bit_byte]
|
19
|
+
@data = data;
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def get_length
|
24
|
+
@data.size
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def write( buffer )
|
29
|
+
( 0...@data.size ).each do |i|
|
30
|
+
buffer.put( @data[i], 8 )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright 2004 by Duncan Robertson (duncan@whomwah.com).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#++
|
11
|
+
|
12
|
+
module RQRCode
|
13
|
+
|
14
|
+
class QRBitBuffer
|
15
|
+
attr_reader :buffer
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@buffer = []
|
19
|
+
@length = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def get( index )
|
24
|
+
buf_index = (index / 8).floor
|
25
|
+
(( (@buffer[buf_index]).rszf(7 - index % 8)) & 1) == 1
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def put( num, length )
|
30
|
+
( 0...length ).each do |i|
|
31
|
+
put_bit((((num).rszf(length - i - 1)) & 1) == 1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def get_length_in_bits
|
37
|
+
@length
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def put_bit( bit )
|
42
|
+
buf_index = ( @length / 8 ).floor
|
43
|
+
if @buffer.size <= buf_index
|
44
|
+
@buffer << 0
|
45
|
+
end
|
46
|
+
|
47
|
+
if bit
|
48
|
+
@buffer[buf_index] |= ((0x80).rszf(@length % 8))
|
49
|
+
end
|
50
|
+
|
51
|
+
@length += 1
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,394 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright 2004 by Duncan Robertson (duncan@whomwah.com).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#++
|
11
|
+
|
12
|
+
module RQRCode #:nodoc:
|
13
|
+
|
14
|
+
QRMODE = {
|
15
|
+
:mode_number => 1 << 0,
|
16
|
+
:mode_alpha_num => 1 << 1,
|
17
|
+
:mode_8bit_byte => 1 << 2,
|
18
|
+
:mode_kanji => 1 << 3
|
19
|
+
}
|
20
|
+
|
21
|
+
QRERRORCORRECTLEVEL = {
|
22
|
+
:l => 1,
|
23
|
+
:m => 0,
|
24
|
+
:q => 3,
|
25
|
+
:h => 2
|
26
|
+
}
|
27
|
+
|
28
|
+
QRMASKPATTERN = {
|
29
|
+
:pattern000 => 0,
|
30
|
+
:pattern001 => 1,
|
31
|
+
:pattern010 => 2,
|
32
|
+
:pattern011 => 3,
|
33
|
+
:pattern100 => 4,
|
34
|
+
:pattern101 => 5,
|
35
|
+
:pattern110 => 6,
|
36
|
+
:pattern111 => 7
|
37
|
+
}
|
38
|
+
|
39
|
+
# Generic QRCode exception class.
|
40
|
+
|
41
|
+
class QRCodeArgumentError < ArgumentError; end
|
42
|
+
class QRCodeRunTimeError < RuntimeError; end
|
43
|
+
|
44
|
+
# This is the main interface for creating your QRCode
|
45
|
+
# You create a new instance of QRCode and then you have
|
46
|
+
# a few simple methods to choose form
|
47
|
+
|
48
|
+
class QRCode
|
49
|
+
attr_reader :modules, :module_count
|
50
|
+
|
51
|
+
PAD0 = 0xEC
|
52
|
+
PAD1 = 0x11
|
53
|
+
|
54
|
+
# Expects a string to be parsed in, other options have defaults
|
55
|
+
|
56
|
+
def initialize( *args )
|
57
|
+
raise QRCodeArgumentError unless args.first.kind_of?( String )
|
58
|
+
|
59
|
+
@data = args.shift
|
60
|
+
options = args.extract_options!
|
61
|
+
level = options[:level] || :h
|
62
|
+
@error_correct_level = QRERRORCORRECTLEVEL[ level.to_sym ]
|
63
|
+
@type_number = options[:size] || 4
|
64
|
+
@module_count = @type_number * 4 + 17
|
65
|
+
@modules = nil
|
66
|
+
@data_cache = nil
|
67
|
+
@data_list = QR8bitByte.new( @data )
|
68
|
+
|
69
|
+
self.make # let's go !
|
70
|
+
end
|
71
|
+
|
72
|
+
# called parsing in a row and col coordinate
|
73
|
+
# * return true or false
|
74
|
+
# * raise exception if row < 0
|
75
|
+
# * raise exception if col < 0
|
76
|
+
# * raise exception if @module_count <= row
|
77
|
+
# * raise exception if @module_count <= col
|
78
|
+
|
79
|
+
def is_dark( row, col )
|
80
|
+
if row < 0 || @module_count <= row || col < 0 || @module_count <= col
|
81
|
+
raise QRCodeRunTimeError, "#{row},#{col}"
|
82
|
+
end
|
83
|
+
@modules[row][col]
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_console
|
87
|
+
(0...@module_count).each do |col|
|
88
|
+
tmp = []
|
89
|
+
(0...@module_count).each do |row|
|
90
|
+
if is_dark(col,row)
|
91
|
+
tmp << "x"
|
92
|
+
else
|
93
|
+
tmp << " "
|
94
|
+
end
|
95
|
+
end
|
96
|
+
puts tmp.join
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
protected
|
101
|
+
|
102
|
+
def make #:nodoc:
|
103
|
+
make_impl( false, get_best_mask_pattern )
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
|
109
|
+
def make_impl( test, mask_pattern ) #:nodoc:
|
110
|
+
@modules = Array.new( @module_count )
|
111
|
+
|
112
|
+
( 0...@module_count ).each do |row|
|
113
|
+
@modules[row] = Array.new( @module_count )
|
114
|
+
end
|
115
|
+
|
116
|
+
setup_position_probe_pattern( 0, 0 )
|
117
|
+
setup_position_probe_pattern( @module_count - 7, 0 )
|
118
|
+
setup_position_probe_pattern( 0, @module_count - 7 )
|
119
|
+
setup_position_adjust_pattern
|
120
|
+
setup_timing_pattern
|
121
|
+
setup_type_info( test, mask_pattern )
|
122
|
+
setup_type_number( test ) if @type_number >= 7
|
123
|
+
|
124
|
+
if @data_cache.nil?
|
125
|
+
@data_cache = QRCode.create_data(
|
126
|
+
@type_number, @error_correct_level, @data_list
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
map_data( @data_cache, mask_pattern )
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def setup_position_probe_pattern( row, col ) #:nodoc:
|
135
|
+
( -1..7 ).each do |r|
|
136
|
+
next if ( row + r ) <= -1 || @module_count <= ( row + r )
|
137
|
+
( -1..7 ).each do |c|
|
138
|
+
next if ( col + c ) <= -1 || @module_count <= ( col + c )
|
139
|
+
if 0 <= r && r <= 6 && ( c == 0 || c == 6 ) || 0 <= c && c <= 6 && ( r == 0 || r == 6 ) || 2 <= r && r <= 4 && 2 <= c && c <= 4
|
140
|
+
@modules[row + r][col + c] = true;
|
141
|
+
else
|
142
|
+
@modules[row + r][col + c] = false;
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def get_best_mask_pattern #:nodoc:
|
150
|
+
min_lost_point = 0
|
151
|
+
pattern = 0
|
152
|
+
|
153
|
+
( 0...8 ).each do |i|
|
154
|
+
make_impl( true, i )
|
155
|
+
lost_point = QRUtil.get_lost_point( self )
|
156
|
+
|
157
|
+
if i == 0 || min_lost_point > lost_point
|
158
|
+
min_lost_point = lost_point
|
159
|
+
pattern = i
|
160
|
+
end
|
161
|
+
end
|
162
|
+
pattern
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
def setup_timing_pattern #:nodoc:
|
167
|
+
( 8...@module_count - 8 ).each do |r|
|
168
|
+
next unless @modules[r][6].nil?
|
169
|
+
@modules[r][6] = (r % 2 == 0)
|
170
|
+
end
|
171
|
+
|
172
|
+
( 8...@module_count - 8 ).each do |c|
|
173
|
+
next unless @modules[6][c].nil?
|
174
|
+
@modules[6][c] = (c % 2 == 0)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
def setup_position_adjust_pattern #:nodoc:
|
180
|
+
pos = QRUtil.get_pattern_position(@type_number)
|
181
|
+
|
182
|
+
( 0...pos.size ).each do |i|
|
183
|
+
( 0...pos.size ).each do |j|
|
184
|
+
row = pos[i]
|
185
|
+
col = pos[j]
|
186
|
+
|
187
|
+
next unless @modules[row][col].nil?
|
188
|
+
|
189
|
+
( -2..2 ).each do |r|
|
190
|
+
( -2..2 ).each do |c|
|
191
|
+
if r == -2 || r == 2 || c == -2 || c == 2 || ( r == 0 && c == 0 )
|
192
|
+
@modules[row + r][col + c] = true
|
193
|
+
else
|
194
|
+
@modules[row + r][col + c] = false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
def setup_type_number( test ) #:nodoc:
|
204
|
+
bits = QRUtil.get_bch_type_number( @type_number )
|
205
|
+
|
206
|
+
( 0...18 ).each do |i|
|
207
|
+
mod = ( !test && ( (bits >> i) & 1) == 1 )
|
208
|
+
@modules[ (i / 3).floor ][ i % 3 + @module_count - 8 - 3 ] = mod
|
209
|
+
end
|
210
|
+
|
211
|
+
( 0...18 ).each do |i|
|
212
|
+
mod = ( !test && ( (bits >> i) & 1) == 1 )
|
213
|
+
@modules[ i % 3 + @module_count - 8 - 3 ][ (i / 3).floor ] = mod
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
def setup_type_info( test, mask_pattern ) #:nodoc:
|
219
|
+
data = (@error_correct_level << 3 | mask_pattern)
|
220
|
+
bits = QRUtil.get_bch_type_info( data )
|
221
|
+
|
222
|
+
# vertical
|
223
|
+
( 0...15 ).each do |i|
|
224
|
+
mod = (!test && ( (bits >> i) & 1) == 1)
|
225
|
+
|
226
|
+
if i < 6
|
227
|
+
@modules[i][8] = mod
|
228
|
+
elsif i < 8
|
229
|
+
@modules[ i + 1 ][8] = mod
|
230
|
+
else
|
231
|
+
@modules[ @module_count - 15 + i ][8] = mod
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
# horizontal
|
237
|
+
( 0...15 ).each do |i|
|
238
|
+
mod = (!test && ( (bits >> i) & 1) == 1)
|
239
|
+
|
240
|
+
if i < 8
|
241
|
+
@modules[8][ @module_count - i - 1 ] = mod
|
242
|
+
elsif i < 9
|
243
|
+
@modules[8][ 15 - i - 1 + 1 ] = mod
|
244
|
+
else
|
245
|
+
@modules[8][ 15 - i - 1 ] = mod
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# fixed module
|
250
|
+
@modules[ @module_count - 8 ][8] = !test
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
def map_data( data, mask_pattern ) #:nodoc:
|
255
|
+
inc = -1
|
256
|
+
row = @module_count - 1
|
257
|
+
bit_index = 7
|
258
|
+
byte_index = 0
|
259
|
+
|
260
|
+
( @module_count - 1 ).step( 1, -2 ) do |col|
|
261
|
+
col = col - 1 if col <= 6
|
262
|
+
|
263
|
+
while true do
|
264
|
+
( 0...2 ).each do |c|
|
265
|
+
|
266
|
+
if @modules[row][ col - c ].nil?
|
267
|
+
dark = false
|
268
|
+
if byte_index < data.size
|
269
|
+
dark = (( (data[byte_index]).rszf( bit_index ) & 1) == 1 )
|
270
|
+
end
|
271
|
+
mask = QRUtil.get_mask( mask_pattern, row, col - c )
|
272
|
+
dark = !dark if mask
|
273
|
+
@modules[row][ col - c ] = dark
|
274
|
+
bit_index -= 1
|
275
|
+
|
276
|
+
if bit_index == -1
|
277
|
+
byte_index += 1
|
278
|
+
bit_index = 7
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
row += inc
|
284
|
+
|
285
|
+
if row < 0 || @module_count <= row
|
286
|
+
row -= inc
|
287
|
+
inc = -inc
|
288
|
+
break
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def QRCode.create_data( type_number, error_correct_level, data_list ) #:nodoc:
|
295
|
+
rs_blocks = QRRSBlock.get_rs_blocks( type_number, error_correct_level )
|
296
|
+
buffer = QRBitBuffer.new
|
297
|
+
|
298
|
+
data = data_list
|
299
|
+
buffer.put( data.mode, 4 )
|
300
|
+
buffer.put(
|
301
|
+
data.get_length, QRUtil.get_length_in_bits( data.mode, type_number )
|
302
|
+
)
|
303
|
+
data.write( buffer )
|
304
|
+
|
305
|
+
total_data_count = 0
|
306
|
+
( 0...rs_blocks.size ).each do |i|
|
307
|
+
total_data_count = total_data_count + rs_blocks[i].data_count
|
308
|
+
end
|
309
|
+
|
310
|
+
if buffer.get_length_in_bits > total_data_count * 8
|
311
|
+
raise QRCodeRunTimeError,
|
312
|
+
"code length overflow. (#{buffer.get_length_in_bits}>#{total_data_count})"
|
313
|
+
end
|
314
|
+
|
315
|
+
if buffer.get_length_in_bits + 4 <= total_data_count * 8
|
316
|
+
buffer.put( 0, 4 )
|
317
|
+
end
|
318
|
+
|
319
|
+
while buffer.get_length_in_bits % 8 != 0
|
320
|
+
buffer.put_bit( false )
|
321
|
+
end
|
322
|
+
|
323
|
+
while true
|
324
|
+
break if buffer.get_length_in_bits >= total_data_count * 8
|
325
|
+
buffer.put( QRCode::PAD0, 8 )
|
326
|
+
break if buffer.get_length_in_bits >= total_data_count * 8
|
327
|
+
buffer.put( QRCode::PAD1, 8 )
|
328
|
+
end
|
329
|
+
|
330
|
+
QRCode.create_bytes( buffer, rs_blocks )
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
def QRCode.create_bytes( buffer, rs_blocks ) #:nodoc:
|
335
|
+
offset = 0
|
336
|
+
max_dc_count = 0
|
337
|
+
max_ec_count = 0
|
338
|
+
dcdata = Array.new( rs_blocks.size )
|
339
|
+
ecdata = Array.new( rs_blocks.size )
|
340
|
+
|
341
|
+
( 0...rs_blocks.size ).each do |r|
|
342
|
+
dc_count = rs_blocks[r].data_count
|
343
|
+
ec_count = rs_blocks[r].total_count - dc_count
|
344
|
+
max_dc_count = [ max_dc_count, dc_count ].max
|
345
|
+
max_ec_count = [ max_ec_count, ec_count ].max
|
346
|
+
dcdata[r] = Array.new( dc_count )
|
347
|
+
|
348
|
+
( 0...dcdata[r].size ).each do |i|
|
349
|
+
dcdata[r][i] = 0xff & buffer.buffer[ i + offset ]
|
350
|
+
end
|
351
|
+
|
352
|
+
offset = offset + dc_count
|
353
|
+
rs_poly = QRUtil.get_error_correct_polynomial( ec_count )
|
354
|
+
raw_poly = QRPolynomial.new( dcdata[r], rs_poly.get_length - 1 )
|
355
|
+
mod_poly = raw_poly.mod( rs_poly )
|
356
|
+
ecdata[r] = Array.new( rs_poly.get_length - 1 )
|
357
|
+
( 0...ecdata[r].size ).each do |i|
|
358
|
+
mod_index = i + mod_poly.get_length - ecdata[r].size
|
359
|
+
ecdata[r][i] = mod_index >= 0 ? mod_poly.get( mod_index ) : 0
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
total_code_count = 0
|
364
|
+
( 0...rs_blocks.size ).each do |i|
|
365
|
+
total_code_count = total_code_count + rs_blocks[i].total_count
|
366
|
+
end
|
367
|
+
|
368
|
+
data = Array.new( total_code_count )
|
369
|
+
index = 0
|
370
|
+
|
371
|
+
( 0...max_dc_count ).each do |i|
|
372
|
+
( 0...rs_blocks.size ).each do |r|
|
373
|
+
if i < dcdata[r].size
|
374
|
+
index += 1
|
375
|
+
data[index-1] = dcdata[r][i]
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
( 0...max_ec_count ).each do |i|
|
381
|
+
( 0...rs_blocks.size ).each do |r|
|
382
|
+
if i < ecdata[r].size
|
383
|
+
index += 1
|
384
|
+
data[index-1] = ecdata[r][i]
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
data
|
390
|
+
end
|
391
|
+
|
392
|
+
end
|
393
|
+
|
394
|
+
end
|