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.
- 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
|