barcode1dtools 0.9.3.0 → 0.9.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/barcode1dtools.rb +6 -6
- data/lib/barcode1dtools/code3of9.rb +177 -0
- data/lib/barcode1dtools/code93.rb +479 -0
- data/lib/barcode1dtools/upc_a.rb +15 -0
- data/lib/barcode1dtools/upc_e.rb +5 -0
- data/test/test_barcode1dcode3of9.rb +22 -0
- data/test/test_barcode1dcode93.rb +104 -0
- data/test/test_barcode1dupca.rb +9 -0
- data/test/test_barcode1dupce.rb +6 -0
- metadata +15 -5
data/lib/barcode1dtools.rb
CHANGED
@@ -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
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
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
|
data/lib/barcode1dtools/upc_a.rb
CHANGED
@@ -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
|
data/lib/barcode1dtools/upc_e.rb
CHANGED
@@ -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
|
data/test/test_barcode1dupca.rb
CHANGED
@@ -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
|
data/test/test_barcode1dupce.rb
CHANGED
@@ -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:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
|
-
-
|
10
|
-
-
|
11
|
-
version: 0.9.
|
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: "
|
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
|