barcode1dtools 0.9.4.1 → 0.9.5.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.
@@ -16,8 +16,8 @@ module Barcode1DTools
16
16
  # 1-dimensional barcode patterns for various code types. The
17
17
  # library currently includes EAN-13, EAN-8, UPC-A, UPC-E, UPC
18
18
  # Supplemental 2, UPC Supplemental 5, Interleaved 2 of 5 (I 2/5),
19
- # Code 3 of 9, and Code 93, but will be expanded to include most 1D
20
- # symbologies in the near future.
19
+ # Code 3 of 9, Code 93, and Codabar, but will be expanded to
20
+ # include most 1D symbologies in the near future.
21
21
  #
22
22
  #== Example
23
23
  # ean13 = Barcode1DTools::EAN13.new('0012676510226', :line_character => 'x', :space_character => ' ')
@@ -119,3 +119,4 @@ require 'barcode1dtools/upc_supplemental_2'
119
119
  require 'barcode1dtools/upc_supplemental_5'
120
120
  require 'barcode1dtools/code3of9'
121
121
  require 'barcode1dtools/code93'
122
+ require 'barcode1dtools/codabar'
@@ -0,0 +1,249 @@
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::Codabar - Create and decode bar patterns for
11
+ # Codabar. The value encoded is a string which may contain the
12
+ # digits 0-9 and the symbols dash "-", dollar sign "$", plus
13
+ # sign "+", colon ":", forward slash "/", and dot ".". There are
14
+ # four start/stop characters which are A, B, C, and D or T, N,
15
+ # asterisk "*", and E. Note that A and T are equivalent, as are
16
+ # B and N, C and asterisk, and D and E. Any may be used as start
17
+ # and stop characters giving 16 possible combinations.
18
+ #
19
+ # Because there is no standard for check digits, we implement
20
+ # neither generation nor checking of one. It is up to the caller
21
+ # to present a check digit if it is part of the payload.
22
+ #
23
+ # Additionally, the caller must present the start and stop
24
+ # characters as part of the value. When decoding, the start/stop
25
+ # cahracters will be presented as A, B, C, or D.
26
+ #
27
+ # val = "A29322930C"
28
+ # bc = Barcode1DTools::Codabar.new(val)
29
+ # pattern = bc.bars
30
+ # rle_pattern = bc.rle
31
+ # width = bc.width
32
+ #
33
+ # The object created is immutable.
34
+ #
35
+ # Barcode1DTools::Codabar creates the patterns that you need to
36
+ # display Codabar barcodes. It can also decode a simple w/n
37
+ # string.
38
+ #
39
+ # Codabar characters consist of 4 bars and 3 spaces, with a narrow
40
+ # space between them. The main characters (0-9, dash, and dollar
41
+ # sign) each have one wide bar and one wide space (hence the
42
+ # alternate name "code 2 of 7"). The start/stop codes have one
43
+ # wide bar and two adjacent wide spaces. The extended characters
44
+ # (dot, forward slash, colon, and plus sign) each have three wide
45
+ # bars and all narrow spaces.
46
+ #
47
+ # There are three formats for the returned pattern:
48
+ #
49
+ # bars - 1s and 0s specifying black lines and white spaces. Actual
50
+ # characters can be changed from "1" and 0" with options
51
+ # :line_character and :space_character.
52
+ #
53
+ # rle - Run-length-encoded version of the pattern. The first
54
+ # number is always a black line, with subsequent digits
55
+ # alternating between spaces and lines. The digits specify
56
+ # the width of each line or space.
57
+ #
58
+ # wn - The native format for this barcode type. The string
59
+ # consists of a series of "w" and "n" characters. The first
60
+ # item is always a black line, with subsequent characters
61
+ # alternating between spaces and lines. A "wide" item
62
+ # is twice the width of a "narrow" item.
63
+ #
64
+ # The "width" method will tell you the total end-to-end width, in
65
+ # units, of the entire barcode. Note that the w/n format is
66
+ # unavailable for this symbology.
67
+ #
68
+ #== Rendering
69
+ #
70
+ # The original Codabar specification actually included a varied
71
+ # w/n ratio depending on whether there were two or three wide
72
+ # elements in a character. Those with two wide elements used a 3:1
73
+ # ratio while those with three wide elements used a 2:1 ratio. In
74
+ # that way, the characters were consistently 10 units wide.
75
+ #
76
+ # Our default ratio is 3:1 for the entire code, but if you include
77
+ # :varied_wn_ratio => true in the options the rle and bars strings
78
+ # will have variable ratio that shifts between 2:1 and 3:1 and the
79
+ # "wn_ratio" option will be ignored.
80
+
81
+ class Codabar < Barcode1D
82
+
83
+ # Character sequence - 0-based offset in this string is character
84
+ # number
85
+ CHAR_SEQUENCE = "0123456789-$:/.+ABCD"
86
+
87
+ # Patterns for making bar codes
88
+ PATTERNS = {
89
+ '0'=> {'val'=>0 ,'wn'=>'nnnnnww'},
90
+ '1'=> {'val'=>1 ,'wn'=>'nnnnwwn'},
91
+ '2'=> {'val'=>2 ,'wn'=>'nnnwnnw'},
92
+ '3'=> {'val'=>3 ,'wn'=>'wwnnnnn'},
93
+ '4'=> {'val'=>4 ,'wn'=>'nnwnnwn'},
94
+ '5'=> {'val'=>5 ,'wn'=>'wnnnnwn'},
95
+ '6'=> {'val'=>6 ,'wn'=>'nwnnnnw'},
96
+ '7'=> {'val'=>7 ,'wn'=>'nwnnwnn'},
97
+ '8'=> {'val'=>8 ,'wn'=>'nwwnnnn'},
98
+ '9'=> {'val'=>9 ,'wn'=>'wnnwnnn'},
99
+ '-'=> {'val'=>10 ,'wn'=>'nnnwwnn'},
100
+ '$'=> {'val'=>11 ,'wn'=>'nnwwnnn'},
101
+ ':'=> {'val'=>12 ,'wn'=>'wnnnwnw'},
102
+ '/'=> {'val'=>13 ,'wn'=>'wnwnnnw'},
103
+ '.'=> {'val'=>14 ,'wn'=>'wnwnwnn'},
104
+ '+'=> {'val'=>15 ,'wn'=>'nnwnwnw'},
105
+
106
+ 'A'=> {'val'=>16 ,'wn'=>'nnwwnwn'},
107
+ 'B'=> {'val'=>17 ,'wn'=>'nwnwnnw'},
108
+ 'C'=> {'val'=>18 ,'wn'=>'nnnwnww'},
109
+ 'D'=> {'val'=>19 ,'wn'=>'nnnwwwn'},
110
+
111
+ 'T'=> {'val'=>16 ,'wn'=>'nnwwnwn'},
112
+ 'N'=> {'val'=>17 ,'wn'=>'nwnwnnw'},
113
+ '*'=> {'val'=>18 ,'wn'=>'nnnwnww'},
114
+ 'E'=> {'val'=>19 ,'wn'=>'nnnwwwn'}
115
+ }
116
+
117
+ DEFAULT_OPTIONS = {
118
+ :line_character => '1',
119
+ :space_character => '0',
120
+ :w_character => 'w',
121
+ :n_character => 'n',
122
+ :wn_ratio => '3',
123
+ :varied_wn_ratio => false
124
+ }
125
+
126
+ attr_reader :start_character, :stop_character, :payload
127
+
128
+ class << self
129
+ # Codabar can encode digits, dash, dollar, colon, forward
130
+ # slash, dot, and plus. The string must start and stop with
131
+ # start/stop characters.
132
+ def can_encode?(value)
133
+ value.to_s =~ /\A[ABCD][0-9\$:\/\.\+\-]*[ABCD]\z/ || value.to_s =~ /\A[TN\*E][0-9\$:\/\.\+\-]*[TN\*E]\z/
134
+ end
135
+
136
+ # We don't generate a check digit
137
+ def generate_check_digit_for(value)
138
+ raise NotImplementedError
139
+ end
140
+
141
+ def validate_check_digit_for(value)
142
+ raise NotImplementedError
143
+ end
144
+
145
+ # Decode a string in rle format. This will return a Codabar
146
+ # object.
147
+ def decode(str, options = {})
148
+ if str =~ /[^1-3]/ && str =~ /[^wn]/
149
+ raise UnencodableCharactersError, "Pattern must be rle or wn"
150
+ end
151
+
152
+ # ensure a wn string
153
+ if str =~ /[1-3]/
154
+ str = str.tr('123','nww')
155
+ end
156
+
157
+ start_stop_pattern_match = Regexp.new(['A','B','C','D'].collect { |c| PATTERNS[c]['wn'] }.join('|'))
158
+
159
+ if str.reverse =~ /\A#{start_stop_pattern_match}n.*?#{start_stop_pattern_match}\z/
160
+ str.reverse!
161
+ end
162
+
163
+ unless str =~ /\A(#{start_stop_pattern_match}n.*?#{start_stop_pattern_match})\z/
164
+ raise UnencodableCharactersError, "Start/stop pattern is not detected."
165
+ end
166
+
167
+ # Adding an "n" to make it easier to scan
168
+ wn_pattern = $1 + 'n'
169
+
170
+ # Each pattern is 4 bars and 3 spaces, with a space between.
171
+ unless wn_pattern.size % 8 == 0
172
+ raise UnencodableCharactersError, "Wrong number of bars."
173
+ end
174
+
175
+ decoded_string = ''
176
+
177
+ wn_pattern.scan(/(.{7})n/).each do |chunk|
178
+
179
+ chunk = chunk.first
180
+ found = false
181
+
182
+ PATTERNS.each do |char,hsh|
183
+ if !['T', 'E', '*', 'N'].include?(char) && chunk == hsh['wn']
184
+ decoded_string += char
185
+ found = true
186
+ break;
187
+ end
188
+ end
189
+
190
+ raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
191
+
192
+ end
193
+
194
+ Codabar.new(decoded_string)
195
+ end
196
+
197
+ end
198
+
199
+ # Options are :line_character, :space_character, :w_character,
200
+ # :n_character, and :varied_wn_ratio.
201
+ def initialize(value, options = {})
202
+
203
+ @options = DEFAULT_OPTIONS.merge(options)
204
+
205
+ # Can we encode this value?
206
+ raise UnencodableCharactersError unless self.class.can_encode?(value)
207
+
208
+ @value = value.to_s
209
+ @check_digit = nil
210
+
211
+ @encoded_string = @value
212
+ md = @value.match(/\A([ABCDTNE\*])(.*?)([ABCDTNE\*])\z/)
213
+ @start_character, @payload, @stop_character = md[1], md[2], md[3]
214
+ end
215
+
216
+ # Returns a string of "w" or "n" ("wide" and "narrow")
217
+ def wn
218
+ @wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
219
+ end
220
+
221
+ # returns a run-length-encoded string representation
222
+ def rle
223
+ if @options[:varied_wn_ratio]
224
+ @rle ||= @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.collect { |p| p.tr('wn',(p=~/.*w.*w.*w/ ? '21' : '31')) }.join('1')
225
+ else
226
+ @rle ||= self.class.wn_to_rle(self.wn, @options)
227
+ end
228
+ end
229
+
230
+ # returns 1s and 0s (for "black" and "white")
231
+ def bars
232
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
233
+ end
234
+
235
+ # returns the total unit width of the bar code
236
+ def width
237
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
238
+ end
239
+
240
+ private
241
+
242
+ # Creates the actual w/n pattern. Note that there is a narrow space
243
+ # between each character.
244
+ def wn_str
245
+ @wn_str ||= @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.join('n')
246
+ end
247
+
248
+ end
249
+ end
@@ -0,0 +1,67 @@
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 Barcode1DToolsCodabarTest < Test::Unit::TestCase
12
+ def setup
13
+ end
14
+
15
+ def teardown
16
+ end
17
+
18
+ # Creates a random Codabar string
19
+ def random_codabar_value
20
+ ['A','B','C','D'][rand(4)] + (1..5+rand(10)).collect { "0123456789-$:/.+"[rand(16),1] }.join + ['A','B','C','D'][rand(4)]
21
+ end
22
+
23
+ def test_no_check_digit
24
+ assert_raise(Barcode1DTools::NotImplementedError) { Barcode1DTools::Codabar.generate_check_digit_for(random_codabar_value) }
25
+ assert_raise(Barcode1DTools::NotImplementedError) { Barcode1DTools::Codabar.validate_check_digit_for(random_codabar_value) }
26
+ end
27
+
28
+ def test_attr_readers
29
+ codabar = Barcode1DTools::Codabar.new('A12345678C')
30
+ assert_nil codabar.check_digit
31
+ assert_equal 'A12345678C', codabar.value
32
+ assert_equal 'A12345678C', codabar.encoded_string
33
+ assert_equal 'A', codabar.start_character
34
+ assert_equal 'C', codabar.stop_character
35
+ assert_equal '12345678', codabar.payload
36
+ end
37
+
38
+ def test_bad_character_errors
39
+ # Characters that cannot be encoded
40
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Codabar.new('thisisnotgood') }
41
+ end
42
+
43
+ # Only need to test wn, as bars and rle are based on wn
44
+ def test_barcode_generation
45
+ codabar = Barcode1DTools::Codabar.new('A1B')
46
+ assert_equal "nnwwnwnnnnnnwwnnnwnwnnw", codabar.wn
47
+ end
48
+
49
+ def test_decode_error
50
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Codabar.decode('x') }
51
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Codabar.decode('x'*60) }
52
+ # proper start & stop, but crap in middle
53
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Codabar.decode('nnnnwwwwnn') }
54
+ # wrong start/stop
55
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Codabar.decode('nwwnwnwnwnwnwnw') }
56
+ end
57
+
58
+ def test_decoding
59
+ random_codabar_num=random_codabar_value
60
+ codabar = Barcode1DTools::Codabar.new(random_codabar_num)
61
+ codabar2 = Barcode1DTools::Codabar.decode(codabar.wn)
62
+ assert_equal codabar.value, codabar2.value
63
+ # Should also work in reverse
64
+ codabar4 = Barcode1DTools::Codabar.decode(codabar.wn.reverse)
65
+ assert_equal codabar.value, codabar4.value
66
+ end
67
+ end
metadata CHANGED
@@ -1,41 +1,29 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: barcode1dtools
3
- version: !ruby/object:Gem::Version
4
- hash: 21
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.5.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 9
9
- - 4
10
- - 1
11
- version: 0.9.4.1
12
6
  platform: ruby
13
- authors:
7
+ authors:
14
8
  - Michael Chaney
15
9
  autorequire:
16
10
  bindir: bin
17
11
  cert_chain: []
18
-
19
- date: 2012-08-05 00:00:00 -05:00
20
- default_executable:
12
+ date: 2012-08-05 00:00:00.000000000Z
21
13
  dependencies: []
22
-
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, Interleaved 2 of 5, EAN-13, EAN-8, UPC-A, UPC-E, UPC\n\
26
- \t Supplemental 2, and UPC Supplemental 5. Patterns are created in\n\
27
- \t either a simple format of bars and spaces or as a run-length encoded\n\
28
- \t string. This only generates and decodes the patterns; actual\n\
29
- \t display or reading from a device must be implemented by the\n\
30
- \t programmer. More symbologies will be added as time permits.\n"
14
+ description: ! "\t Barcode1D is a small library for handling many kinds of\n\t 1-dimensional
15
+ barcodes. Currently implemented are Code 3 of 9, Code\n\t 93, Codabar, Interleaved
16
+ 2 of 5, EAN-13, EAN-8, UPC-A, UPC-E, UPC\n\t Supplemental 2, and UPC Supplemental
17
+ 5. Patterns are created in\n\t either a simple format of bars and spaces or as
18
+ a run-length encoded\n\t string. This only generates and decodes the patterns;
19
+ actual\n\t display or reading from a device must be implemented by the\n\t programmer.
20
+ \ More symbologies will be added as time permits.\n"
31
21
  email: mdchaney@michaelchaney.com
32
22
  executables: []
33
-
34
23
  extensions: []
35
-
36
24
  extra_rdoc_files: []
37
-
38
- files:
25
+ files:
26
+ - lib/barcode1dtools/codabar.rb
39
27
  - lib/barcode1dtools/code3of9.rb
40
28
  - lib/barcode1dtools/code93.rb
41
29
  - lib/barcode1dtools/ean13.rb
@@ -48,6 +36,7 @@ files:
48
36
  - lib/barcode1dtools.rb
49
37
  - MIT-LICENSE
50
38
  - test/test_barcode1d.rb
39
+ - test/test_barcode1dcodabar.rb
51
40
  - test/test_barcode1dcode3of9.rb
52
41
  - test/test_barcode1dcode93.rb
53
42
  - test/test_barcode1dean13.rb
@@ -57,45 +46,35 @@ files:
57
46
  - test/test_barcode1dupce.rb
58
47
  - test/test_barcode1dupcsupp2.rb
59
48
  - test/test_barcode1dupcsupp5.rb
60
- has_rdoc: true
61
49
  homepage: http://rubygems.org/gems/barcode1dtools
62
- licenses:
50
+ licenses:
63
51
  - MIT
64
52
  - GPL-2
65
53
  post_install_message:
66
54
  rdoc_options: []
67
-
68
- require_paths:
55
+ require_paths:
69
56
  - lib
70
- required_ruby_version: !ruby/object:Gem::Requirement
57
+ required_ruby_version: !ruby/object:Gem::Requirement
71
58
  none: false
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- hash: 59
76
- segments:
77
- - 1
78
- - 8
79
- - 6
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
80
62
  version: 1.8.6
81
- required_rubygems_version: !ruby/object:Gem::Requirement
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
64
  none: false
83
- requirements:
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- hash: 3
87
- segments:
88
- - 0
89
- version: "0"
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
90
69
  requirements: []
91
-
92
70
  rubyforge_project:
93
- rubygems_version: 1.5.3
71
+ rubygems_version: 1.8.10
94
72
  signing_key:
95
73
  specification_version: 3
96
74
  summary: Pattern generators for 1D barcodes
97
- test_files:
75
+ test_files:
98
76
  - test/test_barcode1d.rb
77
+ - test/test_barcode1dcodabar.rb
99
78
  - test/test_barcode1dcode3of9.rb
100
79
  - test/test_barcode1dcode93.rb
101
80
  - test/test_barcode1dean13.rb