barcode1dtools 0.9.5.1 → 0.9.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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, Code 93, and Codabar, but will be expanded to
20
- # include most 1D symbologies in the near future.
19
+ # Code 3 of 9, Code 93, Code 11, and Codabar, but will be
20
+ # expanded to 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 => ' ')
@@ -120,3 +120,4 @@ require 'barcode1dtools/upc_supplemental_5'
120
120
  require 'barcode1dtools/code3of9'
121
121
  require 'barcode1dtools/code93'
122
122
  require 'barcode1dtools/codabar'
123
+ require 'barcode1dtools/code11'
@@ -0,0 +1,239 @@
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::Code11 - Create and decode bar patterns for
11
+ # Code11. The value encoded is a string which may contain the
12
+ # digits 0-9 and the dash symbol "-". The standard specifies
13
+ # one or two check digits may be added depending on the length
14
+ # of the payload. Use :checksum_included => true if you have
15
+ # already added a checksum and wish to have it validated, or
16
+ # :skip_checksum => true if you don't wish to add one or have
17
+ # it validated.
18
+ #
19
+ # Code 11 is used in the telecom industry for equipment
20
+ # labeling. It should not be used in any new applications.
21
+ #
22
+ # val = "29382-38"
23
+ # bc = Barcode1DTools::Code11.new(val)
24
+ # pattern = bc.bars
25
+ # rle_pattern = bc.rle
26
+ # width = bc.width
27
+ #
28
+ # The object created is immutable.
29
+ #
30
+ # Barcode1DTools::Code11 creates the patterns that you need to
31
+ # display Code11 barcodes. It can also decode a simple w/n
32
+ # string.
33
+ #
34
+ # Code11 characters consist of 3 bars and 2 spaces, with a narrow
35
+ # space between them. Three of the characters- 0, 9, and "-" -
36
+ # are 6 units wide. The rest are 7 units wide.
37
+ #
38
+ # There are three formats for the returned pattern:
39
+ #
40
+ # bars - 1s and 0s specifying black lines and white spaces. Actual
41
+ # characters can be changed from "1" and 0" with options
42
+ # :line_character and :space_character.
43
+ #
44
+ # rle - Run-length-encoded version of the pattern. The first
45
+ # number is always a black line, with subsequent digits
46
+ # alternating between spaces and lines. The digits specify
47
+ # the width of each line or space.
48
+ #
49
+ # wn - The native format for this barcode type. The string
50
+ # consists of a series of "w" and "n" characters. The first
51
+ # item is always a black line, with subsequent characters
52
+ # alternating between spaces and lines. A "wide" item
53
+ # is twice the width of a "narrow" item.
54
+ #
55
+ # The "width" method will tell you the total end-to-end width, in
56
+ # units, of the entire barcode. Note that the w/n format is
57
+ # unavailable for this symbology.
58
+ #
59
+ #== Rendering
60
+ #
61
+ # The standard w/n ratio seems to be 2:1. There seem to be no real
62
+ # standards for display.
63
+
64
+ class Code11 < Barcode1D
65
+
66
+ # Character sequence - 0-based offset in this string is character
67
+ # number
68
+ CHAR_SEQUENCE = "0123456789-"
69
+
70
+ # Patterns for making bar codes
71
+ PATTERNS = {
72
+ '0'=> {'val'=>0 ,'wn'=>'nnnnw'},
73
+ '1'=> {'val'=>1 ,'wn'=>'wnnnw'},
74
+ '2'=> {'val'=>2 ,'wn'=>'nwnnw'},
75
+ '3'=> {'val'=>3 ,'wn'=>'wwnnn'},
76
+ '4'=> {'val'=>4 ,'wn'=>'nnwnw'},
77
+ '5'=> {'val'=>5 ,'wn'=>'wnwnn'},
78
+ '6'=> {'val'=>6 ,'wn'=>'nwwnn'},
79
+ '7'=> {'val'=>7 ,'wn'=>'nnnww'},
80
+ '8'=> {'val'=>8 ,'wn'=>'wnnwn'},
81
+ '9'=> {'val'=>9 ,'wn'=>'wnnnn'},
82
+ '-'=> {'val'=>10 ,'wn'=>'nnwnn'}
83
+ }
84
+
85
+ GUARD_PATTERN_WN = 'nnwwn'
86
+
87
+ DEFAULT_OPTIONS = {
88
+ :line_character => '1',
89
+ :space_character => '0',
90
+ :w_character => 'w',
91
+ :n_character => 'n',
92
+ :wn_ratio => '2'
93
+ }
94
+
95
+ attr_reader :start_character, :stop_character, :payload
96
+
97
+ class << self
98
+ # Code11 can encode digits and dashes.
99
+ def can_encode?(value)
100
+ value.to_s =~ /\A[0-9\-]+\z/
101
+ end
102
+
103
+ def generate_check_digit_for(value)
104
+ mult = 0
105
+ sum_c = value.to_s.reverse.split('').inject(0) { |a,c| mult = (mult == 11 ? 1 : mult + 1); a + mult * PATTERNS[c]['val'] }
106
+ check_c = CHAR_SEQUENCE[sum_c % 11,1]
107
+ if value.to_s.size > 9
108
+ mult = 0
109
+ sum_k = (value.to_s + check_c).reverse.split('').inject(0) { |a,c| mult = (mult == 10 ? 1 : mult + 1); a + mult * PATTERNS[c]['val'] }
110
+ check_k = CHAR_SEQUENCE[sum_k % 9,1]
111
+ else
112
+ check_k = ''
113
+ end
114
+ "#{check_c}#{check_k}"
115
+ end
116
+
117
+ def validate_check_digit_for(value)
118
+ payload, check_digits = split_payload_and_check_digits(value)
119
+ self.generate_check_digit_for(payload) == check_digits
120
+ end
121
+
122
+ def split_payload_and_check_digits(value)
123
+ if value.to_s.size > 11
124
+ # two check digits
125
+ md = value.to_s.match(/\A(.*)(..)\z/)
126
+ else
127
+ md = value.to_s.match(/\A(.*)(.)\z/)
128
+ end
129
+ [md[1], md[2]]
130
+ end
131
+
132
+ # Decode a string in rle format. This will return a Code11
133
+ # object.
134
+ def decode(str, options = {})
135
+ if str =~ /[^1-3]/ && str =~ /[^wn]/
136
+ raise UnencodableCharactersError, "Pattern must be rle or wn"
137
+ end
138
+
139
+ # ensure a wn string
140
+ if str =~ /[1-3]/
141
+ str = str.tr('123','nww')
142
+ end
143
+
144
+ if str.reverse =~ /\A#{GUARD_PATTERN_WN}n.*?#{GUARD_PATTERN_WN}\z/
145
+ str.reverse!
146
+ end
147
+
148
+ unless str =~ /\A#{GUARD_PATTERN_WN}n(.*?)#{GUARD_PATTERN_WN}\z/
149
+ raise UnencodableCharactersError, "Start/stop pattern is not detected."
150
+ end
151
+
152
+ # Adding an "n" to make it easier to scan
153
+ wn_pattern = $1
154
+
155
+ # Each pattern is 3 bars and 2 spaces, with a space between.
156
+ unless wn_pattern.size % 6 == 0
157
+ raise UnencodableCharactersError, "Wrong number of bars."
158
+ end
159
+
160
+ decoded_string = ''
161
+
162
+ wn_pattern.scan(/(.{5})n/).each do |chunk|
163
+
164
+ chunk = chunk.first
165
+ found = false
166
+
167
+ PATTERNS.each do |char,hsh|
168
+ if chunk == hsh['wn']
169
+ decoded_string += char
170
+ found = true
171
+ break;
172
+ end
173
+ end
174
+
175
+ raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
176
+
177
+ end
178
+
179
+ Code11.new(decoded_string, options)
180
+ end
181
+
182
+ end
183
+
184
+ # Options are :line_character, :space_character, :w_character,
185
+ # :n_character, :checksum_included, and :skip_checksum.
186
+ def initialize(value, options = {})
187
+
188
+ @options = DEFAULT_OPTIONS.merge(options)
189
+
190
+ # Can we encode this value?
191
+ raise UnencodableCharactersError unless self.class.can_encode?(value)
192
+
193
+ @value = value.to_s
194
+
195
+ if @options[:skip_checksum]
196
+ @encoded_string = value.to_s
197
+ @value = value.to_s
198
+ @check_digit = nil
199
+ elsif @options[:checksum_included]
200
+ raise ChecksumError unless self.class.validate_check_digit_for(value)
201
+ @encoded_string = value.to_s
202
+ @value, @check_digit = self.class.split_payload_and_check_digits(value)
203
+ else
204
+ @value = value.to_s
205
+ @check_digit = self.class.generate_check_digit_for(@value)
206
+ @encoded_string = "#{@value}#{@check_digit}"
207
+ end
208
+ end
209
+
210
+ # Returns a string of "w" or "n" ("wide" and "narrow")
211
+ def wn
212
+ @wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
213
+ end
214
+
215
+ # returns a run-length-encoded string representation
216
+ def rle
217
+ @rle ||= self.class.wn_to_rle(self.wn, @options)
218
+ end
219
+
220
+ # returns 1s and 0s (for "black" and "white")
221
+ def bars
222
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
223
+ end
224
+
225
+ # returns the total unit width of the bar code
226
+ def width
227
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
228
+ end
229
+
230
+ private
231
+
232
+ # Creates the actual w/n pattern. Note that there is a narrow space
233
+ # between each character.
234
+ def wn_str
235
+ @wn_str ||= GUARD_PATTERN_WN + 'n' + @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.join('n') + 'n' + GUARD_PATTERN_WN
236
+ end
237
+
238
+ end
239
+ end
@@ -0,0 +1,79 @@
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 Barcode1DToolsCode11Test < Test::Unit::TestCase
12
+ def setup
13
+ end
14
+
15
+ def teardown
16
+ end
17
+
18
+ # Creates a random number from 1 to 10 digits long
19
+ def random_code11_value(len)
20
+ (1..1+rand(len)).collect { "0123456789-"[rand(11),1] }.join
21
+ end
22
+
23
+ def test_checksum_generation
24
+ assert_equal '5', Barcode1DTools::Code11.generate_check_digit_for('123-45')
25
+ end
26
+
27
+ def test_checksum_validation
28
+ assert Barcode1DTools::Code11.validate_check_digit_for('123-455')
29
+ end
30
+
31
+ def test_attr_readers
32
+ code11 = Barcode1DTools::Code11.new('123-45', :checksum_included => false)
33
+ assert_equal '5', code11.check_digit
34
+ assert_equal '123-45', code11.value
35
+ assert_equal '123-455', code11.encoded_string
36
+ end
37
+
38
+ def test_checksum_error
39
+ # proper checksum is 5
40
+ assert_raise(Barcode1DTools::ChecksumError) { Barcode1DTools::Code11.new('123-451', :checksum_included => true) }
41
+ end
42
+
43
+ def test_skip_checksum
44
+ code11 = Barcode1DTools::Code11.new('123-45', :skip_checksum => true)
45
+ assert_nil code11.check_digit
46
+ assert_equal '123-45', code11.value
47
+ assert_equal '123-45', code11.encoded_string
48
+ end
49
+
50
+ def test_bad_character_errors
51
+ # Characters that cannot be encoded
52
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code11.new('thisisnotgood', :checksum_included => false) }
53
+ end
54
+
55
+ # Only need to test wn, as bars and rle are based on wn
56
+ def test_barcode_generation
57
+ code11 = Barcode1DTools::Code11.new('123-455', :skip_checksum => true)
58
+ assert_equal "nnwwnnwnnnwnnwnnwnwwnnnnnnwnnnnnwnwnwnwnnnwnwnnnnnwwn", code11.wn
59
+ end
60
+
61
+ def test_decode_error
62
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code11.decode('x') }
63
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code11.decode('x'*60) }
64
+ # proper start & stop, but crap in middle
65
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code11.decode('nnwwnnnnnnnnnnnwwn') }
66
+ # wrong start/stop
67
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code11.decode('nwwnwnwnwnwnwnw') }
68
+ end
69
+
70
+ def test_decoding
71
+ random_code11_num=random_code11_value(10)
72
+ code11 = Barcode1DTools::Code11.new(random_code11_num, :skip_checksum => true)
73
+ code112 = Barcode1DTools::Code11.decode(code11.wn)
74
+ assert_equal code11.value, code112.value
75
+ # Should also work in reverse
76
+ code114 = Barcode1DTools::Code11.decode(code11.wn.reverse)
77
+ assert_equal code11.value, code114.value
78
+ end
79
+ end
metadata CHANGED
@@ -1,29 +1,44 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: barcode1dtools
3
- version: !ruby/object:Gem::Version
4
- version: 0.9.5.1
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 6
10
+ - 0
11
+ version: 0.9.6.0
6
12
  platform: ruby
7
- authors:
13
+ authors:
8
14
  - Michael Chaney
9
15
  autorequire:
10
16
  bindir: bin
11
17
  cert_chain: []
12
- date: 2012-09-14 00:00:00.000000000Z
18
+
19
+ date: 2012-09-16 00:00:00 -05:00
20
+ default_executable:
13
21
  dependencies: []
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"
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, Code 11, Codabar, Interleaved 2 of 5, EAN-13, EAN-8, UPC-A,\n\
26
+ \t UPC-E, UPC Supplemental 2, and UPC Supplemental 5. Patterns are\n\
27
+ \t created in either a simple format of bars and spaces or as a\n\
28
+ \t run-length encoded string. This only generates and decodes the\n\
29
+ \t patterns; actual display or reading from a device must be\n\
30
+ \t implemented by the programmer. More symbologies will be added as\n\
31
+ \t time permits.\n"
21
32
  email: mdchaney@michaelchaney.com
22
33
  executables: []
34
+
23
35
  extensions: []
36
+
24
37
  extra_rdoc_files: []
25
- files:
38
+
39
+ files:
26
40
  - lib/barcode1dtools/codabar.rb
41
+ - lib/barcode1dtools/code11.rb
27
42
  - lib/barcode1dtools/code3of9.rb
28
43
  - lib/barcode1dtools/code93.rb
29
44
  - lib/barcode1dtools/ean13.rb
@@ -37,6 +52,7 @@ files:
37
52
  - MIT-LICENSE
38
53
  - test/test_barcode1d.rb
39
54
  - test/test_barcode1dcodabar.rb
55
+ - test/test_barcode1dcode11.rb
40
56
  - test/test_barcode1dcode3of9.rb
41
57
  - test/test_barcode1dcode93.rb
42
58
  - test/test_barcode1dean13.rb
@@ -46,35 +62,47 @@ files:
46
62
  - test/test_barcode1dupce.rb
47
63
  - test/test_barcode1dupcsupp2.rb
48
64
  - test/test_barcode1dupcsupp5.rb
65
+ has_rdoc: true
49
66
  homepage: http://rubygems.org/gems/barcode1dtools
50
- licenses:
67
+ licenses:
51
68
  - MIT
52
69
  - GPL-2
53
70
  post_install_message:
54
71
  rdoc_options: []
55
- require_paths:
72
+
73
+ require_paths:
56
74
  - lib
57
- required_ruby_version: !ruby/object:Gem::Requirement
75
+ required_ruby_version: !ruby/object:Gem::Requirement
58
76
  none: false
59
- requirements:
60
- - - ! '>='
61
- - !ruby/object:Gem::Version
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ hash: 59
81
+ segments:
82
+ - 1
83
+ - 8
84
+ - 6
62
85
  version: 1.8.6
63
- required_rubygems_version: !ruby/object:Gem::Requirement
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
87
  none: false
65
- requirements:
66
- - - ! '>='
67
- - !ruby/object:Gem::Version
68
- version: '0'
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 3
92
+ segments:
93
+ - 0
94
+ version: "0"
69
95
  requirements: []
96
+
70
97
  rubyforge_project:
71
- rubygems_version: 1.8.10
98
+ rubygems_version: 1.5.3
72
99
  signing_key:
73
100
  specification_version: 3
74
101
  summary: Pattern generators for 1D barcodes
75
- test_files:
102
+ test_files:
76
103
  - test/test_barcode1d.rb
77
104
  - test/test_barcode1dcodabar.rb
105
+ - test/test_barcode1dcode11.rb
78
106
  - test/test_barcode1dcode3of9.rb
79
107
  - test/test_barcode1dcode93.rb
80
108
  - test/test_barcode1dean13.rb