barby 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,29 @@
1
+ Barby is a Ruby barcode generator. It does not depend on other libraries
2
+ (for the core functionality) and is easily extentable.
3
+
4
+ The barcode objects are separated from the process of generating graphical
5
+ (or other) representations. A barcode's only responsibility is to provide
6
+ a string representation consisting of 1s and 0s representing bars and spaces.
7
+ This string can then be used to generate (for example) an image with the
8
+ RMagickOutputter, or an ASCII string such as the one below.
9
+
10
+ See Barby::Barcode and Barby::Outputter for more information.
11
+
12
+ require 'barby'
13
+ require 'barby/outputter/ascii_outputter'
14
+
15
+ barcode = Barby::Code128B.new('BARBY')
16
+
17
+ puts barcode.to_ascii
18
+
19
+ ## # # # # ## # # ## ## # ### # # ## ### ## # ## ### ### ## ### # ##
20
+ ## # # # # ## # # ## ## # ### # # ## ### ## # ## ### ### ## ### # ##
21
+ ## # # # # ## # # ## ## # ### # # ## ### ## # ## ### ### ## ### # ##
22
+ ## # # # # ## # # ## ## # ### # # ## ### ## # ## ### ### ## ### # ##
23
+ ## # # # # ## # # ## ## # ### # # ## ### ## # ## ### ### ## ### # ##
24
+ ## # # # # ## # # ## ## # ### # # ## ### ## # ## ### ### ## ### # ##
25
+ ## # # # # ## # # ## ## # ### # # ## ### ## # ## ### ### ## ### # ##
26
+ ## # # # # ## # # ## ## # ### # # ## ### ## # ## ### ### ## ### # ##
27
+ ## # # # # ## # # ## ## # ### # # ## ### ## # ## ### ### ## ### # ##
28
+ ## # # # # ## # # ## ## # ### # # ## ### ## # ## ### ### ## ### # ##
29
+ B A R B Y
@@ -0,0 +1,11 @@
1
+ require 'barby/version'
2
+
3
+ require 'barby/barcode'
4
+ require 'barby/barcode/code_128'
5
+ require 'barby/barcode/gs1_128'
6
+ require 'barby/barcode/code_39'
7
+ require 'barby/barcode/ean_13'
8
+ require 'barby/barcode/ean_8'
9
+ require 'barby/barcode/bookland'
10
+
11
+ require 'barby/outputter'
@@ -0,0 +1,95 @@
1
+ module Barby
2
+
3
+
4
+ #The base class for all barcodes. It includes some method_missing magic
5
+ #that is used to find registered outputters.
6
+ #
7
+ #The only interface requirement of a barcode class is that is has an encoding
8
+ #method that returns a string consisting of 1s and 0s representing the barcode's
9
+ #"black" and "white" parts. One digit is the width of the "X dimension"; that is,
10
+ #"101100" represents a single-width bar followed by a single-width space, then
11
+ #a bar and a space twice that width.
12
+ #
13
+ #Example implementation:
14
+ #
15
+ # class StaticBarcode < Barby::Barcode1D
16
+ # def encoding
17
+ # '101100111000111100001'
18
+ # end
19
+ # end
20
+ #
21
+ # require 'barby/outputter/ascii_outputter'
22
+ # puts StaticBarcode.new.to_ascii(:height => 3)
23
+ #
24
+ # # ## ### #### #
25
+ # # ## ### #### #
26
+ # # ## ### #### #
27
+ class Barcode
28
+
29
+
30
+ #Every barcode must have an encoding method. This method returns
31
+ #a string containing a series of 1 and 0, representing bars and
32
+ #spaces. One digit is the width of one "module" or X dimension.
33
+ def encoding
34
+ raise NotImplementedError, 'Every barcode should implement this method'
35
+ end
36
+
37
+
38
+ #Is this barcode valid?
39
+ def valid?
40
+ false
41
+ end
42
+
43
+
44
+ def method_missing(name, *args, &b)#:nodoc:
45
+ #See if an outputter has registered this method
46
+ if self.class.outputters.include?(name)
47
+ klass, method_name = self.class.outputters[name]
48
+ klass.new(self).send(method_name, *args, &b)
49
+ else
50
+ super
51
+ end
52
+ end
53
+
54
+
55
+ #Returns an instantiated outputter for +name+ if any outputter
56
+ #has registered that name
57
+ def outputter_for(name, *a, &b)
58
+ outputter_class_for(name).new(self, *a, &b)
59
+ end
60
+
61
+ #Get the outputter class object for +name+
62
+ def outputter_class_for(name)
63
+ self.class.outputters[name].first
64
+ end
65
+
66
+
67
+ class << self
68
+
69
+ def outputters
70
+ @@outputters ||= {}
71
+ end
72
+
73
+ #Registers an outputter with +name+ so that a call to
74
+ #+name+ on a Barcode instance will be delegated to this outputter
75
+ def register_outputter(name, klass, method_name)
76
+ outputters[name] = [klass, method_name]
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+
84
+ #Most barcodes are one-dimensional. They have bars.
85
+ class Barcode1D < Barcode
86
+ end
87
+
88
+ #There is currently only support for one-dimensional barcodes,
89
+ #but in the future it should also be possible to support barcodes
90
+ #with two dimensions.
91
+ class Barcode2D < Barcode
92
+ end
93
+
94
+
95
+ end
@@ -0,0 +1,37 @@
1
+ require 'barby/barcode/ean_13'
2
+
3
+ module Barby
4
+
5
+ #Bookland barcodes are EAN-13 barcodes with number system
6
+ #978 (hence "Bookland"). The data they encode is an ISBN
7
+ #with its check digit removed. This is a convenience class
8
+ #that takes an ISBN no instead of "pure" EAN-13 data.
9
+ class Bookland < EAN13
10
+
11
+ BOOKLAND_NUMBER_SYSTEM = '978'
12
+
13
+ attr_accessor :isbn
14
+
15
+ def initialize(isbn)
16
+ self.isbn = isbn
17
+ raise ArgumentError, 'data not valid' unless valid?
18
+ end
19
+
20
+ def data
21
+ BOOKLAND_NUMBER_SYSTEM+isbn_only
22
+ end
23
+
24
+ #Removes any non-digit characters, number system and check digit
25
+ #from ISBN, so "978-82-92526-14-9" would result in "829252614"
26
+ def isbn_only
27
+ s = isbn.gsub(/[^0-9]/, '')
28
+ if s.size > 10#Includes number system
29
+ s[3,9]
30
+ else#No number system, may include check digit
31
+ s[0,9]
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,361 @@
1
+ require 'barby/barcode'
2
+
3
+ module Barby
4
+
5
+
6
+ #Code 128 barcodes
7
+ class Code128 < Barcode1D
8
+
9
+ ENCODINGS = {
10
+ 0 => "11011001100", 1 => "11001101100", 2 => "11001100110",
11
+ 3 => "10010011000", 4 => "10010001100", 5 => "10001001100",
12
+ 6 => "10011001000", 7 => "10011000100", 8 => "10001100100",
13
+ 9 => "11001001000", 10 => "11001000100", 11 => "11000100100",
14
+ 12 => "10110011100", 13 => "10011011100", 14 => "10011001110",
15
+ 15 => "10111001100", 16 => "10011101100", 17 => "10011100110",
16
+ 18 => "11001110010", 19 => "11001011100", 20 => "11001001110",
17
+ 21 => "11011100100", 22 => "11001110100", 23 => "11101101110",
18
+ 24 => "11101001100", 25 => "11100101100", 26 => "11100100110",
19
+ 27 => "11101100100", 28 => "11100110100", 29 => "11100110010",
20
+ 30 => "11011011000", 31 => "11011000110", 32 => "11000110110",
21
+ 33 => "10100011000", 34 => "10001011000", 35 => "10001000110",
22
+ 36 => "10110001000", 37 => "10001101000", 38 => "10001100010",
23
+ 39 => "11010001000", 40 => "11000101000", 41 => "11000100010",
24
+ 42 => "10110111000", 43 => "10110001110", 44 => "10001101110",
25
+ 45 => "10111011000", 46 => "10111000110", 47 => "10001110110",
26
+ 48 => "11101110110", 49 => "11010001110", 50 => "11000101110",
27
+ 51 => "11011101000", 52 => "11011100010", 53 => "11011101110",
28
+ 54 => "11101011000", 55 => "11101000110", 56 => "11100010110",
29
+ 57 => "11101101000", 58 => "11101100010", 59 => "11100011010",
30
+ 60 => "11101111010", 61 => "11001000010", 62 => "11110001010",
31
+ 63 => "10100110000", 64 => "10100001100", 65 => "10010110000",
32
+ 66 => "10010000110", 67 => "10000101100", 68 => "10000100110",
33
+ 69 => "10110010000", 70 => "10110000100", 71 => "10011010000",
34
+ 72 => "10011000010", 73 => "10000110100", 74 => "10000110010",
35
+ 75 => "11000010010", 76 => "11001010000", 77 => "11110111010",
36
+ 78 => "11000010100", 79 => "10001111010", 80 => "10100111100",
37
+ 81 => "10010111100", 82 => "10010011110", 83 => "10111100100",
38
+ 84 => "10011110100", 85 => "10011110010", 86 => "11110100100",
39
+ 87 => "11110010100", 88 => "11110010010", 89 => "11011011110",
40
+ 90 => "11011110110", 91 => "11110110110", 92 => "10101111000",
41
+ 93 => "10100011110", 94 => "10001011110", 95 => "10111101000",
42
+ 96 => "10111100010", 97 => "11110101000", 98 => "11110100010",
43
+ 99 => "10111011110", 100 => "10111101110", 101 => "11101011110",
44
+ 102 => "11110101110", 103 => "11010000100", 104 => "11010010000",
45
+ 105 => "11010011100"
46
+ }
47
+
48
+ VALUES = {
49
+ 'A' => {
50
+ 0 => "SP", 1 => "!", 2 => "\"",
51
+ 3 => "#", 4 => "$", 5 => "%",
52
+ 6 => "&", 7 => "'", 8 => "(",
53
+ 9 => ")", 10 => "*", 11 => "+",
54
+ 12 => ",", 13 => "-", 14 => ".",
55
+ 15 => "/", 16 => "0", 17 => "1",
56
+ 18 => "2", 19 => "3", 20 => "4",
57
+ 21 => "5", 22 => "6", 23 => "7",
58
+ 24 => "8", 25 => "9", 26 => ":",
59
+ 27 => ";", 28 => "<", 29 => "=",
60
+ 30 => ">", 31 => "?", 32 => "@",
61
+ 33 => "A", 34 => "B", 35 => "C",
62
+ 36 => "D", 37 => "E", 38 => "F",
63
+ 39 => "G", 40 => "H", 41 => "I",
64
+ 42 => "J", 43 => "K", 44 => "L",
65
+ 45 => "M", 46 => "N", 47 => "O",
66
+ 48 => "P", 49 => "Q", 50 => "R",
67
+ 51 => "S", 52 => "T", 53 => "U",
68
+ 54 => "V", 55 => "W", 56 => "X",
69
+ 57 => "Y", 58 => "Z", 59 => "[",
70
+ 60 => "\\", 61 => "]", 62 => "^",
71
+ 63 => "_", 64 => "\000", 65 => "\001",
72
+ 66 => "\002", 67 => "\003", 68 => "\004",
73
+ 69 => "\005", 70 => "\006", 71 => "\a",
74
+ 72 => "\b", 73 => "\t", 74 => "\n",
75
+ 75 => "\v", 76 => "\f", 77 => "\r",
76
+ 78 => "\016", 79 => "\017", 80 => "\020",
77
+ 81 => "\021", 82 => "\022", 83 => "\023",
78
+ 84 => "\024", 85 => "\025", 86 => "\026",
79
+ 87 => "\027", 88 => "\030", 89 => "\031",
80
+ 90 => "\032", 91 => "\e", 92 => "\034",
81
+ 93 => "\035", 94 => "\036", 95 => "\037",
82
+ 96 => "\303", 97 => "\302", 98 => "SHIFT",
83
+ 99 => "\307", 100 => "\306", 101 => "\304",
84
+ 102 => "\301", 103 => "STARTA", 104 => "STARTB",
85
+ 105 => "STARTC"
86
+ }.invert,
87
+
88
+ 'B' => {
89
+ 0 => "SP", 1 => "!", 2 => "\"", 3 => "#", 4 => "$", 5 => "%",
90
+ 6 => "&", 7 => "'", 8 => "(", 9 => ")", 10 => "*", 11 => "+",
91
+ 12 => ",", 13 => "-", 14 => ".", 15 => "/", 16 => "0", 17 => "1",
92
+ 18 => "2", 19 => "3", 20 => "4", 21 => "5", 22 => "6", 23 => "7",
93
+ 24 => "8", 25 => "9", 26 => ":", 27 => ";", 28 => "<", 29 => "=",
94
+ 30 => ">", 31 => "?", 32 => "@", 33 => "A", 34 => "B", 35 => "C",
95
+ 36 => "D", 37 => "E", 38 => "F", 39 => "G", 40 => "H", 41 => "I",
96
+ 42 => "J", 43 => "K", 44 => "L", 45 => "M", 46 => "N", 47 => "O",
97
+ 48 => "P", 49 => "Q", 50 => "R", 51 => "S", 52 => "T", 53 => "U",
98
+ 54 => "V", 55 => "W", 56 => "X", 57 => "Y", 58 => "Z", 59 => "[",
99
+ 60 => "\\", 61 => "]", 62 => "^", 63 => "_", 64 => "`", 65 => "a",
100
+ 66 => "b", 67 => "c", 68 => "d", 69 => "e", 70 => "f", 71 => "g",
101
+ 72 => "h", 73 => "i", 74 => "j", 75 => "k", 76 => "l", 77 => "m",
102
+ 78 => "n", 79 => "o", 80 => "p", 81 => "q", 82 => "r", 83 => "s",
103
+ 84 => "t", 85 => "u", 86 => "v", 87 => "w", 88 => "x", 89 => "y",
104
+ 90 => "z", 91 => "{", 92 => "|", 93 => "}", 94 => "~", 95 => "\177",
105
+ 96 => "\303", 97 => "\302", 98 => "SHIFT", 99 => "\307", 100 => "\304",
106
+ 101 => "\305", 102 => "\301", 103 => "STARTA", 104 => "STARTB",
107
+ 105 => "STARTC",
108
+ }.invert,
109
+
110
+ 'C' => {
111
+ 0 => "00", 1 => "01", 2 => "02", 3 => "03", 4 => "04", 5 => "05",
112
+ 6 => "06", 7 => "07", 8 => "08", 9 => "09", 10 => "10", 11 => "11",
113
+ 12 => "12", 13 => "13", 14 => "14", 15 => "15", 16 => "16", 17 => "17",
114
+ 18 => "18", 19 => "19", 20 => "20", 21 => "21", 22 => "22", 23 => "23",
115
+ 24 => "24", 25 => "25", 26 => "26", 27 => "27", 28 => "28", 29 => "29",
116
+ 30 => "30", 31 => "31", 32 => "32", 33 => "33", 34 => "34", 35 => "35",
117
+ 36 => "36", 37 => "37", 38 => "38", 39 => "39", 40 => "40", 41 => "41",
118
+ 42 => "42", 43 => "43", 44 => "44", 45 => "45", 46 => "46", 47 => "47",
119
+ 48 => "48", 49 => "49", 50 => "50", 51 => "51", 52 => "52", 53 => "53",
120
+ 54 => "54", 55 => "55", 56 => "56", 57 => "57", 58 => "58", 59 => "59",
121
+ 60 => "60", 61 => "61", 62 => "62", 63 => "63", 64 => "64", 65 => "65",
122
+ 66 => "66", 67 => "67", 68 => "68", 69 => "69", 70 => "70", 71 => "71",
123
+ 72 => "72", 73 => "73", 74 => "74", 75 => "75", 76 => "76", 77 => "77",
124
+ 78 => "78", 79 => "79", 80 => "80", 81 => "81", 82 => "82", 83 => "83",
125
+ 84 => "84", 85 => "85", 86 => "86", 87 => "87", 88 => "88", 89 => "89",
126
+ 90 => "90", 91 => "91", 92 => "92", 93 => "93", 94 => "94", 95 => "95",
127
+ 96 => "96", 97 => "97", 98 => "98", 99 => "99", 100 => "\306", 101 => "\305",
128
+ 102 => "\301", 103 => "STARTA", 104 => "STARTB", 105 => "STARTC"
129
+ }.invert
130
+ }
131
+
132
+ FNC1 = "\xc1"
133
+ FNC2 = "\xc2"
134
+ FNC3 = "\xc3"
135
+ FNC4 = "\xc4"
136
+ CODEA = "\xc5"
137
+ CODEB = "\xc6"
138
+ CODEC = "\xc7"
139
+
140
+ STOP = '11000111010'
141
+ TERMINATE = '11'
142
+
143
+ attr_reader :type
144
+
145
+
146
+ def initialize(data, type)
147
+ self.type = type
148
+ self.data = "#{data}"
149
+ raise ArgumentError, 'Data not valid' unless valid?
150
+ end
151
+
152
+
153
+ def type=(type)
154
+ type.upcase!
155
+ raise ArgumentError, 'type must be A, B or C' unless type =~ /^[ABC]$/
156
+ @type = type
157
+ end
158
+
159
+
160
+ def data
161
+ @data
162
+ end
163
+
164
+ #Set the data for this barcode. If the barcode changes
165
+ #character set, an extra will be created.
166
+ def data=(data)
167
+ data, *extra = data.split(/([#{CODEA+CODEB+CODEC}])/n)
168
+ @data = data
169
+ self.extra = extra.join unless extra.empty?
170
+ end
171
+
172
+ #An "extra" is present if the barcode changes character set. If
173
+ #a 128A barcode changes to C, the extra will be an instance of
174
+ #Code128C. Extras can themselves have an extra if the barcode
175
+ #changes character set again. It's like a linked list, and when
176
+ #there are no more extras, the barcode ends with that object.
177
+ #Most barcodes probably don't change charsets and don't have extras.
178
+ def extra
179
+ @extra
180
+ end
181
+
182
+ #Set the extra for this barcode. The argument is a string starting with the
183
+ #"change character set" symbol. The string may contain several character
184
+ #sets, in which case the extra will itself have an extra.
185
+ def extra=(extra)
186
+ raise ArgumentError, "Extra must begin with \\301, \\302 or \\303" unless extra =~ /^[#{CODEA+CODEB+CODEC}]/n
187
+ type = extra[/([#{CODEA+CODEB+CODEC}])/n, 1]
188
+ data = extra[/[#{CODEA+CODEB+CODEC}](.*)/n, 1]
189
+ @extra = class_for(type).new(data)
190
+ end
191
+
192
+ #Get an array of the individual characters for this barcode. Special
193
+ #characters like FNC1 will be present. Characters from extras are not
194
+ #present.
195
+ def characters
196
+ chars = data.split(//n)
197
+
198
+ if type == 'C'
199
+ result = []
200
+ count = 0
201
+ while count < chars.size
202
+ if chars[count] =~ /^\d$/
203
+ result << "#{chars[count]}#{chars[count+1]}"
204
+ count += 2
205
+ else
206
+ result << chars[count]
207
+ count += 1
208
+ end
209
+ end
210
+ result
211
+ else
212
+ chars
213
+ end
214
+ end
215
+
216
+ #Return the encoding of this barcode as a string of 1 and 0
217
+ def encoding
218
+ start_encoding+data_encoding+extra_encoding+checksum_encoding+stop_encoding
219
+ end
220
+
221
+ #Returns the encoding for the data part of this barcode, without any extras
222
+ def data_encoding
223
+ characters.map do |char|
224
+ encoding_for char
225
+ end.join
226
+ end
227
+
228
+ #Returns the data encoding of this barcode and extras.
229
+ def data_encoding_with_extra_encoding
230
+ data_encoding+extra_encoding
231
+ end
232
+
233
+ #Returns the data encoding of this barcode's extra and its
234
+ #extra until the barcode ends.
235
+ def extra_encoding
236
+ return '' unless extra
237
+ change_code_encoding_for(extra) + extra.data_encoding + extra.extra_encoding
238
+ end
239
+
240
+
241
+ #Calculate the checksum for the data in this barcode. The data includes
242
+ #data from extras.
243
+ def checksum
244
+ pos = 0
245
+ (numbers+extra_numbers).inject(start_num) do |sum,number|
246
+ pos += 1
247
+ sum + (number * pos)
248
+ end % 103
249
+ end
250
+
251
+ #Get the encoding for the checksum
252
+ def checksum_encoding
253
+ encodings[checksum]
254
+ end
255
+
256
+
257
+ #protected
258
+
259
+ #Returns the numeric values for the characters in the barcode in an array
260
+ def numbers
261
+ characters.map do |char|
262
+ values[char]
263
+ end
264
+ end
265
+
266
+ #Returns the numeric values for extras
267
+ def extra_numbers
268
+ return [] unless extra
269
+ [change_code_number_for(extra)] + extra.numbers + extra.extra_numbers
270
+ end
271
+
272
+ def encodings
273
+ ENCODINGS
274
+ end
275
+
276
+ #The start encoding starts the barcode
277
+ def stop_encoding
278
+ STOP+TERMINATE
279
+ end
280
+
281
+ #Find the encoding for the specified character for this barcode
282
+ def encoding_for(char)
283
+ encodings[values[char]]
284
+ end
285
+
286
+ #Find the numeric value for the character that changes the character
287
+ #set to the one represented in +barcode+
288
+ def change_code_number_for(barcode)
289
+ case barcode
290
+ when Code128A then values[CODEA]
291
+ when Code128B then values[CODEB]
292
+ when Code128C then values[CODEC]
293
+ end
294
+ end
295
+
296
+ #Find the encoding to change to the character set in +barcode+
297
+ def change_code_encoding_for(barcode)
298
+ encodings[change_code_number_for(barcode)]
299
+ end
300
+
301
+ def class_for(character)
302
+ case character
303
+ when 'A' then Code128A
304
+ when 'B' then Code128B
305
+ when 'C' then Code128C
306
+ when CODEA then Code128A
307
+ when CODEB then Code128B
308
+ when CODEC then Code128C
309
+ end
310
+ end
311
+
312
+ #Is the data in this barcode valid? Does a lookup of every character
313
+ #and checks if it exists in the character set.
314
+ def valid?
315
+ characters.all?{|c| values.include?(c) }
316
+ end
317
+
318
+ def values
319
+ VALUES[type]
320
+ end
321
+
322
+ def start_num
323
+ values["START#{type}"]
324
+ end
325
+
326
+ def start_encoding
327
+ encodings[start_num]
328
+ end
329
+
330
+
331
+ end
332
+
333
+
334
+ class Code128A < Code128
335
+
336
+ def initialize(data)
337
+ super(data, 'A')
338
+ end
339
+
340
+ end
341
+
342
+
343
+ class Code128B < Code128
344
+
345
+ def initialize(data)
346
+ super(data, 'B')
347
+ end
348
+
349
+ end
350
+
351
+
352
+ class Code128C < Code128
353
+
354
+ def initialize(data)
355
+ super(data, 'C')
356
+ end
357
+
358
+ end
359
+
360
+
361
+ end
@@ -0,0 +1,200 @@
1
+ require 'barby/barcode'
2
+
3
+ module Barby
4
+
5
+
6
+ class Code39 < Barcode1D
7
+
8
+ ENCODINGS = {
9
+ '0' => '101001101101', 'M' => '110110101001',
10
+ '1' => '110100101011', 'N' => '101011010011',
11
+ '2' => '101100101011', 'O' => '110101101001',
12
+ '3' => '110110010101', 'P' => '101101101001',
13
+ '4' => '101001101011', 'Q' => '101010110011',
14
+ '5' => '110100110101', 'R' => '110101011001',
15
+ '6' => '101100110101', 'S' => '101101011001',
16
+ '7' => '101001011011', 'T' => '101011011001',
17
+ '8' => '110100101101', 'U' => '110010101011',
18
+ '9' => '101100101101', 'V' => '100110101011',
19
+ 'A' => '110101001011', 'W' => '110011010101',
20
+ 'B' => '101101001011', 'X' => '100101101011',
21
+ 'C' => '110110100101', 'Y' => '110010110101',
22
+ 'D' => '101011001011', 'Z' => '100110110101',
23
+ 'E' => '110101100101', '-' => '100101011011',
24
+ 'F' => '101101100101', '.' => '110010101101',
25
+ 'G' => '101010011011', ' ' => '100110101101',
26
+ 'H' => '110101001101', '$' => '100100100101',
27
+ 'I' => '101101001101', '/' => '100100101001',
28
+ 'J' => '101011001101', '+' => '100101001001',
29
+ 'K' => '110101010011', '%' => '101001001001',
30
+ 'L' => '101101010011'#, '*' => '100101101101'
31
+ }
32
+
33
+ #In extended mode, each character is replaced with two characters from the "normal" encoding
34
+ EXTENDED_ENCODINGS = {
35
+ "\000" => '%U', " " => " ", "@" => "%V", "`" => "%W",
36
+ "\001" => '$A', "!" => "/A", "A" => "A", "a" => "+A",
37
+ "\002" => '$B', '"' => "/B", "B" => "B", "b" => "+B",
38
+ "\003" => '$C', "#" => "/C", "C" => "C", "c" => "+C",
39
+ "\004" => '$D', "$" => "/D", "D" => "D", "d" => "+D",
40
+ "\005" => '$E', "%" => "/E", "E" => "E", "e" => "+E",
41
+ "\006" => '$F', "&" => "/F", "F" => "F", "f" => "+F",
42
+ "\007" => '$G', "'" => "/G", "G" => "G", "g" => "+G",
43
+ "\010" => '$H', "(" => "/H", "H" => "H", "h" => "+H",
44
+ "\011" => '$I', ")" => "/I", "I" => "I", "i" => "+I",
45
+ "\012" => '$J', "*" => "/J", "J" => "J", "j" => "+J",
46
+ "\013" => '$K', "+" => "/K", "K" => "K", "k" => "+K",
47
+ "\014" => '$L', "," => "/L", "L" => "L", "l" => "+L",
48
+ "\015" => '$M', "-" => "-", "M" => "M", "m" => "+M",
49
+ "\016" => '$N', "." => ".", "N" => "N", "n" => "+N",
50
+ "\017" => '$O', "/" => "/O", "O" => "O", "o" => "+O",
51
+ "\020" => '$P', "0" => "0", "P" => "P", "p" => "+P",
52
+ "\021" => '$Q', "1" => "1", "Q" => "Q", "q" => "+Q",
53
+ "\022" => '$R', "2" => "2", "R" => "R", "r" => "+R",
54
+ "\023" => '$S', "3" => "3", "S" => "S", "s" => "+S",
55
+ "\024" => '$T', "4" => "4", "T" => "T", "t" => "+T",
56
+ "\025" => '$U', "5" => "5", "U" => "U", "u" => "+U",
57
+ "\026" => '$V', "6" => "6", "V" => "V", "v" => "+V",
58
+ "\027" => '$W', "7" => "7", "W" => "W", "w" => "+W",
59
+ "\030" => '$X', "8" => "8", "X" => "X", "x" => "+X",
60
+ "\031" => '$Y', "9" => "9", "Y" => "Y", "y" => "+Y",
61
+ "\032" => '$Z', ":" => "/Z", "Z" => "Z", "z" => "+Z",
62
+ "\033" => '%A', ";" => "%F", "[" => "%K", "{" => "%P",
63
+ "\034" => '%B', "<" => "%G", "\\" => "%L", "|" => "%Q",
64
+ "\035" => '%C', "=" => "%H", "]" => "%M", "}" => "%R",
65
+ "\036" => '%D', ">" => "%I", "^" => "%N", "~" => "%S",
66
+ "\037" => '%E', "?" => "%J", "_" => "%O", "\177" => "%T"
67
+ }
68
+
69
+ CHECKSUM_VALUES = {
70
+ '0' => 0, '1' => 1, '2' => 2, '3' => 3,
71
+ '4' => 4, '5' => 5, '6' => 6, '7' => 7,
72
+ '8' => 8, '9' => 9, 'A' => 10, 'B' => 11,
73
+ 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15,
74
+ 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19,
75
+ 'K' => 20, 'L' => 21, 'N' => 23, 'M' => 22,
76
+ 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27,
77
+ 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31,
78
+ 'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35,
79
+ '-' => 36, '.' => 37, ' ' => 38, '$' => 39,
80
+ '/' => 40, '+' => 41, '%' => 42
81
+ }
82
+
83
+ START_ENCODING = '100101101101' # *
84
+ STOP_ENCODING = '100101101101' # *
85
+
86
+ attr_accessor :data, :spacing, :extended, :include_checksum
87
+
88
+
89
+ def initialize(data, extended=false)
90
+ self.data = data
91
+ self.extended = extended
92
+ raise(ArgumentError, "data is not valid (extended=#{extended?})") unless valid?
93
+ yield self if block_given?
94
+ end
95
+
96
+
97
+ #Returns the characters that were passed in, no matter it they're part of
98
+ #the extended charset or if they're already encodable, "normal" characters
99
+ def raw_characters
100
+ data.split(//)
101
+ end
102
+
103
+ #Returns the encodable characters. If extended mode is enabled, each character will
104
+ #first be replaced by two characters from the encodable charset
105
+ def characters
106
+ chars = raw_characters
107
+ extended ? chars.map{|c| EXTENDED_ENCODINGS[c].split(//) }.flatten : chars
108
+ end
109
+
110
+ def characters_with_checksum
111
+ characters + [checksum_character]
112
+ end
113
+
114
+ def encoded_characters
115
+ characters.map{|c| ENCODINGS[c] }
116
+ end
117
+
118
+ def encoded_characters_with_checksum
119
+ encoded_characters + [checksum_encoding]
120
+ end
121
+
122
+
123
+ #The data part of the encoding (no start+stop characters)
124
+ def data_encoding
125
+ encoded_characters.join(spacing_encoding)
126
+ end
127
+
128
+ def data_encoding_with_checksum
129
+ encoded_characters_with_checksum.join(spacing_encoding)
130
+ end
131
+
132
+
133
+ def encoding
134
+ return encoding_with_checksum if include_checksum?
135
+ start_encoding+spacing_encoding+data_encoding+spacing_encoding+stop_encoding
136
+ end
137
+
138
+ def encoding_with_checksum
139
+ start_encoding+spacing_encoding+data_encoding_with_checksum+spacing_encoding+stop_encoding
140
+ end
141
+
142
+
143
+ #Checksum is optional
144
+ def checksum
145
+ characters.inject(0) do |sum,char|
146
+ sum + CHECKSUM_VALUES[char]
147
+ end % 43
148
+ end
149
+
150
+ def checksum_character
151
+ CHECKSUM_VALUES.invert[checksum]
152
+ end
153
+
154
+ def checksum_encoding
155
+ ENCODINGS[checksum_character]
156
+ end
157
+
158
+ #Set include_checksum to true to make +encoding+ include the checksum
159
+ def include_checksum?
160
+ include_checksum
161
+ end
162
+
163
+
164
+ #Spacing between the characters in xdims. Spacing will be inserted
165
+ #between each character in the encoding
166
+ def spacing
167
+ @spacing || 1
168
+ end
169
+
170
+ def spacing_encoding
171
+ '0' * spacing
172
+ end
173
+
174
+
175
+ def extended?
176
+ extended
177
+ end
178
+
179
+
180
+ def start_encoding
181
+ START_ENCODING
182
+ end
183
+
184
+ def stop_encoding
185
+ STOP_ENCODING
186
+ end
187
+
188
+ def valid?
189
+ if extended?
190
+ raw_characters.all?{|c| EXTENDED_ENCODINGS.include?(c) }
191
+ else
192
+ raw_characters.all?{|c| ENCODINGS.include?(c) }
193
+ end
194
+ end
195
+
196
+
197
+ end
198
+
199
+
200
+ end
@@ -0,0 +1,173 @@
1
+ require 'barby/barcode'
2
+
3
+ module Barby
4
+
5
+ #EAN-13, aka UPC-A, barcodes are the ones you can see at your local
6
+ #supermarket, in your house and, well, everywhere..
7
+ #
8
+ #To use this for a UPC barcode, just add a 0 to the front
9
+ class EAN13 < Barcode1D
10
+
11
+ LEFT_ENCODINGS_ODD = {
12
+ 0 => '0001101', 1 => '0011001', 2 => '0010011',
13
+ 3 => '0111101', 4 => '0100011', 5 => '0110001',
14
+ 6 => '0101111', 7 => '0111011', 8 => '0110111',
15
+ 9 => '0001011'
16
+ }
17
+
18
+ LEFT_ENCODINGS_EVEN = {
19
+ 0 => '0100111', 1 => '0110011', 2 => '0011011',
20
+ 3 => '0100001', 4 => '0011101', 5 => '0111001',
21
+ 6 => '0000101', 7 => '0010001', 8 => '0001001',
22
+ 9 => '0010111'
23
+ }
24
+
25
+ RIGHT_ENCODINGS = {
26
+ 0 => '1110010', 1 => '1100110', 2 => '1101100',
27
+ 3 => '1000010', 4 => '1011100', 5 => '1001110',
28
+ 6 => '1010000', 7 => '1000100', 8 => '1001000',
29
+ 9 => '1110100'
30
+ }
31
+
32
+ #Describes whether the left-hand encoding should use
33
+ #LEFT_ENCODINGS_ODD or LEFT_ENCODINGS_EVEN, based on the
34
+ #first digit in the number system (and the barcode as a whole)
35
+ LEFT_PARITY_MAPS = {
36
+ 0 => [:odd, :odd, :odd, :odd, :odd, :odd], #UPC-A
37
+ 1 => [:odd, :odd, :even, :odd, :even, :even],
38
+ 2 => [:odd, :odd, :even, :even, :odd, :even],
39
+ 3 => [:odd, :odd, :even, :even, :even, :odd],
40
+ 4 => [:odd, :even, :odd, :odd, :even, :even],
41
+ 5 => [:odd, :even, :even, :odd, :odd, :even],
42
+ 6 => [:odd, :even, :even, :even, :odd, :odd],
43
+ 7 => [:odd, :even, :odd, :even, :odd, :even],
44
+ 8 => [:odd, :even, :odd, :even, :even, :odd],
45
+ 9 => [:odd, :even, :even, :odd, :even, :odd]
46
+ }
47
+
48
+ #These are the lines that "stick down" in the graphical representation
49
+ START = '101'
50
+ CENTER = '01010'
51
+ STOP = '101'
52
+
53
+ #EAN-13 barcodes have 12 digits + check digit
54
+ FORMAT = /^\d{12}$/
55
+
56
+ attr_accessor :data
57
+
58
+
59
+ def initialize(data)
60
+ self.data = data
61
+ raise ArgumentError, 'data not valid' unless valid?
62
+ end
63
+
64
+
65
+ def characters
66
+ data.split(//)
67
+ end
68
+
69
+ def numbers
70
+ characters.map{|s| s.to_i }
71
+ end
72
+
73
+ def odd_and_even_numbers
74
+ alternater = false
75
+ numbers.reverse.partition{ alternater = !alternater }
76
+ end
77
+
78
+ #Numbers that are encoded to the left of the center
79
+ #The first digit is not included
80
+ def left_numbers
81
+ numbers[1,6]
82
+ end
83
+
84
+ #Numbers that are encoded to the right of the center
85
+ #The checksum is included here
86
+ def right_numbers
87
+ numbers_with_checksum[7,6]
88
+ end
89
+
90
+ def numbers_with_checksum
91
+ numbers + [checksum]
92
+ end
93
+
94
+
95
+ def data_with_checksum
96
+ data + checksum.to_s
97
+ end
98
+
99
+
100
+ def left_encodings
101
+ left_parity_map.zip(left_numbers).map do |parity,number|
102
+ parity == :odd ? LEFT_ENCODINGS_ODD[number] : LEFT_ENCODINGS_EVEN[number]
103
+ end
104
+ end
105
+
106
+ def right_encodings
107
+ right_numbers.map{|n| RIGHT_ENCODINGS[n] }
108
+ end
109
+
110
+ def left_encoding
111
+ left_encodings.join
112
+ end
113
+
114
+ def right_encoding
115
+ right_encodings.join
116
+ end
117
+
118
+ def encoding
119
+ start_encoding+left_encoding+center_encoding+right_encoding+stop_encoding
120
+ end
121
+
122
+
123
+ #The parities to use for encoding left-hand numbers
124
+ def left_parity_map
125
+ LEFT_PARITY_MAPS[numbers.first]
126
+ end
127
+
128
+
129
+ def weighted_sum
130
+ odds, evens = odd_and_even_numbers
131
+ odds.map!{|n| n * 3 }
132
+ sum = (odds+evens).inject(0){|s,n| s+n }
133
+ end
134
+
135
+ #Mod10
136
+ def checksum
137
+ mod = weighted_sum % 10
138
+ mod.zero? ? 0 : 10-mod
139
+ end
140
+
141
+ def checksum_encoding
142
+ RIGHT_ENCODINGS[checksum]
143
+ end
144
+
145
+
146
+ def valid?
147
+ data =~ FORMAT
148
+ end
149
+
150
+
151
+ #Is this a UPC-A barcode?
152
+ #UPC barcodes are EAN codes that start with 0
153
+ def upc?
154
+ numbers.first.zero?
155
+ end
156
+
157
+
158
+ def start_encoding
159
+ START
160
+ end
161
+
162
+ def center_encoding
163
+ CENTER
164
+ end
165
+
166
+ def stop_encoding
167
+ STOP
168
+ end
169
+
170
+
171
+ end
172
+
173
+ end
@@ -0,0 +1,32 @@
1
+ require 'barby/barcode/ean_13'
2
+
3
+ module Barby
4
+
5
+ #EAN-8 is a sub-set of EAN-13, with only 7 (8) digits
6
+ class EAN8 < EAN13
7
+
8
+ FORMAT = /^\d{7}$/
9
+
10
+
11
+ def left_numbers
12
+ numbers[0,4]
13
+ end
14
+
15
+ def right_numbers
16
+ numbers_with_checksum[4,4]
17
+ end
18
+
19
+
20
+ #Left-hand digits are all encoded using odd parity
21
+ def left_parity_map
22
+ [:odd, :odd, :odd, :odd]
23
+ end
24
+
25
+
26
+ def valid?
27
+ data =~ FORMAT
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,37 @@
1
+ require 'barby/barcode/code_128'
2
+
3
+ module Barby
4
+
5
+
6
+ #AKA EAN-128, UCC-128
7
+ class GS1128 < Code128
8
+
9
+ attr_accessor :application_identifier
10
+
11
+ def initialize(data, type, ai)
12
+ self.application_identifier = ai
13
+ super(data, type)
14
+ end
15
+
16
+
17
+ def data
18
+ FNC1+application_identifier+super
19
+ end
20
+
21
+ def partial_data
22
+ @data
23
+ end
24
+
25
+ def application_identifier_number
26
+ values[application_identifier]
27
+ end
28
+
29
+ def application_identifier_encoding
30
+ encodings[application_identifier_number]
31
+ end
32
+
33
+
34
+ end
35
+
36
+
37
+ end
@@ -0,0 +1,73 @@
1
+ require 'barby/barcode'
2
+
3
+ module Barby
4
+
5
+
6
+ #An Outputter creates something from a barcode. That something can be
7
+ #anything, but is most likely a graphical representation of the barcode.
8
+ #Outputters can register methods on barcodes that will be associated with
9
+ #them.
10
+ #
11
+ #The basic structure of an outputter class:
12
+ #
13
+ # class FooOutputter < Barby::Outputter
14
+ # register :to_foo
15
+ # def to_too
16
+ # do_something_with(barcode.encoding)
17
+ # end
18
+ # end
19
+ #
20
+ #Barcode#to_foo will now be available to all barcodes
21
+ class Outputter
22
+
23
+ attr_accessor :barcode
24
+
25
+
26
+ #An outputter instance will have access to a Barcode
27
+ def initialize(barcode)
28
+ self.barcode = barcode
29
+ end
30
+
31
+
32
+ #Register one or more handler methods with this outputter
33
+ #Barcodes will then be able to use these methods to get the output
34
+ #from the outputter. For example, if you have an ImageOutputter,
35
+ #you could do:
36
+ #
37
+ #register :to_png, :to_gif
38
+ #
39
+ #You could then do aBarcode.to_png and get the result of that method.
40
+ #The class which registers the method will receive the barcode as the only
41
+ #argument, and the default implementation of initialize puts that into
42
+ #the +barcode+ accessor.
43
+ #
44
+ #You can also have different method names on the barcode and the outputter
45
+ #by providing a hash:
46
+ #
47
+ #register :to_png => :create_png, :to_gif => :create_gif
48
+ def self.register(*method_names)
49
+ if method_names.first.is_a? Hash
50
+ method_names.first.each do |name, method_name|
51
+ Barcode.register_outputter(name, self, method_name)
52
+ end
53
+ else
54
+ method_names.each do |name|
55
+ Barcode.register_outputter(name, self, name)
56
+ end
57
+ end
58
+ end
59
+
60
+
61
+ private
62
+
63
+ #Converts the barcode's encoding (a string containing 1s and 0s)
64
+ #to true and false values (1 == true == "black bar")
65
+ def booleans#:doc:
66
+ barcode.encoding.split(//).map{|c| c == '1' }
67
+ end
68
+
69
+
70
+ end
71
+
72
+
73
+ end
@@ -0,0 +1,22 @@
1
+ require 'barby/outputter'
2
+
3
+ module Barby
4
+
5
+ #Outputs an ASCII representation of the barcode. This is mostly useful for printing
6
+ #the barcode directly to the terminal for testing.
7
+ class ASCIIOutputter < Outputter
8
+
9
+ register :to_ascii
10
+
11
+
12
+ def to_ascii(opts={})
13
+ opts = {:height => 10, :xdim => 1, :bar => '#', :space => ' '}.merge(opts)
14
+ Array.new(
15
+ opts[:height],
16
+ booleans.map{|b| (b ? opts[:bar] : opts[:space]) * opts[:xdim] }.join
17
+ ).join("\n")
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,74 @@
1
+ require 'barby/outputter'
2
+
3
+ module Barby
4
+
5
+ class PDFWriterOutputter < Outputter
6
+
7
+ register :annotate_pdf
8
+
9
+ attr_accessor :x, :y, :height, :xdim
10
+
11
+
12
+ def annotate_pdf(pdf, options={})
13
+ previous_options = options.map{|k,v| [k, send(k)] }
14
+ options.each{|k,v| send("#{k}=", v) if respond_to?("#{k}=") }
15
+
16
+ xpos, ypos = x, y
17
+
18
+ widths.each do |array|
19
+ if array.first
20
+ pdf.move_to(xpos, ypos).
21
+ line_to(xpos, ypos+height).
22
+ line_to(xpos+(xdim*array.size), ypos+height).
23
+ line_to(xpos+(xdim*array.size), ypos).
24
+ line_to(xpos, ypos).
25
+ fill
26
+ end
27
+ xpos += (xdim*array.size)
28
+ end
29
+
30
+ previous_options.each{|k,v| send("#{k}=", v) }
31
+
32
+ pdf
33
+ end
34
+
35
+
36
+ def x
37
+ @x || 10
38
+ end
39
+
40
+ def y
41
+ @y || 10
42
+ end
43
+
44
+ def height
45
+ @height || 50
46
+ end
47
+
48
+ def xdim
49
+ @xdim || 1
50
+ end
51
+
52
+ def widths
53
+ widths = []
54
+ count = nil
55
+
56
+ booleans.inject nil do |last,current|
57
+ if current != last
58
+ widths << count if count
59
+ count = [current]
60
+ else
61
+ count << current
62
+ end
63
+ current
64
+ end
65
+
66
+ widths << count
67
+
68
+ widths
69
+ end
70
+
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,87 @@
1
+ require 'barby/outputter'
2
+ require 'RMagick'
3
+
4
+ module Barby
5
+
6
+
7
+ #Renders images from barcodes using RMagick
8
+ class RmagickOutputter < Outputter
9
+
10
+ register :to_png, :to_gif, :to_jpg, :to_image
11
+
12
+ attr_accessor :height, :xdim, :margin
13
+
14
+
15
+ #Returns a string containing a PNG image
16
+ def to_png(*a)
17
+ to_image(*a).to_blob{|i| i.format ='png' }
18
+ end
19
+
20
+ #Returns a string containint a GIF image
21
+ def to_gif(*a)
22
+ to_image(*a).to_blob{|i| i.format ='gif' }
23
+ end
24
+
25
+ #Returns a string containing a JPEG image
26
+ def to_jpg(*a)
27
+ to_image(*a).to_blob{|i| i.format = 'jpg' }
28
+ end
29
+
30
+ #Returns an instance of Magick::Image
31
+ def to_image(opts={})
32
+ opts.each{|k,v| send("#{k}=", v) if respond_to?("#{k}=") }
33
+ canvas = Magick::Image.new(full_width, full_height)
34
+ bars = Magick::Draw.new
35
+
36
+ x = margin
37
+ y = margin
38
+ booleans.each do |bar|
39
+ if bar
40
+ bars.rectangle(x, y, x+(xdim-1), y+height)
41
+ end
42
+ x += xdim
43
+ end
44
+
45
+ bars.draw(canvas)
46
+
47
+ canvas
48
+ end
49
+
50
+
51
+ #The height of the barcode in px
52
+ def height
53
+ @height || 100
54
+ end
55
+
56
+ #X dimension. 1X == 1px
57
+ def xdim
58
+ @xdim || 1
59
+ end
60
+
61
+ #The margin of each edge surrounding the barcode in pixels
62
+ def margin
63
+ @margin || 10
64
+ end
65
+
66
+ #The width of the barcode in px
67
+ def width
68
+ barcode.encoding.length * xdim
69
+ end
70
+
71
+ #The full width of the image. This is the width of the
72
+ #barcode + the left and right margin
73
+ def full_width
74
+ width + (margin * 2)
75
+ end
76
+
77
+ #The height of the image. This is the height of the
78
+ #barcode + the top and bottom margin
79
+ def full_height
80
+ height + (margin * 2)
81
+ end
82
+
83
+
84
+ end
85
+
86
+
87
+ end
@@ -0,0 +1,9 @@
1
+ module Barby #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: barby
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Tore Darell
8
+ autorequire: barby
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-03-17 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: toredarell@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - lib/barby.rb
26
+ - lib/barby
27
+ - lib/barby/barcode.rb
28
+ - lib/barby/barcode
29
+ - lib/barby/barcode/bookland.rb
30
+ - lib/barby/barcode/code_128.rb
31
+ - lib/barby/barcode/code_39.rb
32
+ - lib/barby/barcode/ean_13.rb
33
+ - lib/barby/barcode/ean_8.rb
34
+ - lib/barby/barcode/gs1_128.rb
35
+ - lib/barby/outputter.rb
36
+ - lib/barby/outputter
37
+ - lib/barby/outputter/ascii_outputter.rb
38
+ - lib/barby/outputter/pdfwriter_outputter.rb
39
+ - lib/barby/outputter/rmagick_outputter.rb
40
+ - lib/barby/version.rb
41
+ - README
42
+ has_rdoc: true
43
+ homepage: http://tore.darell.no/
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.0.1
65
+ signing_key:
66
+ specification_version: 2
67
+ summary: A pure-Ruby barcode generator
68
+ test_files: []
69
+