barcode1dtools 0.9.6.0 → 0.9.7.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.
@@ -0,0 +1,223 @@
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::Industrial2of5 - Create and decode bar patterns for
11
+ # Industrial 2 of 5. The value encoded is a number with digits 0-9.
12
+ # Internally, the value is treated as a string to preserve
13
+ # leading zeroes.
14
+ #
15
+ # Use :checksum_included => true if you have already added a
16
+ # checksum and wish to have it validated, or :skip_checksum =>
17
+ # false if you wish to have one added.
18
+ #
19
+ # Industrial 2 of 5 is low-density and limited. It should not be
20
+ # used in any new applications.
21
+ #
22
+ # val = "3423"
23
+ # bc = Barcode1DTools::Industrial2of5.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::Industrial2of5 creates the patterns that you need to
31
+ # display Industrial 2 of 5 barcodes. It can also decode a simple w/n
32
+ # string.
33
+ #
34
+ # Industrial2of5 characters consist of 3 bars and 2 spaces, with a narrow
35
+ # space between them. 2 of the bars/spaces in each symbol are wide.
36
+ #
37
+ # There are three 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
+ # wn - The native format for this barcode type. The string
49
+ # consists of a series of "w" and "n" characters. The first
50
+ # item is always a black line, with subsequent characters
51
+ # alternating between spaces and lines. A "wide" item
52
+ # is twice the width of a "narrow" item.
53
+ #
54
+ # The "width" method will tell you the total end-to-end width, in
55
+ # units, of the entire barcode.
56
+ #
57
+ #== Rendering
58
+ #
59
+ # The standard w/n ratio seems to be 2:1. There seem to be no real
60
+ # standards for display.
61
+
62
+ class Industrial2of5 < Barcode1D
63
+
64
+ # Character sequence - 0-based offset in this string is character
65
+ # number
66
+ CHAR_SEQUENCE = "0123456789"
67
+
68
+ # Patterns for making bar codes. Note that the position
69
+ # weights are 1, 2, 4, and 7 and the last bit is parity.
70
+ # The patterns are for the bars, all spaces are narrow.
71
+ PATTERNS = {
72
+ '0'=> {'val'=>0 ,'wn'=>'nnnnwnwnn'},
73
+ '1'=> {'val'=>1 ,'wn'=>'wnnnnnnnw'},
74
+ '2'=> {'val'=>2 ,'wn'=>'nnwnnnnnw'},
75
+ '3'=> {'val'=>3 ,'wn'=>'wnwnnnnnn'},
76
+ '4'=> {'val'=>4 ,'wn'=>'nnnnwnnnw'},
77
+ '5'=> {'val'=>5 ,'wn'=>'wnnnwnnnn'},
78
+ '6'=> {'val'=>6 ,'wn'=>'nnwnwnnnn'},
79
+ '7'=> {'val'=>7 ,'wn'=>'nnnnnnwnw'},
80
+ '8'=> {'val'=>8 ,'wn'=>'wnnnnnwnn'},
81
+ '9'=> {'val'=>9 ,'wn'=>'nnwnnnwnn'}
82
+ }
83
+
84
+ GUARD_PATTERN_LEFT_WN = 'wnwnn'
85
+ GUARD_PATTERN_RIGHT_WN = 'wnnnw'
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
+ :skip_checksum => true
94
+ }
95
+
96
+ class << self
97
+ # Industrial2of5 can encode digits
98
+ def can_encode?(value)
99
+ value.to_s =~ /\A[0-9]+\z/
100
+ end
101
+
102
+ def generate_check_digit_for(value)
103
+ raise UnencodableCharactersError unless self.can_encode?(value)
104
+ mult = 3
105
+ value = value.reverse.split('').inject(0) { |a,c| mult = 4 - mult ; a + c.to_i * mult }
106
+ (10 - (value % 10)) % 10
107
+ end
108
+
109
+ def validate_check_digit_for(value)
110
+ raise UnencodableCharactersError unless self.can_encode?(value)
111
+ md = value.match(/^(\d+?)(\d)$/)
112
+ self.generate_check_digit_for(md[1]) == md[2].to_i
113
+ end
114
+
115
+ # Decode a string in rle format. This will return a Industrial2of5
116
+ # object.
117
+ def decode(str, options = {})
118
+ if str =~ /[^1-3]/ && str =~ /[^wn]/
119
+ raise UnencodableCharactersError, "Pattern must be rle or wn"
120
+ end
121
+
122
+ # ensure a wn string
123
+ if str =~ /[1-3]/
124
+ str = str.tr('123','nww')
125
+ end
126
+
127
+ if str.reverse =~ /\A#{GUARD_PATTERN_LEFT_WN}n.*?#{GUARD_PATTERN_RIGHT_WN}\z/
128
+ str.reverse!
129
+ end
130
+
131
+ # Note that every other character is an "n"
132
+ unless str =~ /\A([wn]n)+[wn]\z/ && str =~ /\A#{GUARD_PATTERN_LEFT_WN}n(.*?)#{GUARD_PATTERN_RIGHT_WN}\z/
133
+ raise UnencodableCharactersError, "Start/stop pattern is not detected."
134
+ end
135
+
136
+ wn_pattern = $1
137
+
138
+ # Each pattern is 5 bars and 4 spaces, with a space between.
139
+ unless wn_pattern.size % 10 == 0
140
+ raise UnencodableCharactersError, "Wrong number of bars."
141
+ end
142
+
143
+ decoded_string = ''
144
+
145
+ wn_pattern.scan(/(.{9})n/).each do |chunk|
146
+
147
+ chunk = chunk.first
148
+ found = false
149
+
150
+ PATTERNS.each do |char,hsh|
151
+ if chunk == hsh['wn']
152
+ decoded_string += char
153
+ found = true
154
+ break;
155
+ end
156
+ end
157
+
158
+ raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
159
+
160
+ end
161
+
162
+ Industrial2of5.new(decoded_string, options)
163
+ end
164
+
165
+ end
166
+
167
+ # Options are :line_character, :space_character, :w_character,
168
+ # :n_character, :checksum_included, and :skip_checksum.
169
+ def initialize(value, options = {})
170
+
171
+ @options = DEFAULT_OPTIONS.merge(options)
172
+
173
+ # Can we encode this value?
174
+ raise UnencodableCharactersError unless self.class.can_encode?(value)
175
+
176
+ @value = value.to_s
177
+
178
+ if @options[:skip_checksum]
179
+ @encoded_string = value.to_s
180
+ @value = value.to_s
181
+ @check_digit = nil
182
+ elsif @options[:checksum_included]
183
+ @encoded_string = value.to_s
184
+ raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
185
+ md = @encoded_string.match(/^(\d+?)(\d)$/)
186
+ @value, @check_digit = md[1], md[2].to_i
187
+ else
188
+ @value = value.to_s
189
+ @check_digit = self.class.generate_check_digit_for(@value)
190
+ @encoded_string = "#{@value}#{@check_digit}"
191
+ end
192
+ end
193
+
194
+ # Returns a string of "w" or "n" ("wide" and "narrow")
195
+ def wn
196
+ @wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
197
+ end
198
+
199
+ # returns a run-length-encoded string representation
200
+ def rle
201
+ @rle ||= self.class.wn_to_rle(self.wn, @options)
202
+ end
203
+
204
+ # returns 1s and 0s (for "black" and "white")
205
+ def bars
206
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
207
+ end
208
+
209
+ # returns the total unit width of the bar code
210
+ def width
211
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
212
+ end
213
+
214
+ private
215
+
216
+ # Creates the actual w/n pattern. Note that there is a narrow space
217
+ # between every single bar.
218
+ def wn_str
219
+ @wn_str ||= GUARD_PATTERN_LEFT_WN + 'n' + @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.join('n') + 'n' + GUARD_PATTERN_RIGHT_WN
220
+ end
221
+
222
+ end
223
+ end
@@ -0,0 +1,222 @@
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::Matrix2of5 - Create and decode bar patterns for
11
+ # Matrix 2 of 5. The value encoded is a number with digits 0-9.
12
+ # Internally, the value is treated as a string to preserve
13
+ # leading zeroes.
14
+ #
15
+ # Use :checksum_included => true if you have already added a
16
+ # checksum and wish to have it validated, or :skip_checksum =>
17
+ # true if you don't wish to add one or have it validated.
18
+ #
19
+ # Matrix 2 of 5 is low-density and limited. It should not be
20
+ # used in any new applications.
21
+ #
22
+ # val = "3423"
23
+ # bc = Barcode1DTools::Matrix2of5.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::Matrix2of5 creates the patterns that you need to
31
+ # display Matrix 2 of 5 barcodes. It can also decode a simple w/n
32
+ # string.
33
+ #
34
+ # Matrix2of5 characters consist of 3 bars and 2 spaces, with a narrow
35
+ # space between them. 2 of the bars/spaces in each symbol are wide.
36
+ #
37
+ # There are three 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
+ # wn - The native format for this barcode type. The string
49
+ # consists of a series of "w" and "n" characters. The first
50
+ # item is always a black line, with subsequent characters
51
+ # alternating between spaces and lines. A "wide" item
52
+ # is twice the width of a "narrow" item.
53
+ #
54
+ # The "width" method will tell you the total end-to-end width, in
55
+ # units, of the entire barcode.
56
+ #
57
+ #== Rendering
58
+ #
59
+ # The standard w/n ratio seems to be 2:1. There seem to be no real
60
+ # standards for display.
61
+
62
+ class Matrix2of5 < Barcode1D
63
+
64
+ # Character sequence - 0-based offset in this string is character
65
+ # number
66
+ CHAR_SEQUENCE = "0123456789"
67
+
68
+ # Patterns for making bar codes. Note that the position
69
+ # weights are 1, 2, 4, and 7, and the last bit is parity.
70
+ # Each letter is an alternating bar then space, and there
71
+ # is a narrow space between each character.
72
+ PATTERNS = {
73
+ '0'=> {'val'=>0 ,'wn'=>'nnwwn'},
74
+ '1'=> {'val'=>1 ,'wn'=>'wnnnw'},
75
+ '2'=> {'val'=>2 ,'wn'=>'nwnnw'},
76
+ '3'=> {'val'=>3 ,'wn'=>'wwnnn'},
77
+ '4'=> {'val'=>4 ,'wn'=>'nnwnw'},
78
+ '5'=> {'val'=>5 ,'wn'=>'wnwnn'},
79
+ '6'=> {'val'=>6 ,'wn'=>'nwwnn'},
80
+ '7'=> {'val'=>7 ,'wn'=>'nnnww'},
81
+ '8'=> {'val'=>8 ,'wn'=>'wnnwn'},
82
+ '9'=> {'val'=>9 ,'wn'=>'nwnwn'}
83
+ }
84
+
85
+ GUARD_PATTERN_LEFT_WN = 'wnnnn'
86
+ GUARD_PATTERN_RIGHT_WN = 'wnnnn'
87
+
88
+ DEFAULT_OPTIONS = {
89
+ :line_character => '1',
90
+ :space_character => '0',
91
+ :w_character => 'w',
92
+ :n_character => 'n',
93
+ :wn_ratio => '2'
94
+ }
95
+
96
+ class << self
97
+ # Matrix2of5 can encode digits
98
+ def can_encode?(value)
99
+ value.to_s =~ /\A[0-9]+\z/
100
+ end
101
+
102
+ def generate_check_digit_for(value)
103
+ raise UnencodableCharactersError unless self.can_encode?(value)
104
+ mult = 3
105
+ value = value.reverse.split('').inject(0) { |a,c| mult = 4 - mult ; a + c.to_i * mult }
106
+ (10 - (value % 10)) % 10
107
+ end
108
+
109
+ def validate_check_digit_for(value)
110
+ raise UnencodableCharactersError unless self.can_encode?(value)
111
+ md = value.match(/^(\d+?)(\d)$/)
112
+ self.generate_check_digit_for(md[1]) == md[2].to_i
113
+ end
114
+
115
+ # Decode a string in rle format. This will return a Matrix2of5
116
+ # object.
117
+ def decode(str, options = {})
118
+ if str =~ /[^1-3]/ && str =~ /[^wn]/
119
+ raise UnencodableCharactersError, "Pattern must be rle or wn"
120
+ end
121
+
122
+ # ensure a wn string
123
+ if str =~ /[1-3]/
124
+ str = str.tr('123','nww')
125
+ end
126
+
127
+ if str.reverse =~ /\A#{GUARD_PATTERN_LEFT_WN}n.*?#{GUARD_PATTERN_RIGHT_WN}\z/
128
+ str.reverse!
129
+ end
130
+
131
+ unless str =~ /\A#{GUARD_PATTERN_LEFT_WN}n(.*?)#{GUARD_PATTERN_RIGHT_WN}\z/
132
+ raise UnencodableCharactersError, "Start/stop pattern is not detected."
133
+ end
134
+
135
+ wn_pattern = $1
136
+
137
+ # Each pattern is 3 bars and 2 spaces, with a space between.
138
+ unless wn_pattern.size % 6 == 0
139
+ raise UnencodableCharactersError, "Wrong number of bars."
140
+ end
141
+
142
+ decoded_string = ''
143
+
144
+ wn_pattern.scan(/(.{5})n/).each do |chunk|
145
+
146
+ chunk = chunk.first
147
+ found = false
148
+
149
+ PATTERNS.each do |char,hsh|
150
+ if chunk == hsh['wn']
151
+ decoded_string += char
152
+ found = true
153
+ break;
154
+ end
155
+ end
156
+
157
+ raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
158
+
159
+ end
160
+
161
+ Matrix2of5.new(decoded_string, options)
162
+ end
163
+
164
+ end
165
+
166
+ # Options are :line_character, :space_character, :w_character,
167
+ # :n_character, :checksum_included, and :skip_checksum.
168
+ def initialize(value, options = {})
169
+
170
+ @options = DEFAULT_OPTIONS.merge(options)
171
+
172
+ # Can we encode this value?
173
+ raise UnencodableCharactersError unless self.class.can_encode?(value)
174
+
175
+ @value = value.to_s
176
+
177
+ if @options[:skip_checksum]
178
+ @encoded_string = value.to_s
179
+ @value = value.to_s
180
+ @check_digit = nil
181
+ elsif @options[:checksum_included]
182
+ @encoded_string = value.to_s
183
+ raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
184
+ md = @encoded_string.match(/^(\d+?)(\d)$/)
185
+ @value, @check_digit = md[1], md[2].to_i
186
+ else
187
+ @value = value.to_s
188
+ @check_digit = self.class.generate_check_digit_for(@value)
189
+ @encoded_string = "#{@value}#{@check_digit}"
190
+ end
191
+ end
192
+
193
+ # Returns a string of "w" or "n" ("wide" and "narrow")
194
+ def wn
195
+ @wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
196
+ end
197
+
198
+ # returns a run-length-encoded string representation
199
+ def rle
200
+ @rle ||= self.class.wn_to_rle(self.wn, @options)
201
+ end
202
+
203
+ # returns 1s and 0s (for "black" and "white")
204
+ def bars
205
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
206
+ end
207
+
208
+ # returns the total unit width of the bar code
209
+ def width
210
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
211
+ end
212
+
213
+ private
214
+
215
+ # Creates the actual w/n pattern. Note that there is a narrow space
216
+ # between each character.
217
+ def wn_str
218
+ @wn_str ||= GUARD_PATTERN_LEFT_WN + 'n' + @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.join('n') + 'n' + GUARD_PATTERN_RIGHT_WN
219
+ end
220
+
221
+ end
222
+ end
@@ -0,0 +1,223 @@
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::PostNet - Create and decode bar patterns for
11
+ # PostNet. The value encoded is a zip code, which may be 5,
12
+ # 9, or 11 digits long.
13
+ #
14
+ # Use :checksum_included => true if you have already added a
15
+ # checksum and wish to have it validated, or :skip_checksum =>
16
+ # true if you don't wish to add one or have it validated.
17
+ #
18
+ # PostNet is used by the USPS for mail sorting, although it
19
+ # is technically deprecated.
20
+ #
21
+ # val = "37211"
22
+ # bc = Barcode1DTools::PostNet.new(val)
23
+ # pattern = bc.bars
24
+ # rle_pattern = bc.rle
25
+ # width = bc.width
26
+ #
27
+ # The object created is immutable.
28
+ #
29
+ # Barcode1DTools::PostNet creates the patterns that you need to
30
+ # display PostNet barcodes. It can also decode a simple w/n
31
+ # string. In this symbology, "wide" means "tall" and "narrow"
32
+ # means "short".
33
+ #
34
+ # There are three formats for the returned pattern:
35
+ #
36
+ # bars - 1s and 0s specifying black lines and white spaces. Actual
37
+ # characters can be changed from "1" and 0" with options
38
+ # :line_character and :space_character.
39
+ #
40
+ # rle - Run-length-encoded version of the pattern. The first
41
+ # number is always a black line, with subsequent digits
42
+ # alternating between spaces and lines. The digits specify
43
+ # the width of each line or space.
44
+ #
45
+ # wn - The native format for this barcode type. The string
46
+ # consists of a series of "w" and "n" characters. The first
47
+ # item is always a black line, with subsequent characters
48
+ # alternating between spaces and lines. A "wide" item
49
+ # is twice the width of a "narrow" item.
50
+ #
51
+ # The "width" method will tell you the total end-to-end width, in
52
+ # units, of the entire barcode.
53
+ #
54
+ #== Rendering
55
+ #
56
+ # See USPS documentation for exact specifications for display.
57
+
58
+ class PostNet < Barcode1D
59
+
60
+ # Character sequence - 0-based offset in this string is character
61
+ # number
62
+ CHAR_SEQUENCE = "0123456789"
63
+
64
+ # Patterns for making bar codes. Note that the position
65
+ # weights are 7, 4, 2, 1 and the last bit is parity.
66
+ # Each letter is an alternating bar then space, and there
67
+ # is a narrow space between each character.
68
+ PATTERNS = {
69
+ '0'=> {'val'=>0 ,'wn'=>'wwnnn'},
70
+ '1'=> {'val'=>1 ,'wn'=>'nnnww'},
71
+ '2'=> {'val'=>2 ,'wn'=>'nnwnw'},
72
+ '3'=> {'val'=>3 ,'wn'=>'nnwwn'},
73
+ '4'=> {'val'=>4 ,'wn'=>'nwnnw'},
74
+ '5'=> {'val'=>5 ,'wn'=>'nwnwn'},
75
+ '6'=> {'val'=>6 ,'wn'=>'nwwnn'},
76
+ '7'=> {'val'=>7 ,'wn'=>'wnnnw'},
77
+ '8'=> {'val'=>8 ,'wn'=>'wnnwn'},
78
+ '9'=> {'val'=>9 ,'wn'=>'wnwnn'}
79
+ }
80
+
81
+ GUARD_PATTERN_LEFT_WN = 'w'
82
+ GUARD_PATTERN_RIGHT_WN = 'w'
83
+
84
+ DEFAULT_OPTIONS = {
85
+ :line_character => '1',
86
+ :space_character => '0',
87
+ :w_character => 'w',
88
+ :n_character => 'n'
89
+ }
90
+
91
+ class << self
92
+ # PostNet can encode 5, 9, or 11 digits, plus a check digit.
93
+ def can_encode?(value)
94
+ value.to_s =~ /\A\d{5}(\d{4})?(\d{2})?\d?\z/
95
+ end
96
+
97
+ def generate_check_digit_for(value)
98
+ raise UnencodableCharactersError unless self.can_encode?(value)
99
+ value = value.split('').collect { |c| c.to_i }.inject(0) { |a,c| a + c }
100
+ (10 - (value % 10)) % 10
101
+ end
102
+
103
+ def validate_check_digit_for(value)
104
+ raise UnencodableCharactersError unless self.can_encode?(value)
105
+ md = value.match(/^(\d+?)(\d)$/)
106
+ self.generate_check_digit_for(md[1]) == md[2].to_i
107
+ end
108
+
109
+ # Decode a string in rle format. This will return a PostNet
110
+ # object.
111
+ def decode(str, options = {})
112
+ if str =~ /[^1-3]/ && str =~ /[^wn]/
113
+ raise UnencodableCharactersError, "Pattern must be rle or wn"
114
+ end
115
+
116
+ # ensure a wn string
117
+ if str =~ /[1-3]/
118
+ str = str.tr('123','nww')
119
+ end
120
+
121
+ unless str =~ /\A#{GUARD_PATTERN_LEFT_WN}(.*?)#{GUARD_PATTERN_RIGHT_WN}\z/
122
+ raise UnencodableCharactersError, "Start/stop pattern is not detected."
123
+ end
124
+
125
+ wn_pattern = $1
126
+
127
+ # Each pattern is 5 bars
128
+ unless wn_pattern.size % 5 == 0
129
+ raise UnencodableCharactersError, "Wrong number of bars."
130
+ end
131
+
132
+ decoded_string = ''
133
+
134
+ wn_pattern.scan(/.{5}/).each do |chunk|
135
+
136
+ found = false
137
+
138
+ PATTERNS.each do |char,hsh|
139
+ if chunk == hsh['wn']
140
+ decoded_string += char
141
+ found = true
142
+ break;
143
+ end
144
+ end
145
+
146
+ raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
147
+
148
+ end
149
+
150
+ PostNet.new(decoded_string, options)
151
+ end
152
+
153
+ end
154
+
155
+ # Options are :line_character, :space_character, :w_character,
156
+ # :n_character, :checksum_included, and :skip_checksum.
157
+ def initialize(value, options = {})
158
+
159
+ @options = DEFAULT_OPTIONS.merge(options)
160
+
161
+ # Can we encode this value?
162
+ raise UnencodableCharactersError unless self.class.can_encode?(value)
163
+
164
+ @value = value.to_s
165
+
166
+ if @options[:skip_checksum]
167
+ @encoded_string = value.to_s
168
+ @value = value.to_s
169
+ @check_digit = nil
170
+ else
171
+ # Need to guess whether we need a checksum. If there
172
+ # are 5, 9, or 11 digits, we need one. If there are 6,
173
+ # 10, or 12 digits, then it's included. Otherwise it's
174
+ # not a valid number.
175
+
176
+ @value = value.to_s
177
+
178
+ if @options[:checksum_included] || [6,10,12].include?(@value.size)
179
+ @options[:checksum_included] = true
180
+ @encoded_string = value.to_s
181
+ raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
182
+ md = @encoded_string.match(/^(\d+?)(\d)$/)
183
+ @value, @check_digit = md[1], md[2].to_i
184
+ elsif [5,9,11].include?(@value.size)
185
+ @check_digit = self.class.generate_check_digit_for(@value)
186
+ @encoded_string = "#{@value}#{@check_digit}"
187
+ else
188
+ # should be redundant
189
+ raise UnencodableCharactersError
190
+ end
191
+ end
192
+ end
193
+
194
+ # Returns a string of "w" or "n" ("wide" and "narrow")
195
+ def wn
196
+ @wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
197
+ end
198
+
199
+ # returns a run-length-encoded string representation
200
+ def rle
201
+ @rle ||= self.class.wn_to_rle(self.wn, @options)
202
+ end
203
+
204
+ # returns 1s and 0s (for "black" and "white")
205
+ def bars
206
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
207
+ end
208
+
209
+ # returns the total unit width of the bar code
210
+ def width
211
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
212
+ end
213
+
214
+ private
215
+
216
+ # Creates the actual w/n pattern. Note that there is a narrow space
217
+ # between each character.
218
+ def wn_str
219
+ @wn_str ||= GUARD_PATTERN_LEFT_WN + @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.join + GUARD_PATTERN_RIGHT_WN
220
+ end
221
+
222
+ end
223
+ end