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,246 @@
|
|
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_5 - Create pattern for UPC
|
13
|
+
# Supplemental 5 barcodes
|
14
|
+
#
|
15
|
+
# The value encoded is an 5-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. The bar patterns are the same as the left
|
19
|
+
# half of a standard UPC-A.
|
20
|
+
#
|
21
|
+
# num = '53999' # book price is US$39.99
|
22
|
+
# bc = Barcode1DTools::UPC_Supplemental_5.new(num)
|
23
|
+
# pattern = bc.bars
|
24
|
+
# rle_pattern = bc.rle
|
25
|
+
# bc.price # returns the "price" part as 4 digits
|
26
|
+
# bc.currency # returns the first digit currency code
|
27
|
+
# width = bc.width
|
28
|
+
# check_digit = Barcode1DTools::UPC_Supplemental_5.generate_check_digit_for(num)
|
29
|
+
#
|
30
|
+
# This type of barcode consists of 5 digits, and a check digit
|
31
|
+
# (a modulus 10 of the sum of the digits with weights of 3 and
|
32
|
+
# 9) that is encoded in the "parity" of the two barcode
|
33
|
+
# digits. It is positioned to the right of a UPC-A or EAN-13
|
34
|
+
# to create a "Bookland" code.
|
35
|
+
#
|
36
|
+
# The two are scanned together, and typically the scanner will
|
37
|
+
# return the five digits of the supplemental barcode
|
38
|
+
# immediately following the check digit from the main barcode.
|
39
|
+
# You will likely need to use the Barcode::UPC_A or
|
40
|
+
# Barcode::EAN_13 module in addition to this one to create the
|
41
|
+
# full code.
|
42
|
+
#
|
43
|
+
# The 5-digit supplement is generally used on literature, and
|
44
|
+
# represents a type-indicator digit followed by a 4-digit
|
45
|
+
# MSRP. The type is "0" for British Pound units, "5" for US
|
46
|
+
# Dollar units, and 9 for extra information. A code of
|
47
|
+
# "90000" means "no MSRP", "99991" indicates a complimentary
|
48
|
+
# copy, "99990" is used to mark used books (by college
|
49
|
+
# bookstores), and "90001" through "98999" are used internally
|
50
|
+
# by some publishers.
|
51
|
+
#
|
52
|
+
#== Rendering
|
53
|
+
#
|
54
|
+
# The 5-digit supplement is positioned to the right of the
|
55
|
+
# main UPC code, and the human-readable digits are usually
|
56
|
+
# printed above the supplemental barcode.
|
57
|
+
#
|
58
|
+
# A UPC-A is generally rendered at one inch across, then
|
59
|
+
# there's a 1/8th inch gap, then the supplemental. A UPC-A is
|
60
|
+
# 95 units wide, so the gap is 24 units wide. The 5-digit
|
61
|
+
# supplemental barcode is 47 units wide, essentially half an
|
62
|
+
# inch at this scale. Note that regardless of scale, the gap
|
63
|
+
# should be at least the smaller of 1/8th inch or 10 units.
|
64
|
+
|
65
|
+
|
66
|
+
class UPC_Supplemental_5 < Barcode1D
|
67
|
+
|
68
|
+
LEFT_PATTERNS = UPC_A::LEFT_PATTERNS
|
69
|
+
LEFT_PATTERNS_RLE = UPC_A::LEFT_PATTERNS_RLE
|
70
|
+
|
71
|
+
# parity patterns, essentially binary counting where "e" is "1"
|
72
|
+
# and "o" is "0"
|
73
|
+
PARITY_PATTERNS = {
|
74
|
+
'0' => 'eeooo',
|
75
|
+
'1' => 'eoeoo',
|
76
|
+
'2' => 'eooeo',
|
77
|
+
'3' => 'eoooe',
|
78
|
+
'4' => 'oeeoo',
|
79
|
+
'5' => 'ooeeo',
|
80
|
+
'6' => 'oooee',
|
81
|
+
'7' => 'oeoeo',
|
82
|
+
'8' => 'oeooe',
|
83
|
+
'9' => 'ooeoe'
|
84
|
+
};
|
85
|
+
|
86
|
+
LEFT_GUARD_PATTERN = '1011'
|
87
|
+
MIDDLE_GUARD_PATTERN = '01'
|
88
|
+
LEFT_GUARD_PATTERN_RLE = '112'
|
89
|
+
MIDDLE_GUARD_PATTERN_RLE = '11'
|
90
|
+
|
91
|
+
DEFAULT_OPTIONS = {
|
92
|
+
:line_character => '1',
|
93
|
+
:space_character => '0'
|
94
|
+
}
|
95
|
+
|
96
|
+
attr_reader :currency_code
|
97
|
+
attr_reader :price
|
98
|
+
|
99
|
+
class << self
|
100
|
+
# Returns true or false - must be 5 or 6 digits. This
|
101
|
+
# also handles the case where the leading 0 is added.
|
102
|
+
def can_encode?(value, options = nil)
|
103
|
+
if !options
|
104
|
+
value.to_s =~ /^\d{5,6}$/
|
105
|
+
elsif (options[:checksum_included])
|
106
|
+
value.to_s =~ /^\d{6}$/
|
107
|
+
else
|
108
|
+
value.to_s =~ /^\d{5}$/
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Generates check digit given a string to encode. It assumes there
|
113
|
+
# is no check digit on the "value".
|
114
|
+
def generate_check_digit_for(value)
|
115
|
+
mult = 9 # alternates 3 and 9
|
116
|
+
sprintf('%05d',value.to_i).reverse.chars.inject(0) { |a,c| mult = 12 - mult; a + c.to_i * mult } % 10
|
117
|
+
end
|
118
|
+
|
119
|
+
# validates the check digit given a string - assumes check digit
|
120
|
+
# is last digit of string.
|
121
|
+
def validate_check_digit_for(value)
|
122
|
+
raise UnencodableCharactersError unless self.can_encode?(value, :checksum_included => true)
|
123
|
+
md = value.match(/^(\d{5})(\d)$/)
|
124
|
+
self.generate_check_digit_for(md[1]) == md[2].to_i
|
125
|
+
end
|
126
|
+
|
127
|
+
def decode(str)
|
128
|
+
if str.length == 47
|
129
|
+
# bar pattern
|
130
|
+
str = bars_to_rle(str)
|
131
|
+
elsif str.length == 31 && str =~ /^[1-9]+$/
|
132
|
+
# rle
|
133
|
+
else
|
134
|
+
raise UnencodableCharactersError, "Pattern must be 47 unit bar pattern or 31 character rle."
|
135
|
+
end
|
136
|
+
|
137
|
+
# This string is "aaabbbb(ccdddd)" where "aaa" is the left
|
138
|
+
# guard pattern, "bbbb" is the first digit, "cc" is the
|
139
|
+
# intra-digit guard pattern, and "dddd" is the second
|
140
|
+
# digit. (ccdddd) occurs 4 times.
|
141
|
+
|
142
|
+
# See if the string is reversed
|
143
|
+
if str[28..30] == LEFT_GUARD_PATTERN_RLE.reverse && [7,13,19,25].all? { |x| str[29-x,2] == MIDDLE_GUARD_PATTERN_RLE.reverse }
|
144
|
+
str.reverse!
|
145
|
+
end
|
146
|
+
|
147
|
+
# Check the guard patterns
|
148
|
+
unless (str[0..2] == LEFT_GUARD_PATTERN_RLE && [7,13,19,25].all? { |x| str[x,2] == MIDDLE_GUARD_PATTERN_RLE.reverse })
|
149
|
+
raise UnencodableCharactersError, "Missing or incorrect guard patterns"
|
150
|
+
end
|
151
|
+
|
152
|
+
parity_sequence = ''
|
153
|
+
digits = ''
|
154
|
+
left_initial_offset = LEFT_GUARD_PATTERN_RLE.length
|
155
|
+
|
156
|
+
# Decode
|
157
|
+
(0..4).each do |left_offset|
|
158
|
+
found = false
|
159
|
+
digit_rle = str[(left_initial_offset + left_offset*6),4]
|
160
|
+
['o','e'].each do |parity|
|
161
|
+
('0'..'9').each do |digit|
|
162
|
+
if LEFT_PATTERNS_RLE[digit][parity] == digit_rle
|
163
|
+
parity_sequence += parity
|
164
|
+
digits += digit
|
165
|
+
found = true
|
166
|
+
break
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
raise UndecodableCharactersError, "Invalid sequence: #{digit_rle}" unless found
|
171
|
+
end
|
172
|
+
|
173
|
+
# Now, find the parity digit
|
174
|
+
parity_digit = nil
|
175
|
+
('0'..'9').each do |x|
|
176
|
+
if PARITY_PATTERNS[x] == parity_sequence
|
177
|
+
parity_digit = x
|
178
|
+
break
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
raise UndecodableCharactersError, "Weird parity: #{parity_sequence}" unless parity_digit
|
183
|
+
|
184
|
+
UPC_Supplemental_5.new(digits + parity_digit, :checksum_included => true)
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
# Options are :line_character, :space_character, and
|
190
|
+
# :checksum_included.
|
191
|
+
def initialize(value, options = {})
|
192
|
+
|
193
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
194
|
+
|
195
|
+
# Can we encode this value?
|
196
|
+
raise UnencodableCharactersError unless self.class.can_encode?(value, @options)
|
197
|
+
|
198
|
+
if @options[:checksum_included]
|
199
|
+
@encoded_string = value.to_s
|
200
|
+
raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
|
201
|
+
md = @encoded_string.match(/^(\d+?)(\d)$/)
|
202
|
+
@value, @check_digit = md[1], md[2].to_i
|
203
|
+
else
|
204
|
+
# need to add a checksum
|
205
|
+
@value = value.to_s
|
206
|
+
@check_digit = self.class.generate_check_digit_for(@value)
|
207
|
+
@encoded_string = sprintf('%05d%1d',@value.to_i,@check_digit)
|
208
|
+
end
|
209
|
+
|
210
|
+
md = @value.match(/^(\d)(\d{4})/)
|
211
|
+
@currency_code, @price = md[1], md[2]
|
212
|
+
end
|
213
|
+
|
214
|
+
# not usable with EAN-style codes
|
215
|
+
def wn
|
216
|
+
raise NotImplementedError
|
217
|
+
end
|
218
|
+
|
219
|
+
# returns a run-length-encoded string representation
|
220
|
+
def rle
|
221
|
+
if @rle
|
222
|
+
@rle
|
223
|
+
else
|
224
|
+
md = @encoded_string.match(/(\d{5})(\d)$/)
|
225
|
+
@rle = gen_rle(md[1], md[2])
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# returns 1s and 0s (for "black" and "white")
|
230
|
+
def bars
|
231
|
+
@bars ||= self.class.rle_to_bars(self.rle, @options)
|
232
|
+
end
|
233
|
+
|
234
|
+
# returns the total unit width of the bar code
|
235
|
+
def width
|
236
|
+
@width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
|
237
|
+
end
|
238
|
+
|
239
|
+
private
|
240
|
+
|
241
|
+
def gen_rle(payload, parity_digit)
|
242
|
+
LEFT_GUARD_PATTERN_RLE + (0..4).collect { |n| LEFT_PATTERNS_RLE[payload[n,1]][PARITY_PATTERNS[parity_digit][n,1]] }.join(MIDDLE_GUARD_PATTERN_RLE)
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,56 @@
|
|
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 Barcode1DToolsTest < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
@options = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def teardown
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_rle_to_bars
|
20
|
+
assert_equal '111001', Barcode1DTools::Barcode1D.rle_to_bars('321', @options)
|
21
|
+
assert_equal '10011100001', Barcode1DTools::Barcode1D.rle_to_bars('12341', @options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_bars_to_rle
|
25
|
+
assert_equal '321', Barcode1DTools::Barcode1D.bars_to_rle('111001', @options)
|
26
|
+
assert_equal '12341', Barcode1DTools::Barcode1D.bars_to_rle('10011100001', @options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_back_and_forth
|
30
|
+
random_rle = (0..19).collect { |x| (rand * 4 + 1).to_int.to_s }.join
|
31
|
+
random_bars = '1' + (0..((rand(25)+5)*2)).collect { |x| (x.odd? ? '1' : '0')*(rand(5)+1) }.join + '1'
|
32
|
+
random_wn = (0..19).collect { |x| rand < 0.5 ? 'w' : 'n' }.join
|
33
|
+
assert_equal random_rle, Barcode1DTools::Barcode1D.bars_to_rle(Barcode1DTools::Barcode1D.rle_to_bars(random_rle, @options), @options)
|
34
|
+
assert_equal random_bars, Barcode1DTools::Barcode1D.rle_to_bars(Barcode1DTools::Barcode1D.bars_to_rle(random_bars, @options), @options)
|
35
|
+
assert_equal random_wn, Barcode1DTools::Barcode1D.rle_to_wn(Barcode1DTools::Barcode1D.wn_to_rle(random_wn, @options), @options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_bar_pair
|
39
|
+
assert_equal '01', Barcode1DTools::Barcode1D.bar_pair
|
40
|
+
assert_equal '56', Barcode1DTools::Barcode1D.bar_pair({ :space_character => 5, :line_character => 6 })
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_wn_to_rle
|
44
|
+
assert_equal '12121211', Barcode1DTools::Barcode1D.wn_to_rle('nwnwnwnn')
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_rle_to_wn
|
48
|
+
assert_equal 'wnwnwnww', Barcode1DTools::Barcode1D.rle_to_wn('21212122')
|
49
|
+
assert_equal 'wnwnwnww', Barcode1DTools::Barcode1D.rle_to_wn('31313133')
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_wn_pair
|
53
|
+
assert_equal 'wn', Barcode1DTools::Barcode1D.wn_pair
|
54
|
+
assert_equal '65', Barcode1DTools::Barcode1D.wn_pair({ :n_character => '5', :w_character => '6' })
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,102 @@
|
|
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 Barcode1DToolsEAN13Test < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
end
|
14
|
+
|
15
|
+
def teardown
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates a random EAN-13 sans checksum
|
19
|
+
def random_12_digit_number
|
20
|
+
(0..11).collect { |x| ((rand * 10).to_i % 10).to_s }.join
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_checksum_generation
|
24
|
+
assert_equal 7, Barcode1DTools::EAN13.generate_check_digit_for('007820601001')
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_checksum_validation
|
28
|
+
assert Barcode1DTools::EAN13.validate_check_digit_for('0884088516338')
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_attr_readers
|
32
|
+
ean = Barcode1DTools::EAN13.new('088408851633', :checksum_included => false)
|
33
|
+
assert_equal 8, ean.check_digit
|
34
|
+
assert_equal '088408851633', ean.value
|
35
|
+
assert_equal '0884088516338', ean.encoded_string
|
36
|
+
assert_equal '08', ean.number_system
|
37
|
+
assert_equal '84088', ean.manufacturers_code
|
38
|
+
assert_equal '51633', ean.product_code
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_value_fixup
|
42
|
+
ean = Barcode1DTools::EAN13.new('088408851633', :checksum_included => false)
|
43
|
+
assert_equal 8, ean.check_digit
|
44
|
+
assert_equal '088408851633', ean.value
|
45
|
+
assert_equal '0884088516338', ean.encoded_string
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_checksum_error
|
49
|
+
# proper checksum is 8
|
50
|
+
assert_raise(Barcode1DTools::ChecksumError) { Barcode1DTools::EAN13.new('0884088516331', :checksum_included => true) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_value_length_errors
|
54
|
+
# One digit too short
|
55
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN13.new('01234567890', :checksum_included => false) }
|
56
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN13.new('012345678901', :checksum_included => true) }
|
57
|
+
# One digit too long
|
58
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN13.new('0123456789012', :checksum_included => false) }
|
59
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN13.new('01234567890123', :checksum_included => true) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_bad_character_errors
|
63
|
+
# Characters that cannot be encoded
|
64
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN13.new('thisisnotgood', :checksum_included => false) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_width
|
68
|
+
ean = Barcode1DTools::EAN13.new('0041343005796', :checksum_included => true)
|
69
|
+
assert_equal 95, ean.width
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_barcode_generation
|
73
|
+
ean = Barcode1DTools::EAN13.new('0012676510226', :checksum_included => true)
|
74
|
+
assert_equal "10100011010011001001001101011110111011010111101010100111011001101110010110110011011001010000101", ean.bars
|
75
|
+
assert_equal "11132112221212211141312111411111123122213211212221221114111", ean.rle
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_wn_raises_error
|
79
|
+
ean = Barcode1DTools::EAN13.new('0012676510226', :checksum_included => true)
|
80
|
+
assert_raise(Barcode1DTools::NotImplementedError) { ean.wn }
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_decode_error
|
84
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN13.decode('x') }
|
85
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN13.decode('x'*60) }
|
86
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN13.decode('x'*96) }
|
87
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN13.decode('x'*94) }
|
88
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN13.decode('111000011111000011111') }
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_decoding
|
92
|
+
random_ean_num=random_12_digit_number
|
93
|
+
ean = Barcode1DTools::EAN13.new(random_ean_num)
|
94
|
+
ean2 = Barcode1DTools::EAN13.decode(ean.bars)
|
95
|
+
assert_equal ean.value, ean2.value
|
96
|
+
ean3 = Barcode1DTools::EAN13.decode(ean.rle)
|
97
|
+
assert_equal ean.value, ean3.value
|
98
|
+
# Should also work in reverse
|
99
|
+
ean4 = Barcode1DTools::EAN13.decode(ean.bars.reverse)
|
100
|
+
assert_equal ean.value, ean4.value
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,95 @@
|
|
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 Barcode1DToolsEAN8Test < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
end
|
14
|
+
|
15
|
+
def teardown
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates a random EAN-8 sans checksum
|
19
|
+
def random_7_digit_number
|
20
|
+
sprintf('%07d',rand(10000000))
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_checksum_generation
|
24
|
+
assert_equal 4, Barcode1DTools::EAN8.generate_check_digit_for('9638507')
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_checksum_validation
|
28
|
+
assert Barcode1DTools::EAN8.validate_check_digit_for('96385074')
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_attr_readers
|
32
|
+
ean = Barcode1DTools::EAN8.new('9638507', :checksum_included => false)
|
33
|
+
assert_equal 4, ean.check_digit
|
34
|
+
assert_equal '9638507', ean.value
|
35
|
+
assert_equal '96385074', ean.encoded_string
|
36
|
+
assert_equal '963', ean.number_system
|
37
|
+
assert_equal '8507', ean.product_code
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_checksum_error
|
41
|
+
# proper checksum is 4
|
42
|
+
assert_raise(Barcode1DTools::ChecksumError) { Barcode1DTools::EAN8.new('96385070', :checksum_included => true) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_value_length_errors
|
46
|
+
# One digit too short
|
47
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN8.new('123456', :checksum_included => false) }
|
48
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN8.new('1234567', :checksum_included => true) }
|
49
|
+
# One digit too long
|
50
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN8.new('12345678', :checksum_included => false) }
|
51
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN8.new('123456789', :checksum_included => true) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_bad_character_errors
|
55
|
+
# Characters that cannot be encoded
|
56
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN8.new('thisisnotgood', :checksum_included => false) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_width
|
60
|
+
ean = Barcode1DTools::EAN8.new(random_7_digit_number)
|
61
|
+
assert_equal 67, ean.width
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_barcode_generation
|
65
|
+
ean = Barcode1DTools::EAN8.new('96385074', :checksum_included => true)
|
66
|
+
assert_equal "1010001011010111101111010110111010101001110111001010001001011100101", ean.bars
|
67
|
+
|
68
|
+
assert_equal "1113112111414111213111111231321113121132111", ean.rle
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_wn_raises_error
|
72
|
+
ean = Barcode1DTools::EAN8.new(random_7_digit_number)
|
73
|
+
assert_raise(Barcode1DTools::NotImplementedError) { ean.wn }
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_decode_error
|
77
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN8.decode('x') }
|
78
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN8.decode('x'*60) }
|
79
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN8.decode('x'*96) }
|
80
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN8.decode('x'*94) }
|
81
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::EAN8.decode('111000011111000011111') }
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_decoding
|
85
|
+
random_ean_num=random_7_digit_number
|
86
|
+
ean = Barcode1DTools::EAN8.new(random_ean_num)
|
87
|
+
ean2 = Barcode1DTools::EAN8.decode(ean.bars)
|
88
|
+
assert_equal ean.value, ean2.value
|
89
|
+
ean3 = Barcode1DTools::EAN8.decode(ean.rle)
|
90
|
+
assert_equal ean.value, ean3.value
|
91
|
+
# Should also work in reverse
|
92
|
+
ean4 = Barcode1DTools::EAN8.decode(ean.bars.reverse)
|
93
|
+
assert_equal ean.value, ean4.value
|
94
|
+
end
|
95
|
+
end
|