Skip to content

Instantly share code, notes, and snippets.

@kkosuge
Last active September 7, 2015 11:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kkosuge/f546324aac18d60ce271 to your computer and use it in GitHub Desktop.
Save kkosuge/f546324aac18d60ce271 to your computer and use it in GitHub Desktop.
Codabar(NW-7)のバーコードを生成する barby gem のモジュール
require 'barby'
require 'barby/barcode'
require 'barby/barcode/code_128'
require 'barby/outputter/ascii_outputter'
require 'barby/outputter/png_outputter'
require 'pry'
require 'pp'
module Barby
class Codabar < Barcode1D
# Character sequence - 0-based offset in this string is character
# number
CHAR_SEQUENCE = "0123456789-$:/.+ABCD"
# Patterns for making bar codes
PATTERNS = {
'0'=> {'val'=>0 ,'wn'=>'nnnnnww'},
'1'=> {'val'=>1 ,'wn'=>'nnnnwwn'},
'2'=> {'val'=>2 ,'wn'=>'nnnwnnw'},
'3'=> {'val'=>3 ,'wn'=>'wwnnnnn'},
'4'=> {'val'=>4 ,'wn'=>'nnwnnwn'},
'5'=> {'val'=>5 ,'wn'=>'wnnnnwn'},
'6'=> {'val'=>6 ,'wn'=>'nwnnnnw'},
'7'=> {'val'=>7 ,'wn'=>'nwnnwnn'},
'8'=> {'val'=>8 ,'wn'=>'nwwnnnn'},
'9'=> {'val'=>9 ,'wn'=>'wnnwnnn'},
'-'=> {'val'=>10 ,'wn'=>'nnnwwnn'},
'$'=> {'val'=>11 ,'wn'=>'nnwwnnn'},
':'=> {'val'=>12 ,'wn'=>'wnnnwnw'},
'/'=> {'val'=>13 ,'wn'=>'wnwnnnw'},
'.'=> {'val'=>14 ,'wn'=>'wnwnwnn'},
'+'=> {'val'=>15 ,'wn'=>'nnwnwnw'},
'A'=> {'val'=>16 ,'wn'=>'nnwwnwn'},
'B'=> {'val'=>17 ,'wn'=>'nwnwnnw'},
'C'=> {'val'=>18 ,'wn'=>'nnnwnww'},
'D'=> {'val'=>19 ,'wn'=>'nnnwwwn'},
'T'=> {'val'=>16 ,'wn'=>'nnwwnwn'},
'N'=> {'val'=>17 ,'wn'=>'nwnwnnw'},
'*'=> {'val'=>18 ,'wn'=>'nnnwnww'},
'E'=> {'val'=>19 ,'wn'=>'nnnwwwn'}
}
DEFAULT_OPTIONS = {
:line_character => '1',
:space_character => '0',
:w_character => 'w',
:n_character => 'n',
:wn_ratio => '3',
:varied_wn_ratio => false
}
# Holds the start character
attr_reader :start_character
# Holds the stop character
attr_reader :stop_character
# The actual payload (between start/stop characters)
attr_reader :payload
class << self
def rle_to_bars(rle_str, options = {})
str=0
rle_str.split('').inject('') { |a,c| str = 1 - str; a + (str.to_s * c.to_i) }.tr('01', bar_pair(options))
end
# Generate rle pattern from bar string
def bars_to_rle(bar_str, options = {})
bar_str.scan(/(.)(\1*)/).collect { |char,rest| 1+rest.length }.join
end
# Generate rle pattern from wn string
def wn_to_rle(wn_str, options = {})
wn_str.tr(wn_pair(options), (options[:wn_ratio] || 2).to_s + '1')
end
# Generate wn pattern from rle string
def rle_to_wn(rle_str, options = {})
rle_str.tr('123456789', 'nwwwwwwww').tr('wn', wn_pair(options))
end
# Get an "wn" pair from the options
def wn_pair(options = {})
(options[:w_character] || 'w') + (options[:n_character] || 'n')
end
# Get a bar pair from the options
def bar_pair(options = {})
(options[:space_character] || '0').to_s + (options[:line_character] || '1').to_s
end
# Returns true if the value presented can be encoded in a
# Codabar barcode. Codabar can encode digits, dash,
# dollar, colon, forward slash, dot, and plus. The string
# must start and stop with start/stop characters.
def can_encode?(value)
value.to_s =~ /\A[ABCD][0-9\$:\/\.\+\-]*[ABCD]\z/ || value.to_s =~ /\A[TN\*E][0-9\$:\/\.\+\-]*[TN\*E]\z/
end
# Generate a check digit. For Codabar, this
# will raise a NotImplementedError.
def generate_check_digit_for(value)
raise NotImplementedError
end
# Validate the check digit. For Codabar, this
# will raise a NotImplementedError.
def validate_check_digit_for(value)
raise NotImplementedError
end
# Decode a string in rle format. This will return a Codabar
# object.
def decode(str, options = {})
if str =~ /[^1-3]/ && str =~ /[^wn]/
raise UnencodableCharactersError, "Pattern must be rle or wn"
end
# ensure a wn string
if str =~ /[1-3]/
str = str.tr('123','nww')
end
start_stop_pattern_match = Regexp.new(['A','B','C','D'].collect { |c| PATTERNS[c]['wn'] }.join('|'))
if str.reverse =~ /\A#{start_stop_pattern_match}n.*?#{start_stop_pattern_match}\z/
str.reverse!
end
unless str =~ /\A(#{start_stop_pattern_match}n.*?#{start_stop_pattern_match})\z/
raise UnencodableCharactersError, "Start/stop pattern is not detected."
end
# Adding an "n" to make it easier to scan
wn_pattern = $1 + 'n'
# Each pattern is 4 bars and 3 spaces, with a space between.
unless wn_pattern.size % 8 == 0
raise UnencodableCharactersError, "Wrong number of bars."
end
decoded_string = ''
wn_pattern.scan(/(.{7})n/).each do |chunk|
chunk = chunk.first
found = false
PATTERNS.each do |char,hsh|
if !['T', 'E', '*', 'N'].include?(char) && chunk == hsh['wn']
decoded_string += char
found = true
break;
end
end
raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
end
Codabar.new(decoded_string)
end
end
# Create a new Codabar object. Options are :line_character,
# :space_character, :w_character, :n_character, and :varied_wn_ratio.
def initialize(value, options = {})
@options = DEFAULT_OPTIONS.merge(options)
# Can we encode this value?
raise UnencodableCharactersError unless self.class.can_encode?(value)
@value = value.to_s
@check_digit = nil
@encoded_string = @value
md = @value.match(/\A([ABCDTNE\*])(.*?)([ABCDTNE\*])\z/)
@start_character, @payload, @stop_character = md[1], md[2], md[3]
end
# Returns a string of "w" or "n" ("wide" and "narrow")
def wn
@wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
end
def encoding
bars
end
# Returns a run-length-encoded string representation
def rle
if @options[:varied_wn_ratio]
@rle ||= @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.collect { |p| p.tr('wn',(p=~/.*w.*w.*w/ ? '21' : '31')) }.join('1')
else
@rle ||= self.class.wn_to_rle(self.wn, @options)
end
end
# Returns the bar pattern
def bars
@bars ||= self.class.rle_to_bars(self.rle, @options)
end
# Returns the total unit width of the bar code
def width
@width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
end
def two_dimensional?
false
end
private
# Creates the actual w/n pattern. Note that there is a narrow space
# between each character.
def wn_str
@wn_str ||= @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.join('n')
end
end
end
codabar = Barby::Codabar.new('A29322930C')
blob = Barby::PngOutputter.new(codabar).to_png
File.open('barcode.png', 'w'){|f| f.write blob }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment