barcode1dtools 0.9.3.0 → 0.9.4.1

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.
@@ -13,12 +13,11 @@ module Barcode1DTools
13
13
  #= barcode1dtools.rb
14
14
  #
15
15
  # Barcode1DTools is a library for generating and decoding
16
- # 1-dimensional barcode patterns for various code types.
17
- # The library currently includes EAN-13, EAN-8, UPC-A,
18
- # UPC-E, UPC Supplemental 2, UPC Supplemental 5,
19
- # Interleaved 2 of 5 (I 2/5), and Code 3 of 9, but will be
20
- # expanded to include most 1D symbologies in the near
21
- # future.
16
+ # 1-dimensional barcode patterns for various code types. The
17
+ # library currently includes EAN-13, EAN-8, UPC-A, UPC-E, UPC
18
+ # Supplemental 2, UPC Supplemental 5, Interleaved 2 of 5 (I 2/5),
19
+ # Code 3 of 9, and Code 93, but will be expanded to include most 1D
20
+ # symbologies in the near future.
22
21
  #
23
22
  #== Example
24
23
  # ean13 = Barcode1DTools::EAN13.new('0012676510226', :line_character => 'x', :space_character => ' ')
@@ -119,3 +118,4 @@ require 'barcode1dtools/upc_e'
119
118
  require 'barcode1dtools/upc_supplemental_2'
120
119
  require 'barcode1dtools/upc_supplemental_5'
121
120
  require 'barcode1dtools/code3of9'
121
+ require 'barcode1dtools/code93'
@@ -72,6 +72,13 @@ module Barcode1DTools
72
72
  # only for shifting. However, if you need to use a full character
73
73
  # set, Code 128 is probably a better choice.
74
74
  #
75
+ # Note:
76
+ # Please note that Code 3 of 9 is not suggested for new applications
77
+ # due to the fact that the code is sparse and doesn't encode a full
78
+ # range of characters without using the "full ascii extensions",
79
+ # which cause it to be even more sparse. For newer 1D applications
80
+ # use Code 128.
81
+ #
75
82
  #== Rendering
76
83
  #
77
84
  # Code 3 of 9 may be rendered however the programmer wishes. Since
@@ -140,6 +147,156 @@ module Barcode1DTools
140
147
 
141
148
  SIDE_GUARD_PATTERN = 'nwnnwnwnn'
142
149
 
150
+ FULL_ASCII_LOOKUP = [
151
+ '%U', '$A', '$B', '$C', '$D', '$E', '$F', '$G', '$H', '$I',
152
+ '$J', '$K', '$L', '$M', '$N', '$O', '$P', '$Q', '$R', '$S',
153
+ '$T', '$U', '$V', '$W', '$X', '$Y', '$Z', '%A', '%B', '%C',
154
+ '%D', '%E', ' ', '/A', '/B', '/C', '/D', '/E', '/F', '/G',
155
+ '/H', '/I', '/J', '/K', '/L', '-', '.', '/O', '0', '1', '2',
156
+ '3', '4', '5', '6', '7', '8', '9', '/Z', '%F', '%G', '%H',
157
+ '%I', '%J', '%V', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
158
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
159
+ 'U', 'V', 'W', 'X', 'Y', 'Z', '%K', '%L', '%M', '%N', '%O',
160
+ '%W', '+A', '+B', '+C', '+D', '+E', '+F', '+G', '+H', '+I',
161
+ '+J', '+K', '+L', '+M', '+N', '+O', '+P', '+Q', '+R', '+S',
162
+ '+T', '+U', '+V', '+W', '+X', '+Y', '+Z', '%P', '%Q', '%R',
163
+ '%S', '%T'
164
+ ]
165
+
166
+ FULL_ASCII_REVERSE_LOOKUP = {
167
+ '%U' => { 'position' => 0, 'name' => '<NUL>' },
168
+ '$A' => { 'position' => 1, 'name' => '<SOH>' },
169
+ '$B' => { 'position' => 2, 'name' => '<STX>' },
170
+ '$C' => { 'position' => 3, 'name' => '<ETX>' },
171
+ '$D' => { 'position' => 4, 'name' => '<EOT>' },
172
+ '$E' => { 'position' => 5, 'name' => '<ENQ>' },
173
+ '$F' => { 'position' => 6, 'name' => '<ACK>' },
174
+ '$G' => { 'position' => 7, 'name' => '<BEL>' },
175
+ '$H' => { 'position' => 8, 'name' => '<BS>' },
176
+ '$I' => { 'position' => 9, 'name' => '<HT>' },
177
+ '$J' => { 'position' => 10, 'name' => '<LF>' },
178
+ '$K' => { 'position' => 11, 'name' => '<VT>' },
179
+ '$L' => { 'position' => 12, 'name' => '<FF>' },
180
+ '$M' => { 'position' => 13, 'name' => '<CR>' },
181
+ '$N' => { 'position' => 14, 'name' => '<SO>' },
182
+ '$O' => { 'position' => 15, 'name' => '<SI>' },
183
+ '$P' => { 'position' => 16, 'name' => '<DLE>' },
184
+ '$Q' => { 'position' => 17, 'name' => '<DC1>' },
185
+ '$R' => { 'position' => 18, 'name' => '<DC2>' },
186
+ '$S' => { 'position' => 19, 'name' => '<DC3>' },
187
+ '$T' => { 'position' => 20, 'name' => '<DC4>' },
188
+ '$U' => { 'position' => 21, 'name' => '<NAK>' },
189
+ '$V' => { 'position' => 22, 'name' => '<SYN>' },
190
+ '$W' => { 'position' => 23, 'name' => '<ETB>' },
191
+ '$X' => { 'position' => 24, 'name' => '<CAN>' },
192
+ '$Y' => { 'position' => 25, 'name' => '<EM>' },
193
+ '$Z' => { 'position' => 26, 'name' => '<SUB>' },
194
+ '%A' => { 'position' => 27, 'name' => '<ESC>' },
195
+ '%B' => { 'position' => 28, 'name' => '<FS>' },
196
+ '%C' => { 'position' => 29, 'name' => '<GS>' },
197
+ '%D' => { 'position' => 30, 'name' => '<RS>' },
198
+ '%E' => { 'position' => 31, 'name' => '<US>' },
199
+ ' ' => { 'position' => 32, 'name' => ' ' },
200
+ '/A' => { 'position' => 33, 'name' => '!' },
201
+ '/B' => { 'position' => 34, 'name' => '"' },
202
+ '/C' => { 'position' => 35, 'name' => '#' },
203
+ '/D' => { 'position' => 36, 'name' => '$' },
204
+ '/E' => { 'position' => 37, 'name' => '%' },
205
+ '/F' => { 'position' => 38, 'name' => '&' },
206
+ '/G' => { 'position' => 39, 'name' => "'" },
207
+ '/H' => { 'position' => 40, 'name' => '(' },
208
+ '/I' => { 'position' => 41, 'name' => ')' },
209
+ '/J' => { 'position' => 42, 'name' => '*' },
210
+ '/K' => { 'position' => 43, 'name' => '+' },
211
+ '/L' => { 'position' => 44, 'name' => ',' },
212
+ '-' => { 'position' => 45, 'name' => '-' },
213
+ '.' => { 'position' => 46, 'name' => '.' },
214
+ '/O' => { 'position' => 47, 'name' => '/' },
215
+ '0' => { 'position' => 48, 'name' => '0' },
216
+ '1' => { 'position' => 49, 'name' => '1' },
217
+ '2' => { 'position' => 50, 'name' => '2' },
218
+ '3' => { 'position' => 51, 'name' => '3' },
219
+ '4' => { 'position' => 52, 'name' => '4' },
220
+ '5' => { 'position' => 53, 'name' => '5' },
221
+ '6' => { 'position' => 54, 'name' => '6' },
222
+ '7' => { 'position' => 55, 'name' => '7' },
223
+ '8' => { 'position' => 56, 'name' => '8' },
224
+ '9' => { 'position' => 57, 'name' => '9' },
225
+ '/Z' => { 'position' => 58, 'name' => ':' },
226
+ '%F' => { 'position' => 59, 'name' => ';' },
227
+ '%G' => { 'position' => 60, 'name' => '<' },
228
+ '%H' => { 'position' => 61, 'name' => '=' },
229
+ '%I' => { 'position' => 62, 'name' => '>' },
230
+ '%J' => { 'position' => 63, 'name' => '?' },
231
+ '%V' => { 'position' => 64, 'name' => '@' },
232
+ 'A' => { 'position' => 65, 'name' => 'A' },
233
+ 'B' => { 'position' => 66, 'name' => 'B' },
234
+ 'C' => { 'position' => 67, 'name' => 'C' },
235
+ 'D' => { 'position' => 68, 'name' => 'D' },
236
+ 'E' => { 'position' => 69, 'name' => 'E' },
237
+ 'F' => { 'position' => 70, 'name' => 'F' },
238
+ 'G' => { 'position' => 71, 'name' => 'G' },
239
+ 'H' => { 'position' => 72, 'name' => 'H' },
240
+ 'I' => { 'position' => 73, 'name' => 'I' },
241
+ 'J' => { 'position' => 74, 'name' => 'J' },
242
+ 'K' => { 'position' => 75, 'name' => 'K' },
243
+ 'L' => { 'position' => 76, 'name' => 'L' },
244
+ 'M' => { 'position' => 77, 'name' => 'M' },
245
+ 'N' => { 'position' => 78, 'name' => 'N' },
246
+ 'O' => { 'position' => 79, 'name' => 'O' },
247
+ 'P' => { 'position' => 80, 'name' => 'P' },
248
+ 'Q' => { 'position' => 81, 'name' => 'Q' },
249
+ 'R' => { 'position' => 82, 'name' => 'R' },
250
+ 'S' => { 'position' => 83, 'name' => 'S' },
251
+ 'T' => { 'position' => 84, 'name' => 'T' },
252
+ 'U' => { 'position' => 85, 'name' => 'U' },
253
+ 'V' => { 'position' => 86, 'name' => 'V' },
254
+ 'W' => { 'position' => 87, 'name' => 'W' },
255
+ 'X' => { 'position' => 88, 'name' => 'X' },
256
+ 'Y' => { 'position' => 89, 'name' => 'Y' },
257
+ 'Z' => { 'position' => 90, 'name' => 'Z' },
258
+ '%K' => { 'position' => 91, 'name' => '[' },
259
+ '%L' => { 'position' => 92, 'name' => '\\' },
260
+ '%M' => { 'position' => 93, 'name' => ']' },
261
+ '%N' => { 'position' => 94, 'name' => '^' },
262
+ '%O' => { 'position' => 95, 'name' => '_' },
263
+ '%W' => { 'position' => 96, 'name' => '`' },
264
+ '+A' => { 'position' => 97, 'name' => 'a' },
265
+ '+B' => { 'position' => 98, 'name' => 'b' },
266
+ '+C' => { 'position' => 99, 'name' => 'c' },
267
+ '+D' => { 'position' => 100, 'name' => 'd' },
268
+ '+E' => { 'position' => 101, 'name' => 'e' },
269
+ '+F' => { 'position' => 102, 'name' => 'f' },
270
+ '+G' => { 'position' => 103, 'name' => 'g' },
271
+ '+H' => { 'position' => 104, 'name' => 'h' },
272
+ '+I' => { 'position' => 105, 'name' => 'i' },
273
+ '+J' => { 'position' => 106, 'name' => 'j' },
274
+ '+K' => { 'position' => 107, 'name' => 'k' },
275
+ '+L' => { 'position' => 108, 'name' => 'l' },
276
+ '+M' => { 'position' => 109, 'name' => 'm' },
277
+ '+N' => { 'position' => 110, 'name' => 'n' },
278
+ '+O' => { 'position' => 111, 'name' => 'o' },
279
+ '+P' => { 'position' => 112, 'name' => 'p' },
280
+ '+Q' => { 'position' => 113, 'name' => 'q' },
281
+ '+R' => { 'position' => 114, 'name' => 'r' },
282
+ '+S' => { 'position' => 115, 'name' => 's' },
283
+ '+T' => { 'position' => 116, 'name' => 't' },
284
+ '+U' => { 'position' => 117, 'name' => 'u' },
285
+ '+V' => { 'position' => 118, 'name' => 'v' },
286
+ '+W' => { 'position' => 119, 'name' => 'w' },
287
+ '+X' => { 'position' => 120, 'name' => 'x' },
288
+ '+Y' => { 'position' => 121, 'name' => 'y' },
289
+ '+Z' => { 'position' => 122, 'name' => 'z' },
290
+ '%P' => { 'position' => 123, 'name' => '{' },
291
+ '%Q' => { 'position' => 124, 'name' => '|' },
292
+ '%R' => { 'position' => 125, 'name' => '}' },
293
+ '%S' => { 'position' => 126, 'name' => '~' },
294
+ '%T' => { 'position' => 127, 'name' => '<DEL>' },
295
+ '%X' => { 'position' => 127, 'name' => '<DEL>' },
296
+ '%Y' => { 'position' => 127, 'name' => '<DEL>' },
297
+ '%Z' => { 'position' => 127, 'name' => '<DEL>' }
298
+ }
299
+
143
300
  WN_RATIO = 2
144
301
 
145
302
  DEFAULT_OPTIONS = {
@@ -219,6 +376,26 @@ module Barcode1DTools
219
376
 
220
377
  Code3of9.new(decoded_string, options)
221
378
  end
379
+
380
+ # Provide encoding into the "full ascii" format. This
381
+ # allows us to encode any ascii character (0-127) in a Code 3
382
+ # of 9, but it is up to the application to anticipate and
383
+ # handle this. In this encoding, four of the characters
384
+ # ($, %, /, and +) are used as "shift" characters, paired
385
+ # with a letter A-Z that encodes a character that's not
386
+ # available in Code 3 of 9. Because no special characters
387
+ # are used, it's not possible to know if this encoding is
388
+ # used.
389
+ def encode_full_ascii(str)
390
+ str.bytes.collect { |c| FULL_ASCII_LOOKUP[c] }.join
391
+ end
392
+
393
+ # Decodes a "full ascii" string from Code 3 of 9 into standard
394
+ # ascii. Note that this will silently fail if a string is
395
+ # malformed.
396
+ def decode_full_ascii(str)
397
+ str.scan(/[\$%\/+]?[A-Z0-9 \.\-]/).collect { |c| FULL_ASCII_REVERSE_LOOKUP[c]['position'] }.pack('C*')
398
+ end
222
399
  end
223
400
 
224
401
  # Options are :line_character, :space_character, :w_character,
@@ -0,0 +1,479 @@
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::Code93 - Create and decode bar patterns for
11
+ # Code93. The value encoded is a string, and two checksum "digits"
12
+ # (actually characters) will be added to the end before encoding.
13
+ # You may use the option :checksum_included => true when
14
+ # initializing to specify that you have already included a
15
+ # checksum, A ChecksumError will be raised if
16
+ # :checksum_included => true and the checksum is invalid.
17
+ #
18
+ # Code 93 can encode any ascii character (0-127) but will be most
19
+ # efficient for those characters that may be natively encoded as
20
+ # a single character: digits, uppercase letters, and the symbols
21
+ # dash "-", period ".", dollar sign "$", forward slash "/", plus
22
+ # sign "+", percent sign "%", as well as a space " ".
23
+ #
24
+ # val = "THIS IS A TEST"
25
+ # bc = Barcode1DTools::Code93.new(val)
26
+ # pattern = bc.bars
27
+ # rle_pattern = bc.rle
28
+ # width = bc.width
29
+ # # Note that the check digits are actually two of the characters.
30
+ # check_digit = Barcode1DTools::Code93.generate_check_digit_for(num)
31
+ #
32
+ # The object created is immutable.
33
+ #
34
+ # Barcode1DTools::Code93 creates the patterns that you need to
35
+ # display Code 93 barcodes. It can also decode a simple w/n
36
+ # string.
37
+ #
38
+ # Code 93 barcodes consist of lines and spaces that are from
39
+ # one to four units wide each. A particular character has 3 bars
40
+ # and 3 spaces.
41
+ #
42
+ # There are two formats for the returned pattern:
43
+ #
44
+ # bars - 1s and 0s specifying black lines and white spaces. Actual
45
+ # characters can be changed from "1" and 0" with options
46
+ # :line_character and :space_character.
47
+ #
48
+ # rle - Run-length-encoded version of the pattern. The first
49
+ # number is always a black line, with subsequent digits
50
+ # alternating between spaces and lines. The digits specify
51
+ # the width of each line or space.
52
+ #
53
+ # The "width" method will tell you the total end-to-end width, in
54
+ # units, of the entire barcode. Note that the w/n format is
55
+ # unavailable for this symbology.
56
+ #
57
+ #== Miscellaneous Information
58
+ #
59
+ # Code 93 can encode any ascii character from 0 to 127. The design
60
+ # is identical to Code 3 of 9 in most respects, with the main
61
+ # difference being that the "shift" characters are separate from the
62
+ # regular encoded characters and it is thus possible to tell if a
63
+ # particular code is "regular" or "full ascii". This symbology is
64
+ # most efficient if only the native characters are used.
65
+ #
66
+ # This module will automatically promote the code to "full ascii"
67
+ # only if it is needed. In practical terms that means that a dollar
68
+ # sign, for instance, may be rendered one of two ways. If the
69
+ # payload contains only "native" characters, it will be encoded
70
+ # normally. But if any "extended" characters are used, such as a
71
+ # lowercase letter, the dollar sign will be likewise encoded as
72
+ # "(/)D". You can use the accessor "full_ascii" to see if full
73
+ # ascii mode is in effect. The option :force_full_ascii will
74
+ # cause that mode to be used whether needed or not.
75
+ #
76
+ # Internally, the four shift characters are represented as characters
77
+ # 128 "($)", 129 "(%)", 130 "(/)", and 131 "(+)".
78
+ #
79
+ #== Rendering
80
+ #
81
+ # Code 93 may be rendered however the programmer wishes. Since
82
+ # there is a simple mapping between number of characters and length of
83
+ # code, a variable length code should be allowed to grow and shrink to
84
+ # assure the bars are neither too large or too small. Code 93 is
85
+ # often implemented as a font.
86
+ #
87
+ # There is no standard for human-readable text associated with the
88
+ # code, and in fact some applications leave out the human-readable
89
+ # element altogether. The text is typically shown below the barcode
90
+ # where applicable.
91
+
92
+ class Code93 < Barcode1D
93
+
94
+ # Character sequence - 0-based offset in this string is character
95
+ # number
96
+ CHAR_SEQUENCE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%\x80\x81\x82\x83"
97
+
98
+ # Patterns for making bar codes
99
+ PATTERNS = {
100
+ '0'=> {'position' => 0, 'display' => '0', 'rle' => '131112', 'bars' => '100010100'},
101
+ '1'=> {'position' => 1, 'display' => '1', 'rle' => '111213', 'bars' => '101001000'},
102
+ '2'=> {'position' => 2, 'display' => '2', 'rle' => '111312', 'bars' => '101000100'},
103
+ '3'=> {'position' => 3, 'display' => '3', 'rle' => '111411', 'bars' => '101000010'},
104
+ '4'=> {'position' => 4, 'display' => '4', 'rle' => '121113', 'bars' => '100101000'},
105
+ '5'=> {'position' => 5, 'display' => '5', 'rle' => '121212', 'bars' => '100100100'},
106
+ '6'=> {'position' => 6, 'display' => '6', 'rle' => '121311', 'bars' => '100100010'},
107
+ '7'=> {'position' => 7, 'display' => '7', 'rle' => '111114', 'bars' => '101010000'},
108
+ '8'=> {'position' => 8, 'display' => '8', 'rle' => '131211', 'bars' => '100010010'},
109
+ '9'=> {'position' => 9, 'display' => '9', 'rle' => '141111', 'bars' => '100001010'},
110
+ 'A' => {'position' => 10, 'display' => 'A', 'rle' => '211113', 'bars' => '110101000'},
111
+ 'B' => {'position' => 11, 'display' => 'B', 'rle' => '211212', 'bars' => '110100100'},
112
+ 'C' => {'position' => 12, 'display' => 'C', 'rle' => '211311', 'bars' => '110100010'},
113
+ 'D' => {'position' => 13, 'display' => 'D', 'rle' => '221112', 'bars' => '110010100'},
114
+ 'E' => {'position' => 14, 'display' => 'E', 'rle' => '221211', 'bars' => '110010010'},
115
+ 'F' => {'position' => 15, 'display' => 'F', 'rle' => '231111', 'bars' => '110001010'},
116
+ 'G' => {'position' => 16, 'display' => 'G', 'rle' => '112113', 'bars' => '101101000'},
117
+ 'H' => {'position' => 17, 'display' => 'H', 'rle' => '112212', 'bars' => '101100100'},
118
+ 'I' => {'position' => 18, 'display' => 'I', 'rle' => '112311', 'bars' => '101100010'},
119
+ 'J' => {'position' => 19, 'display' => 'J', 'rle' => '122112', 'bars' => '100110100'},
120
+ 'K' => {'position' => 20, 'display' => 'K', 'rle' => '132111', 'bars' => '100011010'},
121
+ 'L' => {'position' => 21, 'display' => 'L', 'rle' => '111123', 'bars' => '101011000'},
122
+ 'M' => {'position' => 22, 'display' => 'M', 'rle' => '111222', 'bars' => '101001100'},
123
+ 'N' => {'position' => 23, 'display' => 'N', 'rle' => '111321', 'bars' => '101000110'},
124
+ 'O' => {'position' => 24, 'display' => 'O', 'rle' => '121122', 'bars' => '100101100'},
125
+ 'P' => {'position' => 25, 'display' => 'P', 'rle' => '131121', 'bars' => '100010110'},
126
+ 'Q' => {'position' => 26, 'display' => 'Q', 'rle' => '212112', 'bars' => '110110100'},
127
+ 'R' => {'position' => 27, 'display' => 'R', 'rle' => '212211', 'bars' => '110110010'},
128
+ 'S' => {'position' => 28, 'display' => 'S', 'rle' => '211122', 'bars' => '110101100'},
129
+ 'T' => {'position' => 29, 'display' => 'T', 'rle' => '211221', 'bars' => '110100110'},
130
+ 'U' => {'position' => 30, 'display' => 'U', 'rle' => '221121', 'bars' => '110010110'},
131
+ 'V' => {'position' => 31, 'display' => 'V', 'rle' => '222111', 'bars' => '110011010'},
132
+ 'W' => {'position' => 32, 'display' => 'W', 'rle' => '112122', 'bars' => '101101100'},
133
+ 'X' => {'position' => 33, 'display' => 'X', 'rle' => '112221', 'bars' => '101100110'},
134
+ 'Y' => {'position' => 34, 'display' => 'Y', 'rle' => '122121', 'bars' => '100110110'},
135
+ 'Z' => {'position' => 35, 'display' => 'Z', 'rle' => '123111', 'bars' => '100111010'},
136
+ '-' => {'position' => 36, 'display' => '-', 'rle' => '121131', 'bars' => '100101110'},
137
+ '.' => {'position' => 37, 'display' => '.', 'rle' => '311112', 'bars' => '111010100'},
138
+ ' ' => {'position' => 38, 'display' => ' ', 'rle' => '311211', 'bars' => '111010010'},
139
+ '$' => {'position' => 39, 'display' => '$', 'rle' => '321111', 'bars' => '111001010'},
140
+ '/' => {'position' => 40, 'display' => '/', 'rle' => '112131', 'bars' => '101101110'},
141
+ '+' => {'position' => 41, 'display' => '+', 'rle' => '113121', 'bars' => '101110110'},
142
+ '%' => {'position' => 42, 'display' => '%', 'rle' => '211131', 'bars' => '110101110'},
143
+ "\x80" => {'position' => 43, 'display' => '($)', 'rle' => '121221', 'bars' => '100100110'},
144
+ "\x81" => {'position' => 44, 'display' => '(%)', 'rle' => '312111', 'bars' => '111011010'},
145
+ "\x82" => {'position' => 45, 'display' => '(/)', 'rle' => '311121', 'bars' => '111010110'},
146
+ "\x83" => {'position' => 46, 'display' => '(+)', 'rle' => '122211', 'bars' => '100110010'},
147
+ }
148
+
149
+ # Note that the right side has another thin line
150
+ LEFT_GUARD_PATTERN = '101011110'
151
+ LEFT_GUARD_PATTERN_RLE = '111141'
152
+ RIGHT_GUARD_PATTERN = LEFT_GUARD_PATTERN + '1'
153
+ RIGHT_GUARD_PATTERN_RLE = LEFT_GUARD_PATTERN_RLE + '1'
154
+
155
+ FULL_ASCII_LOOKUP = [
156
+ "\x81U", "\x80A", "\x80B", "\x80C", "\x80D", "\x80E", "\x80F", "\x80G", "\x80H", "\x80I",
157
+ "\x80J", "\x80K", "\x80L", "\x80M", "\x80N", "\x80O", "\x80P", "\x80Q", "\x80R", "\x80S",
158
+ "\x80T", "\x80U", "\x80V", "\x80W", "\x80X", "\x80Y", "\x80Z", "\x81A", "\x81B", "\x81C",
159
+ "\x81D", "\x81E", " ", "\x82A", "\x82B", "\x82C", "\x82D", "\x82E", "\x82F", "\x82G",
160
+ "\x82H", "\x82I", "\x82J", "\x82K", "\x82L", "-", ".", "\x82O", "0", "1", "2",
161
+ "3", "4", "5", "6", "7", "8", "9", "\x82Z", "\x81F", "\x81G", "\x81H",
162
+ "\x81I", "\x81J", "\x81V", "A", "B", "C", "D", "E", "F", "G", "H",
163
+ "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
164
+ "U", "V", "W", "X", "Y", "Z", "\x81K", "\x81L", "\x81M", "\x81N", "\x81O",
165
+ "\x81W", "\x83A", "\x83B", "\x83C", "\x83D", "\x83E", "\x83F", "\x83G", "\x83H", "\x83I",
166
+ "\x83J", "\x83K", "\x83L", "\x83M", "\x83N", "\x83O", "\x83P", "\x83Q", "\x83R", "\x83S",
167
+ "\x83T", "\x83U", "\x83V", "\x83W", "\x83X", "\x83Y", "\x83Z", "\x81P", "\x81Q", "\x81R",
168
+ "\x81S", "\x81T"
169
+ ]
170
+
171
+ FULL_ASCII_REVERSE_LOOKUP = {
172
+ "\x81U" => { 'position' => 0, 'name' => '<NUL>' },
173
+ "\x80A" => { 'position' => 1, 'name' => '<SOH>' },
174
+ "\x80B" => { 'position' => 2, 'name' => '<STX>' },
175
+ "\x80C" => { 'position' => 3, 'name' => '<ETX>' },
176
+ "\x80D" => { 'position' => 4, 'name' => '<EOT>' },
177
+ "\x80E" => { 'position' => 5, 'name' => '<ENQ>' },
178
+ "\x80F" => { 'position' => 6, 'name' => '<ACK>' },
179
+ "\x80G" => { 'position' => 7, 'name' => '<BEL>' },
180
+ "\x80H" => { 'position' => 8, 'name' => '<BS>' },
181
+ "\x80I" => { 'position' => 9, 'name' => '<HT>' },
182
+ "\x80J" => { 'position' => 10, 'name' => '<LF>' },
183
+ "\x80K" => { 'position' => 11, 'name' => '<VT>' },
184
+ "\x80L" => { 'position' => 12, 'name' => '<FF>' },
185
+ "\x80M" => { 'position' => 13, 'name' => '<CR>' },
186
+ "\x80N" => { 'position' => 14, 'name' => '<SO>' },
187
+ "\x80O" => { 'position' => 15, 'name' => '<SI>' },
188
+ "\x80P" => { 'position' => 16, 'name' => '<DLE>' },
189
+ "\x80Q" => { 'position' => 17, 'name' => '<DC1>' },
190
+ "\x80R" => { 'position' => 18, 'name' => '<DC2>' },
191
+ "\x80S" => { 'position' => 19, 'name' => '<DC3>' },
192
+ "\x80T" => { 'position' => 20, 'name' => '<DC4>' },
193
+ "\x80U" => { 'position' => 21, 'name' => '<NAK>' },
194
+ "\x80V" => { 'position' => 22, 'name' => '<SYN>' },
195
+ "\x80W" => { 'position' => 23, 'name' => '<ETB>' },
196
+ "\x80X" => { 'position' => 24, 'name' => '<CAN>' },
197
+ "\x80Y" => { 'position' => 25, 'name' => '<EM>' },
198
+ "\x80Z" => { 'position' => 26, 'name' => '<SUB>' },
199
+ "\x81A" => { 'position' => 27, 'name' => '<ESC>' },
200
+ "\x81B" => { 'position' => 28, 'name' => '<FS>' },
201
+ "\x81C" => { 'position' => 29, 'name' => '<GS>' },
202
+ "\x81D" => { 'position' => 30, 'name' => '<RS>' },
203
+ "\x81E" => { 'position' => 31, 'name' => '<US>' },
204
+ " " => { 'position' => 32, 'name' => ' ' },
205
+ "\x82A" => { 'position' => 33, 'name' => '!' },
206
+ "\x82B" => { 'position' => 34, 'name' => '"' },
207
+ "\x82C" => { 'position' => 35, 'name' => '#' },
208
+ "\x82D" => { 'position' => 36, 'name' => '$' },
209
+ "\x82E" => { 'position' => 37, 'name' => '%' },
210
+ "\x82F" => { 'position' => 38, 'name' => '&' },
211
+ "\x82G" => { 'position' => 39, 'name' => "'" },
212
+ "\x82H" => { 'position' => 40, 'name' => '(' },
213
+ "\x82I" => { 'position' => 41, 'name' => ')' },
214
+ "\x82J" => { 'position' => 42, 'name' => '*' },
215
+ "\x82K" => { 'position' => 43, 'name' => '+' },
216
+ "\x82L" => { 'position' => 44, 'name' => ',' },
217
+ "-" => { 'position' => 45, 'name' => '-' },
218
+ "." => { 'position' => 46, 'name' => '.' },
219
+ "\x82O" => { 'position' => 47, 'name' => '/' },
220
+ "0" => { 'position' => 48, 'name' => '0' },
221
+ "1" => { 'position' => 49, 'name' => '1' },
222
+ "2" => { 'position' => 50, 'name' => '2' },
223
+ "3" => { 'position' => 51, 'name' => '3' },
224
+ "4" => { 'position' => 52, 'name' => '4' },
225
+ "5" => { 'position' => 53, 'name' => '5' },
226
+ "6" => { 'position' => 54, 'name' => '6' },
227
+ "7" => { 'position' => 55, 'name' => '7' },
228
+ "8" => { 'position' => 56, 'name' => '8' },
229
+ "9" => { 'position' => 57, 'name' => '9' },
230
+ "\x82Z" => { 'position' => 58, 'name' => ':' },
231
+ "\x81F" => { 'position' => 59, 'name' => ';' },
232
+ "\x81G" => { 'position' => 60, 'name' => '<' },
233
+ "\x81H" => { 'position' => 61, 'name' => '=' },
234
+ "\x81I" => { 'position' => 62, 'name' => '>' },
235
+ "\x81J" => { 'position' => 63, 'name' => '?' },
236
+ "\x81V" => { 'position' => 64, 'name' => '@' },
237
+ "A" => { 'position' => 65, 'name' => 'A' },
238
+ "B" => { 'position' => 66, 'name' => 'B' },
239
+ "C" => { 'position' => 67, 'name' => 'C' },
240
+ "D" => { 'position' => 68, 'name' => 'D' },
241
+ "E" => { 'position' => 69, 'name' => 'E' },
242
+ "F" => { 'position' => 70, 'name' => 'F' },
243
+ "G" => { 'position' => 71, 'name' => 'G' },
244
+ "H" => { 'position' => 72, 'name' => 'H' },
245
+ "I" => { 'position' => 73, 'name' => 'I' },
246
+ "J" => { 'position' => 74, 'name' => 'J' },
247
+ "K" => { 'position' => 75, 'name' => 'K' },
248
+ "L" => { 'position' => 76, 'name' => 'L' },
249
+ "M" => { 'position' => 77, 'name' => 'M' },
250
+ "N" => { 'position' => 78, 'name' => 'N' },
251
+ "O" => { 'position' => 79, 'name' => 'O' },
252
+ "P" => { 'position' => 80, 'name' => 'P' },
253
+ "Q" => { 'position' => 81, 'name' => 'Q' },
254
+ "R" => { 'position' => 82, 'name' => 'R' },
255
+ "S" => { 'position' => 83, 'name' => 'S' },
256
+ "T" => { 'position' => 84, 'name' => 'T' },
257
+ "U" => { 'position' => 85, 'name' => 'U' },
258
+ "V" => { 'position' => 86, 'name' => 'V' },
259
+ "W" => { 'position' => 87, 'name' => 'W' },
260
+ "X" => { 'position' => 88, 'name' => 'X' },
261
+ "Y" => { 'position' => 89, 'name' => 'Y' },
262
+ "Z" => { 'position' => 90, 'name' => 'Z' },
263
+ "\x81K" => { 'position' => 91, 'name' => '[' },
264
+ "\x81L" => { 'position' => 92, 'name' => '\\' },
265
+ "\x81M" => { 'position' => 93, 'name' => ']' },
266
+ "\x81N" => { 'position' => 94, 'name' => '^' },
267
+ "\x81O" => { 'position' => 95, 'name' => '_' },
268
+ "\x81W" => { 'position' => 96, 'name' => '`' },
269
+ "\x83A" => { 'position' => 97, 'name' => 'a' },
270
+ "\x83B" => { 'position' => 98, 'name' => 'b' },
271
+ "\x83C" => { 'position' => 99, 'name' => 'c' },
272
+ "\x83D" => { 'position' => 100, 'name' => 'd' },
273
+ "\x83E" => { 'position' => 101, 'name' => 'e' },
274
+ "\x83F" => { 'position' => 102, 'name' => 'f' },
275
+ "\x83G" => { 'position' => 103, 'name' => 'g' },
276
+ "\x83H" => { 'position' => 104, 'name' => 'h' },
277
+ "\x83I" => { 'position' => 105, 'name' => 'i' },
278
+ "\x83J" => { 'position' => 106, 'name' => 'j' },
279
+ "\x83K" => { 'position' => 107, 'name' => 'k' },
280
+ "\x83L" => { 'position' => 108, 'name' => 'l' },
281
+ "\x83M" => { 'position' => 109, 'name' => 'm' },
282
+ "\x83N" => { 'position' => 110, 'name' => 'n' },
283
+ "\x83O" => { 'position' => 111, 'name' => 'o' },
284
+ "\x83P" => { 'position' => 112, 'name' => 'p' },
285
+ "\x83Q" => { 'position' => 113, 'name' => 'q' },
286
+ "\x83R" => { 'position' => 114, 'name' => 'r' },
287
+ "\x83S" => { 'position' => 115, 'name' => 's' },
288
+ "\x83T" => { 'position' => 116, 'name' => 't' },
289
+ "\x83U" => { 'position' => 117, 'name' => 'u' },
290
+ "\x83V" => { 'position' => 118, 'name' => 'v' },
291
+ "\x83W" => { 'position' => 119, 'name' => 'w' },
292
+ "\x83X" => { 'position' => 120, 'name' => 'x' },
293
+ "\x83Y" => { 'position' => 121, 'name' => 'y' },
294
+ "\x83Z" => { 'position' => 122, 'name' => 'z' },
295
+ "\x81P" => { 'position' => 123, 'name' => '{' },
296
+ "\x81Q" => { 'position' => 124, 'name' => '|' },
297
+ "\x81R" => { 'position' => 125, 'name' => '}' },
298
+ "\x81S" => { 'position' => 126, 'name' => '~' },
299
+ "\x81T" => { 'position' => 127, 'name' => '<DEL>' },
300
+ "\x81X" => { 'position' => 127, 'name' => '<DEL>' },
301
+ "\x81Y" => { 'position' => 127, 'name' => '<DEL>' },
302
+ "\x81Z" => { 'position' => 127, 'name' => '<DEL>' }
303
+ }
304
+
305
+ DEFAULT_OPTIONS = {
306
+ :line_character => '1',
307
+ :space_character => '0',
308
+ :force_full_ascii => false
309
+ }
310
+
311
+ attr_accessor :full_ascii
312
+ attr_accessor :full_ascii_value
313
+
314
+ class << self
315
+ # Code 93 can technically encode anything 0-127
316
+ def can_encode?(value)
317
+ value.to_s =~ /\A[\x00-\x7f]*\z/
318
+ end
319
+
320
+ def requires_full_ascii?(value)
321
+ value.to_s !~ /\A[0-9A-Z\-\. \$\/\+%]*\z/
322
+ end
323
+
324
+ # Generates check digit given a string to encode. It assumes there
325
+ # is no check digit on the "value". The check "digit" is
326
+ # actually two characters, and the "position" value in PATTERNS can
327
+ # be used to find the numeric value. Note that the string must
328
+ # already be promoted to full ascii before sending it here.
329
+ def generate_check_digit_for(value)
330
+ mult = 0
331
+ sum_c = value.to_s.reverse.split('').inject(0) { |a,c| mult = (mult == 20 ? 1 : mult + 1); a + mult * PATTERNS[c]['position'] }
332
+ check_c = CHAR_SEQUENCE[sum_c % 47,1]
333
+ mult = 0
334
+ sum_k = (value.to_s + check_c).reverse.split('').inject(0) { |a,c| mult = (mult == 15 ? 1 : mult + 1); a + mult * PATTERNS[c]['position'] }
335
+ check_k = CHAR_SEQUENCE[sum_k % 47,1]
336
+ "#{check_c}#{check_k}"
337
+ end
338
+
339
+ # validates the check digit given a string - assumes check digit
340
+ # is last digit of string.
341
+ def validate_check_digit_for(value)
342
+ md = value.to_s.match(/^(.*)(..)$/)
343
+ self.generate_check_digit_for(md[1]) == md[2]
344
+ end
345
+
346
+ # Decode a string in wn format. This will return a Code93
347
+ # object.
348
+ def decode(str, options = {})
349
+ if str =~ /[^1-4]/
350
+ raise UnencodableCharactersError, "Pattern must be rle"
351
+ end
352
+
353
+ if str.reverse =~ /^#{LEFT_GUARD_PATTERN_RLE}.*?#{RIGHT_GUARD_PATTERN_RLE}$/
354
+ str.reverse!
355
+ end
356
+
357
+ unless str =~ /^#{LEFT_GUARD_PATTERN_RLE}(.*?)#{RIGHT_GUARD_PATTERN_RLE}$/
358
+ raise UnencodableCharactersError, "Start/stop pattern is not detected."
359
+ end
360
+
361
+ rle_pattern = $1
362
+
363
+ unless rle_pattern.size % 6 == 0
364
+ raise UnencodableCharactersError, "Wrong number of bars."
365
+ end
366
+
367
+ decoded_string = ''
368
+
369
+ rle_pattern.scan(/.{6}/).each do |chunk|
370
+
371
+ found = false
372
+
373
+ PATTERNS.each do |char,hsh|
374
+ if chunk == hsh['rle']
375
+ decoded_string += char
376
+ found = true
377
+ break;
378
+ end
379
+ end
380
+
381
+ raise UndecodableCharactersError, "Invalid sequence: #{chunk}" unless found
382
+
383
+ end
384
+
385
+ # assume the last two characters are a checksum
386
+ raise ChecksumError unless self.validate_check_digit_for(decoded_string)
387
+
388
+ md = decoded_string.match(/\A(.*?)(..)\z/)
389
+ payload, checksum = md[1], md[2]
390
+
391
+ decoded_string = decode_full_ascii(payload)
392
+
393
+ Code93.new(decoded_string, options.merge(:checksum_included => false))
394
+ end
395
+
396
+ # Provide encoding into the "full ascii" format. This
397
+ # allows us to encode any ascii character (0-127) in a
398
+ # Code 93.
399
+ def encode_full_ascii(str)
400
+ str.bytes.collect { |c| FULL_ASCII_LOOKUP[c] }.join
401
+ end
402
+
403
+ # Decodes a "full ascii" string from Code 3 of 9 into standard
404
+ # ascii. Note that this will silently fail if a string is
405
+ # malformed.
406
+ def decode_full_ascii(str)
407
+ if str =~ /[\x80-\x83]/
408
+ str.scan(/[\x80-\x83]?[A-Z0-9 \.\-]/).collect { |c| FULL_ASCII_REVERSE_LOOKUP[c]['position'] }.pack('C*')
409
+ else
410
+ str
411
+ end
412
+ end
413
+ end
414
+
415
+ # Options are :line_character, :space_character, :force_full_ascii,
416
+ # and :checksum_included.
417
+ def initialize(value, options = {})
418
+
419
+ @options = DEFAULT_OPTIONS.merge(options)
420
+
421
+ # Can we encode this value?
422
+ raise UnencodableCharactersError unless self.class.can_encode?(value)
423
+
424
+ value = value.to_s
425
+
426
+ if @options[:checksum_included]
427
+ raise ChecksumError unless self.class.validate_check_digit_for(value)
428
+ @encoded_string = value
429
+ md = value.match(/\A(.*?)(..)\z/)
430
+ @value, @check_digit = md[1], md[2]
431
+ else
432
+ @value = value
433
+ if options[:force_full_ascii] || self.class.requires_full_ascii?(value)
434
+ @full_ascii_value = self.class.encode_full_ascii(value)
435
+ @full_ascii = true
436
+ @check_digit = self.class.generate_check_digit_for(@full_ascii_value)
437
+ @encoded_string = "#{@full_ascii_value}#{@check_digit}"
438
+ else
439
+ @full_ascii = false
440
+ @full_ascii_value = @value
441
+ @check_digit = self.class.generate_check_digit_for(@value)
442
+ @encoded_string = "#{@value}#{@check_digit}"
443
+ end
444
+ end
445
+ end
446
+
447
+ # Returns a string of "w" or "n" ("wide" and "narrow")
448
+ def wn
449
+ raise NotImplementedError
450
+ end
451
+
452
+ # returns a run-length-encoded string representation
453
+ def rle
454
+ @rle ||= gen_rle(@encoded_string)
455
+ end
456
+
457
+ # returns 1s and 0s (for "black" and "white")
458
+ def bars
459
+ @bars ||= self.class.rle_to_bars(self.rle, @options)
460
+ end
461
+
462
+ # returns the total unit width of the bar code
463
+ def width
464
+ @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
465
+ end
466
+
467
+ private
468
+
469
+ # Creates the actual w/n pattern. Note that there is a narrow space
470
+ # between each character.
471
+ def gen_rle(str)
472
+ @rle_str ||=
473
+ ([LEFT_GUARD_PATTERN_RLE] +
474
+ str.split('').collect { |c| PATTERNS[c]['rle'] } +
475
+ [RIGHT_GUARD_PATTERN_RLE]).join
476
+ end
477
+
478
+ end
479
+ end
@@ -139,5 +139,20 @@ module Barcode1DTools
139
139
  end
140
140
  end
141
141
 
142
+ # Returns a UPC_E object with the same value
143
+ def to_upc_e
144
+ UPC_E.new(UPC_E.upca_value_to_upce_value(@value), options.merge(:checksum_included => false))
145
+ end
146
+
147
+ # Returns true if the current value may be encoded as UPC-E
148
+ def upc_e_encodable?
149
+ begin
150
+ UPC_E.new(UPC_E.upca_value_to_upce_value(@value), options.merge(:checksum_included => false))
151
+ return true
152
+ rescue UnencodableCharactersError
153
+ return false
154
+ end
155
+ end
156
+
142
157
  end
143
158
  end
@@ -264,6 +264,11 @@ module Barcode1DTools
264
264
  @width ||= rle.split('').inject(0) { |a,c| a + c.to_i }
265
265
  end
266
266
 
267
+ # Returns a UPC_A object with the same value
268
+ def to_upc_a
269
+ UPC_A.new(self.class.upce_value_to_upca_value(@value), options.merge(:checksum_included => false))
270
+ end
271
+
267
272
  private
268
273
 
269
274
  def gen_rle(payload, parity_digit)
@@ -21,6 +21,28 @@ class Barcode1DToolsCode3of9Test < Test::Unit::TestCase
21
21
  (0..x-1).inject('') { |a,c| a + Barcode1DTools::Code3of9::CHAR_SEQUENCE[rand(num_chars),1] }
22
22
  end
23
23
 
24
+ def random_x_character_full_ascii_string(x)
25
+ (1..x).collect { rand(128) }.pack('C*')
26
+ end
27
+
28
+ def all_ascii_random_order
29
+ arr = (0..127).to_a
30
+ ret = []
31
+ while arr.size > 0
32
+ ret.push(arr.delete_at(rand(arr.size)))
33
+ end
34
+ ret.pack("C*")
35
+ end
36
+
37
+ def test_full_ascii
38
+ random_string = random_x_character_full_ascii_string(rand(20) + 10)
39
+ encoded1 = Barcode1DTools::Code3of9.encode_full_ascii(random_string)
40
+ assert_equal random_string, Barcode1DTools::Code3of9.decode_full_ascii(encoded1)
41
+ assert_equal "T+H+I+S +I+S +A +T+E+S+T/A", Barcode1DTools::Code3of9.encode_full_ascii("This is a test!")
42
+ big_random_string = all_ascii_random_order
43
+ assert_equal big_random_string, Barcode1DTools::Code3of9.decode_full_ascii(Barcode1DTools::Code3of9.encode_full_ascii(big_random_string))
44
+ end
45
+
24
46
  def test_checksum_generation
25
47
  assert_equal 'I', Barcode1DTools::Code3of9.generate_check_digit_for('THIS IS A TEST')
26
48
  end
@@ -0,0 +1,104 @@
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 Barcode1DToolsCode93Test < 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_x_character_string(x)
20
+ num_chars = Barcode1DTools::Code93::CHAR_SEQUENCE.size - 4
21
+ (0..x-1).inject('') { |a,c| a + Barcode1DTools::Code93::CHAR_SEQUENCE[rand(num_chars),1] }
22
+ end
23
+
24
+ def random_x_character_full_ascii_string(x)
25
+ (1..x).collect { rand(128) }.pack('C*')
26
+ end
27
+
28
+ def all_ascii_random_order
29
+ arr = (0..127).to_a
30
+ ret = []
31
+ while arr.size > 0
32
+ ret.push(arr.delete_at(rand(arr.size)))
33
+ end
34
+ ret.pack("C*")
35
+ end
36
+
37
+ def test_full_ascii
38
+ random_string = random_x_character_full_ascii_string(rand(20) + 10)
39
+ encoded1 = Barcode1DTools::Code93.encode_full_ascii(random_string)
40
+ assert_equal random_string, Barcode1DTools::Code93.decode_full_ascii(encoded1)
41
+ big_random_string = all_ascii_random_order
42
+ assert_equal big_random_string, Barcode1DTools::Code93.decode_full_ascii(Barcode1DTools::Code93.encode_full_ascii(big_random_string))
43
+ end
44
+
45
+ def test_checksum_generation
46
+ assert_equal '6$', Barcode1DTools::Code93.generate_check_digit_for('WIKIPEDIA')
47
+ end
48
+
49
+ def test_checksum_validation
50
+ assert Barcode1DTools::Code93.validate_check_digit_for('WIKIPEDIA6$')
51
+ end
52
+
53
+ def test_attr_readers
54
+ c3of9 = Barcode1DTools::Code93.new('WIKIPEDIA')
55
+ assert_equal '6$', c3of9.check_digit
56
+ assert_equal 'WIKIPEDIA', c3of9.value
57
+ assert_equal 'WIKIPEDIA6$', c3of9.encoded_string
58
+ end
59
+
60
+ def test_checksum_error
61
+ # proper checksum is 6$
62
+ assert_raise(Barcode1DTools::ChecksumError) { Barcode1DTools::Code93.new('WIKIPEDIA$$', :checksum_included => true) }
63
+ end
64
+
65
+ # Test promotion to full ascii
66
+ def test_promotion_to_full_ascii
67
+ assert Barcode1DTools::Code93.requires_full_ascii?('This is a test')
68
+ assert !Barcode1DTools::Code93.requires_full_ascii?('THIS IS A TEST')
69
+ assert Barcode1DTools::Code93.new('This is a test').full_ascii
70
+ assert !Barcode1DTools::Code93.new('THIS IS A TEST').full_ascii
71
+ end
72
+
73
+ # Test force promotion to full ascii
74
+ def test_force_promotion_to_full_ascii
75
+ bc = Barcode1DTools::Code93.new('THIS IS A TEST!', :force_full_ascii => true)
76
+ assert bc.full_ascii
77
+ assert_equal 'THIS IS A TEST' + Barcode1DTools::Code93::FULL_ASCII_LOOKUP['!'.bytes.first], bc.full_ascii_value
78
+ end
79
+
80
+ # Only need to test rle
81
+ def test_barcode_generation
82
+ c3of9 = Barcode1DTools::Code93.new('WIKIPEDIA')
83
+ assert_equal "1111411121221123111321111123111311212212112211121123112111131213113211111111411", c3of9.rle
84
+ end
85
+
86
+ def test_decode_error
87
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code93.decode('x') }
88
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code93.decode('x'*60) }
89
+ # proper start & stop, but crap in middle
90
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code93.decode(Barcode1DTools::Code93::LEFT_GUARD_PATTERN_RLE + '11111' + Barcode1DTools::Code93::RIGHT_GUARD_PATTERN_RLE) }
91
+ # wrong start/stop
92
+ assert_raise(Barcode1DTools::UnencodableCharactersError) { Barcode1DTools::Code93.decode('22222222222222222') }
93
+ end
94
+
95
+ def test_decoding
96
+ random_c3of9_str=random_x_character_string(rand(10)+5)
97
+ c3of9 = Barcode1DTools::Code93.new(random_c3of9_str)
98
+ c3of92 = Barcode1DTools::Code93.decode(c3of9.rle)
99
+ assert_equal c3of9.value, c3of92.value
100
+ # Should also work in reverse
101
+ c3of94 = Barcode1DTools::Code93.decode(c3of9.rle.reverse)
102
+ assert_equal c3of9.value, c3of94.value
103
+ end
104
+ end
@@ -99,4 +99,13 @@ class Barcode1DToolsUPC_ATest < Test::Unit::TestCase
99
99
  ean4 = Barcode1DTools::UPC_A.decode(ean.bars.reverse)
100
100
  assert_equal ean.value, ean4.value
101
101
  end
102
+
103
+ def test_upc_e_conversion
104
+ upc_e = Barcode1DTools::UPC_E.new('0384754')
105
+ upc_a = upc_e.to_upc_a
106
+ assert_equal upc_e.value, upc_a.to_upc_e.value
107
+ assert upc_a.upc_e_encodable?
108
+ upc_a_2 = Barcode1DTools::UPC_A.new('012676510226', :checksum_included => true)
109
+ assert !upc_a_2.upc_e_encodable?
110
+ end
102
111
  end
@@ -124,4 +124,10 @@ class Barcode1DToolsUPC_ETest < Test::Unit::TestCase
124
124
  upce4 = Barcode1DTools::UPC_E.decode(upce.bars.reverse)
125
125
  assert_equal upce.value, upce4.value
126
126
  end
127
+
128
+ def test_upc_a_conversion
129
+ upc_e = Barcode1DTools::UPC_E.new('0384754')
130
+ upc_a = upc_e.to_upc_a
131
+ assert_equal upc_e.value, upc_a.to_upc_e.value
132
+ end
127
133
  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: 11
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 3
10
- - 0
11
- version: 0.9.3.0
9
+ - 4
10
+ - 1
11
+ version: 0.9.4.1
12
12
  platform: ruby
13
13
  authors:
14
14
  - Michael Chaney
@@ -20,7 +20,14 @@ date: 2012-08-05 00:00:00 -05:00
20
20
  default_executable:
21
21
  dependencies: []
22
22
 
23
- description: " Barcode1D is a small library for handling many kinds of\n 1-dimensional barcodes. Currently implemented are Code 3 of 9,\n Interleaved 2 of 5, EAN-13, EAN-8, UPC-A, UPC-E, UPC Supplemental 2,\n and UPC Supplemental 5. Patterns are created in either a simple\n format of bars and spaces or as a run-length encoded string. This\n only generates and decodes the patterns; actual display or reading\n from a device must be implemented by the programmer. More\n symbologies will be added as time permits.\n"
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, Interleaved 2 of 5, EAN-13, EAN-8, UPC-A, UPC-E, UPC\n\
26
+ \t Supplemental 2, and UPC Supplemental 5. Patterns are created in\n\
27
+ \t either a simple format of bars and spaces or as a run-length encoded\n\
28
+ \t string. This only generates and decodes the patterns; actual\n\
29
+ \t display or reading from a device must be implemented by the\n\
30
+ \t programmer. More symbologies will be added as time permits.\n"
24
31
  email: mdchaney@michaelchaney.com
25
32
  executables: []
26
33
 
@@ -30,6 +37,7 @@ extra_rdoc_files: []
30
37
 
31
38
  files:
32
39
  - lib/barcode1dtools/code3of9.rb
40
+ - lib/barcode1dtools/code93.rb
33
41
  - lib/barcode1dtools/ean13.rb
34
42
  - lib/barcode1dtools/ean8.rb
35
43
  - lib/barcode1dtools/interleaved2of5.rb
@@ -41,6 +49,7 @@ files:
41
49
  - MIT-LICENSE
42
50
  - test/test_barcode1d.rb
43
51
  - test/test_barcode1dcode3of9.rb
52
+ - test/test_barcode1dcode93.rb
44
53
  - test/test_barcode1dean13.rb
45
54
  - test/test_barcode1dean8.rb
46
55
  - test/test_barcode1di2of5.rb
@@ -88,6 +97,7 @@ summary: Pattern generators for 1D barcodes
88
97
  test_files:
89
98
  - test/test_barcode1d.rb
90
99
  - test/test_barcode1dcode3of9.rb
100
+ - test/test_barcode1dcode93.rb
91
101
  - test/test_barcode1dean13.rb
92
102
  - test/test_barcode1dean8.rb
93
103
  - test/test_barcode1di2of5.rb