barcode1dtools 0.9.5.1 → 0.9.6.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, 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