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.
- data/MIT-LICENSE +21 -0
- data/lib/barcode1dtools.rb +119 -0
- data/lib/barcode1dtools/ean13.rb +389 -0
- data/lib/barcode1dtools/ean8.rb +252 -0
- data/lib/barcode1dtools/interleaved2of5.rb +248 -0
- data/lib/barcode1dtools/upc_a.rb +143 -0
- data/lib/barcode1dtools/upc_e.rb +274 -0
- data/lib/barcode1dtools/upc_supplemental_2.rb +221 -0
- data/lib/barcode1dtools/upc_supplemental_5.rb +246 -0
- data/test/test_barcode1d.rb +56 -0
- data/test/test_barcode1dean13.rb +102 -0
- data/test/test_barcode1dean8.rb +95 -0
- data/test/test_barcode1di2of5.rb +86 -0
- data/test/test_barcode1dupca.rb +102 -0
- data/test/test_barcode1dupce.rb +127 -0
- data/test/test_barcode1dupcsupp2.rb +89 -0
- data/test/test_barcode1dupcsupp5.rb +94 -0
- metadata +93 -0
@@ -0,0 +1,252 @@
|
|
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::EAN_8 - Create pattern for EAN-8 barcodes
|
13
|
+
|
14
|
+
# The value encoded is a 7-digit number, and a checksum digit will
|
15
|
+
# 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 = '96385074'
|
20
|
+
# bc = Barcode1DTools::EAN8.new(num)
|
21
|
+
# pattern = bc.bars
|
22
|
+
# rle_pattern = bc.rle
|
23
|
+
# width = bc.width
|
24
|
+
# check_digit = Barcode1DTools::EAN83.generate_check_digit_for(num)
|
25
|
+
#
|
26
|
+
# The object created is immutable.
|
27
|
+
#
|
28
|
+
# There are two formats for the returned pattern (wn format is
|
29
|
+
# not available):
|
30
|
+
#
|
31
|
+
# bars - 1s and 0s specifying black lines and white spaces. Actual
|
32
|
+
# characters can be changed from "1" and 0" with options
|
33
|
+
# :line_character and :space_character. Each character
|
34
|
+
# in the string renders to a single unit width.
|
35
|
+
#
|
36
|
+
# rle - Run-length-encoded version of the pattern. The first
|
37
|
+
# number is always a black line, with subsequent digits
|
38
|
+
# alternating between spaces and lines. The digits specify
|
39
|
+
# the width of each line or space.
|
40
|
+
#
|
41
|
+
# The "width" method will tell you the total end-to-end width, in
|
42
|
+
# units, of the entire barcode.
|
43
|
+
#
|
44
|
+
# Unlike some of the other barcodes, e.g. Code 3 of 9, there is no "wnstr" for
|
45
|
+
# EAN & UPC style barcodes because the bars and spaces are variable width from
|
46
|
+
# 1 to 4 units.
|
47
|
+
#
|
48
|
+
# A EAN-8 barcode has 3 elements:
|
49
|
+
# 1. A 2 or 3 digit "number system" designation
|
50
|
+
# 2. A 4 or 5 digit manufacturer's code
|
51
|
+
# 3. A single digit checksum
|
52
|
+
#
|
53
|
+
# Note than an EAN-8 is not analogous to a UPC-E. In particular, there
|
54
|
+
# is no way to create an EAN-13 from and EAN-8 and vice versa. The
|
55
|
+
# numbers are assigned within each system by a central authority.
|
56
|
+
#
|
57
|
+
# The bar patterns are the same as EAN-13, with nothing encoded in the
|
58
|
+
# parity. All bars on the left use the "odd" parity set.
|
59
|
+
#
|
60
|
+
# RENDERING
|
61
|
+
#
|
62
|
+
# When rendered, two sets of four digits are shown at the bottom of the
|
63
|
+
# code, aligned with the bottom of the code, and with the middle guard
|
64
|
+
# pattern bars extending down between them. A supplemental 2 or 5 may
|
65
|
+
# be used.
|
66
|
+
|
67
|
+
class EAN8 < Barcode1D
|
68
|
+
|
69
|
+
# patterns to create the bar codes:
|
70
|
+
LEFT_PATTERNS = EAN13::LEFT_PATTERNS
|
71
|
+
LEFT_PATTERNS_RLE = EAN13::LEFT_PATTERNS_RLE
|
72
|
+
RIGHT_PATTERNS = EAN13::RIGHT_PATTERNS
|
73
|
+
RIGHT_PATTERNS_RLE = EAN13::RIGHT_PATTERNS_RLE
|
74
|
+
|
75
|
+
SIDE_GUARD_PATTERN=EAN13::SIDE_GUARD_PATTERN
|
76
|
+
MIDDLE_GUARD_PATTERN=EAN13::MIDDLE_GUARD_PATTERN
|
77
|
+
SIDE_GUARD_PATTERN_RLE=EAN13::SIDE_GUARD_PATTERN_RLE
|
78
|
+
MIDDLE_GUARD_PATTERN_RLE=EAN13::MIDDLE_GUARD_PATTERN_RLE
|
79
|
+
|
80
|
+
DEFAULT_OPTIONS = {
|
81
|
+
:line_character => '1',
|
82
|
+
:space_character => '0'
|
83
|
+
}
|
84
|
+
|
85
|
+
# Specific for EAN
|
86
|
+
attr_reader :number_system
|
87
|
+
attr_reader :product_code
|
88
|
+
|
89
|
+
class << self
|
90
|
+
# returns true or false - must be 7-8 digits
|
91
|
+
def can_encode?(value, options = nil)
|
92
|
+
if !options
|
93
|
+
value.to_s =~ /^\d{7,8}$/
|
94
|
+
elsif (options[:checksum_included])
|
95
|
+
value.to_s =~ /^\d{8}$/
|
96
|
+
else
|
97
|
+
value.to_s =~ /^\d{7}$/
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Generates check digit given a string to encode. It assumes there
|
102
|
+
# is no check digit on the "value".
|
103
|
+
def generate_check_digit_for(value)
|
104
|
+
raise UnencodableCharactersError unless self.can_encode?(value, :checksum_included => false)
|
105
|
+
mult = 1
|
106
|
+
value = value.split('').inject(0) { |a,c| mult = 4 - mult ; a + c.to_i * mult }
|
107
|
+
(10 - (value % 10)) % 10
|
108
|
+
end
|
109
|
+
|
110
|
+
# validates the check digit given a string - assumes check digit
|
111
|
+
# is last digit of string.
|
112
|
+
def validate_check_digit_for(value)
|
113
|
+
raise UnencodableCharactersError unless self.can_encode?(value, :checksum_included => true)
|
114
|
+
md = value.match(/^(\d{7})(\d)$/)
|
115
|
+
self.generate_check_digit_for(md[1]) == md[2].to_i
|
116
|
+
end
|
117
|
+
|
118
|
+
# Decode a string representing an rle or bar pattern EAN-13.
|
119
|
+
# Note that the string might be backward or forward. This
|
120
|
+
# will return an EAN8 object.
|
121
|
+
def decode(str)
|
122
|
+
if str.length == 67
|
123
|
+
# bar pattern
|
124
|
+
str = bars_to_rle(str)
|
125
|
+
elsif str.length == 43
|
126
|
+
# rle
|
127
|
+
else
|
128
|
+
raise UnencodableCharactersError, "Pattern must be 67 unit bar pattern or 43 character rle."
|
129
|
+
end
|
130
|
+
|
131
|
+
# Check the guard patterns
|
132
|
+
unless str[0..2] == SIDE_GUARD_PATTERN_RLE && str[40..42] == SIDE_GUARD_PATTERN_RLE && str[19..23] == MIDDLE_GUARD_PATTERN_RLE
|
133
|
+
raise UnencodableCharactersError, "Missing or incorrect guard patterns"
|
134
|
+
end
|
135
|
+
|
136
|
+
# Now I have an rle pattern, simply need to decode
|
137
|
+
# according to the LEFT_PATTERNS_RLE, keeping track
|
138
|
+
# of the parity for each position.
|
139
|
+
|
140
|
+
# Set up the decoder
|
141
|
+
left_parity_sequence = ''
|
142
|
+
left_digits = ''
|
143
|
+
right_parity_sequence = ''
|
144
|
+
right_digits = ''
|
145
|
+
left_initial_offset = SIDE_GUARD_PATTERN_RLE.length
|
146
|
+
right_initial_offset = SIDE_GUARD_PATTERN_RLE.length + (4*4) + MIDDLE_GUARD_PATTERN_RLE.length
|
147
|
+
|
148
|
+
# Decode the left side
|
149
|
+
(0..3).each do |left_offset|
|
150
|
+
found = false
|
151
|
+
digit_rle = str[(left_initial_offset + left_offset*4),4]
|
152
|
+
['o','e'].each do |parity|
|
153
|
+
('0'..'9').each do |digit|
|
154
|
+
if LEFT_PATTERNS_RLE[digit][parity] == digit_rle
|
155
|
+
left_parity_sequence += parity
|
156
|
+
left_digits += digit
|
157
|
+
found = true
|
158
|
+
break
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
raise UndecodableCharactersError, "Invalid sequence: #{digit_rle}" unless found
|
163
|
+
end
|
164
|
+
|
165
|
+
# Decode the right side
|
166
|
+
(0..3).each do |right_offset|
|
167
|
+
found = false
|
168
|
+
digit_rle = str[(right_initial_offset + right_offset*4),4]
|
169
|
+
['o','e'].each do |parity|
|
170
|
+
('0'..'9').each do |digit|
|
171
|
+
if LEFT_PATTERNS_RLE[digit][parity] == digit_rle
|
172
|
+
right_parity_sequence += parity
|
173
|
+
right_digits += digit
|
174
|
+
found = true
|
175
|
+
break
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
raise UndecodableCharactersError, "Invalid sequence: #{digit_rle}" unless found
|
180
|
+
end
|
181
|
+
|
182
|
+
# If left parity sequence is 'eeee', the string is reversed
|
183
|
+
if left_parity_sequence == 'eeee'
|
184
|
+
left_digits, right_digits, left_parity_sequence = right_digits.reverse, left_digits.reverse, right_parity_sequence.reverse.tr('eo','oe')
|
185
|
+
end
|
186
|
+
|
187
|
+
# Debugging
|
188
|
+
#puts "Left digits: #{left_digits} Left parity: #{left_parity_sequence}"
|
189
|
+
#puts "Right digits: #{right_digits} Right parity: #{right_parity_sequence}"
|
190
|
+
|
191
|
+
EAN8.new(left_digits + right_digits, :checksum_included => true)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Options are :line_character, :space_character, and
|
196
|
+
# :checksum_included.
|
197
|
+
def initialize(value, options = {})
|
198
|
+
|
199
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
200
|
+
|
201
|
+
# Can we encode this value?
|
202
|
+
raise UnencodableCharactersError unless self.class.can_encode?(value, @options)
|
203
|
+
|
204
|
+
if @options[:checksum_included]
|
205
|
+
@encoded_string = sprintf('%08d', value.to_i)
|
206
|
+
raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
|
207
|
+
md = @encoded_string.match(/^(\d+?)(\d)$/)
|
208
|
+
@value, @check_digit = md[1], md[2].to_i
|
209
|
+
else
|
210
|
+
# need to add a checksum
|
211
|
+
@value = sprintf('%07d', value.to_i)
|
212
|
+
@check_digit = self.class.generate_check_digit_for(@value)
|
213
|
+
@encoded_string = "#{@value}#{@check_digit}"
|
214
|
+
end
|
215
|
+
|
216
|
+
md = @value.match(/^(\d{3})(\d{4})/)
|
217
|
+
@number_system, @product_code = md[1], md[2]
|
218
|
+
end
|
219
|
+
|
220
|
+
# not usable with EAN codes
|
221
|
+
def wn
|
222
|
+
raise NotImplementedError
|
223
|
+
end
|
224
|
+
|
225
|
+
# returns a run-length-encoded string representation
|
226
|
+
def rle
|
227
|
+
if @rle
|
228
|
+
@rle
|
229
|
+
else
|
230
|
+
md = @encoded_string.match(/^(\d{4})(\d{4})/)
|
231
|
+
@rle = gen_rle(md[1], md[2])
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# returns 1s and 0s (for "black" and "white")
|
236
|
+
def bars
|
237
|
+
@bars ||= self.class.rle_to_bars(self.rle, @options)
|
238
|
+
end
|
239
|
+
|
240
|
+
# returns the total unit width of the bar code
|
241
|
+
def width
|
242
|
+
@width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
|
247
|
+
def gen_rle(left_half, right_half)
|
248
|
+
(SIDE_GUARD_PATTERN_RLE + (0..3).collect { |n| LEFT_PATTERNS_RLE[left_half[n,1]]['o'] }.join + MIDDLE_GUARD_PATTERN_RLE + right_half.split('').collect { |c| RIGHT_PATTERNS_RLE[c] }.join + SIDE_GUARD_PATTERN_RLE)
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,248 @@
|
|
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::Interleaved2of5 - Create pattern for Interleaved 2 of 5
|
11
|
+
# (also known as I 2/5 or ITF) barcodes. The value encoded is an
|
12
|
+
# integer, and a checksum digit will be added. You can add the option
|
13
|
+
# :checksum_included => true when initializing to specify that you
|
14
|
+
# have already included a checksum, or :skip_checksum => true to
|
15
|
+
# specify that no checksum should be added or checked. A ChecksumError
|
16
|
+
# will be raised if :checksum_included => true, :skip_checksum is
|
17
|
+
# missing or false, and the last digit is invalid as a checksum.
|
18
|
+
#
|
19
|
+
# I 2/5 can only encode an even number of digits (including the
|
20
|
+
# checksum), so a "0" will be prepended if there is an odd number of
|
21
|
+
# digits. The 0 has no effect on the checksum. Note that sometimes
|
22
|
+
# an odd number of digits is encoded with 5 narrow spaces for the last
|
23
|
+
# digit. We do not encode this way but can handle decoding.
|
24
|
+
#
|
25
|
+
# num = 238982
|
26
|
+
# bc = Barcode1DTools::Interleaved2of5.new(num)
|
27
|
+
# pattern = bc.bars
|
28
|
+
# rle_pattern = bc.rle
|
29
|
+
# wn_pattern = bc.wn
|
30
|
+
# width = bc.width
|
31
|
+
# check_digit = Barcode1DTools::Interleaved2of5.generate_check_digit_for(num)
|
32
|
+
#
|
33
|
+
# The object created is immutable.
|
34
|
+
#
|
35
|
+
# Barcode1DTools::Interleaved2of5 creates the patterns that you need to
|
36
|
+
# display Interleaved 2 of 5 (also known as I 2/5) barcodes. It can also
|
37
|
+
# decode a simple w/n string.
|
38
|
+
#
|
39
|
+
# I 2/5 barcodes consist of lines and spaces that are either "wide" or
|
40
|
+
# "narrow", with "wide" lines or spaces being twice the width of
|
41
|
+
# narrow lines or spaces.
|
42
|
+
#
|
43
|
+
# There are three formats for the returned pattern:
|
44
|
+
#
|
45
|
+
# bars - 1s and 0s specifying black lines and white spaces. Actual
|
46
|
+
# characters can be changed from "1" and 0" with options
|
47
|
+
# :line_character and :space_character.
|
48
|
+
#
|
49
|
+
# rle - Run-length-encoded version of the pattern. The first
|
50
|
+
# number is always a black line, with subsequent digits
|
51
|
+
# alternating between spaces and lines. The digits specify
|
52
|
+
# the width of each line or space.
|
53
|
+
#
|
54
|
+
# wn - The native format for this barcode type. The string
|
55
|
+
# consists of a series of "w" and "n" characters. The first
|
56
|
+
# item is always a black line, with subsequent characters
|
57
|
+
# alternating between spaces and lines. A "wide" item
|
58
|
+
# is twice the width of a "narrow" item.
|
59
|
+
#
|
60
|
+
# The "width" method will tell you the total end-to-end width, in
|
61
|
+
# units, of the entire barcode.
|
62
|
+
#
|
63
|
+
# In this encoding, pairs of digits are interleaved with each other,
|
64
|
+
# so the first digit is the "bars" and the second digit is the
|
65
|
+
# "spaces". Each digit consists of 5 sets of wide or narrow bars or
|
66
|
+
# spaces, with 2 of the 5 being wide. The pattern can be calculated
|
67
|
+
# by considering a "weight" for each position: 1, 2, 4, 7, and 0, with
|
68
|
+
# "0" itself being represented by 4 + 7. So, 3 is 1 + 2, or "wwnnn",
|
69
|
+
# while 7 is "nnnww" (7 + 0). More information is available on
|
70
|
+
# Wikipedia.
|
71
|
+
|
72
|
+
class Interleaved2of5 < Barcode1D
|
73
|
+
|
74
|
+
# patterns and such go here
|
75
|
+
PATTERNS = {
|
76
|
+
'start' => 'nnnn',
|
77
|
+
'0' => 'nnwwn',
|
78
|
+
'1' => 'wnnnw',
|
79
|
+
'2' => 'nwnnw',
|
80
|
+
'3' => 'wwnnn',
|
81
|
+
'4' => 'nnwnw',
|
82
|
+
'5' => 'wnwnn',
|
83
|
+
'6' => 'nwwnn',
|
84
|
+
'7' => 'nnnww',
|
85
|
+
'8' => 'wnnwn',
|
86
|
+
'9' => 'nwnwn',
|
87
|
+
'stop' => 'wnn'
|
88
|
+
}
|
89
|
+
|
90
|
+
WN_RATIO = 2
|
91
|
+
|
92
|
+
DEFAULT_OPTIONS = {
|
93
|
+
:w_character => 'w',
|
94
|
+
:n_character => 'n',
|
95
|
+
:line_character => '1',
|
96
|
+
:space_character => '0',
|
97
|
+
:wn_ratio => WN_RATIO
|
98
|
+
}
|
99
|
+
|
100
|
+
class << self
|
101
|
+
# returns true or false
|
102
|
+
def can_encode?(value)
|
103
|
+
value.is_a?(Integer) || value.to_s =~ /^[0-9]+$/
|
104
|
+
end
|
105
|
+
|
106
|
+
# Generates check digit given a string to encode. It assumes there
|
107
|
+
# is no check digit on the "value". Note that if value has an even
|
108
|
+
# number of digits, a "0" will be prepended for this operation.
|
109
|
+
def generate_check_digit_for(value)
|
110
|
+
raise UnencodableCharactersError unless self.can_encode?(value)
|
111
|
+
mult = 1
|
112
|
+
value = value.to_s
|
113
|
+
value = "0#{value}" if value.size.even?
|
114
|
+
value = value.split('').inject(0) { |a,c| mult = 4 - mult ; a + c.to_i * mult }
|
115
|
+
(10 - (value % 10)) % 10
|
116
|
+
end
|
117
|
+
|
118
|
+
# validates the check digit given a string - assumes check digit
|
119
|
+
# is last digit of string.
|
120
|
+
def validate_check_digit_for(value)
|
121
|
+
raise UnencodableCharactersError unless self.can_encode?(value)
|
122
|
+
md = value.to_s.match(/^(\d+)(\d)$/)
|
123
|
+
self.generate_check_digit_for(md[1]) == md[2].to_i
|
124
|
+
end
|
125
|
+
|
126
|
+
# Decode a string in wn format. This will return an Interleaved2of5
|
127
|
+
# object.
|
128
|
+
def decode(str, options = {})
|
129
|
+
if str =~ /[^wn]/
|
130
|
+
raise UnencodableCharactersError, "Pattern must contain only \"w\" and \"n\"."
|
131
|
+
end
|
132
|
+
|
133
|
+
if str.reverse =~ /^#{PATTERNS['start']}.*?#{PATTERNS['stop']}$/
|
134
|
+
str.reverse!
|
135
|
+
end
|
136
|
+
|
137
|
+
unless str =~ /^#{PATTERNS['start']}(.*?)#{PATTERNS['stop']}$/
|
138
|
+
raise UnencodableCharactersError, "Start/stop pattern is not detected."
|
139
|
+
end
|
140
|
+
|
141
|
+
numeric_pattern = $1
|
142
|
+
|
143
|
+
unless numeric_pattern.size % 10 == 0
|
144
|
+
raise UnencodableCharactersError, "Wrong number of bars."
|
145
|
+
end
|
146
|
+
|
147
|
+
decoded_string = ''
|
148
|
+
|
149
|
+
numeric_pattern.scan(/(.{10})/).each do |chunk|
|
150
|
+
chunk = chunk[0]
|
151
|
+
|
152
|
+
num1 = chunk[0,1] + chunk[2,1] + chunk[4,1] + chunk[6,1] + chunk[8,1]
|
153
|
+
num2 = chunk[1,1] + chunk[3,1] + chunk[5,1] + chunk[7,1] + chunk[9,1]
|
154
|
+
|
155
|
+
found = false
|
156
|
+
('0'..'9').each do |digit|
|
157
|
+
if PATTERNS[digit] == num1
|
158
|
+
decoded_string += digit
|
159
|
+
found = true
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
raise UndecodableCharactersError, "Invalid sequence: #{num1}" unless found
|
164
|
+
|
165
|
+
# nnnnn is a sequence sometimes used in the spaces of the last
|
166
|
+
# digit to indicate there is no last digit.
|
167
|
+
if num2 != 'nnnnn'
|
168
|
+
found = false
|
169
|
+
('0'..'9').each do |digit|
|
170
|
+
if PATTERNS[digit] == num2
|
171
|
+
decoded_string += digit
|
172
|
+
found = true
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
raise UndecodableCharactersError, "Invalid sequence: #{num2}" unless found
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
Interleaved2of5.new(decoded_string.to_i, options)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Options are :line_character, :space_character, :w_character,
|
185
|
+
# :n_character, :skip_checksum, and :checksum_included.
|
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
|
+
if @options[:skip_checksum]
|
194
|
+
@encoded_string = value.to_s
|
195
|
+
@value = value.to_i
|
196
|
+
@check_digit = nil
|
197
|
+
elsif @options[:checksum_included]
|
198
|
+
raise ChecksumError unless self.class.validate_check_digit_for(value)
|
199
|
+
@encoded_string = value.to_s
|
200
|
+
@value = value.to_i / 10
|
201
|
+
@check_digit = value % 10
|
202
|
+
else
|
203
|
+
# need to add a checksum
|
204
|
+
@value = value.to_i
|
205
|
+
@check_digit = self.class.generate_check_digit_for(@value)
|
206
|
+
@encoded_string = "#{@value.to_s}#{@check_digit}"
|
207
|
+
end
|
208
|
+
|
209
|
+
@encoded_string = '0' + @encoded_string if @encoded_string.size.odd?
|
210
|
+
end
|
211
|
+
|
212
|
+
# Returns a string of "w" or "n" ("wide" and "narrow")
|
213
|
+
def wn
|
214
|
+
@wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
|
215
|
+
end
|
216
|
+
|
217
|
+
# returns a run-length-encoded string representation
|
218
|
+
def rle
|
219
|
+
@rle ||= self.class.wn_to_rle(self.wn, @options)
|
220
|
+
end
|
221
|
+
|
222
|
+
# returns 1s and 0s (for "black" and "white")
|
223
|
+
def bars
|
224
|
+
@bars ||= self.class.rle_to_bars(self.rle, @options)
|
225
|
+
end
|
226
|
+
|
227
|
+
# returns the total unit width of the bar code
|
228
|
+
def width
|
229
|
+
@width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def wn_str
|
235
|
+
@wn_str ||=
|
236
|
+
PATTERNS['start'] +
|
237
|
+
@encoded_string.unpack('A2'*(@encoded_string.size/2)).inject('') { |a,str| a + interleave(str) } +
|
238
|
+
PATTERNS['stop']
|
239
|
+
end
|
240
|
+
|
241
|
+
# Requires a two-digit string
|
242
|
+
def interleave(two_digits)
|
243
|
+
bars_pattern = PATTERNS[two_digits[0,1]]
|
244
|
+
spaces_pattern = PATTERNS[two_digits[1,1]]
|
245
|
+
(0..4).inject('') { |ret,x| ret + bars_pattern[x,1] + spaces_pattern[x,1] }
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|