#!/usr/bin/env ruby
#--
# Copyright 2004 by Duncan Robertson (duncan@whomwah.com).
# All rights reserved.
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#++
module RQRCode #:nodoc:
QRMODE = {
:mode_number => 1 << 0,
:mode_alpha_numk => 1 << 1,
:mode_8bit_byte => 1 << 2,
:mode_kanji => 1 << 3
}
QRERRORCORRECTLEVEL = {
:l => 1,
:m => 0,
:q => 3,
:h => 2
}
QRMASKPATTERN = {
:pattern000 => 0,
:pattern001 => 1,
:pattern010 => 2,
:pattern011 => 3,
:pattern100 => 4,
:pattern101 => 5,
:pattern110 => 6,
:pattern111 => 7
}
# StandardErrors
class QRCodeArgumentError < ArgumentError; end
class QRCodeRunTimeError < RuntimeError; end
# == Creation
#
# QRCode objects expect only one required constructor parameter
# and an optional hash of any other. Here's a few examples:
#
# qr = RQRCode::QRCode.new('hello world')
# qr = RQRCode::QRCode.new('hello world', :size => 1, :level => :m )
#
class QRCode
attr_reader :modules, :module_count
PAD0 = 0xEC
PAD1 = 0x11
# Expects a string to be parsed in, other args are optional
#
# # string - the string you wish to encode
# # size - the size of the qrcode
# # level - the error correction level
# qr = RQRCode::QRCode.new('hello world', :size => 1, :level => :m )
#
def initialize( *args )
raise QRCodeArgumentError unless args.first.kind_of?( String )
@data = args.shift
options = args.extract_options!
level = options[:level] || :h
@error_correct_level = QRERRORCORRECTLEVEL[ level.to_sym ]
@type_number = options[:size] || 4
@module_count = @type_number * 4 + 17
@modules = nil
@data_cache = nil
@data_list = QR8bitByte.new( @data )
self.make
end
# is_dark is called with a +col+ and +row+ parameter. This will
# return true or false based on whether that coordinate exists in the
# matrix returned. It would normally be called while iterating through
# modules. A simple example would be:
#
# instance.is_dark( 10, 10 ) => true
#
def is_dark( row, col )
if row < 0 || @module_count <= row || col < 0 || @module_count <= col
raise QRCodeRunTimeError, "#{row},#{col}"
end
@modules[row][col]
end
# This is a public method that returns the QR Code you have
# generated as a string. It will not be able to be read
# in this format by a QR Code reader, but will give you an
# idea if the final outout. It takes two optional args
# +:true+ and +:false+ which are there for you to choose
# how the output looks. Here's an example of it's use:
#
# instance.to_s =>
# xxxxxxx x x x x x xx xxxxxxx
# x x xxx xxxxxx xxx x x
# x xxx x xxxxx x xx x xxx x
#
# instance._to_s( :true => 'E', :false => 'Q') =>
# EEEEEEEQEQQEQEQQQEQEQQEEQQEEEEEEE
# EQQQQQEQQEEEQQEEEEEEQEEEQQEQQQQQE
# EQEEEQEQQEEEEEQEQQQQQQQEEQEQEEEQE
#
def to_s( *args )
options = args.extract_options!
row = options[:true] || 'x'
col = options[:false] || ' '
res = []
@modules.each_index do |c|
tmp = []
@modules.each_index do |r|
if is_dark(c,r)
tmp << row
else
tmp << col
end
end
res << tmp.join
end
res.join("\n")
end
protected
def make #:nodoc:
make_impl( false, get_best_mask_pattern )
end
private
def make_impl( test, mask_pattern ) #:nodoc:
@modules = Array.new( @module_count )
( 0...@module_count ).each do |row|
@modules[row] = Array.new( @module_count )
end
setup_position_probe_pattern( 0, 0 )
setup_position_probe_pattern( @module_count - 7, 0 )
setup_position_probe_pattern( 0, @module_count - 7 )
setup_position_adjust_pattern
setup_timing_pattern
setup_type_info( test, mask_pattern )
setup_type_number( test ) if @type_number >= 7
if @data_cache.nil?
@data_cache = QRCode.create_data(
@type_number, @error_correct_level, @data_list
)
end
map_data( @data_cache, mask_pattern )
end
def setup_position_probe_pattern( row, col ) #:nodoc:
( -1..7 ).each do |r|
next if ( row + r ) <= -1 || @module_count <= ( row + r )
( -1..7 ).each do |c|
next if ( col + c ) <= -1 || @module_count <= ( col + c )
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
@modules[row + r][col + c] = true;
else
@modules[row + r][col + c] = false;
end
end
end
end
def get_best_mask_pattern #:nodoc:
min_lost_point = 0
pattern = 0
( 0...8 ).each do |i|
make_impl( true, i )
lost_point = QRUtil.get_lost_point( self )
if i == 0 || min_lost_point > lost_point
min_lost_point = lost_point
pattern = i
end
end
pattern
end
def setup_timing_pattern #:nodoc:
( 8...@module_count - 8 ).each do |r|
next unless @modules[r][6].nil?
@modules[r][6] = (r % 2 == 0)
end
( 8...@module_count - 8 ).each do |c|
next unless @modules[6][c].nil?
@modules[6][c] = (c % 2 == 0)
end
end
def setup_position_adjust_pattern #:nodoc:
pos = QRUtil.get_pattern_position(@type_number)
( 0...pos.size ).each do |i|
( 0...pos.size ).each do |j|
row = pos[i]
col = pos[j]
next unless @modules[row][col].nil?
( -2..2 ).each do |r|
( -2..2 ).each do |c|
if r == -2 || r == 2 || c == -2 || c == 2 || ( r == 0 && c == 0 )
@modules[row + r][col + c] = true
else
@modules[row + r][col + c] = false
end
end
end
end
end
end
def setup_type_number( test ) #:nodoc:
bits = QRUtil.get_bch_type_number( @type_number )
( 0...18 ).each do |i|
mod = ( !test && ( (bits >> i) & 1) == 1 )
@modules[ (i / 3).floor ][ i % 3 + @module_count - 8 - 3 ] = mod
end
( 0...18 ).each do |i|
mod = ( !test && ( (bits >> i) & 1) == 1 )
@modules[ i % 3 + @module_count - 8 - 3 ][ (i / 3).floor ] = mod
end
end
def setup_type_info( test, mask_pattern ) #:nodoc:
data = (@error_correct_level << 3 | mask_pattern)
bits = QRUtil.get_bch_type_info( data )
# vertical
( 0...15 ).each do |i|
mod = (!test && ( (bits >> i) & 1) == 1)
if i < 6
@modules[i][8] = mod
elsif i < 8
@modules[ i + 1 ][8] = mod
else
@modules[ @module_count - 15 + i ][8] = mod
end
end
# horizontal
( 0...15 ).each do |i|
mod = (!test && ( (bits >> i) & 1) == 1)
if i < 8
@modules[8][ @module_count - i - 1 ] = mod
elsif i < 9
@modules[8][ 15 - i - 1 + 1 ] = mod
else
@modules[8][ 15 - i - 1 ] = mod
end
end
# fixed module
@modules[ @module_count - 8 ][8] = !test
end
def map_data( data, mask_pattern ) #:nodoc:
inc = -1
row = @module_count - 1
bit_index = 7
byte_index = 0
( @module_count - 1 ).step( 1, -2 ) do |col|
col = col - 1 if col <= 6
while true do
( 0...2 ).each do |c|
if @modules[row][ col - c ].nil?
dark = false
if byte_index < data.size
dark = (( (data[byte_index]).rszf( bit_index ) & 1) == 1 )
end
mask = QRUtil.get_mask( mask_pattern, row, col - c )
dark = !dark if mask
@modules[row][ col - c ] = dark
bit_index -= 1
if bit_index == -1
byte_index += 1
bit_index = 7
end
end
end
row += inc
if row < 0 || @module_count <= row
row -= inc
inc = -inc
break
end
end
end
end
def QRCode.create_data( type_number, error_correct_level, data_list ) #:nodoc:
rs_blocks = QRRSBlock.get_rs_blocks( type_number, error_correct_level )
buffer = QRBitBuffer.new
data = data_list
buffer.put( data.mode, 4 )
buffer.put(
data.get_length, QRUtil.get_length_in_bits( data.mode, type_number )
)
data.write( buffer )
total_data_count = 0
( 0...rs_blocks.size ).each do |i|
total_data_count = total_data_count + rs_blocks[i].data_count
end
if buffer.get_length_in_bits > total_data_count * 8
raise QRCodeRunTimeError,
"code length overflow. (#{buffer.get_length_in_bits}>#{total_data_count})"
end
if buffer.get_length_in_bits + 4 <= total_data_count * 8
buffer.put( 0, 4 )
end
while buffer.get_length_in_bits % 8 != 0
buffer.put_bit( false )
end
while true
break if buffer.get_length_in_bits >= total_data_count * 8
buffer.put( QRCode::PAD0, 8 )
break if buffer.get_length_in_bits >= total_data_count * 8
buffer.put( QRCode::PAD1, 8 )
end
QRCode.create_bytes( buffer, rs_blocks )
end
def QRCode.create_bytes( buffer, rs_blocks ) #:nodoc:
offset = 0
max_dc_count = 0
max_ec_count = 0
dcdata = Array.new( rs_blocks.size )
ecdata = Array.new( rs_blocks.size )
( 0...rs_blocks.size ).each do |r|
dc_count = rs_blocks[r].data_count
ec_count = rs_blocks[r].total_count - dc_count
max_dc_count = [ max_dc_count, dc_count ].max
max_ec_count = [ max_ec_count, ec_count ].max
dcdata[r] = Array.new( dc_count )
( 0...dcdata[r].size ).each do |i|
dcdata[r][i] = 0xff & buffer.buffer[ i + offset ]
end
offset = offset + dc_count
rs_poly = QRUtil.get_error_correct_polynomial( ec_count )
raw_poly = QRPolynomial.new( dcdata[r], rs_poly.get_length - 1 )
mod_poly = raw_poly.mod( rs_poly )
ecdata[r] = Array.new( rs_poly.get_length - 1 )
( 0...ecdata[r].size ).each do |i|
mod_index = i + mod_poly.get_length - ecdata[r].size
ecdata[r][i] = mod_index >= 0 ? mod_poly.get( mod_index ) : 0
end
end
total_code_count = 0
( 0...rs_blocks.size ).each do |i|
total_code_count = total_code_count + rs_blocks[i].total_count
end
data = Array.new( total_code_count )
index = 0
( 0...max_dc_count ).each do |i|
( 0...rs_blocks.size ).each do |r|
if i < dcdata[r].size
index += 1
data[index-1] = dcdata[r][i]
end
end
end
( 0...max_ec_count ).each do |i|
( 0...rs_blocks.size ).each do |r|
if i < ecdata[r].size
index += 1
data[index-1] = ecdata[r][i]
end
end
end
data
end
end
end