pdf-labels 1.0.1 → 2.0.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.
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