Class: Barcode1DTools::EAN8

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

Overview

Barcode1DTools::EAN_8 - Create pattern for EAN-8 barcodes. The value encoded is a 7-digit number, 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 = '96385074'
bc = Barcode1DTools::EAN8.new(num)
pattern = bc.bars
rle_pattern = bc.rle
width = bc.width
check_digit = Barcode1DTools::EAN83.generate_check_digit_for(num)

Other Information

The object created is immutable.

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 “wnstr” for EAN & UPC style barcodes because the bars and spaces are variable width from 1 to 4 units.

An EAN-8 barcode has 3 elements:

  1. A 2 or 3 digit “number system” designation

  2. A 4 or 5 digit manufacturer’s code

  3. A single digit checksum

Note than an EAN-8 is not analogous to a UPC-E. In particular, there is no way to create an EAN-13 from and EAN-8 and vice versa. The numbers are assigned within EAN-8 by a central authority.

The bar patterns are the same as EAN-13, with nothing encoded in the parity. All bars on the left use the “odd” parity set.

Rendering

When rendered, two sets of four digits are shown at the bottom of the code, aligned with the bottom of the code, and with the middle guard pattern bars extending down between them. A supplemental 2 or 5 may be used.

Constant Summary collapse

LEFT_PATTERNS =

Left patterns from EAN-13

EAN13::LEFT_PATTERNS
LEFT_PATTERNS_RLE =

Left rle patterns from EAN-13

EAN13::LEFT_PATTERNS_RLE
RIGHT_PATTERNS =

Right patterns from EAN-13

EAN13::RIGHT_PATTERNS
RIGHT_PATTERNS_RLE =

Right rle patterns from EAN-13

EAN13::RIGHT_PATTERNS_RLE
SIDE_GUARD_PATTERN =

Guard pattern from EAN-13

EAN13::SIDE_GUARD_PATTERN
MIDDLE_GUARD_PATTERN =

Middle Guard pattern from EAN-13

EAN13::MIDDLE_GUARD_PATTERN
SIDE_GUARD_PATTERN_RLE =

Guard pattern from EAN-13 as an rle

EAN13::SIDE_GUARD_PATTERN_RLE
MIDDLE_GUARD_PATTERN_RLE =

Middle Guard pattern from EAN-13 as an rle

EAN13::MIDDLE_GUARD_PATTERN_RLE
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

bar_pair, bars_to_rle, rle_to_bars, rle_to_wn, wn_pair, wn_to_rle

Constructor Details

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

Create an EAN8 object with a given value. Options are :line_character, :space_character, and :checksum_included.



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

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 = sprintf('%08d', value.to_i)
    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 = sprintf('%07d', value.to_i)
    @check_digit = self.class.generate_check_digit_for(@value)
    @encoded_string = "#{@value}#{@check_digit}"
  end

  md = @value.match(/^(\d{3})(\d{4})/)
  @number_system, @product_code = md[1], md[2]
end

Instance Attribute Details

#number_systemObject (readonly)

Specific for EAN-8 - the number system part of the payload



95
96
97
# File 'lib/barcode1dtools/ean8.rb', line 95

def number_system
  @number_system
end

#product_codeObject (readonly)

Specific for EAN-8 - the product code part of the payload



97
98
99
# File 'lib/barcode1dtools/ean8.rb', line 97

def product_code
  @product_code
end

Class Method Details

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

Returns true if value can be encoded in EAN-8 - must be 7-8 digits.

Returns:

  • (Boolean)


101
102
103
104
105
106
107
108
109
# File 'lib/barcode1dtools/ean8.rb', line 101

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

.decode(str) ⇒ Object

Decode a string representing an rle or bar pattern EAN-13. Note that the string might be backward or forward. This will return an EAN8 object.



131
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/barcode1dtools/ean8.rb', line 131

def decode(str)
  if str.length == 67
    # bar pattern
    str = bars_to_rle(str)
  elsif str.length == 43
    # rle
  else
    raise UnencodableCharactersError, "Pattern must be 67 unit bar pattern or 43 character rle."
  end

  # Check the guard patterns
  unless str[0..2] == SIDE_GUARD_PATTERN_RLE && str[40..42] == SIDE_GUARD_PATTERN_RLE && str[19..23] == MIDDLE_GUARD_PATTERN_RLE
    raise UnencodableCharactersError, "Missing or incorrect guard patterns"
  end

  # Now I have an rle pattern, simply need to decode
  # according to the LEFT_PATTERNS_RLE, keeping track
  # of the parity for each position.

  # Set up the decoder
  left_parity_sequence = ''
  left_digits = ''
  right_parity_sequence = ''
  right_digits = ''
  left_initial_offset = SIDE_GUARD_PATTERN_RLE.length
  right_initial_offset = SIDE_GUARD_PATTERN_RLE.length + (4*4) + MIDDLE_GUARD_PATTERN_RLE.length

  # Decode the left side
  (0..3).each do |left_offset|
    found = false
    digit_rle = str[(left_initial_offset + left_offset*4),4]
    ['o','e'].each do |parity|
      ('0'..'9').each do |digit|
        if LEFT_PATTERNS_RLE[digit][parity] == digit_rle
          left_parity_sequence += parity
          left_digits += digit
          found = true
          break
        end
      end
    end
    raise UndecodableCharactersError, "Invalid sequence: #{digit_rle}" unless found
  end

  # Decode the right side
  (0..3).each do |right_offset|
    found = false
    digit_rle = str[(right_initial_offset + right_offset*4),4]
    ['o','e'].each do |parity|
      ('0'..'9').each do |digit|
        if LEFT_PATTERNS_RLE[digit][parity] == digit_rle
          right_parity_sequence += parity
          right_digits += digit
          found = true
          break
        end
      end
    end
    raise UndecodableCharactersError, "Invalid sequence: #{digit_rle}" unless found
  end

  # If left parity sequence is 'eeee', the string is reversed
  if left_parity_sequence == 'eeee'
    left_digits, right_digits, left_parity_sequence = right_digits.reverse, left_digits.reverse, right_parity_sequence.reverse.tr('eo','oe')
  end

  # Debugging
  #puts "Left digits: #{left_digits} Left parity: #{left_parity_sequence}"
  #puts "Right digits: #{right_digits} Right parity: #{right_parity_sequence}"

  EAN8.new(left_digits + right_digits, :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”.



113
114
115
116
117
118
# File 'lib/barcode1dtools/ean8.rb', line 113

def generate_check_digit_for(value)
  raise UnencodableCharactersError unless self.can_encode?(value, :checksum_included => false)
  mult = 1
  value = value.split('').inject(0) { |a,c| mult = 4 - mult ; a + c.to_i * mult }
  (10 - (value % 10)) % 10
end

.validate_check_digit_for(value) ⇒ Object

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



122
123
124
125
126
# File 'lib/barcode1dtools/ean8.rb', line 122

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

Instance Method Details

#barsObject

Returns 1s and 0s (for “black” and “white”)



248
249
250
# File 'lib/barcode1dtools/ean8.rb', line 248

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

#rleObject

returns a run-length-encoded string representation.



238
239
240
241
242
243
244
245
# File 'lib/barcode1dtools/ean8.rb', line 238

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

#widthObject

Returns the total unit width of the bar code.



253
254
255
# File 'lib/barcode1dtools/ean8.rb', line 253

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

#wnObject

EAN-based codes cannot create a w/n string, so this will raise an error.



233
234
235
# File 'lib/barcode1dtools/ean8.rb', line 233

def wn
  raise NotImplementedError
end