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.
- data/lib/barcode1dtools.rb +3 -2
- data/lib/barcode1dtools/code11.rb +239 -0
- data/test/test_barcode1dcode11.rb +79 -0
- metadata +54 -26
data/lib/barcode1dtools.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
18
|
+
|
19
|
+
date: 2012-09-16 00:00:00 -05:00
|
20
|
+
default_executable:
|
13
21
|
dependencies: []
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
\
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|