barcode1dtools 0.9.8.0 → 0.9.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,8 +18,7 @@ module Barcode1DTools
18
18
  # Supplemental 2, UPC Supplemental 5, Interleaved 2 of 5 (I 2/5),
19
19
  # COOP 2 of 5, Matrix 2 of 5, Industrial 2 of 5, IATA 2 of 5,
20
20
  # PostNet, Plessey, MSI (Modified Plessey), Code 3 of 9, Code 93,
21
- # Code 11, and Codabar, but will be expanded to include most 1D
22
- # symbologies in the near future.
21
+ # Code 11, Code 128, and Codabar.
23
22
  #
24
23
  #== Example
25
24
  # ean13 = Barcode1DTools::EAN13.new('0012676510226', :line_character => 'x', :space_character => ' ')
@@ -130,3 +129,4 @@ require 'barcode1dtools/matrix2of5'
130
129
  require 'barcode1dtools/postnet'
131
130
  require 'barcode1dtools/plessey'
132
131
  require 'barcode1dtools/msi'
132
+ require 'barcode1dtools/code128'
@@ -0,0 +1,500 @@
1
+ #--
2
+ # Copyright 2012 Michael Chaney Consulting Corporation
3
+ #
4
+ # Released under the terms of the MIT License or the GNU
5
+ # General Public License, v. 2
6
+ #++
7
+
8
+ module Barcode1DTools
9
+
10
+ # Barcode1DTools::Code128 - Create and decode bar patterns for
11
+ # Code 128. Code 128 is the king of 1D bar codes and should be
12
+ # used for any alpha-numeric application. Code 128 can encode
13
+ # any character from 0 to 255, although it is most efficient
14
+ # when using only 0 to 95 or 32 to 127. It is also very
15
+ # efficient at encoding only digits, although Interleaved 2 of 5
16
+ # is also a good choice with potentially less overhead.
17
+ #
18
+ # Code 128 barcodes always include a checksum, and the checksum
19
+ # is calculated from the encoded value rather than the payload.
20
+ # Because of this, there are no options for including a check
21
+ # digit or validating one. It is always included.
22
+ #
23
+ # val = "29382-38"
24
+ # bc = Barcode1DTools::Code128.new(val)
25
+ # pattern = bc.bars
26
+ # rle_pattern = bc.rle
27
+ # width = bc.width
28
+ #
29
+ # The object created is immutable.
30
+ #
31
+ # Barcode1DTools::Code128 creates the patterns that you need to
32
+ # display Code 128 barcodes. It can also decode a simple rle or
33
+ # bar pattern string.
34
+ #
35
+ # Code128 characters consist of 3 bars and 3 spaces.
36
+ #
37
+ # There are two formats for the returned pattern:
38
+ #
39
+ # bars - 1s and 0s specifying black lines and white spaces. Actual
40
+ # characters can be changed from "1" and 0" with options
41
+ # :line_character and :space_character.
42
+ #
43
+ # rle - Run-length-encoded version of the pattern. The first
44
+ # number is always a black line, with subsequent digits
45
+ # alternating between spaces and lines. The digits specify
46
+ # the width of each line or space.
47
+ #
48
+ # The "width" method will tell you the total end-to-end width, in
49
+ # units, of the entire barcode.
50
+ #
51
+ #== Rendering
52
+ #
53
+ # The quiet zone on each side should be at least the greater of 10
54
+ # unit widths or 6.4mm. Typically a textual rendition of the
55
+ # payload is shown underneath the bars.
56
+
57
+ class Code128 < Barcode1D
58
+
59
+ # Patterns for making bar codes
60
+ PATTERNS = [
61
+ '212222', '222122', '222221', '121223', '121322', '131222',
62
+ '122213', '122312', '132212', '221213', '221312', '231212',
63
+ '112232', '122132', '122231', '113222', '123122', '123221',
64
+ '223211', '221132', '221231', '213212', '223112', '312131',
65
+ '311222', '321122', '321221', '312212', '322112', '322211',
66
+ '212123', '212321', '232121', '111323', '131123', '131321',
67
+ '112313', '132113', '132311', '211313', '231113', '231311',
68
+ '112133', '112331', '132131', '113123', '113321', '133121',
69
+ '313121', '211331', '231131', '213113', '213311', '213131',
70
+ '311123', '311321', '331121', '312113', '312311', '332111',
71
+ '314111', '221411', '431111', '111224', '111422', '121124',
72
+ '121421', '141122', '141221', '112214', '112412', '122114',
73
+ '122411', '142112', '142211', '241211', '221114', '413111',
74
+ '241112', '134111', '111242', '121142', '121241', '114212',
75
+ '124112', '124211', '411212', '421112', '421211', '212141',
76
+ '214121', '412121', '111143', '111341', '131141', '114113',
77
+ '114311', '411113', '411311', '113141', '114131', '311141',
78
+ '411131', '211412', '211214', '211232',
79
+ '2331112'
80
+ ]
81
+
82
+ # Quicker decoding
83
+ PATTERN_LOOKUP = (0..106).inject({}) { |a,c| a[PATTERNS[c]] = c; a }
84
+
85
+ # For ease. These can also be looked up in any
86
+ # ASCII_TO_CODE_x hashes symbolically, e.g.
87
+ # START_A == ASCII_TO_CODE_A[:start_a]
88
+ START_A = 103
89
+ START_B = 104
90
+ START_C = 105
91
+ SHIFT = 98
92
+ CODE_A = 101
93
+ CODE_B = 100
94
+ CODE_C = 99
95
+ STOP = 106
96
+ FNC_1 = 102
97
+ FNC_2 = 97
98
+ FNC_3 = 96
99
+ # Note that FNC_4 is 100 in set B and 101 in set A
100
+
101
+ GUARD_PATTERN_RIGHT_RLE = PATTERNS[STOP]
102
+ START_A_RLE = PATTERNS[START_A]
103
+ START_B_RLE = PATTERNS[START_B]
104
+ START_C_RLE = PATTERNS[START_C]
105
+
106
+ LOW_ASCII_LABELS = [
107
+ 'NUL', 'SOH', 'STX', 'ETX', 'EOT', 'ENQ', 'ACK', 'BEL',
108
+ 'BS', 'HT', 'LF', 'VT', 'FF', 'CR', 'SO', 'SI', 'DLE',
109
+ 'DC1', 'DC2', 'DC3', 'DC4', 'NAK', 'SYN', 'ETB', 'CAN',
110
+ 'EM', 'SUB', 'ESC', 'FS', 'GS', 'RS', 'US'
111
+ ]
112
+
113
+ # Code A encodes ASCII NUL (\x0) to _ (\x5f, dec. 95). Note
114
+ # that they are not sequential - it starts with space through
115
+ # underscore, then has nul to \x1f. Finally, it has FNC 1-4.
116
+ CODE_A_TO_ASCII = ((32..95).to_a + (0..31).to_a).collect { |c| [c].pack('C') } + [ :fnc_3, :fnc_2, :shift_b, :code_c, :code_b, :fnc_4, :fnc_1, :start_a, :start_b, :start_c, :stop ]
117
+ ASCII_TO_CODE_A = (0..(CODE_A_TO_ASCII.length-1)).inject({}) { |a,c| a[CODE_A_TO_ASCII[c]] = c; a }
118
+ CODE_A_TO_HIGH_ASCII = CODE_A_TO_ASCII.collect { |a| a.is_a?(Symbol) ? a : [a.unpack("C").first+128].pack("C") }
119
+ HIGH_ASCII_TO_CODE_A = (0..(CODE_A_TO_HIGH_ASCII.length-1)).inject({}) { |a,c| a[CODE_A_TO_HIGH_ASCII[c]] = c; a }
120
+
121
+ # Code B encodes ASCII space (\x20, dec. 32) to DEL (\x7f,
122
+ # dec. 127). This is identical to Code A for the first 63
123
+ # characters. It also includes FNC 1-4 with FNC 4 in a
124
+ # different position than in set A.
125
+ CODE_B_TO_ASCII = (32..127).collect { |c| [c].pack('C') } + [ :fnc_3, :fnc_2, :shift_a, :code_c, :fnc_4, :code_a, :fnc_1, :start_a, :start_b, :start_c, :stop ]
126
+ ASCII_TO_CODE_B = (0..(CODE_B_TO_ASCII.length-1)).inject({}) { |a,c| a[CODE_B_TO_ASCII[c]] = c; a }
127
+ CODE_B_TO_HIGH_ASCII = CODE_B_TO_ASCII.collect { |a| a.is_a?(Symbol) ? a : [a.unpack("C").first+128].pack("C") }
128
+ HIGH_ASCII_TO_CODE_B = (0..(CODE_B_TO_HIGH_ASCII.length-1)).inject({}) { |a,c| a[CODE_B_TO_HIGH_ASCII[c]] = c; a }
129
+
130
+ # Code C encodes digit pairs 00 to 99 as well as FNC 1.
131
+ CODE_C_TO_ASCII = ("00".."99").to_a + [ :code_b, :code_a, :fnc_1, :start_a, :start_b, :start_c, :stop ]
132
+ ASCII_TO_CODE_C = (0..(CODE_C_TO_ASCII.length-1)).inject({}) { |a,c| a[CODE_C_TO_ASCII[c]] = c; a }
133
+
134
+ DEFAULT_OPTIONS = {
135
+ :line_character => '1',
136
+ :space_character => '0'
137
+ }
138
+
139
+ class << self
140
+ # Code128 can encode anything
141
+ def can_encode?(value)
142
+ true
143
+ end
144
+
145
+ def generate_check_digit_for(value)
146
+ md = parse_code128(value)
147
+ start = md[1].unpack('C')
148
+ mult=0
149
+ [md[2].unpack('C*').inject(start.first) { |a,c| (mult+=1)*c+a } % 103].pack('C')
150
+ end
151
+
152
+ def validate_check_digit_for(value)
153
+ payload, check_digit = split_payload_and_check_digit(value)
154
+ self.generate_check_digit_for(payload) == check_digit
155
+ end
156
+
157
+ def split_payload_and_check_digit(value)
158
+ md = value.to_s.match(/\A(.*)(.)\z/)
159
+ [md[1], md[2]]
160
+ end
161
+
162
+ # Returns match data - 1: start character 2: payload
163
+ # 3: check digit 4: stop character
164
+ def parse_code128(str)
165
+ str.match(/\A([\x67-\x69])([\x00-\x66]*?)(?:([\x00-\x66])(\x6a))?\z/)
166
+ end
167
+
168
+ # Convert a code128 encoded string to an ASCII/Latin-1
169
+ # representation. The return value is an array if there
170
+ # are any FNC codes included. Use the option
171
+ # :no_latin1 => true to simply return FNC 4 instead of
172
+ # coding the following characters to the high Latin-1
173
+ # range. Use :raw_array => true if you wish to see an
174
+ # array of the actual characters in the code. It will
175
+ # turn any ASCII/Latin-1 characters to their standard
176
+ # representation, but it also includes all start, shift,
177
+ # code change, etc. characters. Useful for debugging.
178
+ def code128_to_latin1(str, options = {})
179
+ ret = []
180
+ in_high_latin1 = false
181
+ shift_codeset = nil
182
+ shift_latin1 = false
183
+ current_codeset = 'A'
184
+ current_lookup = CODE_A_TO_ASCII
185
+ md = parse_code128(str)
186
+ raise UndecodeableCharactersError unless md
187
+ start_item = CODE_A_TO_ASCII[md[1].unpack('C').first]
188
+ if start_item == :start_a
189
+ current_codeset = 'A'
190
+ current_lookup = CODE_A_TO_ASCII
191
+ elsif start_item == :start_b
192
+ current_codeset = 'B'
193
+ current_lookup = CODE_B_TO_ASCII
194
+ elsif start_item == :start_c
195
+ current_codeset = 'C'
196
+ current_lookup = CODE_C_TO_ASCII
197
+ end
198
+ ret.push(start_item) if options[:raw_array]
199
+ md[2].unpack("C*").each do |char|
200
+ if shift_codeset
201
+ this_item = shift_codeset[char]
202
+ shift_codeset = nil
203
+ else
204
+ this_item = current_lookup[char]
205
+ end
206
+ if this_item.is_a? Symbol
207
+ # Symbols might be change code (code_a, code_b, code_c),
208
+ # shift for a single item (shift_a, shift_b),
209
+ # or an fnc 1-4. If it's fnc_4, handle the high latin-1.
210
+ # Might also be the start code.
211
+ if this_item == :code_a
212
+ current_codeset = 'A'
213
+ current_lookup = CODE_A_TO_ASCII
214
+ elsif this_item == :code_b
215
+ current_codeset = 'B'
216
+ current_lookup = CODE_B_TO_ASCII
217
+ elsif this_item == :code_c
218
+ current_codeset = 'C'
219
+ current_lookup = CODE_C_TO_ASCII
220
+ elsif this_item == :shift_a
221
+ shift_codeset = CODE_A_TO_ASCII
222
+ elsif this_item == :shift_b
223
+ shift_codeset = CODE_B_TO_ASCII
224
+ elsif this_item == :fnc_4 && !options[:no_latin1]
225
+ if shift_latin1
226
+ in_high_latin1 = !in_high_latin1
227
+ shift_latin1 = false
228
+ else
229
+ shift_latin1 = true
230
+ end
231
+ elsif !options[:raw_array]
232
+ ret.push this_item
233
+ end
234
+ ret.push(this_item) if options[:raw_array]
235
+ elsif in_high_latin1 && ['A', 'B'].include?(current_codeset)
236
+ # Currently processing as latin-1. If we find the shift,
237
+ # handle as regular character.
238
+ if shift_latin1
239
+ ret.push this_item
240
+ shift_latin1 = false
241
+ else
242
+ ret.push [this_item.unpack('C').first+128].pack('C')
243
+ end
244
+ elsif shift_latin1
245
+ # One character as latin-1
246
+ ret.push [this_item.unpack('C').first+128].pack('C')
247
+ shift_latin1 = false
248
+ else
249
+ # regular character
250
+ ret.push this_item
251
+ end
252
+ end
253
+ unless options[:raw_array]
254
+ ret = ret.inject([]) { |a,c| (a.size==0 || a.last.is_a?(Symbol) || c.is_a?(Symbol)) ? a.push(c) : (a[a.size-1] += c); a }
255
+ end
256
+ # Make sure it's Latin-1 for Ruby 1.9+
257
+ if RUBY_VERSION >= "1.9"
258
+ ret = ret.collect { |c| c.force_encoding('ISO-8859-1') }
259
+ end
260
+ if options[:raw_array]
261
+ ret.push(md[2].unpack('C').first)
262
+ ret.push(:stop)
263
+ end
264
+ ret
265
+ end
266
+
267
+ # Pass an array or string for encoding. The result is
268
+ # a string that is a Code 128 representation of the input.
269
+ # We do optimize, but perhaps not perfectly. The
270
+ # optimization should cover 99% of cases very well,
271
+ # although I'm sure an edge case could be created that
272
+ # would be suboptimal.
273
+ def latin1_to_code128(str, options = {})
274
+ if str.is_a?(String)
275
+ str = [str]
276
+ elsif !str.is_a?(Array)
277
+ raise UnencodableCharactersError
278
+ end
279
+ arr = str.inject([]) { |a,c| c.is_a?(Symbol) ? a.push(c) : a.push(c.to_s.split('')) ; a}.flatten
280
+ # Now, create a set of maps to see how this will map to each
281
+ # code set.
282
+ map_a = arr.collect { |c| ASCII_TO_CODE_A[c] ? 'a' : HIGH_ASCII_TO_CODE_A[c] ? 'A' : '-' }
283
+ map_b = arr.collect { |c| ASCII_TO_CODE_B[c] ? 'b' : HIGH_ASCII_TO_CODE_B[c] ? 'B' : '-' }
284
+ last_is_digit=false
285
+ map_c = arr.collect do |c|
286
+ if c.is_a?(Symbol) && c == :fnc_1
287
+ if last_is_digit
288
+ last_is_digit = false
289
+ ['-','-']
290
+ else
291
+ 'c'
292
+ end
293
+ elsif c.is_a?(String) && c >= '0' && c <= '9'
294
+ if last_is_digit
295
+ last_is_digit = false
296
+ ['c','C']
297
+ else
298
+ last_is_digit = true
299
+ nil
300
+ end
301
+ elsif last_is_digit
302
+ last_is_digit = false
303
+ ['-','-']
304
+ else
305
+ '-'
306
+ end
307
+ end.flatten.compact
308
+ map_c.push('-') if last_is_digit
309
+ # Let's do it
310
+ map_a = map_a.join + '-'
311
+ map_b = map_b.join + '-'
312
+ map_c = map_c.join
313
+ codepoints = ''
314
+ # Strategy - create an a/b map first. We'll do this based on
315
+ # the least switching required which can be determined via a
316
+ # regexp ("aaa--aa" has two switches, for instance). After
317
+ # that is created, we can then go in and fill in runs from
318
+ # C - runs of 6 or more in the middle or 4 or more at either
319
+ # end. "444a" would just be encoded in set B, for instance,
320
+ # but "4444a" would be encoded in C then B.
321
+ # In the real world, switching between A and B is rare so
322
+ # we're not trying too hard to optimize it here.
323
+ in_codeset = nil
324
+ x = 0
325
+ while x < map_a.length - 1 do
326
+ map_a_len = map_a.index('-',x).to_i - x
327
+ map_b_len = map_b.index('-',x).to_i - x
328
+ if map_a_len >= map_b_len
329
+ codepoints += map_a[x,map_a_len]
330
+ x += map_a_len
331
+ else
332
+ codepoints += map_b[x,map_b_len]
333
+ x += map_b_len
334
+ end
335
+ end
336
+ # Now, fix up runs of C. Look for runs of 4+ at the ends
337
+ # and 6+ in the middle. The runs must have cC in them, as
338
+ # there's no gains from changing FNC 1 to set C.
339
+ runs = map_c.split(/(c[cC]+Cc*)/)
340
+ offset = 0
341
+ 0.upto(runs.length-1) do |x|
342
+ if x==0 || x==runs.length-1
343
+ # only needs to be 4
344
+ if runs[x] =~ /c[cC]+C/
345
+ codepoints[offset,runs[x].length] = runs[x]
346
+ end
347
+ offset += runs[x].length
348
+ else
349
+ # must be 6+
350
+ if runs[x] =~ /c[cC]{3,}C/
351
+ codepoints[offset,runs[x].length] = runs[x]
352
+ end
353
+ offset += runs[x].length
354
+ end
355
+ end
356
+ #{ :map_a => map_a, :map_b => map_b, :map_c => map_c, :codepoints => codepoints }
357
+ # Now, create the string
358
+ ret = []
359
+ current_set = codepoints[0,1].downcase
360
+ ret.push(current_set == 'a' ? START_A : current_set == 'b' ? START_B : START_C)
361
+ 0.upto(codepoints.length-1) do |x|
362
+ if codepoints[x,1].downcase != current_set
363
+ current_set = codepoints[x,1].downcase
364
+ ret.push(current_set == 'a' ? CODE_A : current_set == 'b' ? CODE_B : CODE_C)
365
+ end
366
+ if current_set == 'c' && codepoints[x,1] == 'c'
367
+ # ignore capital Cs
368
+ if arr[x] == :fnc_1
369
+ ret.push(FNC_1)
370
+ else
371
+ ret.push((arr[x]+arr[x+1]).to_i)
372
+ end
373
+ elsif ['A','B'].include?(codepoints[x,1])
374
+ # Find FNC_4 and push it (101 in A and 100 in B)
375
+ # now push the letter looked up in CODE_x_TO_HIGH_ASCII
376
+ if codepoints[x,1] == 'A'
377
+ ret.push(HIGH_ASCII_TO_CODE_A[:fnc_4])
378
+ ret.push(HIGH_ASCII_TO_CODE_A[arr[x]])
379
+ else
380
+ ret.push(HIGH_ASCII_TO_CODE_B[:fnc_4])
381
+ ret.push(HIGH_ASCII_TO_CODE_B[arr[x]])
382
+ end
383
+ elsif ['a','b'].include?(codepoints[x,1])
384
+ # find the letter in CODE_x_TO_ASCII and push it
385
+ if codepoints[x,1] == 'a'
386
+ ret.push(ASCII_TO_CODE_A[arr[x]])
387
+ else
388
+ ret.push(ASCII_TO_CODE_B[arr[x]])
389
+ end
390
+ end
391
+ end
392
+ check_digit = generate_check_digit_for(ret.pack('C*'))
393
+ ret.push(check_digit.unpack('C').first)
394
+ ret.push(ASCII_TO_CODE_A[:stop])
395
+ ret.pack('C*')
396
+ end
397
+
398
+ # Decode a string in rle format. This will return a Code128
399
+ # object.
400
+ def decode(str, options = {})
401
+ if str =~ /[^1-4]/ && str =~ /[^wn]/
402
+ raise UnencodableCharactersError, "Pattern must be rle or bar pattern"
403
+ end
404
+
405
+ # ensure an rle string
406
+ if str !~ /\A[1-4]+\z/
407
+ str = bars_to_rle(str)
408
+ end
409
+
410
+ if str.reverse =~ /\A(#{START_A_RLE}|#{START_B_RLE}|#{START_C_RLE})(.*?)(#{GUARD_PATTERN_RIGHT_RLE})\z/
411
+ str.reverse!
412
+ end
413
+
414
+ unless str =~ /\A(#{START_A_RLE}|#{START_B_RLE}|#{START_C_RLE})(.*?)(#{GUARD_PATTERN_RIGHT_RLE})\z/
415
+ raise UnencodableCharactersError, "Start/stop pattern is not detected."
416
+ end
417
+
418
+ # Each pattern is 3 bars and 3 spaces, with an extra bar
419
+ # at the end.
420
+ unless (str.size - 1) % 6 == 0
421
+ raise UnencodableCharactersError, "Wrong number of bars."
422
+ end
423
+
424
+ points = []
425
+
426
+ str.scan(/.{6}/).each do |chunk|
427
+
428
+ found = false
429
+
430
+ char = PATTERN_LOOKUP[chunk]
431
+ if char
432
+ points.push(char)
433
+ elsif chunk == '233111'
434
+ # stop
435
+ points.push(STOP)
436
+ else
437
+ raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
438
+ end
439
+
440
+ end
441
+
442
+ decoded_string = code128_to_latin1(points.pack('C*'))
443
+
444
+ Code128.new(decoded_string, options)
445
+ end
446
+
447
+ end
448
+
449
+ # Options are :line_character, :space_character, and
450
+ # :raw_value.
451
+ def initialize(value, options = {})
452
+
453
+ @options = DEFAULT_OPTIONS.merge(options)
454
+
455
+ if options[:raw_value]
456
+ @encoded_string = value
457
+ @value = self.class.code128_to_latin1(value, options)
458
+ else
459
+ @value = value.to_s
460
+ # In ruby 1.9, change to Latin-1 if it's in another encoding.
461
+ # Really, the caller needs to handle this.
462
+ if RUBY_VERSION >= "1.9" && !['US-ASCII','ISO-8859-1'].include?(value.encoding)
463
+ value = value.encode('ISO-8859-1')
464
+ end
465
+ raise UnencodableCharactersError unless self.class.can_encode?(value)
466
+ @encoded_string = self.class.latin1_to_code128(@value, options)
467
+ end
468
+
469
+ md = self.class.parse_code128(@encoded_string)
470
+ @check_digit = md[3]
471
+ end
472
+
473
+ # variable bar width, no w/n string
474
+ def wn
475
+ raise NotImplementedError
476
+ end
477
+
478
+ # returns a run-length-encoded string representation
479
+ def rle
480
+ @rle ||= gen_rle(@encoded_string, @options)
481
+ end
482
+
483
+ # returns 1s and 0s (for "black" and "white")
484
+ def bars
485
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
486
+ end
487
+
488
+ # returns the total unit width of the bar code
489
+ def width
490
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
491
+ end
492
+
493
+ private
494
+
495
+ def gen_rle(encoded_string, options)
496
+ @rle_str ||= @encoded_string.split('').collect { |c| PATTERNS[c.unpack('C').first] }.join
497
+ end
498
+
499
+ end
500
+ end
@@ -0,0 +1,76 @@
1
+ #--
2
+ # Copyright 2012 Michael Chaney Consulting Corporation
3
+ #
4
+ # Released under the terms of the MIT License or the GNU
5
+ # General Public License, v. 2
6
+ #++
7
+
8
+ require 'test/unit'
9
+ require 'barcode1dtools'
10
+
11
+ class Barcode1DToolsCode128Test < Test::Unit::TestCase
12
+ def setup
13
+ end
14
+
15
+ def teardown
16
+ end
17
+
18
+ def random_x_character_full_ascii_string(x)
19
+ (1..x).collect { rand(128) }.pack('C*')
20
+ end
21
+
22
+ def random_x_character_latin1_string(x)
23
+ str=(1..x).collect { rand(256) }.pack('C*')
24
+ if RUBY_VERSION >= "1.9"
25
+ str.force_encoding('ISO-8859-1')
26
+ else
27
+ str
28
+ end
29
+ end
30
+
31
+ def random_crazy_code128_value(x)
32
+ funcs = [ :fnc_1, :fnc_2, :fnc_3, :fnc_4 ]
33
+ (1..x).collect { |y| y.odd? ? funcs[rand(funcs.length)] : random_x_character_full_ascii_string(rand(5)+5) }
34
+ end
35
+
36
+ # Test encode/decode
37
+ def test_full_ascii
38
+ random_string = random_x_character_full_ascii_string(rand(20) + 10)
39
+ encoded1 = Barcode1DTools::Code128.latin1_to_code128(random_string)
40
+ assert_equal [random_string], Barcode1DTools::Code128.code128_to_latin1(encoded1)
41
+ end
42
+
43
+ def test_latin1
44
+ random_string = random_x_character_latin1_string(rand(20) + 10)
45
+ encoded1 = Barcode1DTools::Code128.latin1_to_code128(random_string)
46
+ assert_equal [random_string], Barcode1DTools::Code128.code128_to_latin1(encoded1)
47
+ end
48
+
49
+ def test_crazy_code128
50
+ random_string = random_crazy_code128_value(rand(5) + 10)
51
+ encoded1 = Barcode1DTools::Code128.latin1_to_code128(random_string, :no_latin1 => true)
52
+ assert_equal random_string, Barcode1DTools::Code128.code128_to_latin1(encoded1, :no_latin1 => true)
53
+ end
54
+
55
+ def test_attr_readers
56
+ c128 = Barcode1DTools::Code128.new('Hello!')
57
+ assert_equal 'Hello!', c128.value
58
+ end
59
+
60
+ def test_decode_error
61
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code128.decode('x') }
62
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code128.decode('x'*60) }
63
+ # wrong start/stop
64
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code128.decode('22222222222222222') }
65
+ end
66
+
67
+ def test_decoding
68
+ random_c128_str=random_x_character_latin1_string(rand(10)+5)
69
+ c128 = Barcode1DTools::Code128.new(random_c128_str)
70
+ c1282 = Barcode1DTools::Code128.decode(c128.rle)
71
+ assert_equal c128.value, c1282.value
72
+ # Should also work in reverse
73
+ c1284 = Barcode1DTools::Code128.decode(c128.rle.reverse)
74
+ assert_equal c128.value, c1284.value
75
+ end
76
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: barcode1dtools
3
3
  version: !ruby/object:Gem::Version
4
- hash: 39
4
+ hash: 35
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 8
9
+ - 9
10
10
  - 0
11
- version: 0.9.8.0
11
+ version: 0.9.9.0
12
12
  platform: ruby
13
13
  authors:
14
14
  - Michael Chaney
@@ -16,20 +16,19 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2012-09-16 00:00:00 -05:00
19
+ date: 2012-09-22 00:00:00 -05:00
20
20
  default_executable:
21
21
  dependencies: []
22
22
 
23
23
  description: "\t Barcode1D is a small library for handling many kinds of\n\
24
- \t 1-dimensional barcodes. Currently implemented are Code 3 of 9, Code\n\
25
- \t 93, Code 11, Codabar, Interleaved 2 of 5, COOP 2 of 5, Matrix 2 of\n\
26
- \t 5, Industrial 2 of 5, IATA 2 of 5, PostNet, Plessey, MSI (Modified\n\
27
- \t Plessey), EAN-13, EAN-8, UPC-A, UPC-E, UPC Supplemental 2, and UPC\n\
28
- \t Supplemental 5. Patterns are created in either a simple format of\n\
29
- \t bars and spaces or as a run-length encoded string. This only\n\
30
- \t generates and decodes the patterns; actual display or reading from a\n\
31
- \t device must be implemented by the programmer. More symbologies will\n\
32
- \t be added as time permits.\n"
24
+ \t 1-dimensional barcodes. Currently implemented are Code 128, Code 3\n\
25
+ \t of 9, Code 93, Code 11, Codabar, Interleaved 2 of 5, COOP 2 of 5,\n\
26
+ \t Matrix 2 of 5, Industrial 2 of 5, IATA 2 of 5, PostNet, Plessey, MSI\n\
27
+ \t (Modified Plessey), EAN-13, EAN-8, UPC-A, UPC-E, UPC Supplemental 2,\n\
28
+ \t and UPC Supplemental 5. Patterns are created in either a simple\n\
29
+ \t format of bars and spaces or as a run-length encoded string. This\n\
30
+ \t only generates and decodes the patterns; actual display or reading\n\
31
+ \t from a device must be implemented by the programmer.\n"
33
32
  email: mdchaney@michaelchaney.com
34
33
  executables: []
35
34
 
@@ -40,6 +39,7 @@ extra_rdoc_files: []
40
39
  files:
41
40
  - lib/barcode1dtools/codabar.rb
42
41
  - lib/barcode1dtools/code11.rb
42
+ - lib/barcode1dtools/code128.rb
43
43
  - lib/barcode1dtools/code3of9.rb
44
44
  - lib/barcode1dtools/code93.rb
45
45
  - lib/barcode1dtools/coop2of5.rb
@@ -61,6 +61,7 @@ files:
61
61
  - test/test_barcode1d.rb
62
62
  - test/test_barcode1dcodabar.rb
63
63
  - test/test_barcode1dcode11.rb
64
+ - test/test_barcode1dcode128.rb
64
65
  - test/test_barcode1dcode3of9.rb
65
66
  - test/test_barcode1dcode93.rb
66
67
  - test/test_barcode1dcoop2of5.rb
@@ -118,6 +119,7 @@ test_files:
118
119
  - test/test_barcode1d.rb
119
120
  - test/test_barcode1dcodabar.rb
120
121
  - test/test_barcode1dcode11.rb
122
+ - test/test_barcode1dcode128.rb
121
123
  - test/test_barcode1dcode3of9.rb
122
124
  - test/test_barcode1dcode93.rb
123
125
  - test/test_barcode1dcoop2of5.rb