Module: ZplRender::GraphicField

Defined in:
lib/zpl_render/graphic_field.rb

Overview

Decodes ZPL graphic data (^GF and ~DG) into a Canvas.

Supports:

  • ASCII hex data, including Zebra's run-length compression scheme (G-Y repeat counts, g-z counts in multiples of 20, ',' zero-fill to end of row, '!' one-fill to end of row, ':' repeat previous row)
  • :B64: (base64) and :Z64: (base64 + zlib deflate) wrapped data
  • raw binary data (^GFB)

Class Method Summary collapse

Class Method Details

.decode(data, bytes_per_row, total_bytes) ⇒ Object

bytes_per_row determines the pixel width (8 px per byte).



18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/zpl_render/graphic_field.rb', line 18

def decode(data, bytes_per_row, total_bytes)
  return Canvas.new(1, 1) if bytes_per_row.to_i < 1 || total_bytes.to_i < 1

  rows = (total_bytes.to_f / bytes_per_row).ceil
  raw = if data =~ /\A:?([BZ])64:/
          decode_b64_z64(data)
        elsif data.encoding == Encoding::BINARY && data =~ /[^0-9A-Fa-f,:!\sG-Yg-z]/n
          data # raw binary (^GFB)
        else
          decode_ascii_hex(data, bytes_per_row)
        end
  to_canvas(raw, bytes_per_row, rows)
end

.decode_ascii_hex(data, bytes_per_row) ⇒ Object

Expand Zebra RLE ASCII hex into raw bytes.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/zpl_render/graphic_field.rb', line 43

def decode_ascii_hex(data, bytes_per_row)
  nibbles_per_row = bytes_per_row * 2
  out = []
  row = []
  prev_row = nil
  count = 0
  i = 0
  flush = lambda do
    row << 0 while row.length < nibbles_per_row
    out.concat(row[0, nibbles_per_row])
    prev_row = row[0, nibbles_per_row]
    row = []
  end

  while i < data.length
    ch = data[i]
    i += 1
    case ch
    when /[0-9A-Fa-f]/
      nib = ch.to_i(16)
      (count.zero? ? 1 : count).times { row << nib }
      count = 0
      flush.call if row.length >= nibbles_per_row
    when "G".."Y"
      count += ch.ord - "G".ord + 1
    when "g".."z"
      count += (ch.ord - "g".ord + 1) * 20
    when ","
      row << 0 while row.length < nibbles_per_row
      flush.call
      count = 0
    when "!"
      row << 0xF while row.length < nibbles_per_row
      flush.call
      count = 0
    when ":"
      flush.call unless row.empty?
      if prev_row
        out.concat(prev_row)
      else
        out.concat(Array.new(nibbles_per_row, 0))
      end
      count = 0
    end
    # whitespace and anything else is ignored
  end
  flush.call unless row.empty?

  bytes = +""
  out.each_slice(2) { |hi, lo| bytes << ((hi << 4) | (lo || 0)).chr }
  bytes.b
end

.decode_b64_z64(data) ⇒ Object



32
33
34
35
36
37
38
39
40
# File 'lib/zpl_render/graphic_field.rb', line 32

def decode_b64_z64(data)
  m = data.match(/\A:?([BZ])64:([A-Za-z0-9+\/=\s]*):?[0-9A-Fa-f]{0,4}\z/m)
  return "" unless m

  decoded = m[2].unpack1("m") # lenient base64, same as Base64.decode64
  m[1] == "Z" ? Zlib::Inflate.inflate(decoded) : decoded
rescue Zlib::Error
  ""
end

.to_canvas(raw, bytes_per_row, rows) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/zpl_render/graphic_field.rb', line 96

def to_canvas(raw, bytes_per_row, rows)
  width = bytes_per_row * 8
  canvas = Canvas.new(width, [rows, 1].max)
  rows.times do |y|
    bytes_per_row.times do |bx|
      byte = raw.getbyte(y * bytes_per_row + bx)
      next if byte.nil? || byte.zero?

      8.times do |bit|
        canvas.set(bx * 8 + bit, y) if (byte >> (7 - bit)) & 1 == 1
      end
    end
  end
  canvas
end