barcode1dtools 0.9.6.0 → 0.9.7.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.
@@ -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