barcode1dtools 0.9.6.0 → 0.9.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -16,8 +16,9 @@ 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, Code 11, and Codabar, but will be
20
- # expanded to include most 1D symbologies in the near future.
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, Code 11, and Codabar, but will
21
+ # be expanded to include most 1D symbologies in the near future.
21
22
  #
22
23
  #== Example
23
24
  # ean13 = Barcode1DTools::EAN13.new('0012676510226', :line_character => 'x', :space_character => ' ')
@@ -121,3 +122,8 @@ require 'barcode1dtools/code3of9'
121
122
  require 'barcode1dtools/code93'
122
123
  require 'barcode1dtools/codabar'
123
124
  require 'barcode1dtools/code11'
125
+ require 'barcode1dtools/coop2of5'
126
+ require 'barcode1dtools/industrial2of5'
127
+ require 'barcode1dtools/iata2of5'
128
+ require 'barcode1dtools/matrix2of5'
129
+ require 'barcode1dtools/postnet'
@@ -62,8 +62,7 @@ module Barcode1DTools
62
62
  # is twice the width of a "narrow" item.
63
63
  #
64
64
  # The "width" method will tell you the total end-to-end width, in
65
- # units, of the entire barcode. Note that the w/n format is
66
- # unavailable for this symbology.
65
+ # units, of the entire barcode.
67
66
  #
68
67
  #== Rendering
69
68
  #
@@ -53,8 +53,7 @@ module Barcode1DTools
53
53
  # is twice the width of a "narrow" item.
54
54
  #
55
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.
56
+ # units, of the entire barcode.
58
57
  #
59
58
  #== Rendering
60
59
  #
@@ -0,0 +1,222 @@
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::Coop2of5 - Create and decode bar patterns for
11
+ # COOP 2 of 5. The value encoded is a number with digits 0-9.
12
+ # Internally, the value is treated as a string to preserve
13
+ # leading zeroes.
14
+ #
15
+ # Use :checksum_included => true if you have already added a
16
+ # checksum and wish to have it validated, or :skip_checksum =>
17
+ # true if you don't wish to add one or have it validated.
18
+ #
19
+ # COOP 2 of 5 is low-density and limited. It should not be
20
+ # used in any new applications.
21
+ #
22
+ # val = "3423"
23
+ # bc = Barcode1DTools::Coop2of5.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::Coop2of5 creates the patterns that you need to
31
+ # display COOP 2 of 5 barcodes. It can also decode a simple w/n
32
+ # string.
33
+ #
34
+ # Coop2of5 characters consist of 3 bars and 2 spaces, with a narrow
35
+ # space between them. 2 of the bars/spaces in each symbol are wide.
36
+ #
37
+ # There are three formats for the returned pattern:
38
+ #
39
+ # bars - 1s and 0s specifying black lines and white spaces. Actual
40
+ # characters can be changed from "1" and 0" with options
41
+ # :line_character and :space_character.
42
+ #
43
+ # rle - Run-length-encoded version of the pattern. The first
44
+ # number is always a black line, with subsequent digits
45
+ # alternating between spaces and lines. The digits specify
46
+ # the width of each line or space.
47
+ #
48
+ # wn - The native format for this barcode type. The string
49
+ # consists of a series of "w" and "n" characters. The first
50
+ # item is always a black line, with subsequent characters
51
+ # alternating between spaces and lines. A "wide" item
52
+ # is twice the width of a "narrow" item.
53
+ #
54
+ # The "width" method will tell you the total end-to-end width, in
55
+ # units, of the entire barcode.
56
+ #
57
+ #== Rendering
58
+ #
59
+ # The standard w/n ratio seems to be 2:1. There seem to be no real
60
+ # standards for display.
61
+
62
+ class Coop2of5 < Barcode1D
63
+
64
+ # Character sequence - 0-based offset in this string is character
65
+ # number
66
+ CHAR_SEQUENCE = "0123456789"
67
+
68
+ # Patterns for making bar codes. Note that the position
69
+ # weights are 7, 4, 2, 1 and the last bit is parity.
70
+ # Each letter is an alternating bar then space, and there
71
+ # is a narrow space between each character.
72
+ PATTERNS = {
73
+ '0'=> {'val'=>0 ,'wn'=>'wwnnn'},
74
+ '1'=> {'val'=>1 ,'wn'=>'nnnww'},
75
+ '2'=> {'val'=>2 ,'wn'=>'nnwnw'},
76
+ '3'=> {'val'=>3 ,'wn'=>'nnwwn'},
77
+ '4'=> {'val'=>4 ,'wn'=>'nwnnw'},
78
+ '5'=> {'val'=>5 ,'wn'=>'nwnwn'},
79
+ '6'=> {'val'=>6 ,'wn'=>'nwwnn'},
80
+ '7'=> {'val'=>7 ,'wn'=>'wnnnw'},
81
+ '8'=> {'val'=>8 ,'wn'=>'wnnwn'},
82
+ '9'=> {'val'=>9 ,'wn'=>'wnwnn'}
83
+ }
84
+
85
+ GUARD_PATTERN_LEFT_WN = 'wnw'
86
+ GUARD_PATTERN_RIGHT_WN = 'nww'
87
+
88
+ DEFAULT_OPTIONS = {
89
+ :line_character => '1',
90
+ :space_character => '0',
91
+ :w_character => 'w',
92
+ :n_character => 'n',
93
+ :wn_ratio => '2'
94
+ }
95
+
96
+ class << self
97
+ # Coop2of5 can encode digits
98
+ def can_encode?(value)
99
+ value.to_s =~ /\A[0-9]+\z/
100
+ end
101
+
102
+ def generate_check_digit_for(value)
103
+ raise UnencodableCharactersError unless self.can_encode?(value)
104
+ mult = 3
105
+ value = value.reverse.split('').inject(0) { |a,c| mult = 4 - mult ; a + c.to_i * mult }
106
+ (10 - (value % 10)) % 10
107
+ end
108
+
109
+ def validate_check_digit_for(value)
110
+ raise UnencodableCharactersError unless self.can_encode?(value)
111
+ md = value.match(/^(\d+?)(\d)$/)
112
+ self.generate_check_digit_for(md[1]) == md[2].to_i
113
+ end
114
+
115
+ # Decode a string in rle format. This will return a Coop2of5
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}n.*?#{GUARD_PATTERN_RIGHT_WN}\z/
128
+ str.reverse!
129
+ end
130
+
131
+ unless str =~ /\A#{GUARD_PATTERN_LEFT_WN}n(.*?)#{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 3 bars and 2 spaces, with a space between.
138
+ unless wn_pattern.size % 6 == 0
139
+ raise UnencodableCharactersError, "Wrong number of bars."
140
+ end
141
+
142
+ decoded_string = ''
143
+
144
+ wn_pattern.scan(/(.{5})n/).each do |chunk|
145
+
146
+ chunk = chunk.first
147
+ found = false
148
+
149
+ PATTERNS.each do |char,hsh|
150
+ if chunk == hsh['wn']
151
+ decoded_string += char
152
+ found = true
153
+ break;
154
+ end
155
+ end
156
+
157
+ raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
158
+
159
+ end
160
+
161
+ Coop2of5.new(decoded_string, options)
162
+ end
163
+
164
+ end
165
+
166
+ # Options are :line_character, :space_character, :w_character,
167
+ # :n_character, :checksum_included, and :skip_checksum.
168
+ def initialize(value, options = {})
169
+
170
+ @options = DEFAULT_OPTIONS.merge(options)
171
+
172
+ # Can we encode this value?
173
+ raise UnencodableCharactersError unless self.class.can_encode?(value)
174
+
175
+ @value = value.to_s
176
+
177
+ if @options[:skip_checksum]
178
+ @encoded_string = value.to_s
179
+ @value = value.to_s
180
+ @check_digit = nil
181
+ elsif @options[:checksum_included]
182
+ @encoded_string = value.to_s
183
+ raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
184
+ md = @encoded_string.match(/^(\d+?)(\d)$/)
185
+ @value, @check_digit = md[1], md[2].to_i
186
+ else
187
+ @value = value.to_s
188
+ @check_digit = self.class.generate_check_digit_for(@value)
189
+ @encoded_string = "#{@value}#{@check_digit}"
190
+ end
191
+ end
192
+
193
+ # Returns a string of "w" or "n" ("wide" and "narrow")
194
+ def wn
195
+ @wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
196
+ end
197
+
198
+ # returns a run-length-encoded string representation
199
+ def rle
200
+ @rle ||= self.class.wn_to_rle(self.wn, @options)
201
+ end
202
+
203
+ # returns 1s and 0s (for "black" and "white")
204
+ def bars
205
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
206
+ end
207
+
208
+ # returns the total unit width of the bar code
209
+ def width
210
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
211
+ end
212
+
213
+ private
214
+
215
+ # Creates the actual w/n pattern. Note that there is a narrow space
216
+ # between each character.
217
+ def wn_str
218
+ @wn_str ||= GUARD_PATTERN_LEFT_WN + 'n' + @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.join('n') + 'n' + GUARD_PATTERN_RIGHT_WN
219
+ end
220
+
221
+ end
222
+ end
@@ -0,0 +1,223 @@
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::IATA2of5 - Create and decode bar patterns for
11
+ # IATA 2 of 5. The value encoded is a number with digits 0-9.
12
+ # Internally, the value is treated as a string to preserve
13
+ # leading zeroes. This is identical to Industrial 2 of 5 but
14
+ # with different guard patterns.
15
+ #
16
+ # Use :checksum_included => true if you have already added a
17
+ # checksum and wish to have it validated, or :skip_checksum =>
18
+ # true if you don't want to add it or wish to skip checking.
19
+ #
20
+ # IATA 2 of 5 is low-density and limited. It should not be
21
+ # used in any new applications.
22
+ #
23
+ # val = "3423"
24
+ # bc = Barcode1DTools::IATA2of5.new(val)
25
+ # pattern = bc.bars
26
+ # rle_pattern = bc.rle
27
+ # width = bc.width
28
+ #
29
+ # The object created is immutable.
30
+ #
31
+ # Barcode1DTools::IATA2of5 creates the patterns that you need to
32
+ # display IATA 2 of 5 barcodes. It can also decode a simple w/n
33
+ # string.
34
+ #
35
+ # IATA2of5 characters consist of 3 bars and 2 spaces, with a narrow
36
+ # space between them. 2 of the bars/spaces in each symbol are 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.
57
+ #
58
+ #== Rendering
59
+ #
60
+ # The standard w/n ratio seems to be 2:1. There seem to be no real
61
+ # standards for display.
62
+
63
+ class IATA2of5 < Barcode1D
64
+
65
+ # Character sequence - 0-based offset in this string is character
66
+ # number
67
+ CHAR_SEQUENCE = "0123456789"
68
+
69
+ # Patterns for making bar codes. Note that the position
70
+ # weights are 1, 2, 4, and 7 and the last bit is parity.
71
+ # The patterns are for the bars, all spaces are narrow.
72
+ PATTERNS = {
73
+ '0'=> {'val'=>0 ,'wn'=>'nnnnwnwnn'},
74
+ '1'=> {'val'=>1 ,'wn'=>'wnnnnnnnw'},
75
+ '2'=> {'val'=>2 ,'wn'=>'nnwnnnnnw'},
76
+ '3'=> {'val'=>3 ,'wn'=>'wnwnnnnnn'},
77
+ '4'=> {'val'=>4 ,'wn'=>'nnnnwnnnw'},
78
+ '5'=> {'val'=>5 ,'wn'=>'wnnnwnnnn'},
79
+ '6'=> {'val'=>6 ,'wn'=>'nnwnwnnnn'},
80
+ '7'=> {'val'=>7 ,'wn'=>'nnnnnnwnw'},
81
+ '8'=> {'val'=>8 ,'wn'=>'wnnnnnwnn'},
82
+ '9'=> {'val'=>9 ,'wn'=>'nnwnnnwnn'}
83
+ }
84
+
85
+ GUARD_PATTERN_LEFT_WN = 'nnn'
86
+ GUARD_PATTERN_RIGHT_WN = 'wnn'
87
+
88
+ DEFAULT_OPTIONS = {
89
+ :line_character => '1',
90
+ :space_character => '0',
91
+ :w_character => 'w',
92
+ :n_character => 'n',
93
+ :wn_ratio => '2'
94
+ }
95
+
96
+ class << self
97
+ # IATA2of5 can encode digits
98
+ def can_encode?(value)
99
+ value.to_s =~ /\A[0-9]+\z/
100
+ end
101
+
102
+ def generate_check_digit_for(value)
103
+ raise UnencodableCharactersError unless self.can_encode?(value)
104
+ mult = 3
105
+ value = value.reverse.split('').inject(0) { |a,c| mult = 4 - mult ; a + c.to_i * mult }
106
+ (10 - (value % 10)) % 10
107
+ end
108
+
109
+ def validate_check_digit_for(value)
110
+ raise UnencodableCharactersError unless self.can_encode?(value)
111
+ md = value.match(/^(\d+?)(\d)$/)
112
+ self.generate_check_digit_for(md[1]) == md[2].to_i
113
+ end
114
+
115
+ # Decode a string in rle format. This will return a IATA2of5
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}n.*?#{GUARD_PATTERN_RIGHT_WN}\z/
128
+ str.reverse!
129
+ end
130
+
131
+ # Note that every other character is an "n"
132
+ unless str =~ /\A([wn]n)+[wn]\z/ && str =~ /\A#{GUARD_PATTERN_LEFT_WN}n(.*?)#{GUARD_PATTERN_RIGHT_WN}\z/
133
+ raise UnencodableCharactersError, "Start/stop pattern is not detected."
134
+ end
135
+
136
+ wn_pattern = $1
137
+
138
+ # Each pattern is 5 bars and 4 spaces, with a space between.
139
+ unless wn_pattern.size % 10 == 0
140
+ raise UnencodableCharactersError, "Wrong number of bars."
141
+ end
142
+
143
+ decoded_string = ''
144
+
145
+ wn_pattern.scan(/(.{9})n/).each do |chunk|
146
+
147
+ chunk = chunk.first
148
+ found = false
149
+
150
+ PATTERNS.each do |char,hsh|
151
+ if chunk == hsh['wn']
152
+ decoded_string += char
153
+ found = true
154
+ break;
155
+ end
156
+ end
157
+
158
+ raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
159
+
160
+ end
161
+
162
+ IATA2of5.new(decoded_string, options)
163
+ end
164
+
165
+ end
166
+
167
+ # Options are :line_character, :space_character, :w_character,
168
+ # :n_character, :checksum_included, and :skip_checksum.
169
+ def initialize(value, options = {})
170
+
171
+ @options = DEFAULT_OPTIONS.merge(options)
172
+
173
+ # Can we encode this value?
174
+ raise UnencodableCharactersError unless self.class.can_encode?(value)
175
+
176
+ @value = value.to_s
177
+
178
+ if @options[:skip_checksum]
179
+ @encoded_string = value.to_s
180
+ @value = value.to_s
181
+ @check_digit = nil
182
+ elsif @options[:checksum_included]
183
+ @encoded_string = value.to_s
184
+ raise ChecksumError unless self.class.validate_check_digit_for(@encoded_string)
185
+ md = @encoded_string.match(/^(\d+?)(\d)$/)
186
+ @value, @check_digit = md[1], md[2].to_i
187
+ else
188
+ @value = value.to_s
189
+ @check_digit = self.class.generate_check_digit_for(@value)
190
+ @encoded_string = "#{@value}#{@check_digit}"
191
+ end
192
+ end
193
+
194
+ # Returns a string of "w" or "n" ("wide" and "narrow")
195
+ def wn
196
+ @wn ||= wn_str.tr('wn', @options[:w_character].to_s + @options[:n_character].to_s)
197
+ end
198
+
199
+ # returns a run-length-encoded string representation
200
+ def rle
201
+ @rle ||= self.class.wn_to_rle(self.wn, @options)
202
+ end
203
+
204
+ # returns 1s and 0s (for "black" and "white")
205
+ def bars
206
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
207
+ end
208
+
209
+ # returns the total unit width of the bar code
210
+ def width
211
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
212
+ end
213
+
214
+ private
215
+
216
+ # Creates the actual w/n pattern. Note that there is a narrow space
217
+ # between every single bar.
218
+ def wn_str
219
+ @wn_str ||= GUARD_PATTERN_LEFT_WN + 'n' + @encoded_string.split('').collect { |c| PATTERNS[c]['wn'] }.join('n') + 'n' + GUARD_PATTERN_RIGHT_WN
220
+ end
221
+
222
+ end
223
+ end