barcode1dtools 0.9.2

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,143 @@
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 'barcode1dtools/ean13'
9
+
10
+ module Barcode1DTools
11
+
12
+ # Barcode1DTools::UPC_A - Create pattern for UPC-A barcodes
13
+ #
14
+ # The value encoded is an 11-digit integer, and a checksum digit
15
+ # will be added. You can add the option :checksum_included => true
16
+ # when initializing to specify that you have already included a
17
+ # checksum.
18
+ #
19
+ # # Note that this number is a UPC-A, with the number system of 08,
20
+ # # manufacturer's code of "28999", product code of "00682", and a
21
+ # # checksum of "3" (not included)
22
+ # num = '82899900682'
23
+ # bc = Barcode1DTools::UPC_A.new(num)
24
+ # pattern = bc.bars
25
+ # rle_pattern = bc.rle
26
+ # width = bc.width
27
+ # check_digit = Barcode1DTools::UPC_A.generate_check_digit_for(num)
28
+ #
29
+ # A UPC-A barcode is an EAN-13 with an initial digit of "0" (that
30
+ # is the left digit of the number system designator). Like the
31
+ # EAN-13, the code is broken into a single-digit number system,
32
+ # 5 digit manufacturer code, 5 digit product code, and single
33
+ # digit checksum.
34
+ #
35
+ # The number system is:
36
+ # 0, 1, 6, 7, 8 - standard UPC codes
37
+ # 2 - a product weight- generally calculated at the store.
38
+ # 3 - pharmaceuticals
39
+ # 4 - used for loyalty cards at stores
40
+ # 5 - coupons
41
+ # 9 - coupons
42
+ # 978 - for books, with a 10-digit ISBN coming after 978
43
+ #
44
+ # For code 2, the manufacturer code becomes an item number, and the
45
+ # product number is used for either the weight or the price, with
46
+ # the first digit determining which.
47
+ #
48
+ # For 5 and 9, the manufacturer code is the same as a standard
49
+ # UPC. The first three digits of the product code are used as a
50
+ # "family code" set by the manufacturer, and the last two digits
51
+ # are a "coupon code" which determines the amount of the discount
52
+ # based on a table released by the GS1 US (formerly the UCC).
53
+ # Code 5 coupons may be doubled or tripled, but code 9 may not.
54
+ #
55
+ #== Rendering
56
+ #
57
+ # The UPC-A is typically rendered at 1-1.5 inch across, and half
58
+ # an inch high. The number system digit and checksum digit are
59
+ # shown on the left and right sides of the code. The other two
60
+ # sets of five digits are rendered at the bottom of the barcode.
61
+ # The alignment can be either bottom of the text flush with
62
+ # bottom of barcode, or middle of text aligned with bottom of
63
+ # barcode. The two sets of five digits are separated by the two
64
+ # middle guard bars which always extend to the bottom. There
65
+ # should be at least 9 spaces of quiet area on either side of
66
+ # the code.
67
+
68
+ class UPC_A < EAN13
69
+
70
+ class << self
71
+ # Returns true or false - must be 11-13 digits. This
72
+ # also handles the case where the leading 0 is added.
73
+ def can_encode?(value, options = nil)
74
+ if !options
75
+ value.to_s =~ /^0?[0-9]{11,12}$/
76
+ elsif (options[:checksum_included])
77
+ value.to_s =~ /^0?[0-9]{12}$/
78
+ else
79
+ value.to_s =~ /^0?[0-9]{11}$/
80
+ end
81
+ end
82
+
83
+ # Generates check digit given a string to encode. It assumes there
84
+ # is no check digit on the "value".
85
+ def generate_check_digit_for(value)
86
+ super('0' + value)
87
+ end
88
+
89
+ # validates the check digit given a string - assumes check digit
90
+ # is last digit of string.
91
+ def validate_check_digit_for(value)
92
+ raise UnencodableCharactersError unless self.can_encode?(value, :checksum_included => true)
93
+ md = value.match(/^(0?\d{11})(\d)$/)
94
+ self.generate_check_digit_for(md[1]) == md[2].to_i
95
+ end
96
+
97
+ def decode(value)
98
+ ean = super(value)
99
+ if ean.value[0,1] == '0'
100
+ new(ean.value[1,11])
101
+ else
102
+ raise UnencodableCharactersError
103
+ end
104
+ end
105
+ end
106
+
107
+ # Options are :line_character, :space_character, and
108
+ # :checksum_included.
109
+ def initialize(value, options = {})
110
+
111
+ @options = DEFAULT_OPTIONS.merge(options)
112
+
113
+ # Can we encode this value?
114
+ raise UnencodableCharactersError unless self.class.can_encode?(value, @options)
115
+
116
+ if @options[:checksum_included]
117
+ @encoded_string = value.to_s
118
+ raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
119
+ md = @encoded_string.match(/^(\d+?)(\d)$/)
120
+ @value, @check_digit = md[1], md[2].to_i
121
+ else
122
+ # need to add a checksum
123
+ @value = value.to_s
124
+ @check_digit = self.class.generate_check_digit_for(@value)
125
+ @encoded_string = "#{@value}#{@check_digit}"
126
+ end
127
+
128
+ md = @value.match(/^(\d)(\d{5})(\d{5})/)
129
+ @number_system, @manufacturers_code, @product_code = md[1], md[2], md[3]
130
+ end
131
+
132
+ # returns a run-length-encoded string representation
133
+ def rle
134
+ if @rle
135
+ @rle
136
+ else
137
+ md = @encoded_string.match(/^(\d{6})(\d{6})/)
138
+ @rle = gen_rle('0', md[1], md[2])
139
+ end
140
+ end
141
+
142
+ end
143
+ end
@@ -0,0 +1,274 @@
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 'barcode1dtools/upc_a'
9
+
10
+ module Barcode1DTools
11
+
12
+ # Barcode1DTools::UPC_E - Create pattern for UPC-E barcodes
13
+ #
14
+ # The value encoded is an 6-digit integer, and a checksum digit
15
+ # will be added. You can add the option :checksum_included => true
16
+ # when initializing to specify that you have already included a
17
+ # checksum.
18
+ #
19
+ # num = '394932'
20
+ # bc = Barcode1DTools::UPC_E.new(num)
21
+ # pattern = bc.bars
22
+ # rle_pattern = bc.rle
23
+ # width = bc.width
24
+ # check_digit = Barcode1DTools::UPC_E.generate_check_digit_for(num)
25
+ #
26
+ # A UPC-E barcode is an abbreviated form of UPC-A, but can encode
27
+ # only a few codes. The checksum is derived from the full UPC-A
28
+ # digit sequence rather than the 6 digits of the UPC-E. The checksum
29
+ # is encoded as the parity, with the bar patterns the same as the left
30
+ # side of a standard UPC-A.
31
+ #
32
+ # The last digit of the UPC-E determines the pattern used to convert
33
+ # to a UPC-A.
34
+ #
35
+ # UPC-E UPC-A equivalent
36
+ # 2 digits for manufacturer code (plus last digit), 3 digits for product
37
+ # XXNNN0 0XX000-00NNN
38
+ # XXNNN1 0XX100-00NNN
39
+ # XXNNN2 0XX200-00NNN
40
+ # 3 digits for manufacturer code, 2 digits for product
41
+ # XXXNN3 0XXX00-000NN
42
+ # 4 digits for manufacturer code, 1 digit for product
43
+ # XXXXN4 0XXXX0-0000N
44
+ # 5 digits for manufacturer code, 1 digit for product (5-9)
45
+ # XXXXX5 0XXXXX-00005
46
+ # XXXXX6 0XXXXX-00006
47
+ # XXXXX7 0XXXXX-00007
48
+ # XXXXX8 0XXXXX-00008
49
+ # XXXXX9 0XXXXX-00009
50
+ #
51
+ #== Rendering
52
+ #
53
+ # The UPC-E is made for smaller items. Generally, they are rendered
54
+ # with the number system digit (0) on the left of the bars and the
55
+ # checksum on the right. The 6-digit payload is shown below the bars
56
+ # with the end guard bars extending half-way down the digits. The
57
+ # number system and check digit might be rendered in a slightly smaller
58
+ # font size. The UPC-E uses the same bar patterns as the left half of
59
+ # a regular UPC-A, but there is no middle pattern and the right guard
60
+ # pattern has an extra line/space pair.
61
+
62
+ class UPC_E < Barcode1D
63
+
64
+ LEFT_PATTERNS = UPC_A::LEFT_PATTERNS
65
+ LEFT_PATTERNS_RLE = UPC_A::LEFT_PATTERNS_RLE
66
+
67
+ PARITY_PATTERNS = {
68
+ '0' => 'eeeooo',
69
+ '1' => 'eeoeoo',
70
+ '2' => 'eeooeo',
71
+ '3' => 'eeoooe',
72
+ '4' => 'eoeeoo',
73
+ '5' => 'eooeeo',
74
+ '6' => 'eoooee',
75
+ '7' => 'eoeoeo',
76
+ '8' => 'eoeooe',
77
+ '9' => 'eooeoe',
78
+ };
79
+
80
+ LEFT_GUARD_PATTERN = '101'
81
+ RIGHT_GUARD_PATTERN = '010101'
82
+ LEFT_GUARD_PATTERN_RLE = '111'
83
+ RIGHT_GUARD_PATTERN_RLE = '111111'
84
+
85
+ DEFAULT_OPTIONS = {
86
+ :line_character => '1',
87
+ :space_character => '0'
88
+ }
89
+
90
+ # For UPC-E
91
+ attr_reader :number_system
92
+ attr_reader :manufacturers_code
93
+ attr_reader :product_code
94
+ attr_reader :upca_value
95
+
96
+ class << self
97
+ # Returns true or false - must be 6-8 digits. This
98
+ # also handles the case where the leading 0 is added.
99
+ def can_encode?(value, options = nil)
100
+ if !options
101
+ value.to_s =~ /^0?[0-9]{6,7}$/
102
+ elsif (options[:checksum_included])
103
+ value.to_s =~ /^0?[0-9]{7}$/
104
+ else
105
+ value.to_s =~ /^0?[0-9]{6}$/
106
+ end
107
+ end
108
+
109
+ # Generates check digit given a string to encode. It assumes there
110
+ # is no check digit on the "value".
111
+ def generate_check_digit_for(value)
112
+ UPC_A.generate_check_digit_for(self.upce_value_to_upca_value(value))
113
+ end
114
+
115
+ # validates the check digit given a string - assumes check digit
116
+ # is last digit of string.
117
+ def validate_check_digit_for(value)
118
+ raise UnencodableCharactersError unless self.can_encode?(value, :checksum_included => true)
119
+ md = value.match(/^(0?\d{6})(\d)$/)
120
+ self.generate_check_digit_for(md[1]) == md[2].to_i
121
+ end
122
+
123
+ def decode(str)
124
+ if str.length == 51
125
+ # bar pattern
126
+ str = bars_to_rle(str)
127
+ elsif str.length == 33 && str =~ /^[1-9]+$/
128
+ # rle
129
+ else
130
+ raise UnencodableCharactersError, "Pattern must be 51 unit bar pattern or 33 character rle."
131
+ end
132
+
133
+ # See if the string is reversed
134
+ if str[0..5] == RIGHT_GUARD_PATTERN_RLE && str[30..32] == LEFT_GUARD_PATTERN_RLE
135
+ str.reverse!
136
+ end
137
+
138
+ # Check the guard patterns
139
+ unless (str[0..2] == LEFT_GUARD_PATTERN_RLE && str[27..32] == RIGHT_GUARD_PATTERN_RLE)
140
+ raise UnencodableCharactersError, "Missing or incorrect guard patterns"
141
+ end
142
+
143
+ parity_sequence = ''
144
+ digits = ''
145
+ initial_offset = LEFT_GUARD_PATTERN_RLE.length
146
+
147
+ # Decode
148
+ (0..5).each do |offset|
149
+ found = false
150
+ digit_rle = str[(initial_offset + offset*4),4]
151
+ ['o','e'].each do |parity|
152
+ ('0'..'9').each do |digit|
153
+ if LEFT_PATTERNS_RLE[digit][parity] == digit_rle
154
+ parity_sequence += parity
155
+ digits += digit
156
+ found = true
157
+ break
158
+ end
159
+ end
160
+ end
161
+ raise UndecodableCharactersError, "Invalid sequence: #{digit_rle}" unless found
162
+ end
163
+
164
+ # Now, find the parity digit
165
+ parity_digit = nil
166
+ ('0'..'9').each do |x|
167
+ if PARITY_PATTERNS[x] == parity_sequence
168
+ parity_digit = x
169
+ break
170
+ end
171
+ end
172
+
173
+ raise UndecodableCharactersError, "Weird parity: #{parity_sequence}" unless parity_digit
174
+
175
+ UPC_E.new('0' + digits + parity_digit, :checksum_included => true)
176
+ end
177
+
178
+ def upce_value_to_upca_value(value, options = {})
179
+ raise UnencodableCharactersError unless self.can_encode?(value, options)
180
+ # remove the check digit if it was included
181
+ value = value.to_i % 10 if options[:checksum_included]
182
+ value = sprintf('%06d', value.to_i)
183
+ if value =~ /(\d\d)(\d\d\d)([012])/
184
+ upca_value = "0#{$1}#{$3}0000#{$2}"
185
+ elsif value =~ /(\d\d\d)(\d\d)(3)/
186
+ upca_value = "0#{$1}00000#{$2}"
187
+ elsif value =~ /(\d\d\d\d)(\d)(4)/
188
+ upca_value = "0#{$1}00000#{$2}"
189
+ elsif value =~ /(\d\d\d\d\d)([5-9])/
190
+ upca_value = "0#{$1}0000#{$2}"
191
+ else
192
+ raise UnencodableCharactersError, "Cannot change UPC-E #{value} to UPC-A"
193
+ end
194
+ upca_value
195
+ end
196
+
197
+ def upca_value_to_upce_value(value, options = {})
198
+ raise UnencodableCharactersError unless UPC_A.can_encode?(value, options)
199
+ value = value % 10 if options[:checksum_included]
200
+ value = sprintf('%011d', value.to_i)
201
+ if value =~ /^0(\d\d\d\d[1-9])0000([5-9])/
202
+ upce_value = "0#{$1}#{$2}"
203
+ elsif value =~ /^0(\d\d\d[1-9])00000(\d)/
204
+ upce_value = "0#{$1}#{$2}4"
205
+ elsif value =~ /^0(\d\d)([012])0000(\d\d\d)/
206
+ upce_value = "0#{$1}#{$3}#{$2}"
207
+ elsif value =~ /^0(\d\d[3-9])00000(\d\d)/
208
+ upce_value = "0#{$1}#{$2}3"
209
+ else
210
+ raise UnencodableCharactersError, "Cannot change UPC-A #{value} to UPC-E"
211
+ end
212
+ upce_value
213
+ end
214
+ end
215
+
216
+ # Options are :line_character, :space_character, and
217
+ # :checksum_included.
218
+ def initialize(value, options = {})
219
+
220
+ @options = DEFAULT_OPTIONS.merge(options)
221
+
222
+ # Can we encode this value?
223
+ raise UnencodableCharactersError unless self.class.can_encode?(value, @options)
224
+
225
+ if @options[:checksum_included]
226
+ @encoded_string = value.to_s
227
+ raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
228
+ md = @encoded_string.match(/^(\d+?)(\d)$/)
229
+ @value, @check_digit = md[1], md[2].to_i
230
+ else
231
+ # need to add a checksum
232
+ @value = value.to_s
233
+ @check_digit = self.class.generate_check_digit_for(@value)
234
+ @encoded_string = "#{@value}#{@check_digit}"
235
+ end
236
+
237
+ @upca_value = self.class.upce_value_to_upca_value(@value)
238
+ md = @upca_value.match(/^(\d)(\d{5})(\d{5})/)
239
+ @number_system, @manufacturers_code, @product_code = md[1], md[2], md[3]
240
+ end
241
+
242
+ # not usable with EAN-style codes
243
+ def wn
244
+ raise NotImplementedError
245
+ end
246
+
247
+ # returns a run-length-encoded string representation
248
+ def rle
249
+ if @rle
250
+ @rle
251
+ else
252
+ md = @encoded_string.match(/(\d{6})(\d)$/)
253
+ @rle = gen_rle(md[1], md[2])
254
+ end
255
+ end
256
+
257
+ # returns 1s and 0s (for "black" and "white")
258
+ def bars
259
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
260
+ end
261
+
262
+ # returns the total unit width of the bar code
263
+ def width
264
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
265
+ end
266
+
267
+ private
268
+
269
+ def gen_rle(payload, parity_digit)
270
+ (LEFT_GUARD_PATTERN_RLE + (0..5).collect { |n| LEFT_PATTERNS_RLE[payload[n,1]][PARITY_PATTERNS[parity_digit][n,1]] }.join('') + RIGHT_GUARD_PATTERN_RLE)
271
+ end
272
+
273
+ end
274
+ end
@@ -0,0 +1,221 @@
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 'barcode1dtools/upc_a'
9
+
10
+ module Barcode1DTools
11
+
12
+ # Barcode1DTools::UPC_Supplemental_2 - Create pattern for UPC
13
+ # Supplemental 2 barcodes
14
+ #
15
+ # The value encoded is an 2-digit integer, and a checksum digit
16
+ # will be added. You can add the option :checksum_included => true
17
+ # when initializing to specify that you have already included a
18
+ # checksum.
19
+ #
20
+ # num = '24'
21
+ # bc = Barcode1DTools::UPC_Supplemental_2.new(num)
22
+ # pattern = bc.bars
23
+ # rle_pattern = bc.rle
24
+ # width = bc.width
25
+ # check_digit = Barcode1DTools::UPC_E.generate_check_digit_for(num)
26
+ #
27
+ # This type of barcode consists of 2 digits, and a check digit
28
+ # (simply a modulus 4 of the number encoded) that is encoded in
29
+ # the "parity" of the two barcode digits. The bar patterns are the
30
+ # same as the left half of a standard UPC-A.
31
+ #
32
+ # The 2-digit supplement is generally used on periodicals as an
33
+ # "issue number", so that the UPC-A code may remain the same
34
+ # across issues. The two are scanned together, and typically the
35
+ # scanner will return the two digits of the supplemental barcode
36
+ # immediately following the check digit from the main UPC-A. You
37
+ # will likely need to use the Barcode::UPC_A module in addition
38
+ # to this one to create the full code.
39
+ #
40
+ #== Rendering
41
+ #
42
+ # The 2-digit supplement is positioned to the right of the main UPC
43
+ # code, and the human-readable digits are usually printed above the
44
+ # supplemental barcode. UPC-A is generally rendered at one inch
45
+ # across, then there's a 1/8th inch gap, then the supplemental. A
46
+ # UPC-A is 95 units wide, so the gap is 24 units wide. The
47
+ # supplemental barcode is 20 units wide. The author hasn't viewed
48
+ # the specification, but note that the UPC (and more generally EAN)
49
+ # barcode system never a bar or space of more than four units
50
+ # width. Given that, the gap should likely be at last 10 units
51
+ # wide.
52
+
53
+ class UPC_Supplemental_2 < Barcode1D
54
+
55
+ LEFT_PATTERNS = UPC_A::LEFT_PATTERNS
56
+ LEFT_PATTERNS_RLE = UPC_A::LEFT_PATTERNS_RLE
57
+
58
+ # parity patterns, essentially binary counting where "e" is "1"
59
+ # and "o" is "0"
60
+ PARITY_PATTERNS = {
61
+ '0' => 'oo',
62
+ '1' => 'oe',
63
+ '2' => 'eo',
64
+ '3' => 'ee',
65
+ };
66
+
67
+ LEFT_GUARD_PATTERN = '1011'
68
+ MIDDLE_GUARD_PATTERN = '01'
69
+ LEFT_GUARD_PATTERN_RLE = '112'
70
+ MIDDLE_GUARD_PATTERN_RLE = '11'
71
+
72
+ DEFAULT_OPTIONS = {
73
+ :line_character => '1',
74
+ :space_character => '0'
75
+ }
76
+
77
+ class << self
78
+ # Returns true or false - must be 1-3 digits. This
79
+ # also handles the case where the leading 0 is added.
80
+ def can_encode?(value, options = nil)
81
+ if !options
82
+ value.to_s =~ /^\d{1,3}$/
83
+ elsif (options[:checksum_included])
84
+ value.to_s =~ /^\d\d\d?$/
85
+ else
86
+ value.to_s =~ /^\d\d?$/ && (0..99).include?(value.to_i)
87
+ end
88
+ end
89
+
90
+ # Generates check digit given a string to encode. It assumes there
91
+ # is no check digit on the "value".
92
+ def generate_check_digit_for(value)
93
+ value.to_i % 4
94
+ end
95
+
96
+ # validates the check digit given a string - assumes check digit
97
+ # is last digit of string.
98
+ def validate_check_digit_for(value)
99
+ raise UnencodableCharactersError unless self.can_encode?(value, :checksum_included => true)
100
+ md = value.match(/^(\d\d)(\d)$/)
101
+ self.generate_check_digit_for(md[1]) == md[2].to_i
102
+ end
103
+
104
+ def decode(str)
105
+ if str.length == 20
106
+ # bar pattern
107
+ str = bars_to_rle(str)
108
+ elsif str.length == 13 && str =~ /^[1-9]+$/
109
+ # rle
110
+ else
111
+ raise UnencodableCharactersError, "Pattern must be 20 unit bar pattern or 13 character rle."
112
+ end
113
+
114
+ # This string is "aaabbbbccdddd" where "aaa" is the left
115
+ # guard pattern, "bbbb" is the first digit, "cc" is the
116
+ # intra-digit guard pattern, and "dddd" is the second
117
+ # digit.
118
+
119
+ # See if the string is reversed
120
+ if str[10..12] == LEFT_GUARD_PATTERN_RLE.reverse && str[4..5] == MIDDLE_GUARD_PATTERN_RLE.reverse
121
+ str.reverse!
122
+ end
123
+
124
+ # Check the guard patterns
125
+ unless (str[0..2] == LEFT_GUARD_PATTERN_RLE && str[7..8] == MIDDLE_GUARD_PATTERN_RLE)
126
+ raise UnencodableCharactersError, "Missing or incorrect guard patterns"
127
+ end
128
+
129
+ parity_sequence = ''
130
+ digits = ''
131
+
132
+ # Decode
133
+ [str[3..6], str[9..12]].each do |digit_rle|
134
+ found = false
135
+ ['o','e'].each do |parity|
136
+ ('0'..'9').each do |digit|
137
+ if LEFT_PATTERNS_RLE[digit][parity] == digit_rle
138
+ parity_sequence += parity
139
+ digits += digit
140
+ found = true
141
+ break
142
+ end
143
+ end
144
+ end
145
+ raise UndecodableCharactersError, "Invalid sequence: #{digit_rle}" unless found
146
+ end
147
+
148
+ # Now, find the parity digit
149
+ parity_digit = nil
150
+ ('0'..'3').each do |x|
151
+ if PARITY_PATTERNS[x] == parity_sequence
152
+ parity_digit = x
153
+ break
154
+ end
155
+ end
156
+
157
+ raise UndecodableCharactersError, "Weird parity: #{parity_sequence}" unless parity_digit
158
+
159
+ UPC_Supplemental_2.new(digits + parity_digit, :checksum_included => true)
160
+ end
161
+
162
+ end
163
+
164
+ # Options are :line_character, :space_character, and
165
+ # :checksum_included.
166
+ def initialize(value, options = {})
167
+
168
+ @options = DEFAULT_OPTIONS.merge(options)
169
+
170
+ # Can we encode this value?
171
+ raise UnencodableCharactersError unless self.class.can_encode?(value, @options)
172
+
173
+ if @options[:checksum_included]
174
+ @encoded_string = value.to_s
175
+ raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
176
+ md = @encoded_string.match(/^(\d+?)(\d)$/)
177
+ @value, @check_digit = md[1].to_i, md[2].to_i
178
+ else
179
+ # need to add a checksum
180
+ @value = value.to_i
181
+ @check_digit = self.class.generate_check_digit_for(@value)
182
+ @encoded_string = sprintf('%02d%1d',@value,@check_digit)
183
+ end
184
+ end
185
+
186
+ # not usable with EAN-style codes
187
+ def wn
188
+ raise NotImplementedError
189
+ end
190
+
191
+ # returns a run-length-encoded string representation
192
+ def rle
193
+ if @rle
194
+ @rle
195
+ else
196
+ md = @encoded_string.match(/(\d\d)(\d)$/)
197
+ @rle = gen_rle(md[1], md[2])
198
+ end
199
+ end
200
+
201
+ # returns 1s and 0s (for "black" and "white")
202
+ def bars
203
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
204
+ end
205
+
206
+ # returns the total unit width of the bar code
207
+ def width
208
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
209
+ end
210
+
211
+ private
212
+
213
+ def gen_rle(payload, parity_digit)
214
+ LEFT_GUARD_PATTERN_RLE +
215
+ LEFT_PATTERNS_RLE[payload[0,1]][PARITY_PATTERNS[parity_digit][0,1]] +
216
+ MIDDLE_GUARD_PATTERN_RLE +
217
+ LEFT_PATTERNS_RLE[payload[1,1]][PARITY_PATTERNS[parity_digit][1,1]]
218
+ end
219
+
220
+ end
221
+ end