barby 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/bin/barby +40 -0
  2. data/lib/barby.rb +3 -0
  3. data/lib/barby/barcode.rb +6 -0
  4. data/lib/barby/barcode/code_128.rb +16 -3
  5. data/lib/barby/barcode/code_25.rb +161 -0
  6. data/lib/barby/barcode/code_25_interleaved.rb +73 -0
  7. data/lib/barby/barcode/code_93.rb +225 -0
  8. data/lib/barby/barcode/qr_code.rb +93 -0
  9. data/lib/barby/outputter.rb +56 -2
  10. data/lib/barby/outputter/ascii_outputter.rb +20 -1
  11. data/lib/barby/outputter/cairo_outputter.rb +38 -7
  12. data/lib/barby/outputter/pdfwriter_outputter.rb +44 -35
  13. data/lib/barby/outputter/png_outputter.rb +48 -16
  14. data/lib/barby/outputter/prawn_outputter.rb +100 -0
  15. data/lib/barby/outputter/rmagick_outputter.rb +48 -20
  16. data/lib/barby/vendor.rb +3 -0
  17. data/vendor/rqrcode/CHANGELOG +21 -0
  18. data/vendor/rqrcode/COPYING +19 -0
  19. data/vendor/rqrcode/README +98 -0
  20. data/vendor/rqrcode/Rakefile +65 -0
  21. data/vendor/rqrcode/lib/rqrcode.rb +13 -0
  22. data/vendor/rqrcode/lib/rqrcode/core_ext.rb +5 -0
  23. data/vendor/rqrcode/lib/rqrcode/core_ext/array.rb +5 -0
  24. data/vendor/rqrcode/lib/rqrcode/core_ext/array/behavior.rb +9 -0
  25. data/vendor/rqrcode/lib/rqrcode/core_ext/integer.rb +5 -0
  26. data/vendor/rqrcode/lib/rqrcode/core_ext/integer/bitwise.rb +11 -0
  27. data/vendor/rqrcode/lib/rqrcode/qrcode.rb +4 -0
  28. data/vendor/rqrcode/lib/rqrcode/qrcode/qr_8bit_byte.rb +35 -0
  29. data/vendor/rqrcode/lib/rqrcode/qrcode/qr_bit_buffer.rb +56 -0
  30. data/vendor/rqrcode/lib/rqrcode/qrcode/qr_code.rb +421 -0
  31. data/vendor/rqrcode/lib/rqrcode/qrcode/qr_math.rb +63 -0
  32. data/vendor/rqrcode/lib/rqrcode/qrcode/qr_polynomial.rb +78 -0
  33. data/vendor/rqrcode/lib/rqrcode/qrcode/qr_rs_block.rb +134 -0
  34. data/vendor/rqrcode/lib/rqrcode/qrcode/qr_util.rb +254 -0
  35. data/vendor/rqrcode/test/runtest.rb +78 -0
  36. data/vendor/rqrcode/test/test_data.rb +21 -0
  37. metadata +86 -44
@@ -0,0 +1,93 @@
1
+ require 'rqrcode'
2
+ require 'barby/barcode'
3
+
4
+ module Barby
5
+
6
+
7
+ #QrCode is a thin wrapper around the RQRCode library
8
+ class QrCode < Barcode2D
9
+
10
+ #Maximum sizes for each correction level for binary data
11
+ #It's an array
12
+ SIZES = {
13
+ #L M Q H
14
+ 1 => [17, 14, 11, 7], 2 => [32, 26, 20, 14],
15
+ 3 => [53, 42, 32, 24], 4 => [78, 62, 46, 34],
16
+ 5 => [106, 84, 60, 44], 6 => [134, 106, 74, 58],
17
+ 7 => [154, 122, 86, 64], 8 => [192, 152, 108, 84],
18
+ 9 => [230, 180, 130, 98], 10 => [271, 213, 151, 119]#,
19
+
20
+ #I think RQRCode only supports sizes up to 10
21
+ # 11 => [321, 251, 177, 137], 12 => [367, 287, 203, 155],
22
+ # 13 => [425, 331, 241, 177], 14 => [458, 362, 258, 194],
23
+ # 15 => [520, 412, 292, 220], 16 => [586, 450, 322, 250],
24
+ # 17 => [644, 504, 364, 280], 18 => [718, 560, 394, 310],
25
+ # 19 => [792, 624, 442, 338], 20 => [858, 666, 482, 382],
26
+ # 21 => [929, 711, 509, 403], 22 => [1003, 779, 565, 439],
27
+ # 23 => [1091, 857, 611, 461], 24 => [1171, 911, 661, 511],
28
+ # 25 => [1273, 997, 715, 535], 26 => [1367, 1059, 751, 593],
29
+ # 27 => [1465, 1125, 805, 625], 28 => [1528, 1190, 868, 658],
30
+ # 29 => [1628, 1264, 908, 698], 30 => [1732, 1370, 982, 742],
31
+ # 31 => [1840, 1452, 1030, 790], 32 => [1952, 1538, 1112, 842],
32
+ # 33 => [2068, 1628, 1168, 898], 34 => [2188, 1722, 1228, 958],
33
+ # 35 => [2303, 1809, 1283, 983], 36 => [2431, 1911, 1351, 1051],
34
+ # 37 => [2563, 1989, 1423, 1093], 38 => [2699, 2099, 1499, 1139],
35
+ # 39 => [2809, 2213, 1579, 1219], 40 => [2953, 2331, 1663, 1273]
36
+ }.sort
37
+
38
+ LEVELS = { :l => 0, :m => 1, :q => 2, :h => 3 }
39
+
40
+ attr_reader :data
41
+ attr_writer :level
42
+
43
+
44
+ def initialize(data, options={})
45
+ self.data = data
46
+ options.each{|k,v| send("#{k}=", v) }
47
+ raise(ArgumentError, "data too large") unless size
48
+ end
49
+
50
+
51
+ def data=(data)
52
+ @data = data
53
+ end
54
+
55
+
56
+ def encoding
57
+ rqrcode.modules.map{|r| r.inject(''){|s,m| s << (m ? '1' : '0') } }
58
+ end
59
+
60
+
61
+ #Error correction level
62
+ #Can be one of [:l, :m, :q, :h] (7%, 15%, 25%, 30%)
63
+ def level
64
+ @level || :l
65
+ end
66
+
67
+
68
+ def size
69
+ level_index = LEVELS[level]
70
+ length = data.length
71
+ found_size = nil
72
+ SIZES.each do |size,max_values|
73
+ if max_values[level_index] >= length
74
+ found_size = size
75
+ break
76
+ end
77
+ end
78
+ found_size
79
+ end
80
+
81
+
82
+ private
83
+
84
+ #Generate an RQRCode object with the available values
85
+ def rqrcode
86
+ RQRCode::QRCode.new(data, :level => level, :size => size)
87
+ end
88
+
89
+
90
+ end
91
+
92
+
93
+ end
@@ -62,8 +62,62 @@ module Barby
62
62
 
63
63
  #Converts the barcode's encoding (a string containing 1s and 0s)
64
64
  #to true and false values (1 == true == "black bar")
65
- def booleans#:doc:
66
- barcode.encoding.split(//).map{|c| c == '1' }
65
+ #
66
+ #If the barcode is 2D, each line will be converted to an array
67
+ #in the same way
68
+ def booleans(reload=false)#:doc:
69
+ if barcode.two_dimensional?
70
+ encoding(reload).map{|l| l.split(//).map{|c| c == '1' } }
71
+ else
72
+ encoding(reload).split(//).map{|c| c == '1' }
73
+ end
74
+ end
75
+
76
+
77
+ #Returns the barcode's encoding. The encoding
78
+ #is cached and can be reloaded by passing true
79
+ def encoding(reload=false)#:doc:
80
+ @encoding = barcode.encoding if reload
81
+ @encoding ||= barcode.encoding
82
+ end
83
+
84
+
85
+ #Collects continuous groups of bars and spaces (1 and 0)
86
+ #into arrays where the first item is true or false (1 or 0)
87
+ #and the second is the size of the group
88
+ #
89
+ #For example, "1100111000" becomes [[true,2],[false,2],[true,3],[false,3]]
90
+ def boolean_groups(reload=false)
91
+ if barcode.two_dimensional?
92
+ encoding(reload).map do |line|
93
+ line.scan(/1+|0+/).map do |group|
94
+ [group[0,1] == '1', group.size]
95
+ end
96
+ end
97
+ else
98
+ encoding(reload).scan(/1+|0+/).map do |group|
99
+ [group[0,1] == '1', group.size]
100
+ end
101
+ end
102
+ end
103
+
104
+
105
+ def with_options(options={})
106
+ original_options = options.inject({}) do |origs,pair|
107
+ if respond_to?(pair.first) && respond_to?("#{pair.first}=")
108
+ origs[pair.first] = send(pair.first)
109
+ send("#{pair.first}=", pair.last)
110
+ end
111
+ origs
112
+ end
113
+
114
+ rv = yield
115
+
116
+ original_options.each do |attribute,value|
117
+ send("#{attribute}=", value)
118
+ end
119
+
120
+ rv
67
121
  end
68
122
 
69
123
 
@@ -4,19 +4,38 @@ module Barby
4
4
 
5
5
  #Outputs an ASCII representation of the barcode. This is mostly useful for printing
6
6
  #the barcode directly to the terminal for testing.
7
+ #
8
+ #Registers to_ascii
7
9
  class ASCIIOutputter < Outputter
8
10
 
9
11
  register :to_ascii
10
12
 
11
13
 
12
14
  def to_ascii(opts={})
13
- opts = {:height => 10, :xdim => 1, :bar => '#', :space => ' '}.merge(opts)
15
+ default_opts = {:height => 10, :xdim => 1, :bar => '#', :space => ' '}
16
+ default_opts.update(:height => 1, :bar => ' X ', :space => ' ') if barcode.two_dimensional?
17
+ opts = default_opts.merge(opts)
18
+
19
+ if barcode.two_dimensional?
20
+ booleans.map do |bools|
21
+ line_to_ascii(bools, opts)
22
+ end.join("\n")
23
+ else
24
+ line_to_ascii(booleans, opts)
25
+ end
26
+ end
27
+
28
+
29
+ private
30
+
31
+ def line_to_ascii(booleans, opts)
14
32
  Array.new(
15
33
  opts[:height],
16
34
  booleans.map{|b| (b ? opts[:bar] : opts[:space]) * opts[:xdim] }.join
17
35
  ).join("\n")
18
36
  end
19
37
 
38
+
20
39
  end
21
40
 
22
41
  end
@@ -1,7 +1,12 @@
1
+ require 'barby/outputter'
1
2
  require 'cairo'
2
3
  require 'stringio'
3
4
 
4
5
  module Barby
6
+
7
+ #Uses Cairo to render a barcode to a number of formats: PNG, PS, EPS, PDF and SVG
8
+ #
9
+ #Registers methods render_to_cairo_context, to_png, to_ps, to_eps, to_pdf and to_svg
5
10
  class CairoOutputter < Outputter
6
11
 
7
12
  register :render_to_cairo_context
@@ -18,6 +23,7 @@ module Barby
18
23
  attr_writer :x, :y, :xdim, :height, :margin
19
24
 
20
25
 
26
+ #Render the barcode onto a Cairo context
21
27
  def render_to_cairo_context(context, options={})
22
28
  if context.respond_to?(:have_current_point?) and
23
29
  context.have_current_point?
@@ -29,15 +35,30 @@ module Barby
29
35
 
30
36
  _xdim = xdim(options)
31
37
  _height = height(options)
38
+ original_current_x = current_x
32
39
  context.save do
33
40
  context.set_source_color(:black)
34
41
  context.fill do
35
- barcode.encoding.scan(/(?:0+|1+)/).each do |codes|
36
- current_width = _xdim * codes.size
37
- if codes[0] == ?1
38
- context.rectangle(current_x, current_y, current_width, _height)
42
+ if barcode.two_dimensional?
43
+ boolean_groups.each do |groups|
44
+ groups.each do |bar,amount|
45
+ current_width = _xdim * amount
46
+ if bar
47
+ context.rectangle(current_x, current_y, current_width, _xdim)
48
+ end
49
+ current_x += current_width
50
+ end
51
+ current_x = original_current_x
52
+ current_y += _xdim
53
+ end
54
+ else
55
+ boolean_groups.each do |bar,amount|
56
+ current_width = _xdim * amount
57
+ if bar
58
+ context.rectangle(current_x, current_y, current_width, _height)
59
+ end
60
+ current_x += current_width
39
61
  end
40
- current_x += current_width
41
62
  end
42
63
  end
43
64
  end
@@ -46,6 +67,7 @@ module Barby
46
67
  end
47
68
 
48
69
 
70
+ #Render the barcode to a PNG image
49
71
  def to_png(options={})
50
72
  output_to_string_io do |io|
51
73
  Cairo::ImageSurface.new(options[:format],
@@ -58,6 +80,7 @@ module Barby
58
80
  end
59
81
 
60
82
 
83
+ #Render the barcode to a PS document
61
84
  def to_ps(options={})
62
85
  output_to_string_io do |io|
63
86
  Cairo::PSSurface.new(io,
@@ -70,11 +93,13 @@ module Barby
70
93
  end
71
94
 
72
95
 
96
+ #Render the barcode to an EPS document
73
97
  def to_eps(options={})
74
98
  to_ps(options.merge(:eps => true))
75
99
  end
76
100
 
77
101
 
102
+ #Render the barcode to a PDF document
78
103
  def to_pdf(options={})
79
104
  output_to_string_io do |io|
80
105
  Cairo::PDFSurface.new(io,
@@ -86,6 +111,7 @@ module Barby
86
111
  end
87
112
 
88
113
 
114
+ #Render the barcode to an SVG document
89
115
  def to_svg(options={})
90
116
  output_to_string_io do |io|
91
117
  Cairo::SVGSurface.new(io,
@@ -106,11 +132,15 @@ module Barby
106
132
  end
107
133
 
108
134
  def width(options={})
109
- barcode.encoding.length * xdim(options)
135
+ (barcode.two_dimensional? ? encoding.first.length : encoding.length) * xdim(options)
110
136
  end
111
137
 
112
138
  def height(options={})
113
- @height || options[:height] || 50
139
+ if barcode.two_dimensional?
140
+ encoding.size * xdim(options)
141
+ else
142
+ @height || options[:height] || 50
143
+ end
114
144
  end
115
145
 
116
146
  def full_width(options={})
@@ -151,4 +181,5 @@ module Barby
151
181
 
152
182
 
153
183
  end
184
+
154
185
  end
@@ -2,6 +2,9 @@ require 'barby/outputter'
2
2
 
3
3
  module Barby
4
4
 
5
+ #Annotates a PDFWriter document with the barcode
6
+ #
7
+ #Registers the annotate_pdf method
5
8
  class PDFWriterOutputter < Outputter
6
9
 
7
10
  register :annotate_pdf
@@ -9,25 +12,50 @@ module Barby
9
12
  attr_accessor :x, :y, :height, :xdim
10
13
 
11
14
 
15
+ #Annotate a PDFWriter document with the barcode
16
+ #
17
+ #Valid options are:
18
+ #
19
+ #x, y - The point in the document to start rendering from
20
+ #height - The height of the bars in PDF units
21
+ #xdim - The X dimension in PDF units
12
22
  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
23
+ with_options options do
24
+
25
+ xpos, ypos = x, y
26
+ orig_xpos = xpos
27
+
28
+ if barcode.two_dimensional?
29
+ boolean_groups.reverse_each do |groups|
30
+ groups.each do |bar,amount|
31
+ if bar
32
+ pdf.move_to(xpos, ypos).
33
+ line_to(xpos, ypos+xdim).
34
+ line_to(xpos+(xdim*amount), ypos+xdim).
35
+ line_to(xpos+(xdim*amount), ypos).
36
+ line_to(xpos, ypos).
37
+ fill
38
+ end
39
+ xpos += (xdim*amount)
40
+ end
41
+ xpos = orig_xpos
42
+ ypos += xdim
43
+ end
44
+ else
45
+ boolean_groups.each do |bar,amount|
46
+ if bar
47
+ pdf.move_to(xpos, ypos).
48
+ line_to(xpos, ypos+height).
49
+ line_to(xpos+(xdim*amount), ypos+height).
50
+ line_to(xpos+(xdim*amount), ypos).
51
+ line_to(xpos, ypos).
52
+ fill
53
+ end
54
+ xpos += (xdim*amount)
55
+ end
26
56
  end
27
- xpos += (xdim*array.size)
28
- end
29
57
 
30
- previous_options.each{|k,v| send("#{k}=", v) }
58
+ end
31
59
 
32
60
  pdf
33
61
  end
@@ -49,25 +77,6 @@ module Barby
49
77
  @xdim || 1
50
78
  end
51
79
 
52
- def widths
53
- widths = []
54
- count = nil
55
-
56
- booleans.inject nil do |previous,current|
57
- if current != previous
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
80
 
72
81
  end
73
82
 
@@ -1,46 +1,70 @@
1
+ require 'barby/outputter'
1
2
  require 'png'
2
3
 
3
4
  module Barby
4
5
 
6
+ #Renders the barcode to a PNG image using the "png" gem (gem install png)
7
+ #
8
+ #Registers the to_png and to_canvas methods
5
9
  class PngOutputter < Outputter
6
10
 
7
11
  register :to_png, :to_canvas
8
12
 
9
- attr_accessor :xdim, :width, :height, :margin
13
+ attr_accessor :xdim, :ydim, :width, :height, :margin
10
14
 
11
15
 
16
+ #Creates a PNG::Canvas object and renders the barcode on it
12
17
  def to_canvas(opts={})
13
- orig_opts = opts.inject({}){|h,p| send("#{p.first}=", p.last) if respond_to?("#{p.first}="); h.update(p.first => p.last) }
14
- canvas = PNG::Canvas.new(full_width, full_height, PNG::Color::White)
15
-
16
- x, y = margin, margin
17
- booleans.each do |bar|
18
- if bar
19
- x.upto(x+(xdim-1)) do |xx|
20
- y.upto y+(height-1) do |yy|
21
- canvas[xx,yy] = PNG::Color::Black
18
+ with_options opts do
19
+ canvas = PNG::Canvas.new(full_width, full_height, PNG::Color::White)
20
+
21
+ if barcode.two_dimensional?
22
+ x, y = margin, margin
23
+ booleans.reverse_each do |line|
24
+ line.each do |bar|
25
+ if bar
26
+ x.upto(x+(xdim-1)) do |xx|
27
+ y.upto y+(ydim-1) do |yy|
28
+ canvas[xx,yy] = PNG::Color::Black
29
+ end
30
+ end
31
+ end
32
+ x += xdim
22
33
  end
34
+ y += ydim
35
+ x = margin
36
+ end
37
+ else
38
+ x, y = margin, margin
39
+ booleans.each do |bar|
40
+ if bar
41
+ x.upto(x+(xdim-1)) do |xx|
42
+ y.upto y+(height-1) do |yy|
43
+ canvas[xx,yy] = PNG::Color::Black
44
+ end
45
+ end
46
+ end
47
+ x += xdim
23
48
  end
24
49
  end
25
- x += xdim
26
- end
27
50
 
28
- orig_opts.each{|k,v| send("#{k}=", v) if respond_to?("#{k}=") }
29
- canvas
51
+ canvas
52
+ end
30
53
  end
31
54
 
32
55
 
56
+ #Renders the barcode to a PNG image
33
57
  def to_png(*a)
34
58
  PNG.new(to_canvas(*a)).to_blob
35
59
  end
36
60
 
37
61
 
38
62
  def width
39
- barcode.encoding.length * xdim
63
+ length * xdim
40
64
  end
41
65
 
42
66
  def height
43
- @height || 100
67
+ barcode.two_dimensional? ? (ydim * encoding.length) : (@height || 100)
44
68
  end
45
69
 
46
70
  def full_width
@@ -55,10 +79,18 @@ module Barby
55
79
  @xdim || 1
56
80
  end
57
81
 
82
+ def ydim
83
+ @ydim || xdim
84
+ end
85
+
58
86
  def margin
59
87
  @margin || 10
60
88
  end
61
89
 
90
+ def length
91
+ barcode.two_dimensional? ? encoding.first.length : encoding.length
92
+ end
93
+
62
94
 
63
95
  end
64
96