Class: Barby::Code128

Inherits:
Barcode1D show all
Defined in:
lib/barby/barcode/code_128.rb

Overview

Code 128 barcodes

Note that you must provide a type for each object, either by passing a string as a second parameter to Code128.new or by instantiating one of the child classes.

You can switch type by using the CODEA, CODEB and CODEC characters:

“305” => A “306” => B “307” => C

As an example, here’s one that starts out as type A and then switches to B and then C:

Code128A.new("ABC123\306def\3074567")

GS1-128/EAN-128/UCC-128

To make a GS1-128 code, prefix the data with FNC1 and the Application Identifier:

#AI=00, data=12345
Code128.new("#{Code128::FNC1}0012345")

Direct Known Subclasses

Code128A, Code128B, Code128C, GS1128

Constant Summary collapse

FNC1 =
"\xc1"
FNC2 =
"\xc2"
FNC3 =
"\xc3"
FNC4 =
"\xc4"
CODEA =
"\xc5"
CODEB =
"\xc6"
CODEC =
"\xc7"
SHIFT =
"\xc8"
STARTA =
"\xc9"
STARTB =
"\xca"
STARTC =
"\xcb"
STOP =
'11000111010'
TERMINATE =
'11'
ENCODINGS =
{
  0 => "11011001100", 1 => "11001101100", 2 => "11001100110",
  3 => "10010011000", 4 => "10010001100", 5 => "10001001100",
  6 => "10011001000", 7 => "10011000100", 8 => "10001100100",
  9 => "11001001000", 10 => "11001000100", 11 => "11000100100",
  12 => "10110011100", 13 => "10011011100", 14 => "10011001110",
  15 => "10111001100", 16 => "10011101100", 17 => "10011100110",
  18 => "11001110010", 19 => "11001011100", 20 => "11001001110",
  21 => "11011100100", 22 => "11001110100", 23 => "11101101110",
  24 => "11101001100", 25 => "11100101100", 26 => "11100100110",
  27 => "11101100100", 28 => "11100110100", 29 => "11100110010",
  30 => "11011011000", 31 => "11011000110", 32 => "11000110110",
  33 => "10100011000", 34 => "10001011000", 35 => "10001000110",
  36 => "10110001000", 37 => "10001101000", 38 => "10001100010",
  39 => "11010001000", 40 => "11000101000", 41 => "11000100010",
  42 => "10110111000", 43 => "10110001110", 44 => "10001101110",
  45 => "10111011000", 46 => "10111000110", 47 => "10001110110",
  48 => "11101110110", 49 => "11010001110", 50 => "11000101110",
  51 => "11011101000", 52 => "11011100010", 53 => "11011101110",
  54 => "11101011000", 55 => "11101000110", 56 => "11100010110",
  57 => "11101101000", 58 => "11101100010", 59 => "11100011010",
  60 => "11101111010", 61 => "11001000010", 62 => "11110001010",
  63 => "10100110000", 64 => "10100001100", 65 => "10010110000",
  66 => "10010000110", 67 => "10000101100", 68 => "10000100110",
  69 => "10110010000", 70 => "10110000100", 71 => "10011010000",
  72 => "10011000010", 73 => "10000110100", 74 => "10000110010",
  75 => "11000010010", 76 => "11001010000", 77 => "11110111010",
  78 => "11000010100", 79 => "10001111010", 80 => "10100111100",
  81 => "10010111100", 82 => "10010011110", 83 => "10111100100",
  84 => "10011110100", 85 => "10011110010", 86 => "11110100100",
  87 => "11110010100", 88 => "11110010010", 89 => "11011011110",
  90 => "11011110110", 91 => "11110110110", 92 => "10101111000",
  93 => "10100011110", 94 => "10001011110", 95 => "10111101000",
  96 => "10111100010", 97 => "11110101000", 98 => "11110100010",
  99 => "10111011110", 100 => "10111101110", 101 => "11101011110",
  102 => "11110101110", 103 => "11010000100", 104 => "11010010000",
  105 => "11010011100"
}
VALUES =
{
  'A' => {
    0 => " ",      1 => "!",        2 => "\"",
    3 => "#",       4 => "$",        5 => "%",
    6 => "&",       7 => "'",        8 => "(",
    9 => ")",       10 => "*",       11 => "+",
    12 => ",",      13 => "-",       14 => ".",
    15 => "/",      16 => "0",       17 => "1",
    18 => "2",      19 => "3",       20 => "4",
    21 => "5",      22 => "6",       23 => "7",
    24 => "8",      25 => "9",       26 => ":",
    27 => ";",      28 => "<",       29 => "=",
    30 => ">",      31 => "?",       32 => "@",
    33 => "A",      34 => "B",       35 => "C",
    36 => "D",      37 => "E",       38 => "F",
    39 => "G",      40 => "H",       41 => "I",
    42 => "J",      43 => "K",       44 => "L",
    45 => "M",      46 => "N",       47 => "O",
    48 => "P",      49 => "Q",       50 => "R",
    51 => "S",      52 => "T",       53 => "U",
    54 => "V",      55 => "W",       56 => "X",
    57 => "Y",      58 => "Z",       59 => "[",
    60 => "\\",     61 => "]",       62 => "^",
    63 => "_",      64 => "\000",    65 => "\001",
    66 => "\002",   67 => "\003",    68 => "\004",
    69 => "\005",   70 => "\006",    71 => "\a",
    72 => "\b",     73 => "\t",      74 => "\n",
    75 => "\v",     76 => "\f",      77 => "\r",
    78 => "\016",   79 => "\017",    80 => "\020",
    81 => "\021",   82 => "\022",    83 => "\023",
    84 => "\024",   85 => "\025",    86 => "\026",
    87 => "\027",   88 => "\030",    89 => "\031",
    90 => "\032",   91 => "\e",      92 => "\034",
    93 => "\035",   94 => "\036",    95 => "\037",
    96 => FNC3,     97 => FNC2,      98 => SHIFT,
    99 => CODEC,    100 => CODEB,    101 => FNC4,
    102 => FNC1,    103 => STARTA,   104 => STARTB,
    105 => STARTC
  }.invert,

  'B' => {
    0 => " ", 1 => "!", 2 => "\"", 3 => "#", 4 => "$", 5 => "%",
    6 => "&", 7 => "'", 8 => "(", 9 => ")", 10 => "*", 11 => "+",
    12 => ",", 13 => "-", 14 => ".", 15 => "/", 16 => "0", 17 => "1",
    18 => "2", 19 => "3", 20 => "4", 21 => "5", 22 => "6", 23 => "7",
    24 => "8", 25 => "9", 26 => ":", 27 => ";", 28 => "<", 29 => "=",
    30 => ">", 31 => "?", 32 => "@", 33 => "A", 34 => "B", 35 => "C",
    36 => "D", 37 => "E", 38 => "F", 39 => "G", 40 => "H", 41 => "I",
    42 => "J", 43 => "K", 44 => "L", 45 => "M", 46 => "N", 47 => "O",
    48 => "P", 49 => "Q", 50 => "R", 51 => "S", 52 => "T", 53 => "U",
    54 => "V", 55 => "W", 56 => "X", 57 => "Y", 58 => "Z", 59 => "[",
    60 => "\\", 61 => "]", 62 => "^", 63 => "_", 64 => "`", 65 => "a",
    66 => "b", 67 => "c", 68 => "d", 69 => "e", 70 => "f", 71 => "g",
    72 => "h", 73 => "i", 74 => "j", 75 => "k", 76 => "l", 77 => "m",
    78 => "n", 79 => "o", 80 => "p", 81 => "q", 82 => "r", 83 => "s",
    84 => "t", 85 => "u", 86 => "v", 87 => "w", 88 => "x", 89 => "y",
    90 => "z", 91 => "{", 92 => "|", 93 => "}", 94 => "~", 95 => "\177",
    96 => FNC3, 97 => FNC2, 98 => SHIFT, 99 => CODEC, 100 => FNC4,
    101 => CODEA, 102 => FNC1, 103 => STARTA, 104 => STARTB,
    105 => STARTC,
  }.invert,

  'C' => {
    0 => "00", 1 => "01", 2 => "02", 3 => "03", 4 => "04", 5 => "05",
    6 => "06", 7 => "07", 8 => "08", 9 => "09", 10 => "10", 11 => "11",
    12 => "12", 13 => "13", 14 => "14", 15 => "15", 16 => "16", 17 => "17",
    18 => "18", 19 => "19", 20 => "20", 21 => "21", 22 => "22", 23 => "23",
    24 => "24", 25 => "25", 26 => "26", 27 => "27", 28 => "28", 29 => "29",
    30 => "30", 31 => "31", 32 => "32", 33 => "33", 34 => "34", 35 => "35",
    36 => "36", 37 => "37", 38 => "38", 39 => "39", 40 => "40", 41 => "41",
    42 => "42", 43 => "43", 44 => "44", 45 => "45", 46 => "46", 47 => "47",
    48 => "48", 49 => "49", 50 => "50", 51 => "51", 52 => "52", 53 => "53",
    54 => "54", 55 => "55", 56 => "56", 57 => "57", 58 => "58", 59 => "59",
    60 => "60", 61 => "61", 62 => "62", 63 => "63", 64 => "64", 65 => "65",
    66 => "66", 67 => "67", 68 => "68", 69 => "69", 70 => "70", 71 => "71",
    72 => "72", 73 => "73", 74 => "74", 75 => "75", 76 => "76", 77 => "77",
    78 => "78", 79 => "79", 80 => "80", 81 => "81", 82 => "82", 83 => "83",
    84 => "84", 85 => "85", 86 => "86", 87 => "87", 88 => "88", 89 => "89",
    90 => "90", 91 => "91", 92 => "92", 93 => "93", 94 => "94", 95 => "95",
    96 => "96", 97 => "97", 98 => "98", 99 => "99", 100 => CODEB, 101 => CODEA,
    102 => FNC1, 103 => STARTA, 104 => STARTB, 105 => STARTC
  }.invert
}
CONTROL_CHARACTERS =
VALUES['A'].invert.values_at(*(64..95).to_a)
CTRL_RE =
/#{CONTROL_CHARACTERS.join('|')}/
LOWR_RE =
/[a-z]/
DGTS_RE =
/\d{4,}/
CODEC_CHARS_RE =

pairs of digits and FNC1 characters

/(?:\d{2}|#{FNC1}){2,}/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Barcode

#method_missing, #outputter_class_for, #outputter_for, outputters, register_outputter, #two_dimensional?

Constructor Details

#initialize(data, type = nil) ⇒ Code128

Returns a new instance of Code128.

Raises:

  • (ArgumentError)


174
175
176
177
178
179
180
181
182
# File 'lib/barby/barcode/code_128.rb', line 174

def initialize(data, type=nil)
  if type
    self.type = type
    self.data = "#{data}"
  else
    self.type, self.data = self.class.determine_best_type_for_data("#{data}")
  end
  raise ArgumentError, 'Data not valid' unless valid?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Barby::Barcode

Instance Attribute Details

#typeObject

Returns the value of attribute type.



171
172
173
# File 'lib/barby/barcode/code_128.rb', line 171

def type
  @type
end

Class Method Details

.apply_shortest_encoding_for_data(data) ⇒ Object

Insert code shift and switch characters where appropriate to get the shortest encoding possible



428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/barby/barcode/code_128.rb', line 428

def apply_shortest_encoding_for_data(data)
  extract_codec(data).map do |block|
    if codec_segment?(block)
      "#{CODEC}#{block}"
    else
      if control_before_lowercase?(block)
        handle_code_a(block)
      else
        handle_code_b(block)
      end
    end
  end.join
end

.class_for(character) ⇒ Object



414
415
416
417
418
419
420
421
422
423
# File 'lib/barby/barcode/code_128.rb', line 414

def class_for(character)
  case character
  when 'A' then Code128A
  when 'B' then Code128B
  when 'C' then Code128C
  when CODEA then Code128A
  when CODEB then Code128B
  when CODEC then Code128C
  end
end

.determine_best_type_for_data(data) ⇒ Object



442
443
444
445
446
447
448
449
450
# File 'lib/barby/barcode/code_128.rb', line 442

def determine_best_type_for_data(data)
  data = apply_shortest_encoding_for_data(data)
  type = case data.slice!(0)
         when CODEA then 'A'
         when CODEB then 'B'
         when CODEC then 'C'
         end
  [type, data]
end

Instance Method Details

#change_code_encoding_for(barcode) ⇒ Object

Find the encoding to change to the character set in barcode



367
368
369
# File 'lib/barby/barcode/code_128.rb', line 367

def change_code_encoding_for(barcode)
  encodings[change_code_number_for(barcode)]
end

#change_code_for(barcode) ⇒ Object

Find the character that changes the character set to the one represented in barcode



356
357
358
# File 'lib/barby/barcode/code_128.rb', line 356

def change_code_for(barcode)
  change_code_for_class(barcode.class)
end

#change_code_for_class(klass) ⇒ Object



350
351
352
# File 'lib/barby/barcode/code_128.rb', line 350

def change_code_for_class(klass)
  {Code128A => CODEA, Code128B => CODEB, Code128C => CODEC}[klass]
end

#change_code_number_for(barcode) ⇒ Object

Find the numeric value for the character that changes the character set to the one represented in barcode



362
363
364
# File 'lib/barby/barcode/code_128.rb', line 362

def change_code_number_for(barcode)
  values[change_code_for(barcode)]
end

#charactersObject

Get an array of the individual characters for this barcode. Special characters like FNC1 will be present. Characters from extras are not present.



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/barby/barcode/code_128.rb', line 257

def characters
  chars = data.split(//n)

  if type == 'C'
    result = []
    count = 0
    while count < chars.size
      if chars[count] =~ /^\d$/
        #If encountering a digit, next char/byte *must* be second digit in pair. I.e. if chars[count] is 5,
        #chars[count+1] must be /[0-9]/, otherwise it's not valid
        result << "#{chars[count]}#{chars[count+1]}"
        count += 2
      else
        result << chars[count]
        count += 1
      end
    end
    result
  else
    chars
  end
end

#checksumObject

Calculate the checksum for the data in this barcode. The data includes data from extras.



307
308
309
310
311
312
313
# File 'lib/barby/barcode/code_128.rb', line 307

def checksum
  pos = 0
  (numbers+extra_numbers).inject(start_num) do |sum,number|
    pos += 1
    sum + (number * pos)
  end % 103
end

#checksum_encodingObject

Get the encoding for the checksum



316
317
318
# File 'lib/barby/barcode/code_128.rb', line 316

def checksum_encoding
  encodings[checksum]
end

#class_for(character) ⇒ Object



371
372
373
# File 'lib/barby/barcode/code_128.rb', line 371

def class_for(character)
  self.class.class_for(character)
end

#dataObject



197
198
199
# File 'lib/barby/barcode/code_128.rb', line 197

def data
  @data
end

#data=(data) ⇒ Object

Set the data for this barcode. If the barcode changes character set, an extra will be created.



228
229
230
231
232
# File 'lib/barby/barcode/code_128.rb', line 228

def data=(data)
  data, *extra = data.split(/([#{CODEA+CODEB+CODEC}])/n)
  @data = data || ''
  self.extra = extra.join unless extra.empty?
end

#data_encodingObject

Returns the encoding for the data part of this barcode, without any extras



286
287
288
289
290
# File 'lib/barby/barcode/code_128.rb', line 286

def data_encoding
  characters.map do |char|
    encoding_for char
  end.join
end

#data_encoding_with_extra_encodingObject

Returns the data encoding of this barcode and extras.



293
294
295
# File 'lib/barby/barcode/code_128.rb', line 293

def data_encoding_with_extra_encoding
  data_encoding+extra_encoding
end

#encodingObject

Return the encoding of this barcode as a string of 1 and 0



281
282
283
# File 'lib/barby/barcode/code_128.rb', line 281

def encoding
  start_encoding+data_encoding+extra_encoding+checksum_encoding+stop_encoding
end

#encoding_for(char) ⇒ Object

Find the encoding for the specified character for this barcode



346
347
348
# File 'lib/barby/barcode/code_128.rb', line 346

def encoding_for(char)
  encodings[values[char]]
end

#encodingsObject



336
337
338
# File 'lib/barby/barcode/code_128.rb', line 336

def encodings
  ENCODINGS
end

#extraObject

An “extra” is present if the barcode changes character set. If a 128A barcode changes to C, the extra will be an instance of Code128C. Extras can themselves have an extra if the barcode changes character set again. It’s like a linked list, and when there are no more extras, the barcode ends with that object. Most barcodes probably don’t change charsets and don’t have extras.



240
241
242
243
# File 'lib/barby/barcode/code_128.rb', line 240

def extra
  return @extra if defined?(@extra)
  @extra = nil
end

#extra=(extra) ⇒ Object

Set the extra for this barcode. The argument is a string starting with the “change character set” symbol. The string may contain several character sets, in which case the extra will itself have an extra.

Raises:

  • (ArgumentError)


248
249
250
251
252
# File 'lib/barby/barcode/code_128.rb', line 248

def extra=(extra)
  raise ArgumentError, "Extra must begin with \\305, \\306 or \\307" unless extra =~ /^[#{CODEA+CODEB+CODEC}]/n
  type, data = extra[0,1], extra[1..-1]
  @extra = class_for(type).new(data)
end

#extra_encodingObject

Returns the data encoding of this barcode’s extra and its extra until the barcode ends.



299
300
301
302
# File 'lib/barby/barcode/code_128.rb', line 299

def extra_encoding
  return '' unless extra
  change_code_encoding_for(extra) + extra.data_encoding + extra.extra_encoding
end

#extra_numbersObject

Returns the numeric values for extras



331
332
333
334
# File 'lib/barby/barcode/code_128.rb', line 331

def extra_numbers
  return [] unless extra
  [change_code_number_for(extra)] + extra.numbers + extra.extra_numbers
end

#full_dataObject

Returns the data for this barcode plus that for the entire extra chain, excluding all change codes



203
204
205
# File 'lib/barby/barcode/code_128.rb', line 203

def full_data
  data + full_extra_data
end

#full_data_with_change_codesObject

Returns the data for this barcode plus that for the entire extra chain, including all change codes prefixing each extra



209
210
211
# File 'lib/barby/barcode/code_128.rb', line 209

def full_data_with_change_codes
  data + full_extra_data_with_change_code
end

#full_extra_dataObject

Returns the full_data for the extra or an empty string if there is no extra



214
215
216
217
# File 'lib/barby/barcode/code_128.rb', line 214

def full_extra_data
  return '' unless extra
  extra.full_data
end

#full_extra_data_with_change_codeObject

Returns the full_data for the extra with the change code for the extra prepended. If there is no extra, an empty string is returned



221
222
223
224
# File 'lib/barby/barcode/code_128.rb', line 221

def full_extra_data_with_change_code
  return '' unless extra
  change_code_for(extra) + extra.full_data_with_change_codes
end

#numbersObject

Returns the numeric values for the characters in the barcode in an array



324
325
326
327
328
# File 'lib/barby/barcode/code_128.rb', line 324

def numbers
  characters.map do |char|
    values[char]
  end
end

#start_characterObject



386
387
388
389
390
391
392
# File 'lib/barby/barcode/code_128.rb', line 386

def start_character
  case type
  when 'A' then STARTA
  when 'B' then STARTB
  when 'C' then STARTC
  end
end

#start_encodingObject



398
399
400
# File 'lib/barby/barcode/code_128.rb', line 398

def start_encoding
  encodings[start_num]
end

#start_numObject



394
395
396
# File 'lib/barby/barcode/code_128.rb', line 394

def start_num
  values[start_character]
end

#stop_encodingObject

The start encoding starts the barcode



341
342
343
# File 'lib/barby/barcode/code_128.rb', line 341

def stop_encoding
  STOP+TERMINATE
end

#to_sObject



192
193
194
# File 'lib/barby/barcode/code_128.rb', line 192

def to_s
  full_data
end

#valid?Boolean

Is the data in this barcode valid? Does a lookup of every character and checks if it exists in the character set. An empty data string will also be reported as invalid.

Returns:

  • (Boolean)


378
379
380
# File 'lib/barby/barcode/code_128.rb', line 378

def valid?
  characters.any? && characters.all?{|c| values.include?(c) }
end

#valuesObject



382
383
384
# File 'lib/barby/barcode/code_128.rb', line 382

def values
  VALUES[type]
end