Class: Barcode1DTools::UPC_E

Inherits:
Barcode1D show all
Defined in:
lib/barcode1dtools/upc_e.rb

Overview

Barcode1DTools::UPC_E - Create pattern for UPC-E barcodes. The value encoded is an 6-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 = '394932'
bc = Barcode1DTools::UPC_E.new(num)
pattern = bc.bars
rle_pattern = bc.rle
width = bc.width
check_digit = Barcode1DTools::UPC_E.generate_check_digit_for(num)

Other Information

A UPC-E barcode is an abbreviated form of UPC-A, but can encode only a few codes. The checksum is derived from the full UPC-A digit sequence rather than the 6 digits of the UPC-E. The checksum is encoded as the parity, with the bar patterns the same as the left side of a standard UPC-A.

The last digit of the UPC-E determines the pattern used to convert to a UPC-A.

UPC-E      UPC-A equivalent
2 digits for manufacturer code (plus last digit), 3 digits for product
XXNNN0     0XX000-00NNN
XXNNN1     0XX100-00NNN
XXNNN2     0XX200-00NNN
3 digits for manufacturer code, 2 digits for product
XXXNN3     0XXX00-000NN
4 digits for manufacturer code, 1 digit for product
XXXXN4     0XXXX0-0000N
5 digits for manufacturer code, 1 digit for product (5-9)
XXXXX5     0XXXXX-00005
XXXXX6     0XXXXX-00006
XXXXX7     0XXXXX-00007
XXXXX8     0XXXXX-00008
XXXXX9     0XXXXX-00009

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 UPC-E is made for smaller items. Generally, they are rendered with the number system digit (0) on the left of the bars and the checksum on the right. The 6-digit payload is shown below the bars with the end guard bars extending half-way down the digits. The number system and check digit might be rendered in a slightly smaller font size. The UPC-E uses the same bar patterns as the left half of a regular UPC-A, but there is no middle pattern and the right guard pattern has an extra line/space pair.

Constant Summary collapse

LEFT_PATTERNS =

Using the left patterns from UPC-A

UPC_A::LEFT_PATTERNS
LEFT_PATTERNS_RLE =

Using the left patterns from UPC-A

UPC_A::LEFT_PATTERNS_RLE
PARITY_PATTERNS =

UPC-E uses a different set of parity patterns to encode the check digit.

{
  '0' => 'eeeooo',
  '1' => 'eeoeoo',
  '2' => 'eeooeo',
  '3' => 'eeoooe',
  '4' => 'eoeeoo',
  '5' => 'eooeeo',
  '6' => 'eoooee',
  '7' => 'eoeoeo',
  '8' => 'eoeooe',
  '9' => 'eooeoe',
}
LEFT_GUARD_PATTERN =

Left guard pattern

'101'
RIGHT_GUARD_PATTERN =

Right guard pattern

'010101'
LEFT_GUARD_PATTERN_RLE =

Left guard pattern as RLE

'111'
RIGHT_GUARD_PATTERN_RLE =

Right guard pattern as RLE

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

Instance Attribute Summary collapse

Attributes inherited from Barcode1D

#check_digit, #encoded_string, #options, #value

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Barcode1D

bars_to_rle, rle_to_bars, rle_to_wn, wn_to_rle

Constructor Details

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

Create a new UPC-E object for a given value. Options are :line_character, :space_character, and :checksum_included.



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/barcode1dtools/upc_e.rb', line 254

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], md[2].to_i
  else
    # need to add a checksum
    @value = value.to_s
    @check_digit = self.class.generate_check_digit_for(@value)
    @encoded_string = "#{@value}#{@check_digit}"
  end

  @upca_value = self.class.upce_value_to_upca_value(@value)
  md = @upca_value.match(/^(\d)(\d{5})(\d{5})/)
  @number_system, @manufacturers_code, @product_code = md[1], md[2], md[3]
end

Instance Attribute Details

#manufacturers_codeObject (readonly)

For UPC-E - the manufacturer’s code part of the item number



121
122
123
# File 'lib/barcode1dtools/upc_e.rb', line 121

def manufacturers_code
  @manufacturers_code
end

#number_systemObject (readonly)

For UPC-E - the number system part of the item number



119
120
121
# File 'lib/barcode1dtools/upc_e.rb', line 119

def number_system
  @number_system
end

#product_codeObject (readonly)

For UPC-E - the product code part of the item number



123
124
125
# File 'lib/barcode1dtools/upc_e.rb', line 123

def product_code
  @product_code
end

#upca_valueObject (readonly)

For UPC-E - the UPC-A value for this UPC-E value



125
126
127
# File 'lib/barcode1dtools/upc_e.rb', line 125

def upca_value
  @upca_value
end

Class Method Details

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

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

Returns:

  • (Boolean)


130
131
132
133
134
135
136
137
138
# File 'lib/barcode1dtools/upc_e.rb', line 130

def can_encode?(value, options = nil)
  if !options
    value.to_s =~ /^0?[0-9]{6,7}$/
  elsif (options[:checksum_included])
    value.to_s =~ /^0?[0-9]{7}$/
  else
    value.to_s =~ /^0?[0-9]{6}$/
  end
end

.decode(str) ⇒ Object

Decodes a bar pattern or rle string that represents a UPC-E. Returns a UPC_E object.



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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/barcode1dtools/upc_e.rb', line 156

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

  # See if the string is reversed
  if str[0..5] == RIGHT_GUARD_PATTERN_RLE && str[30..32] == LEFT_GUARD_PATTERN_RLE
    str.reverse!
  end

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

  parity_sequence = ''
  digits = ''
  initial_offset = LEFT_GUARD_PATTERN_RLE.length

  # Decode
  (0..5).each do |offset|
    found = false
    digit_rle = str[(initial_offset + offset*4),4]
    ['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'..'9').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_E.new('0' + 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”.



142
143
144
# File 'lib/barcode1dtools/upc_e.rb', line 142

def generate_check_digit_for(value)
  UPC_A.generate_check_digit_for(self.upce_value_to_upca_value(value))
end

.upca_value_to_upce_value(value, options = {}) ⇒ Object

Converts the given UPC-A value to a UPC-E value.



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/barcode1dtools/upc_e.rb', line 232

def upca_value_to_upce_value(value, options = {})
  raise UnencodableCharactersError unless UPC_A.can_encode?(value, options)
  value = value % 10 if options[:checksum_included]
  value = sprintf('%011d', value.to_i)
  if value =~ /^0(\d\d\d\d[1-9])0000([5-9])/
    upce_value = "0#{$1}#{$2}"
  elsif value =~ /^0(\d\d\d[1-9])00000(\d)/
    upce_value = "0#{$1}#{$2}4"
  elsif value =~ /^0(\d\d)([012])0000(\d\d\d)/
    upce_value = "0#{$1}#{$3}#{$2}"
  elsif value =~ /^0(\d\d[3-9])00000(\d\d)/
    upce_value = "0#{$1}#{$2}3"
  else
    raise UnencodableCharactersError, "Cannot change UPC-A #{value} to UPC-E"
  end
  upce_value
end

.upce_value_to_upca_value(value, options = {}) ⇒ Object

Converts the given UPC-E value to a UPC-A value.



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/barcode1dtools/upc_e.rb', line 212

def upce_value_to_upca_value(value, options = {})
  raise UnencodableCharactersError unless self.can_encode?(value, options)
  # remove the check digit if it was included
  value = value.to_i % 10 if options[:checksum_included]
  value = sprintf('%06d', value.to_i)
  if value =~ /(\d\d)(\d\d\d)([012])/
    upca_value = "0#{$1}#{$3}0000#{$2}"
  elsif value =~ /(\d\d\d)(\d\d)(3)/
    upca_value = "0#{$1}00000#{$2}"
  elsif value =~ /(\d\d\d\d)(\d)(4)/
    upca_value = "0#{$1}00000#{$2}"
  elsif value =~ /(\d\d\d\d\d)([5-9])/
    upca_value = "0#{$1}0000#{$2}"
  else
    raise UnencodableCharactersError, "Cannot change UPC-E #{value} to UPC-A"
  end
  upca_value
end

.validate_check_digit_for(value) ⇒ Object

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



148
149
150
151
152
# File 'lib/barcode1dtools/upc_e.rb', line 148

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

Instance Method Details

#barsObject

Returns a bar pattern.



294
295
296
# File 'lib/barcode1dtools/upc_e.rb', line 294

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

#rleObject

Returns a run-length-encoded string representation.



284
285
286
287
288
289
290
291
# File 'lib/barcode1dtools/upc_e.rb', line 284

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

#to_upc_aObject

Returns a UPC_A object with the same value.



304
305
306
# File 'lib/barcode1dtools/upc_e.rb', line 304

def to_upc_a
  UPC_A.new(self.class.upce_value_to_upca_value(@value), options.merge(:checksum_included => false))
end

#widthObject

Returns the total unit width of the bar code.



299
300
301
# File 'lib/barcode1dtools/upc_e.rb', line 299

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

#wnObject

W/N strings are not usable with EAN-style codes.



279
280
281
# File 'lib/barcode1dtools/upc_e.rb', line 279

def wn
  raise NotImplementedError
end