Class: Barcode1DTools::UPC_Supplemental_2

Inherits:
Barcode1D
  • Object
show all
Defined in:
lib/barcode1dtools/upc_supplemental_2.rb

Overview

Barcode1DTools::UPC_Supplemental_2 - Create pattern for UPC Supplemental 2 barcodes. The value encoded is an 2-digit integer, and a checksum digit will be added. You can add the option :checksum_included => true when initializing to specify that you have already included a checksum.

Example

num = '24'
bc = Barcode1DTools::UPC_Supplemental_2.new(num)
pattern = bc.bars
rle_pattern = bc.rle
width = bc.width
check_digit = Barcode1DTools::UPC_Supplemental_2.generate_check_digit_for(num)

Other Information

This type of barcode consists of 2 digits, and a check digit (simply a modulus 4 of the number encoded) that is encoded in the “parity” of the two barcode digits. The bar patterns are the same as the left half of a standard UPC-A.

The 2-digit supplement is generally used on periodicals as an “issue number”, so that the UPC-A code may remain the same across issues. The two are scanned together, and typically the scanner will return the two digits of the supplemental barcode immediately following the check digit from the main UPC-A. You will likely need to use the Barcode::UPC_A module in addition to this one to create the full code.

Formats

There are two formats for the returned pattern (wn format is not available):

bars - 1s and 0s specifying black lines and white spaces. Actual characters can be changed from “1” and 0“ with options :line_character and :space_character.

rle - Run-length-encoded version of the pattern. The first number is always a black line, with subsequent digits alternating between spaces and lines. The digits specify the width of each line or space.

The “width” method will tell you the total end-to-end width, in units, of the entire barcode.

Unlike some of the other barcodes, e.g. Code 3 of 9, there is no “w/n” format for EAN & UPC style barcodes because the bars and spaces are variable width from 1 to 4 units.

Rendering

The 2-digit supplement is positioned to the right of the main UPC code, and the human-readable digits are usually printed above the supplemental barcode. UPC-A is generally rendered at one inch across, then there’s a 1/8th inch gap, then the supplemental. A UPC-A is 95 units wide, so the gap is 24 units wide. The supplemental barcode is 20 units wide. The author hasn’t viewed the specification, but note that the UPC (and more generally EAN) barcode system never include a bar or space of more than four units width. Given that, the gap should likely be at last 10 units wide.

Constant Summary collapse

LEFT_PATTERNS =

The bar patterns are the left bar patterns of UPC-A/EAN-13

UPC_A::LEFT_PATTERNS
LEFT_PATTERNS_RLE =

The rle bar patterns are the left bar patterns of UPC-A/EAN-13

UPC_A::LEFT_PATTERNS_RLE
PARITY_PATTERNS =

Parity patterns, essentially binary counting where “e” is “1” and “o” is “0”.

{
  '0' => 'oo',
  '1' => 'oe',
  '2' => 'eo',
  '3' => 'ee',
}
LEFT_GUARD_PATTERN =

Left guard pattern

'1011'
MIDDLE_GUARD_PATTERN =

Middle guard pattern

'01'
LEFT_GUARD_PATTERN_RLE =

Left guard pattern as an rle

'112'
MIDDLE_GUARD_PATTERN_RLE =

Middle guard pattern as an rle

'11'
DEFAULT_OPTIONS =
{
  :line_character => '1',
  :space_character => '0'
}

Instance Attribute Summary

Attributes inherited from Barcode1D

#check_digit, #encoded_string, #options, #value

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Barcode1D

bar_pair, bars_to_rle, rle_to_bars, rle_to_wn, wn_pair, wn_to_rle

Constructor Details

#initialize(value, options = {}) ⇒ UPC_Supplemental_2

Create a new UPC_Supplemental_2 object from a given value. Options are :line_character, :space_character, and :checksum_included.



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/barcode1dtools/upc_supplemental_2.rb', line 190

def initialize(value, options = {})

  @options = DEFAULT_OPTIONS.merge(options)

  # Can we encode this value?
  raise UnencodableCharactersError unless self.class.can_encode?(value, @options)

  if @options[:checksum_included]
    @encoded_string = value.to_s
    raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
    md = @encoded_string.match(/^(\d+?)(\d)$/)
    @value, @check_digit = md[1].to_i, md[2].to_i
  else
    # need to add a checksum
    @value = value.to_i
    @check_digit = self.class.generate_check_digit_for(@value)
    @encoded_string = sprintf('%02d%1d',@value,@check_digit)
  end
end

Class Method Details

.can_encode?(value, options = nil) ⇒ Boolean

Returns true or false - must be 1-3 digits. This also handles the case where the leading 0 is added.

Returns:

  • (Boolean)


106
107
108
109
110
111
112
113
114
# File 'lib/barcode1dtools/upc_supplemental_2.rb', line 106

def can_encode?(value, options = nil)
  if !options
    value.to_s =~ /^\d{1,3}$/
  elsif (options[:checksum_included])
    value.to_s =~ /^\d\d\d?$/
  else
    value.to_s =~ /^\d\d?$/ && (0..99).include?(value.to_i)
  end
end

.decode(str) ⇒ Object

Decodes a bar or rle pattern and returns a UPC_Supplemental_2 object.



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/barcode1dtools/upc_supplemental_2.rb', line 132

def decode(str)
  if str.length == 20
    # bar pattern
    str = bars_to_rle(str)
  elsif str.length == 13 && str =~ /^[1-9]+$/
    # rle
  else
    raise UnencodableCharactersError, "Pattern must be 20 unit bar pattern or 13 character rle."
  end

  # This string is "aaabbbbccdddd" where "aaa" is the left
  # guard pattern, "bbbb" is the first digit, "cc" is the
  # intra-digit guard pattern, and "dddd" is the second
  # digit.

  # Check the guard patterns
  unless (str[0..2] == LEFT_GUARD_PATTERN_RLE && str[7..8] == MIDDLE_GUARD_PATTERN_RLE)
    raise UnencodableCharactersError, "Missing or incorrect guard patterns"
  end

  parity_sequence = ''
  digits = ''

  # Decode
  [str[3..6], str[9..12]].each do |digit_rle|
    found = false
    ['o','e'].each do |parity|
      ('0'..'9').each do |digit|
        if LEFT_PATTERNS_RLE[digit][parity] == digit_rle
          parity_sequence += parity
          digits += digit
          found = true
          break
        end
      end
    end
    raise UndecodableCharactersError, "Invalid sequence: #{digit_rle}" unless found
  end

  # Now, find the parity digit
  parity_digit = nil
  ('0'..'3').each do |x|
    if PARITY_PATTERNS[x] == parity_sequence
      parity_digit = x
      break
    end
  end

  raise UndecodableCharactersError, "Weird parity: #{parity_sequence}" unless parity_digit

  UPC_Supplemental_2.new(digits + parity_digit, :checksum_included => true)
end

.generate_check_digit_for(value) ⇒ Object

Generates check digit given a string to encode. It assumes there is no check digit on the “value”.



118
119
120
# File 'lib/barcode1dtools/upc_supplemental_2.rb', line 118

def generate_check_digit_for(value)
  value.to_i % 4
end

.validate_check_digit_for(value) ⇒ Object

Validates the check digit given a string - assumes check digit is last digit of string.



124
125
126
127
128
# File 'lib/barcode1dtools/upc_supplemental_2.rb', line 124

def validate_check_digit_for(value)
  raise UnencodableCharactersError unless self.can_encode?(value, :checksum_included => true)
  md = value.match(/^(\d\d)(\d)$/)
  self.generate_check_digit_for(md[1]) == md[2].to_i
end

Instance Method Details

#barsObject

Returns a bar pattern.



227
228
229
# File 'lib/barcode1dtools/upc_supplemental_2.rb', line 227

def bars
  @bars ||= self.class.rle_to_bars(self.rle, @options)
end

#rleObject

Returns a run-length-encoded string representation.



217
218
219
220
221
222
223
224
# File 'lib/barcode1dtools/upc_supplemental_2.rb', line 217

def rle
  if @rle
    @rle
  else
    md = @encoded_string.match(/(\d\d)(\d)$/)
    @rle = gen_rle(md[1], md[2])
  end
end

#widthObject

Returns the total unit width of the bar code.



232
233
234
# File 'lib/barcode1dtools/upc_supplemental_2.rb', line 232

def width
  @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
end

#wnObject

W/N patterns are not usable with EAN-style codes. This will raise an error.



212
213
214
# File 'lib/barcode1dtools/upc_supplemental_2.rb', line 212

def wn
  raise NotImplementedError
end