barcode1dtools 0.9.7.0 → 0.9.8.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 +5 -2
- data/lib/barcode1dtools/msi.rb +262 -0
- data/lib/barcode1dtools/plessey.rb +208 -0
- data/test/test_barcode1dmsi.rb +79 -0
- data/test/test_barcode1dplessey.rb +53 -0
- metadata +16 -10
data/lib/barcode1dtools.rb
CHANGED
@@ -17,8 +17,9 @@ module Barcode1DTools
|
|
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
19
|
# COOP 2 of 5, Matrix 2 of 5, Industrial 2 of 5, IATA 2 of 5,
|
20
|
-
# PostNet, Code 3 of 9, Code 93,
|
21
|
-
# be expanded to include most 1D
|
20
|
+
# PostNet, Plessey, MSI (Modified Plessey), Code 3 of 9, Code 93,
|
21
|
+
# Code 11, and Codabar, but will be expanded to include most 1D
|
22
|
+
# symbologies in the near future.
|
22
23
|
#
|
23
24
|
#== Example
|
24
25
|
# ean13 = Barcode1DTools::EAN13.new('0012676510226', :line_character => 'x', :space_character => ' ')
|
@@ -127,3 +128,5 @@ require 'barcode1dtools/industrial2of5'
|
|
127
128
|
require 'barcode1dtools/iata2of5'
|
128
129
|
require 'barcode1dtools/matrix2of5'
|
129
130
|
require 'barcode1dtools/postnet'
|
131
|
+
require 'barcode1dtools/plessey'
|
132
|
+
require 'barcode1dtools/msi'
|
@@ -0,0 +1,262 @@
|
|
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::MSI - Create and decode bar patterns for
|
11
|
+
# MSI. The value encoded is a string which may contain the
|
12
|
+
# digits 0-9.
|
13
|
+
#
|
14
|
+
# There are four possible check digit calculations, and you
|
15
|
+
# may use the option :check_digit => 'x' to choose which
|
16
|
+
# one to use. "x" may be one of "mod 10", "mod 11",
|
17
|
+
# "mod 1010", or "mod 1110". The default is "mod 10".
|
18
|
+
# For a mod 11 check digit, you may use :check_style =>
|
19
|
+
# 'ibm' or 'ncr'.
|
20
|
+
#
|
21
|
+
# MSI is a terrible symbology in modern terms and should
|
22
|
+
# not be used in any new applications.
|
23
|
+
#
|
24
|
+
# val = "2898289238"
|
25
|
+
# bc = Barcode1DTools::MSI.new(val)
|
26
|
+
# pattern = bc.bars
|
27
|
+
# rle_pattern = bc.rle
|
28
|
+
# width = bc.width
|
29
|
+
#
|
30
|
+
# The object created is immutable.
|
31
|
+
#
|
32
|
+
# Barcode1DTools::MSI creates the patterns that you need to
|
33
|
+
# display MSI barcodes. It can also decode a simple w/n
|
34
|
+
# string.
|
35
|
+
#
|
36
|
+
# MSI characters consist of 4 bars and 4 spaces. The
|
37
|
+
# representation is simply binary where a binary "0" is
|
38
|
+
# represented as a narrow bar followed by a wide space and
|
39
|
+
# a binary "1" is a wide bar followed by a narrow space.
|
40
|
+
# The bits are ordered descending, so 9 is 1001 binary,
|
41
|
+
# "wn nw nw wn" in w/n format.
|
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
|
+
#== Rendering
|
64
|
+
#
|
65
|
+
# The author is aware of no standards for display.
|
66
|
+
|
67
|
+
class MSI < Barcode1D
|
68
|
+
|
69
|
+
# Character sequence - 0-based offset in this string is character
|
70
|
+
# number
|
71
|
+
CHAR_SEQUENCE = "0123456789"
|
72
|
+
|
73
|
+
# Patterns for making bar codes
|
74
|
+
PATTERNS = {
|
75
|
+
'0'=> {'val'=>0 ,'wn'=>'nwnwnwnw'},
|
76
|
+
'1'=> {'val'=>1 ,'wn'=>'nwnwnwwn'},
|
77
|
+
'2'=> {'val'=>2 ,'wn'=>'nwnwwnnw'},
|
78
|
+
'3'=> {'val'=>3 ,'wn'=>'nwnwwnwn'},
|
79
|
+
'4'=> {'val'=>4 ,'wn'=>'nwwnnwnw'},
|
80
|
+
'5'=> {'val'=>5 ,'wn'=>'nwwnnwwn'},
|
81
|
+
'6'=> {'val'=>6 ,'wn'=>'nwwnwnnw'},
|
82
|
+
'7'=> {'val'=>7 ,'wn'=>'nwwnwnwn'},
|
83
|
+
'8'=> {'val'=>8 ,'wn'=>'wnnwnwnw'},
|
84
|
+
'9'=> {'val'=>9 ,'wn'=>'wnnwnwwn'}
|
85
|
+
}
|
86
|
+
|
87
|
+
GUARD_PATTERN_LEFT_WN = 'wn'
|
88
|
+
GUARD_PATTERN_RIGHT_WN = 'nwn'
|
89
|
+
|
90
|
+
DEFAULT_OPTIONS = {
|
91
|
+
:line_character => '1',
|
92
|
+
:space_character => '0',
|
93
|
+
:w_character => 'w',
|
94
|
+
:n_character => 'n',
|
95
|
+
:wn_ratio => '2',
|
96
|
+
:check_digit => 'mod 10',
|
97
|
+
:check_style => 'ibm'
|
98
|
+
}
|
99
|
+
|
100
|
+
class << self
|
101
|
+
# MSI can encode digits
|
102
|
+
def can_encode?(value)
|
103
|
+
value.to_s =~ /\A\d+\z/
|
104
|
+
end
|
105
|
+
|
106
|
+
def generate_check_digit_for(value, options = {})
|
107
|
+
if options[:check_digit] == 'mod 10'
|
108
|
+
generate_mod10_check_digit_for(value).to_s
|
109
|
+
elsif options[:check_digit] == 'mod 11'
|
110
|
+
generate_mod11_check_digit_for(value, options[:check_style]).to_s
|
111
|
+
elsif options[:check_digit] == 'mod 1010'
|
112
|
+
mod10 = generate_mod10_check_digit_for(value)
|
113
|
+
mod10_2 = generate_mod10_check_digit_for(value + mod10.to_s)
|
114
|
+
"#{mod10}#{mod10_2}"
|
115
|
+
elsif options[:check_digit] == 'mod 1110'
|
116
|
+
mod11 = generate_mod11_check_digit_for(value, options[:check_style])
|
117
|
+
mod10_2 = generate_mod10_check_digit_for(value + mod11.to_s)
|
118
|
+
"#{mod11}#{mod10_2}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def validate_check_digit_for(value, options = {})
|
123
|
+
payload, check_digits = split_payload_and_check_digits(value, options)
|
124
|
+
self.generate_check_digit_for(payload, options) == check_digits
|
125
|
+
end
|
126
|
+
|
127
|
+
def split_payload_and_check_digits(value, options = {})
|
128
|
+
if options[:check_digit] == 'mod 1010' || options[:check_digit] == 'mod 1110'
|
129
|
+
md = value.to_s.match(/\A(.*?)(..)\z/)
|
130
|
+
else
|
131
|
+
md = value.to_s.match(/\A(.*?)(.)\z/)
|
132
|
+
end
|
133
|
+
[md[1], md[2]]
|
134
|
+
end
|
135
|
+
|
136
|
+
def generate_mod10_check_digit_for(value)
|
137
|
+
value = value.to_s
|
138
|
+
valarr = value.scan(/\d\d?/)
|
139
|
+
if value.size.odd?
|
140
|
+
odd = valarr.collect { |c| c[0,1] }
|
141
|
+
even = valarr.collect { |c| c[1,1] }
|
142
|
+
else
|
143
|
+
odd = valarr.collect { |c| c[1,1] }
|
144
|
+
even = valarr.collect { |c| c[0,1] }
|
145
|
+
end
|
146
|
+
odd = (odd.join.to_i * 2).to_s.split('').inject(0) { |a,c| a + c.to_i }
|
147
|
+
even = even.inject(0) { |a,c| a + c.to_i }
|
148
|
+
(10 - ((odd + even) % 10)) % 10
|
149
|
+
end
|
150
|
+
|
151
|
+
def generate_mod11_check_digit_for(value, style)
|
152
|
+
max = (style == 'ncr' ? 9 : 7)
|
153
|
+
value = value.to_s
|
154
|
+
weight = 1
|
155
|
+
sum = value.split('').reverse.inject(0) { |a,c| weight = (weight == max ? 2 : weight + 1); a + weight * c.to_i }
|
156
|
+
(11 - (sum % 11)) % 11
|
157
|
+
end
|
158
|
+
|
159
|
+
# Decode a string in rle format. This will return a MSI
|
160
|
+
# object.
|
161
|
+
def decode(str, options = {})
|
162
|
+
if str =~ /[^1-3]/ && str =~ /[^wn]/
|
163
|
+
raise UnencodableCharactersError, "Pattern must be rle or wn"
|
164
|
+
end
|
165
|
+
|
166
|
+
# ensure a wn string
|
167
|
+
if str =~ /[1-3]/
|
168
|
+
str = str.tr('123','nww')
|
169
|
+
end
|
170
|
+
|
171
|
+
if str.reverse =~ /\A#{GUARD_PATTERN_LEFT_WN}.*?#{GUARD_PATTERN_RIGHT_WN}\z/
|
172
|
+
str.reverse!
|
173
|
+
end
|
174
|
+
|
175
|
+
unless str =~ /\A#{GUARD_PATTERN_LEFT_WN}(.*?)#{GUARD_PATTERN_RIGHT_WN}\z/
|
176
|
+
raise UnencodableCharactersError, "Start/stop pattern is not detected."
|
177
|
+
end
|
178
|
+
|
179
|
+
wn_pattern = $1
|
180
|
+
|
181
|
+
# Each pattern is 4 bars and 4 spaces, with a space between.
|
182
|
+
unless wn_pattern.size % 8 == 0
|
183
|
+
raise UnencodableCharactersError, "Wrong number of bars."
|
184
|
+
end
|
185
|
+
|
186
|
+
decoded_string = ''
|
187
|
+
|
188
|
+
wn_pattern.scan(/.{8}/).each do |chunk|
|
189
|
+
|
190
|
+
found = false
|
191
|
+
|
192
|
+
PATTERNS.each do |char,hsh|
|
193
|
+
if chunk == hsh['wn']
|
194
|
+
decoded_string += char
|
195
|
+
found = true
|
196
|
+
break;
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
MSI.new(decoded_string, options)
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
# Options are :line_character, :space_character, :w_character,
|
210
|
+
# :n_character, :checksum_included, and :skip_checksum.
|
211
|
+
def initialize(value, options = {})
|
212
|
+
|
213
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
214
|
+
|
215
|
+
# Can we encode this value?
|
216
|
+
raise UnencodableCharactersError unless self.class.can_encode?(value)
|
217
|
+
|
218
|
+
if @options[:skip_checksum]
|
219
|
+
@encoded_string = value.to_s
|
220
|
+
@value = value.to_s
|
221
|
+
@check_digit = nil
|
222
|
+
elsif @options[:checksum_included]
|
223
|
+
raise ChecksumError unless self.class.validate_check_digit_for(value, options)
|
224
|
+
@encoded_string = value.to_s
|
225
|
+
@value, @check_digit = self.class.split_payload_and_check_digits(value, options)
|
226
|
+
else
|
227
|
+
@value = value.to_s
|
228
|
+
@check_digit = self.class.generate_check_digit_for(@value, options)
|
229
|
+
@encoded_string = "#{@value}#{@check_digit}"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Returns a string of "w" or "n" ("wide" and "narrow")
|
234
|
+
def wn
|
235
|
+
@wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
|
236
|
+
end
|
237
|
+
|
238
|
+
# returns a run-length-encoded string representation
|
239
|
+
def rle
|
240
|
+
@rle ||= self.class.wn_to_rle(self.wn, @options)
|
241
|
+
end
|
242
|
+
|
243
|
+
# returns 1s and 0s (for "black" and "white")
|
244
|
+
def bars
|
245
|
+
@bars ||= self.class.rle_to_bars(self.rle, @options)
|
246
|
+
end
|
247
|
+
|
248
|
+
# returns the total unit width of the bar code
|
249
|
+
def width
|
250
|
+
@width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
|
251
|
+
end
|
252
|
+
|
253
|
+
private
|
254
|
+
|
255
|
+
# Creates the actual w/n pattern. Note that there is a narrow space
|
256
|
+
# between each character.
|
257
|
+
def wn_str
|
258
|
+
@wn_str ||= GUARD_PATTERN_LEFT_WN + @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.join + GUARD_PATTERN_RIGHT_WN
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
end
|
@@ -0,0 +1,208 @@
|
|
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::Plessey - Create and decode bar patterns for
|
11
|
+
# Plessey. The value encoded is a string which may contain the
|
12
|
+
# digits 0-9 and the letters A-F (0-15 hexadecimal).
|
13
|
+
#
|
14
|
+
# According to Wikipedia, a Plessey code should contain a two
|
15
|
+
# digit CRC8 checksum. This code does not provide checksum
|
16
|
+
# generation or validation.
|
17
|
+
#
|
18
|
+
# Plessey is a terrible symbology in modern terms and should
|
19
|
+
# not be used in any new applications.
|
20
|
+
#
|
21
|
+
# val = "2898289238AF"
|
22
|
+
# bc = Barcode1DTools::Plessey.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::Plessey creates the patterns that you need to
|
30
|
+
# display Plessey barcodes. It can also decode a simple w/n
|
31
|
+
# string.
|
32
|
+
#
|
33
|
+
# Plessey characters consist of 4 bars and 4 spaces. The
|
34
|
+
# representation is simply binary where a binary "0" is
|
35
|
+
# represented as a narrow bar followed by a wide space and
|
36
|
+
# a binary "1" is a wide bar followed by a narrow space.
|
37
|
+
# The bits are ordered ascending, so 9 is 1001 binary,
|
38
|
+
# "wn nw nw wn" in w/n format.
|
39
|
+
#
|
40
|
+
# There are three formats for the returned pattern:
|
41
|
+
#
|
42
|
+
# bars - 1s and 0s specifying black lines and white spaces. Actual
|
43
|
+
# characters can be changed from "1" and 0" with options
|
44
|
+
# :line_character and :space_character.
|
45
|
+
#
|
46
|
+
# rle - Run-length-encoded version of the pattern. The first
|
47
|
+
# number is always a black line, with subsequent digits
|
48
|
+
# alternating between spaces and lines. The digits specify
|
49
|
+
# the width of each line or space.
|
50
|
+
#
|
51
|
+
# wn - The native format for this barcode type. The string
|
52
|
+
# consists of a series of "w" and "n" characters. The first
|
53
|
+
# item is always a black line, with subsequent characters
|
54
|
+
# alternating between spaces and lines. A "wide" item
|
55
|
+
# is twice the width of a "narrow" item.
|
56
|
+
#
|
57
|
+
# The "width" method will tell you the total end-to-end width, in
|
58
|
+
# units, of the entire barcode.
|
59
|
+
#
|
60
|
+
#== Rendering
|
61
|
+
#
|
62
|
+
# The author is aware of no standards for display.
|
63
|
+
|
64
|
+
class Plessey < Barcode1D
|
65
|
+
|
66
|
+
# Character sequence - 0-based offset in this string is character
|
67
|
+
# number
|
68
|
+
CHAR_SEQUENCE = "0123456789ABCDEF"
|
69
|
+
|
70
|
+
# Patterns for making bar codes
|
71
|
+
PATTERNS = {
|
72
|
+
'0'=> {'val'=>0 ,'wn'=>'nwnwnwnw'},
|
73
|
+
'1'=> {'val'=>1 ,'wn'=>'wnnwnwnw'},
|
74
|
+
'2'=> {'val'=>2 ,'wn'=>'nwwnnwnw'},
|
75
|
+
'3'=> {'val'=>3 ,'wn'=>'wnwnnwnw'},
|
76
|
+
'4'=> {'val'=>4 ,'wn'=>'nwnwwnnw'},
|
77
|
+
'5'=> {'val'=>5 ,'wn'=>'wnnwwnnw'},
|
78
|
+
'6'=> {'val'=>6 ,'wn'=>'nwwnwnnw'},
|
79
|
+
'7'=> {'val'=>7 ,'wn'=>'wnwnwnnw'},
|
80
|
+
'8'=> {'val'=>8 ,'wn'=>'nwnwnwwn'},
|
81
|
+
'9'=> {'val'=>9 ,'wn'=>'wnnwnwwn'},
|
82
|
+
'A'=> {'val'=>10 ,'wn'=>'nwwnnwwn'},
|
83
|
+
'B'=> {'val'=>11 ,'wn'=>'wnwnnwwn'},
|
84
|
+
'C'=> {'val'=>12 ,'wn'=>'nwnwwnwn'},
|
85
|
+
'D'=> {'val'=>13 ,'wn'=>'wnnwwnwn'},
|
86
|
+
'E'=> {'val'=>14 ,'wn'=>'nwwnwnwn'},
|
87
|
+
'F'=> {'val'=>15 ,'wn'=>'wnwnwnwn'}
|
88
|
+
}
|
89
|
+
|
90
|
+
GUARD_PATTERN_LEFT_WN = 'wnwnnwwn'
|
91
|
+
GUARD_PATTERN_RIGHT_WN = 'wnwnnwnw'
|
92
|
+
|
93
|
+
DEFAULT_OPTIONS = {
|
94
|
+
:line_character => '1',
|
95
|
+
:space_character => '0',
|
96
|
+
:w_character => 'w',
|
97
|
+
:n_character => 'n',
|
98
|
+
:wn_ratio => '2'
|
99
|
+
}
|
100
|
+
|
101
|
+
class << self
|
102
|
+
# Plessey can encode digits and A-F.
|
103
|
+
def can_encode?(value)
|
104
|
+
value.to_s =~ /\A[0-9A-F]+\z/
|
105
|
+
end
|
106
|
+
|
107
|
+
def generate_check_digit_for(value)
|
108
|
+
raise NotImplementedError
|
109
|
+
end
|
110
|
+
|
111
|
+
def validate_check_digit_for(value)
|
112
|
+
raise NotImplementedError
|
113
|
+
end
|
114
|
+
|
115
|
+
# Decode a string in rle format. This will return a Plessey
|
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}.*?#{GUARD_PATTERN_RIGHT_WN}\z/
|
128
|
+
str.reverse!
|
129
|
+
end
|
130
|
+
|
131
|
+
unless str =~ /\A#{GUARD_PATTERN_LEFT_WN}(.*?)#{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 4 bars and 4 spaces, with a space between.
|
138
|
+
unless wn_pattern.size % 8 == 0
|
139
|
+
raise UnencodableCharactersError, "Wrong number of bars."
|
140
|
+
end
|
141
|
+
|
142
|
+
decoded_string = ''
|
143
|
+
|
144
|
+
wn_pattern.scan(/.{8}/).each do |chunk|
|
145
|
+
|
146
|
+
found = false
|
147
|
+
|
148
|
+
PATTERNS.each do |char,hsh|
|
149
|
+
if chunk == hsh['wn']
|
150
|
+
decoded_string += char
|
151
|
+
found = true
|
152
|
+
break;
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
Plessey.new(decoded_string, options)
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
# Options are :line_character, :space_character, :w_character,
|
166
|
+
# :n_character, :checksum_included, and :skip_checksum.
|
167
|
+
def initialize(value, options = {})
|
168
|
+
|
169
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
170
|
+
|
171
|
+
# Can we encode this value?
|
172
|
+
raise UnencodableCharactersError unless self.class.can_encode?(value)
|
173
|
+
|
174
|
+
@value = value.to_s
|
175
|
+
@encoded_string = value.to_s
|
176
|
+
@check_digit = nil
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns a string of "w" or "n" ("wide" and "narrow")
|
180
|
+
def wn
|
181
|
+
@wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
|
182
|
+
end
|
183
|
+
|
184
|
+
# returns a run-length-encoded string representation
|
185
|
+
def rle
|
186
|
+
@rle ||= self.class.wn_to_rle(self.wn, @options)
|
187
|
+
end
|
188
|
+
|
189
|
+
# returns 1s and 0s (for "black" and "white")
|
190
|
+
def bars
|
191
|
+
@bars ||= self.class.rle_to_bars(self.rle, @options)
|
192
|
+
end
|
193
|
+
|
194
|
+
# returns the total unit width of the bar code
|
195
|
+
def width
|
196
|
+
@width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
# Creates the actual w/n pattern. Note that there is a narrow space
|
202
|
+
# between each character.
|
203
|
+
def wn_str
|
204
|
+
@wn_str ||= GUARD_PATTERN_LEFT_WN + @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.join + GUARD_PATTERN_RIGHT_WN
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
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 Barcode1DToolsMSITest < 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_msi_value(len)
|
20
|
+
(1..1+rand(len)).collect { "0123456789"[rand(11),1] }.join
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_checksum_generation
|
24
|
+
assert_equal '4', Barcode1DTools::MSI.generate_check_digit_for('1234567', :check_digit => 'mod 10')
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_checksum_validation
|
28
|
+
assert Barcode1DTools::MSI.validate_check_digit_for('12345674', :check_digit => 'mod 10')
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_attr_readers
|
32
|
+
msi = Barcode1DTools::MSI.new('1234567', :check_digit => 'mod 10', :checksum_included => false)
|
33
|
+
assert_equal '4', msi.check_digit
|
34
|
+
assert_equal '1234567', msi.value
|
35
|
+
assert_equal '12345674', msi.encoded_string
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_checksum_error
|
39
|
+
# proper checksum is 5
|
40
|
+
assert_raise(Barcode1DTools::ChecksumError) { Barcode1DTools::MSI.new('12345671', :check_digit => 'mod 10', :checksum_included => true) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_skip_checksum
|
44
|
+
msi = Barcode1DTools::MSI.new('12345', :skip_checksum => true)
|
45
|
+
assert_nil msi.check_digit
|
46
|
+
assert_equal '12345', msi.value
|
47
|
+
assert_equal '12345', msi.encoded_string
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_bad_character_errors
|
51
|
+
# Characters that cannot be encoded
|
52
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::MSI.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
|
+
msi = Barcode1DTools::MSI.new('12345674', :skip_checksum => true, :line_character => '#', :space_character => ' ')
|
58
|
+
assert_equal "## # # # ## # # ## # # # ## ## # ## # # # ## # ## # ## ## # # ## ## ## # ## # # # #", msi.bars
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_decode_error
|
62
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::MSI.decode('x') }
|
63
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::MSI.decode('x'*60) }
|
64
|
+
# proper start & stop, but crap in middle
|
65
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::MSI.decode('nnwwnnnnnnnnnnnwwn') }
|
66
|
+
# wrong start/stop
|
67
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::MSI.decode('nwwnwnwnwnwnwnw') }
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_decoding
|
71
|
+
random_msi_num=random_msi_value(10)
|
72
|
+
msi = Barcode1DTools::MSI.new(random_msi_num, :skip_checksum => true)
|
73
|
+
msi2 = Barcode1DTools::MSI.decode(msi.wn)
|
74
|
+
assert_equal msi.value, msi2.value
|
75
|
+
# Should also work in reverse
|
76
|
+
msi4 = Barcode1DTools::MSI.decode(msi.wn.reverse)
|
77
|
+
assert_equal msi.value, msi4.value
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,53 @@
|
|
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 Barcode1DToolsPlesseyTest < 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_plessey_value(len)
|
20
|
+
(1..1+rand(len)).collect { "0123456789"[rand(11),1] }.join
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_attr_readers
|
24
|
+
plessey = Barcode1DTools::Plessey.new('12345')
|
25
|
+
assert_nil plessey.check_digit
|
26
|
+
assert_equal '12345', plessey.value
|
27
|
+
assert_equal '12345', plessey.encoded_string
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_bad_character_errors
|
31
|
+
# Characters that cannot be encoded
|
32
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Plessey.new('thisisnotgood', :checksum_included => false) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_decode_error
|
36
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Plessey.decode('x') }
|
37
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Plessey.decode('x'*60) }
|
38
|
+
# proper start & stop, but crap in middle
|
39
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Plessey.decode('nnwwnnnnnnnnnnnwwn') }
|
40
|
+
# wrong start/stop
|
41
|
+
assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Plessey.decode('nwwnwnwnwnwnwnw') }
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_decoding
|
45
|
+
random_plessey_num=random_plessey_value(10)
|
46
|
+
plessey = Barcode1DTools::Plessey.new(random_plessey_num)
|
47
|
+
plessey2 = Barcode1DTools::Plessey.decode(plessey.wn)
|
48
|
+
assert_equal plessey.value, plessey2.value
|
49
|
+
# Should also work in reverse
|
50
|
+
plessey4 = Barcode1DTools::Plessey.decode(plessey.wn.reverse)
|
51
|
+
assert_equal plessey.value, plessey4.value
|
52
|
+
end
|
53
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: barcode1dtools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 39
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
|
-
-
|
9
|
+
- 8
|
10
10
|
- 0
|
11
|
-
version: 0.9.
|
11
|
+
version: 0.9.8.0
|
12
12
|
platform: ruby
|
13
13
|
authors:
|
14
14
|
- Michael Chaney
|
@@ -23,13 +23,13 @@ dependencies: []
|
|
23
23
|
description: "\t Barcode1D is a small library for handling many kinds of\n\
|
24
24
|
\t 1-dimensional barcodes. Currently implemented are Code 3 of 9, Code\n\
|
25
25
|
\t 93, Code 11, Codabar, Interleaved 2 of 5, COOP 2 of 5, Matrix 2 of\n\
|
26
|
-
\t 5, Industrial 2 of 5, IATA 2 of 5, PostNet,
|
27
|
-
\t UPC-E, UPC Supplemental 2, and UPC
|
28
|
-
\t created in either a simple format of
|
29
|
-
\t run-length encoded string. This only
|
30
|
-
\t patterns; actual display or reading from a
|
31
|
-
\t implemented by the programmer. More symbologies will
|
32
|
-
\t time permits.\n"
|
26
|
+
\t 5, Industrial 2 of 5, IATA 2 of 5, PostNet, Plessey, MSI (Modified\n\
|
27
|
+
\t Plessey), EAN-13, EAN-8, UPC-A, UPC-E, UPC Supplemental 2, and UPC\n\
|
28
|
+
\t Supplemental 5. Patterns are created in either a simple format of\n\
|
29
|
+
\t bars and spaces or as a run-length encoded string. This only\n\
|
30
|
+
\t generates and decodes the patterns; actual display or reading from a\n\
|
31
|
+
\t device must be implemented by the programmer. More symbologies will\n\
|
32
|
+
\t be added as time permits.\n"
|
33
33
|
email: mdchaney@michaelchaney.com
|
34
34
|
executables: []
|
35
35
|
|
@@ -49,6 +49,8 @@ files:
|
|
49
49
|
- lib/barcode1dtools/industrial2of5.rb
|
50
50
|
- lib/barcode1dtools/interleaved2of5.rb
|
51
51
|
- lib/barcode1dtools/matrix2of5.rb
|
52
|
+
- lib/barcode1dtools/msi.rb
|
53
|
+
- lib/barcode1dtools/plessey.rb
|
52
54
|
- lib/barcode1dtools/postnet.rb
|
53
55
|
- lib/barcode1dtools/upc_a.rb
|
54
56
|
- lib/barcode1dtools/upc_e.rb
|
@@ -68,6 +70,8 @@ files:
|
|
68
70
|
- test/test_barcode1diata2of5.rb
|
69
71
|
- test/test_barcode1dindustrial2of5.rb
|
70
72
|
- test/test_barcode1dmatrix2of5.rb
|
73
|
+
- test/test_barcode1dmsi.rb
|
74
|
+
- test/test_barcode1dplessey.rb
|
71
75
|
- test/test_barcode1dpostnet.rb
|
72
76
|
- test/test_barcode1dupca.rb
|
73
77
|
- test/test_barcode1dupce.rb
|
@@ -123,6 +127,8 @@ test_files:
|
|
123
127
|
- test/test_barcode1diata2of5.rb
|
124
128
|
- test/test_barcode1dindustrial2of5.rb
|
125
129
|
- test/test_barcode1dmatrix2of5.rb
|
130
|
+
- test/test_barcode1dmsi.rb
|
131
|
+
- test/test_barcode1dplessey.rb
|
126
132
|
- test/test_barcode1dpostnet.rb
|
127
133
|
- test/test_barcode1dupca.rb
|
128
134
|
- test/test_barcode1dupce.rb
|