barcode1dtools 0.9.8.0 → 0.9.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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