class Qrio::QrMatrix

Constants

ALIGNMENT_CENTERS
BLOCK_STRUCTURE
ERROR_CORRECTION_LEVEL
FORMAT_MASK
MODE
WORD_WIDTHS

Public Class Methods

new(*args) click to toggle source
Calls superclass method Qrio::Matrix::new
# File lib/qrio/qr_matrix.rb, line 3
def initialize(*args)
  super
  @unmasked = false
end

Public Instance Methods

block_count() click to toggle source
# File lib/qrio/qr_matrix.rb, line 231
def block_count
  block_structure.inject(0){|sum, (blocks,data,ecc)| sum += blocks }
end
block_structure() click to toggle source
# File lib/qrio/qr_matrix.rb, line 235
def block_structure
  @block_structure ||= begin
    @short_blocks = []
    @long_blocks  = []

    params = block_structure_params.dup

    @short_blocks = params.slice!(0,3)
    structure = [@short_blocks]

    unless params.empty?
      @long_blocks = @short_blocks.dup
      @long_blocks[0] = params[0]
      structure << @long_blocks
    end

    structure
  end
end
data_or_correction?(x, y) click to toggle source
# File lib/qrio/qr_matrix.rb, line 259
def data_or_correction?(x, y)
  ! in_finder_pattern?(x, y)    &&
  ! in_alignment_pattern?(x, y) &&
  ! in_alignment_line?(x, y)
end
draw_alignment_patterns() click to toggle source
# File lib/qrio/qr_matrix.rb, line 282
def draw_alignment_patterns
  rows = ALIGNMENT_CENTERS[version - 1].dup
  cols = rows.dup

  cols.each do |cy|
    rows.each do |cx|
      unless in_finder_pattern?(cx, cy)
        ((cy - 2)...(cy + 2)).each do |y|
          ((cx - 2)...(cx + 2)).each do |x|
            self[x, y] = (cx - x).abs == 2 ||
                         (cy - y).abs == 2 ||
                         (x == cx && y == cy)
          end
        end
      end
    end
  end
end
ecc_bytes_per_block() click to toggle source
# File lib/qrio/qr_matrix.rb, line 255
def ecc_bytes_per_block
  block_structure.first.last
end
error_correction_level() click to toggle source
# File lib/qrio/qr_matrix.rb, line 125
def error_correction_level
  ERROR_CORRECTION_LEVEL[read_format[:error_correction]]
end
in_alignment_line?(x, y) click to toggle source
# File lib/qrio/qr_matrix.rb, line 301
def in_alignment_line?(x, y)
  (x == 6) || (y == 6)
end
in_alignment_pattern?(x, y) click to toggle source
# File lib/qrio/qr_matrix.rb, line 271
def in_alignment_pattern?(x, y)
  return false if version == 1

  alignment_centers = ALIGNMENT_CENTERS[version - 1]

  cy = alignment_centers.detect{|c| (c - y).abs <= 2 }
  cx = alignment_centers.detect{|c| (c - x).abs <= 2 }

  cx && cy && ! in_finder_pattern?(cx, cy)
end
in_finder_pattern?(x, y) click to toggle source
# File lib/qrio/qr_matrix.rb, line 265
def in_finder_pattern?(x, y)
  (x < 9           && y < 9) ||
  (x > (width - 9) && y < 9) ||
  (x < 9           && y > (height - 9))
end
mask_pattern() click to toggle source
# File lib/qrio/qr_matrix.rb, line 129
def mask_pattern
  read_format[:mask_pattern]
end
mode() click to toggle source
# File lib/qrio/qr_matrix.rb, line 216
def mode
  MODE[@mode]
end
raw_bytes() click to toggle source

raw bytestream, as read from the QR symbol

# File lib/qrio/qr_matrix.rb, line 163
def raw_bytes
  @raw_bytes ||= read_raw_bytes
end
text() click to toggle source
# File lib/qrio/qr_matrix.rb, line 179
def text
  @text ||= begin
    text = []

    unmask unless @unmasked

    # deinterlace
    @blocks = []

    byte_pointer = 0

    # TODO : handle ragged block sizes
    block_structure.each do |count, data, ecc|
      data.times do |word_index|
        block_count.times do |blk_index|
          @blocks[blk_index] ||= []
          @blocks[blk_index] << raw_bytes[byte_pointer]
          byte_pointer += 1
        end
      end
    end

    @blocks = @blocks.flatten

    set_mode

    character_count = read

    character_count.times do |idx|
      byte = read
      text << byte.chr
    end

    text.join
  end
end
to_s() click to toggle source
# File lib/qrio/qr_matrix.rb, line 167
def to_s
  str = ""
  rows.each do |row|
    row.each do |m|
      str << (m ? '#' : ' ')
    end
    str << "\n"
  end

  str
end
unmask() click to toggle source
# File lib/qrio/qr_matrix.rb, line 137
def unmask
  p = [
    lambda{|x,y| (x + y) % 2 == 0 },
    lambda{|x,y| x % 2 == 0 },
    lambda{|x,y| y % 3 == 0 },
    lambda{|x,y| (x + y) % 3 == 0 },
    lambda{|x,y| ((x / 2) + (y / 3)) % 2 == 0 },
    lambda{|x,y| prod = x * y;   (prod % 2) + (prod % 3) == 0 },
    lambda{|x,y| prod = x * y; (((prod % 2) + (prod % 3)) % 2) == 0 },
    lambda{|x,y| prod = x * y; sum = x + y; (((prod % 3) + (sum % 2)) % 2) == 0 }
  ][mask_pattern]

  raise "could not load mask pattern #{ mask_pattern }" unless p

  0.upto(height - 1) do |y|
    0.upto(width - 1) do |x|
      if data_or_correction?(x, y)
        self[x, y] = self[x, y] ^ p.call(x, y)
      end
    end
  end

  @unmasked = ! @unmasked
end
version() click to toggle source
# File lib/qrio/qr_matrix.rb, line 133
def version
  (width - 17) / 4
end
word_size() click to toggle source
# File lib/qrio/qr_matrix.rb, line 220
def word_size
  @word_size ||= begin
    widths = WORD_WIDTHS[mode]
    version_width = widths.detect{|k,v| k.include? version }

    raise "Could not find word width" if version_width.nil?

    version_width.last
  end
end

Private Instance Methods

add_bit(x, y) click to toggle source
# File lib/qrio/qr_matrix.rb, line 358
def add_bit(x, y)
  if data_or_correction?(x, y)
    @byte.push self[x, y]

    if @byte.length == 8
      @raw_bytes << @byte.map{|b| b ? '1' : '0' }.join.to_i(2)
      @byte = []
    end
  end
end
block_structure_params() click to toggle source
# File lib/qrio/qr_matrix.rb, line 319
def block_structure_params
  BLOCK_STRUCTURE[version - 1][read_format[:error_correction]].dup
end
read(bits=nil) click to toggle source

read bits bits from bitstream and return the binary

# File lib/qrio/qr_matrix.rb, line 324
def read(bits=nil)
  bits ||= word_size
  binary = []

  bits.times do |i|
    block_index, bit_index = @pointer.divmod(8)
    data = @blocks[block_index] || 0
    binary << (((data >> (7 - bit_index)) & 1) == 1)
    @pointer += 1
  end

  binary.map{|b| b ? '1' : '0' }.join.to_i(2)
end
read_format() click to toggle source
# File lib/qrio/qr_matrix.rb, line 369
def read_format
  @format ||= begin
    bits = 0

    0.upto(5) do |x|
      bits = bits << 1
      bits += 1 if self[x, 8]
    end

    bits = bits << 1
    bits += 1 if self[7, 8]
    bits = bits << 1
    bits += 1 if self[8, 8]
    bits = bits << 1
    bits += 1 if self[8, 7]

    5.downto(0) do |y|
      bits = bits << 1
      bits += 1 if self[8, y]
    end

    format_string     = (bits ^ FORMAT_MASK).to_s(2).rjust(15, '0')

    # TODO check BCH error detection
    # TODO if too many errors, read alternate format blocks

    {
      :error_correction    => format_string[0,2].to_i(2),
      :mask_pattern        => format_string[2,3].to_i(2),
      :bch_error_detection => format_string[5..-1].to_i(2)
    }
  end
end
read_raw_bytes() click to toggle source
# File lib/qrio/qr_matrix.rb, line 338
def read_raw_bytes
  @raw_bytes = []
  @byte      = []

  (0..(width - 3)).step(2) do |bcol|
    bcol = width - 1 - bcol
    scanning_up = ((bcol / 2) % 2) == 0
    bcol -= 1 if bcol <= 6

    (0..(height - 1)).each do |brow|
      brow = height - 1 - brow if scanning_up

      add_bit(bcol, brow)
      add_bit(bcol - 1, brow)
    end
  end

  @raw_bytes
end
set_data_length() click to toggle source
# File lib/qrio/qr_matrix.rb, line 315
def set_data_length
  @data_length ||= read
end
set_mode() click to toggle source
# File lib/qrio/qr_matrix.rb, line 307
def set_mode
  @mode ||= begin
    @pointer ||= 0
    mode_number = read(4)
  end
  raise "Unknown mode #{ @mode }" unless mode
end