pdf-labels 1.0.1 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/History.txt +9 -1
  2. data/LICENCE +0 -0
  3. data/Manifest.txt +32 -31
  4. data/Rakefile +2 -2
  5. data/fonts/Code128.afm +275 -0
  6. data/fonts/Code128.pfb +0 -0
  7. data/fonts/Code128.ttf +0 -0
  8. data/fonts/Code2of5interleaved.afm +275 -0
  9. data/fonts/Code2of5interleaved.pfb +0 -0
  10. data/fonts/Code2of5interleaved.ttf +0 -0
  11. data/fonts/Code3de9.afm +275 -0
  12. data/fonts/Code3de9.pfb +0 -0
  13. data/fonts/Code3de9.ttf +0 -0
  14. data/fonts/CodeDatamatrix.afm +275 -0
  15. data/fonts/CodeDatamatrix.pfb +0 -0
  16. data/fonts/CodeDatamatrix.ttf +0 -0
  17. data/fonts/CodeEAN13.afm +275 -0
  18. data/fonts/CodeEAN13.pfb +0 -0
  19. data/fonts/CodeEAN13.ttf +0 -0
  20. data/fonts/CodePDF417.afm +275 -0
  21. data/fonts/CodePDF417.pfb +0 -0
  22. data/fonts/CodePDF417.ttf +0 -0
  23. data/init.rb +2 -0
  24. data/lib/pdf/label.rb +12 -0
  25. data/lib/pdf/label/alias.rb +11 -0
  26. data/lib/pdf/label/batch.rb +276 -0
  27. data/lib/pdf/label/glabel_template.rb +38 -0
  28. data/lib/pdf/label/label.rb +55 -0
  29. data/lib/pdf/label/layout.rb +15 -0
  30. data/lib/pdf/label/length_node.rb +52 -0
  31. data/lib/pdf/label/markup.rb +26 -0
  32. data/lib/pdf/label/template.rb +39 -0
  33. data/pdf-labels-patch-to-r62.txt +98 -0
  34. data/templates/avery-us-templates.xml +0 -0
  35. data/templates/glabels-2.0.dtd +0 -0
  36. data/test/test_pdf_label_page.rb +76 -18
  37. data/vendor/pdf/writer.rb +4 -1
  38. data/vendor/pdf/writer/object/font.rb +4 -4
  39. data/vendor/pdf/writer/object/viewerpreferences.rb +2 -2
  40. data/vendor/pdf_writer_font_patch.diff +35 -0
  41. metadata +84 -66
  42. data/lib/alias.rb +0 -8
  43. data/lib/glabel_template.rb +0 -36
  44. data/lib/label.rb +0 -52
  45. data/lib/layout.rb +0 -13
  46. data/lib/length_node.rb +0 -47
  47. data/lib/markup.rb +0 -25
  48. data/lib/pdf_label_page.rb +0 -171
  49. data/lib/pdf_labels.rb +0 -3
  50. data/lib/template.rb +0 -37
Binary file
Binary file
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'pdf/label'
2
+ PDF_LABELS_ROOT = File.expand_path(File.dirname(__FILE__))
@@ -0,0 +1,12 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/label/batch')
2
+
3
+ module Pdf
4
+ module Label
5
+ VERSION = '2.0.1'
6
+
7
+ #We want the barcode fonts to be loaded as availible fonts in the font path
8
+ root = File.expand_path(File.dirname(__FILE__) + "/../../")
9
+ PDF::Writer::FONT_PATH << (root + "/fonts")
10
+ PDF::Writer::FontMetrics::METRICS_PATH << (root + "/fonts")
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ require 'xml/mapping'
2
+ require File.expand_path(File.dirname(__FILE__) + '/length_node')
3
+
4
+ module Pdf
5
+ module Label
6
+ class Alias
7
+ include XML::Mapping
8
+ text_node :name, "@name"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,276 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + "/../../../vendor")
2
+ require 'xml/mapping'
3
+ require File.expand_path(File.dirname(__FILE__) + '/glabel_template')
4
+ require 'pdf/writer'
5
+ #--- require 'breakpoint'
6
+
7
+ module Pdf
8
+ module Label
9
+ class Batch
10
+ attr_accessor :gt, :template, :label, :pdf, :barcode_font
11
+ @@gt = nil
12
+ def initialize(template_name, pdf_opts = {})
13
+ @@gt || self.class.load_template_set
14
+ unless @template = @@gt.find_template(template_name)
15
+ raise "Template not found!"
16
+ end
17
+ #if the template specifies the paper type, and the user didn't use it.
18
+ if @template.size && !pdf_opts.has_key?(:paper)
19
+ pdf_opts[:paper] = @template.size.gsub(/^.*-/,'')
20
+ end
21
+ #TODO figure out how to cope with multiple label types on a page
22
+ @label = @template.labels["0"]
23
+ #TODO figure out how to handle multiple layouts
24
+ @layout = @label.layouts[0]
25
+ @labels_per_page = @layout.nx * @layout.ny
26
+ @zero_based_labels_per_page = @labels_per_page - 1
27
+
28
+ @pdf = PDF::Writer.new(pdf_opts)
29
+ @pdf.margins_pt(0, 0, 0, 0)
30
+
31
+ # Turn off print scaling in the generated PDF to ensure
32
+ # that the labels are positioned correctly when printing
33
+ # TODO This goes boom! @pdf.viewer_preferences('PrintScaling', '/None')
34
+ end
35
+
36
+ def self.load_template_set(template_set_file=nil)
37
+ template_set_file ||= File.expand_path(File.dirname(__FILE__) + "/../../../templates/avery-us-templates.xml")
38
+ @@gt = GlabelsTemplate.load_from_file(template_set_file)
39
+ end
40
+
41
+ def self.all_template_names
42
+ @@gt || self.load_template_set
43
+ @@gt.find_all_templates
44
+ end
45
+
46
+ def self.all_barcode_fonts
47
+ {"Code128.afm" => :translation_needed,
48
+ "Code2of5interleaved.afm" => :translation_needed,
49
+ "Code3de9.afm" => :code39,
50
+ "CodeDatamatrix.afm" => :translation_needed,
51
+ "CodeEAN13.afm" => :translation_needed,
52
+ "CodePDF417.afm" => :translation_needed}
53
+ end
54
+
55
+ def code39(text)
56
+ out = text.upcase
57
+ raise "Characters Not Encodable in Code3of9" unless out.match(/^[0-9A-Z\-\. \/\$\+%\*]+$/)
58
+ out = "*" + out unless out.match(/^\*/)
59
+ out = out + "*" unless out.match(/\*$/)
60
+ return out
61
+ end
62
+
63
+ def translation_needed(text)
64
+ $stderr.puts("This barcode format does not automatically get formatted yet")
65
+ #TODO - Rob need to add barcode formatting
66
+ return text
67
+ end
68
+
69
+ =begin rdoc
70
+ add_label takes an argument hash.
71
+ [:position] Which label slot to print. Positions are top to bottom, left to right so position 1 is the label in the top lefthand corner. Defaults to 0
72
+ [:x & :y] The (x,y) coordinates on the page to print the text. Ignored if position is specified.
73
+ [:text] What you want to print in the label. Defaults to the (x,y) of the top left corner of the label.
74
+ [:use_margin] If the label has a markupMargin, setting this argument to true will respect that margin when writing text. Defaults to true.
75
+ [:justification] Values can be :left, :right, :center, :full. Defaults to :left
76
+ [:offset_x, offset_y] If your printer doesn't want to print with out margins you can define these values to fine tune printout.
77
+ =end
78
+ def add_label(options = {})
79
+ label_x, label_y, label_width = setup_add_label_options(options)
80
+
81
+ text = options[:text] || "[#{label_x / 72}, #{label_y / 72}]"
82
+
83
+ arg_hash = setup_arg_hash(options, label_x, label_y, label_width)
84
+
85
+ @pdf.y = label_y
86
+ @pdf.text(text,arg_hash)
87
+ end
88
+
89
+ =begin rdoc
90
+ You can add the same text to many labels this way, takes all the arguments of add_label, but must have position instead of x,y. Requires count.
91
+ [:count] - Number of labels to print
92
+ =end
93
+ def add_many_labels(options = {})
94
+ if (options[:x] || options[:y]) && !options[:position]
95
+ raise "Can't use X,Y with add_many_labels, you must use position"
96
+ end
97
+ if !options[:position]
98
+ options[:position] = 0
99
+ end
100
+ raise "Count required" unless options[:count]
101
+ count = options[:count]
102
+ count.times do
103
+ add_label(options)
104
+ options[:position] = options[:position] + 1
105
+ end
106
+ end
107
+ =begin rdoc
108
+ To facilitate aligning a printer we give a method that prints the outlines of the labels
109
+ =end
110
+ def draw_boxes(write_coord = true, draw_markups = true)
111
+ @layout.nx.times do |x|
112
+ @layout.ny.times do |y|
113
+ box_x, box_y = get_x_y(x, y)
114
+ @pdf.rounded_rectangle(box_x,
115
+ box_y,
116
+ @label.width.as_pts,
117
+ @label.height.as_pts,
118
+ @label.round.as_pts).stroke
119
+ if write_coord
120
+ text = "#{box_x / 72}, #{box_y / 72}, #{@label.width.number}, #{label.height.number}"
121
+ add_label(:x => box_x, :y => box_y, :text => text)
122
+ end
123
+
124
+ if draw_markups
125
+ @label.markupMargins.each {|margin|
126
+ size = margin.size.as_pts
127
+ @pdf.rounded_rectangle(box_x + size,
128
+ box_y - margin.size.as_pts,
129
+ @label.width.as_pts - 2*size,
130
+ @label.height.as_pts - 2*size,
131
+ @label.round.as_pts).stroke
132
+ }
133
+ @label.markupLines.each {|line|
134
+ @pdf.line(box_x + line.x1.as_pts,
135
+ box_y + line.y1.as_pts,
136
+ box_x + line.x2.as_pts,
137
+ box_y + line.y2.as_pts).stroke
138
+ }
139
+ =begin TODO Draw cirles
140
+ @label.markupCircles.each {|cicle|
141
+ @pdf.
142
+ =end
143
+ end
144
+
145
+ end
146
+ end
147
+ end
148
+
149
+ =begin rdoc
150
+ add_label takes an argument hash.
151
+ [:position] Which label slot to print. Positions are top to bottom, left to right so position 1 is the label in the top lefthand corner. Defaults to 0
152
+ [:x & :y] The (x,y) coordinates on the page to print the text. Ignored if position is specified.
153
+ [:text] What you want to print in the label. Defaults to the (x,y) of the top left corner of the label.
154
+ [:use_margin] If the label has a markupMargin, setting this argument to true will respect that margin when writing text. Defaults to true.
155
+ [:justification] Values can be :left, :right, :center, :full. Defaults to :left
156
+ [:offset_x, offset_y] If your printer doesn't want to print with out margins you can define these values to fine tune printout.
157
+ =end
158
+ def add_barcode_label(options = {})
159
+ label_x, label_y, label_width = setup_add_label_options(options)
160
+
161
+ text = options[:text] || "[#{label_x / 72}, #{label_y / 72}]"
162
+
163
+ bar_text = setup_bar_text(options, text)
164
+
165
+ arg_hash = setup_arg_hash(options, label_x, label_y, label_width)
166
+
167
+ bar_hash = arg_hash.clone
168
+ bar_hash[:font_size] = options[:bar_size] || 12
169
+
170
+ old_font = @pdf.current_font!
171
+ @pdf.select_font(self.barcode_font)
172
+
173
+ @pdf.y = label_y
174
+ @pdf.text(bar_text,bar_hash)
175
+
176
+ @pdf.select_font(old_font)
177
+ @pdf.text(text,arg_hash)
178
+ end
179
+
180
+ def save_as(file_name)
181
+ @pdf.save_as(file_name)
182
+ end
183
+
184
+ def barcode_font
185
+ @barcode_font ||= "Code3de9.afm"
186
+ end
187
+
188
+ def barcode_font=(value)
189
+ if Pdf::Label::Batch.all_barcode_fonts.keys.include?(value)
190
+ @barcode_font = value
191
+ return @barcode_font
192
+ else
193
+ raise "Barcode Font Not Found for #{value}"
194
+ end
195
+ end
196
+
197
+ protected
198
+
199
+ =begin rdoc
200
+ Position is top to bottom, left to right, starting at 1 and ending at the end of the page
201
+ =end
202
+ def position_to_x_y(position)
203
+ x = (position * 1.0 / @layout.ny).floor
204
+ y = position % @layout.ny
205
+ return get_x_y(x, y)
206
+ end
207
+
208
+ def get_x_y(x, y)
209
+ label_y = @pdf.absolute_top_margin
210
+ label_y = label_y + @pdf.top_margin
211
+ label_y = label_y - @layout.y0.as_pts
212
+ label_y = label_y - y * @layout.dy.as_pts
213
+
214
+ label_x = @pdf.absolute_left_margin
215
+ label_x = label_x - @pdf.left_margin
216
+ label_x = label_x + @layout.x0.as_pts
217
+ label_x = label_x + x * @layout.dx.as_pts
218
+
219
+ return label_x, label_y
220
+ end
221
+
222
+ def setup_add_label_options(options)
223
+ if position = options[:position]
224
+ # condition to handle multi-page PDF generation. If true, we're past the first page
225
+ if position > @zero_based_labels_per_page
226
+ # if remainder is zero, we're dealing with the first label of a new page
227
+ @pdf.new_page if ((position) % @labels_per_page) == 0
228
+ # Translate the position to a value between 1 and the number of labels for a given page
229
+ position = position - (position/@labels_per_page)*@labels_per_page
230
+ end
231
+ label_x, label_y = position_to_x_y(position)
232
+ elsif((label_x = options[:x]) && (label_y = options[:y]))
233
+ else
234
+ label_x, label_y = position_to_x_y(0)
235
+ end
236
+ #line wrap margin
237
+ label_width = label_x + @label.width.as_pts
238
+
239
+ if (use_margin = options[:use_margin]).nil?
240
+ use_margin = true
241
+ end
242
+ if use_margin
243
+ @label.markupMargins.each {|margin|
244
+ label_x = label_x + margin.size.as_pts
245
+ label_y = label_y - margin.size.as_pts
246
+ label_width = label_width - margin.size.as_pts
247
+ }
248
+ end
249
+
250
+ if offset = options[:offset_x]
251
+ label_x = label_x + offset
252
+ label_width = label_width + offset
253
+ end
254
+ if offset = options[:offset_y]
255
+ label_y = label_y + offset
256
+ end
257
+ return label_x, label_y, label_width
258
+ end
259
+
260
+ def setup_arg_hash(options, label_x, label_y, label_width)
261
+ arg_hash = {:justification => (options[:justification] || :left),
262
+ :font_size => (options[:font_size] || 12)}
263
+
264
+ arg_hash = arg_hash.merge :absolute_left => label_x,
265
+ :absolute_right => label_width
266
+
267
+ end
268
+
269
+ def setup_bar_text(options, text)
270
+ bar_text = options[:bar_text] || text
271
+ bar_text = send(Pdf::Label::Batch.all_barcode_fonts[self.barcode_font], bar_text)
272
+ end
273
+
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,38 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/template')
2
+ module Pdf
3
+ module Label
4
+ class GlabelsTemplate
5
+ include XML::Mapping
6
+
7
+ hash_node :templates, "Template", "@name", :class => Template, :default_value => Hash.new
8
+
9
+ def find_all_templates
10
+ return @t unless @t.nil?
11
+ @t = []
12
+ templates.each {|t|
13
+ @t << "#{t[1].name}"
14
+ t[1].alias.each {|a|
15
+ @t << "#{a[1].name}"
16
+ }
17
+ }
18
+ return @t
19
+ end
20
+
21
+
22
+ def find_template(t_name)
23
+ return find_all_with_templates if t_name == :all
24
+ if t = templates[t_name]
25
+ return t
26
+ else
27
+ templates.each { |t|
28
+ if t[1].alias[t_name]
29
+ return t[1]
30
+ end
31
+ }
32
+ end
33
+ return nil
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,55 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/length_node')
2
+ require File.expand_path(File.dirname(__FILE__) + '/layout')
3
+ require File.expand_path(File.dirname(__FILE__) + '/markup')
4
+
5
+ module Pdf
6
+ module Label
7
+
8
+ class Label
9
+ include XML::Mapping
10
+ attr_accessor :shape
11
+ numeric_node :id, "@id"
12
+ array_node :markupMargins, "Markup-margin", :class => MarkupMargin, :default_value => nil
13
+ array_node :markupLines, "Markup-line", :class => MarkupLine, :default_value => nil
14
+ array_node :markupCircles, "Markup-circle", :class => MarkupCircle, :default_value => nil
15
+
16
+ array_node :layouts, "Layout", :class => Layout
17
+
18
+ def markups
19
+ @markups = Hash.new
20
+ @markups = @markups.merge @markupMargins
21
+ @markups = @markups.merge @markupLines
22
+ @markups = @markups.merge @markupCircles
23
+ @markups
24
+ end
25
+
26
+ end
27
+
28
+
29
+
30
+ class LabelRectangle < Label
31
+ length_node :width, "@width"
32
+ length_node :height, "@height"
33
+ length_node :round, "@round", :default_value => "0 pt"
34
+ length_node :waste, "@waste", :default_value => "0 pt"
35
+ length_node :x_waste, "@x_waste", :default_value => "0 pt"
36
+ length_node :y_waste, "@y_waste", :default_value => "0 pt"
37
+ @kind = "Rectangle"
38
+ end
39
+
40
+ class LabelRound < Label
41
+ length_node :radius, "@radius"
42
+ length_node :waste, "@radius", :default_value => "0 pt"
43
+ @kind = "Round"
44
+ end
45
+
46
+ class LabelCD < Label
47
+ length_node :radius, "@radius"
48
+ length_node :hole, "@hole"
49
+ length_node :width, "@width", :default_value => ""
50
+ length_node :height, "@height", :default_value => ""
51
+ length_node :waste, "@waste", :default_value => ""
52
+ @kind = "CD"
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/length_node')
2
+
3
+ module Pdf
4
+ module Label
5
+ class Layout
6
+ include XML::Mapping
7
+ numeric_node :nx, "@nx"
8
+ numeric_node :ny, "@ny"
9
+ length_node :x0, "@x0", :default_value => "0 pt"
10
+ length_node :y0, "@y0", :default_value => "0 pt"
11
+ length_node :dx, "@dx", :default_value => "0 pt"
12
+ length_node :dy, "@dy", :default_value => "0 pt"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ require 'xml/mapping/base'
2
+
3
+ module Pdf
4
+ module Label
5
+ class Length
6
+ attr_accessor :value, :unit, :number
7
+
8
+ def initialize(value)
9
+ @value = value
10
+ @number = value.match(/[\d\.]*/)[0].to_f
11
+ @unit = value.delete("#{number}").strip
12
+ end
13
+
14
+ #Return the numeric portion as a Points
15
+ def as_pts
16
+ if @unit =~ /pt/
17
+ return @number
18
+ elsif @unit =~ /in/
19
+ return @number * 72 #72.270
20
+ elsif @unit =~ /mm/
21
+ return @number * 2.83464566929134
22
+ elsif @unit =~ /cm/
23
+ return @number * 28.3464566929134
24
+ elsif @unit =~ /pc/
25
+ return 1.0 * @number / 12
26
+ elsif @unit == ''
27
+ return @number
28
+ else
29
+ raise "Unit #{unit} unknown"
30
+ end
31
+ end
32
+ end
33
+
34
+ class LengthNode < XML::Mapping::SingleAttributeNode
35
+ def initialize_impl(path)
36
+ @path = XML::XXPath.new(path)
37
+ end
38
+
39
+ def extract_attr_value(xml)
40
+ @value = default_when_xpath_err{@path.first(xml).text}
41
+ Length.new(@value)
42
+ end
43
+
44
+ def set_attr_value(xml, value)
45
+ raise "Not a Length: #{value}" unless Length===value
46
+ @path.first(xml,:ensure_created=>true).text = value.value
47
+ end
48
+ end
49
+
50
+ XML::Mapping.add_node_class LengthNode
51
+ end
52
+ end