class Dmtx::DataMatrix
Constants
- C40
- TEXT
- X12
Attributes
height[R]
width[R]
Public Class Methods
new(msg, rect: false)
click to toggle source
# File lib/dmtx/data_matrix.rb, line 47 def initialize(msg, rect: false) @m = [] @width = 0 @height = 0 encode(msg, rect) end
Public Instance Methods
inspect()
click to toggle source
# File lib/dmtx/data_matrix.rb, line 54 def inspect "#<#{self.class.name}:0x#{object_id.to_s(16)} #{width}x#{height}>" end
to_i()
click to toggle source
# File lib/dmtx/data_matrix.rb, line 58 def to_i (0..(height - 1)).inject(0) { |i, y| (0..(width - 1)).inject(i) { |j, x| (j << 1) | (bit?(x,y) ? 1 : 0) } } end
to_png(mod: 8, pad: 2, bgcolor: nil, color: '
click to toggle source
# File lib/dmtx/data_matrix.rb, line 100 def to_png(mod: 8, pad: 2, bgcolor: nil, color: '#000') raise ArgumentError, 'module size too small' unless mod > 0 raise ArgumentError, 'padding too small' unless pad >= 0 color = color ? ChunkyPNG::Color(color) : ChunkyPNG::Color('black') bgcolor = bgcolor ? ChunkyPNG::Color(bgcolor) : ChunkyPNG::Color::TRANSPARENT width_px = mod * (width + pad * 2) height_px = mod * (height + pad * 2) png = ChunkyPNG::Image.new(width_px, height_px, bgcolor) sx = sy = mod * pad (0..(height - 1)).each do |y| (0..(width - 1)).each do |x| png.rect(sx + x * mod, sy + y * mod, sx + (x + 1) * mod, sy + (y + 1) * mod, color, color) if bit?(x,y) end end png end
to_s(pad: 2)
click to toggle source
# File lib/dmtx/data_matrix.rb, line 62 def to_s(pad: 2) (0..(height - 1)).inject('') do |s, y| (0..(width - 1)).inject(s) { |t, x| t << (bit?(x,y) ? '██' : ' ') } << "\n" end end
to_svg(dim: 256, pad: 2, bgcolor: nil, color: '
click to toggle source
# File lib/dmtx/data_matrix.rb, line 68 def to_svg(dim: 256, pad: 2, bgcolor: nil, color: '#000') raise ArgumentError, 'illegal dimension' unless dim > 0 raise ArgumentError, 'illegal padding' unless pad >= 0 color ||= '#000' path = '' sx = width + pad * 2 sy = height + pad * 2 mx = [1, 0, 0, 1, pad, pad] y = height while y > 0 y -= 1 d = 0 x = width while x > 0 x -= 1 path << "M#{x},#{y}h1v1h-1v-1z" if bit?(x,y) end end builder = Builder::XmlMarkup.new builder.tag! 'svg', xmlns: 'http://www.w3.org/2000/svg', viewBox: [0, 0, sx, sy].join(' '), width: dim * sx / sy, height: dim, fill: color, 'shape-rendering' => 'crispEdges', version: '1.1' do |svg| svg.path fill: bgcolor, d: "M0,0v#{sy}h#{sx}V0H0Z" if bgcolor svg.path transform: "matrix(#{mx.map(&:to_s).join(',')})", d: path end builder.target! end
Private Instance Methods
ascii_encode(t)
click to toggle source
# File lib/dmtx/data_matrix.rb, line 128 def ascii_encode(t) bytes, result = t.bytes, [] while c = bytes.shift if !bytes.empty? && c > 47 && c < 58 && bytes.first > 47 && bytes.first < 58 result << (c - 48) * 10 + bytes.shift + 82 elsif c > 127 result << 235 result << ((c - 127) & 255) else result << c + 1 end end result end
base_encode(t)
click to toggle source
# File lib/dmtx/data_matrix.rb, line 143 def base_encode(t) bytes, result = t.bytes, [231] result << (37 + (bytes.size / 250) & 255) if bytes.size > 250 result << (bytes.size % 250 + 149 * (result.size + 1) % 255 + 1 & 255) bytes.each { |c| result << (c + 149 * (result.size + 1) % 255 + 1 & 255) } result end
bit!(x, y)
click to toggle source
# File lib/dmtx/data_matrix.rb, line 119 def bit!(x, y) @m[y] ||= [] @m[y][x] = 1 end
bit?(x, y)
click to toggle source
# File lib/dmtx/data_matrix.rb, line 124 def bit?(x, y) @m[y] && @m[y][x] end
edifact_encode(t)
click to toggle source
# File lib/dmtx/data_matrix.rb, line 151 def edifact_encode(t) bytes = t.bytes return [] if bytes.any? { |c| c < 32 || c > 94 } l = (bytes.size + 1) & -4 cw = 0 result = l > 0 ? [240] : [] (0..(l - 1)).each do |i| ch = i < l - 1 ? bytes[i] : 31 cw = cw * 64 + (ch & 63) if i & 3 == 3 result << (cw >> 16) result << (cw >> 8 & 255) result << (cw & 255) cw = 0 end end return result if l > bytes.size result.concat ascii_encode(bytes[(l == 0 ? 0 : l - 1)..-1].to_a.pack('C*')) end
encode(text, rct)
click to toggle source
# File lib/dmtx/data_matrix.rb, line 217 def encode(text, rct) enc = encodings(text).values.reject(&:empty?).min_by(&:size) el = enc.size nc = nr = 1 j = -1 b = 1 rs = [] rc = [] lg = [] ex = [] if rct && el < 50 k = [16, 7, 28, 11, 24, 14, 32, 18, 32, 24, 44, 28] begin w = k[j += 1] h = 6 + (j & 12) l = w * h / 8 end while l - k[j += 1] < el nc = 2 if w > 25 else w = h = 6 i = 2 k = [5, 7, 10, 12, 14, 18, 20, 24, 28, 36, 42, 48, 56, 68, 84, 112, 144, 192, 224, 272, 336, 408, 496, 620] begin j += 1 return [0, 0] if j == k.size i = 4 + i & 12 if w > 11 * i w = h += i l = (w * h) >> 3 end while l - k[j] < el nr = nc = 2 * (w / 54) + 2 if w > 27 b = 2 * (l >> 9) + 2 if l > 255 end s = k[j] fw = w / nc fh = h / nr # first padding if el < l - s enc[el] = 129 el += 1 end # more padding while el < l - s enc[el] = (((149 * (el += 1)) % 253) + 130) % 254 # WTF end s /= b # log / exp table of Galois field i, j = 0, 1 while i < 255 ex[i] = j lg[j] = i j += j j ^= 301 if j > 255 i += 1 end # RS generator polynomial rs[s], i = 0, 1 while i <= s j = s - i rs[j] = 1 while j < s rs[j] = rs[j + 1] ^ ex[(lg[rs[j]] + i) % 255] j += 1 end i += 1 end # RS correction data for each block c = 0 while c < b i = 0 while i <= s rc[i] = 0 i += 1 end i = c while i < el j = 0 x = rc[0] ^ enc[i] while j < s rc[j] = rc[j + 1] ^ (x.nonzero? ? ex[(lg[rs[j]] + lg[x]) % 255] : 0) j += 1 end i += b end # interleaved correction data i = 0 while i < s enc[el + c + i * b] = rc[i] i += 1 end c += 1 end # layout perimeter finder pattern # horizontal i = 0 while i < h + 2 * nr j = 0 while j < w + 2 * nc bit!(j, i + fh + 1) bit!(j, i) if j & 1 == 0 j += 1 end i += fh + 2 end # vertical i = 0 while i < w + 2 * nc j = 0 while j < h bit!(i, j + (j / fh) * 2 + 1) bit!(i + fw + 1, j + (j / fh) * 2) if j & 1 == 1 j += 1 end i += fw + 2 end s, c, r = 2, 0, 4 b = [0, 0, -1, 0, -2, 0, 0, -1, -1, -1, -2, -1, -1, -2, -2, -2] # diagonal steps i = 0 while i < l if r == h - 3 && c == -1 # corner A layout k = [w, 6 - h, w, 5 - h, w, 4 - h, w, 3 - h, w - 1, 3 - h, 3, 2, 2, 2, 1, 2] elsif r == h + 1 && c == 1 && w & 7 == 0 && h & 7 == 6 # corner D layout k = [w - 2, -h, w - 3, -h, w - 4, -h, w - 2, -1 - h, w - 3, -1 - h, w - 4, -1 - h, w - 2, -2, -1, -2] else if r == 0 && c == w - 2 && (w & 3).nonzero? # corner B: omit upper left r -= s c += s next end if r < 0 || c >= w || r >= h || c < 0 # outside s = -s r += 2 + s / 2 c += 2 - s / 2 while r < 0 || c >= w || r >= h || c < 0 r -= s c += s end end if r == h - 2 && c == 0 && (w & 3).nonzero? # corner B layout k = [w - 1, 3 - h, w - 1, 2 - h, w - 2, 2 - h, w - 3, 2 - h, w - 4, 2 - h, 0, 1, 0, 0, 0, -1] elsif r == h - 2 && c == 0 && w & 7 == 4 # corner C layout k = [w - 1, 5 - h, w - 1, 4 - h, w - 1, 3 - h, w - 1, 2 - h, w - 2, 2 - h, 0, 1, 0, 0, 0, -1] elsif r == 1 && c == w - 1 && w & 7 == 0 && h & 7 == 6 # omit corner D r -= s c += s next else # nominal L-shape layout k = b end end # layout each bit el = enc[i] i += 1 j = 0 while el > 0 if (el & 1).nonzero? x = c + k[j] y = r + k[j + 1] # wrap around if x < 0 x += w y += 4 - ((w + 4) & 7) end if y < 0 y += h x += 4 - ((h + 4) & 7) end # region gap bit!(x + 2 * (x / fw) + 1, y + 2 * (y / fh) + 1) end j += 2 el >>= 1 end r -= s c += s end # unfilled corner i = w while (i & 3).nonzero? bit!(i, i) i -= 1 end @width = w + 2 * nc @height = h + 2 * nr end
encodings(msg)
click to toggle source
# File lib/dmtx/data_matrix.rb, line 208 def encodings(msg) { ascii: ascii_encode(msg), c40: text_encode(msg, C40), txt: text_encode(msg, TEXT), x12: text_encode(msg, X12), edifact: edifact_encode(msg), base: base_encode(msg) } end
text_encode(t, s)
click to toggle source
# File lib/dmtx/data_matrix.rb, line 171 def text_encode(t, s) cc = cw = 0 bytes, result = t.bytes, [s[0]] l = bytes.size push = lambda do |v| cw = 40 * cw + v cc += 1 if cc == 3 result << ((cw += 1) >> 8) result << (cw & 255) cc = cw = 0 end end i = 0 while i < l break if 0 == cc && i == l - 1 ch = bytes[i] if ch > 127 && 238 != result[0] push.(1) push.(30) ch -= 128 end j = 1 j += 3 while ch > s[j] x = s[j + 1] return [] if 8 == x || (9 == x && 0 == cc && i == l - 1) break if x < 5 && cc == 2 && i == l - 1 push.(x) if x < 5 push.(ch - s[j + 2]) i += 1 end push.(0) if 2 == cc && 238 != result[0] result << 254 result.concat ascii_encode(bytes[(i - cc)..-1].to_a.pack('C*')) if cc > 0 || i < l result end