pdf-wrapper 0.2.1 → 0.3.0

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.
@@ -200,6 +200,18 @@ module PDF
200
200
  return width / Pango::SCALE
201
201
  end
202
202
 
203
+ def default_text_options
204
+ { :font => @default_font,
205
+ :font_size => @default_font_size,
206
+ :alignment => :left,
207
+ :wrap => :wordchar,
208
+ :justify => false,
209
+ :spacing => 0,
210
+ :color => nil,
211
+ :markup => nil
212
+ }
213
+ end
214
+
203
215
  private
204
216
 
205
217
  # takes a string and a range of options and creates a pango layout for us. Pango
@@ -299,18 +311,6 @@ module PDF
299
311
  return layout
300
312
  end
301
313
 
302
- def default_text_options
303
- { :font => @default_font,
304
- :font_size => @default_font_size,
305
- :alignment => :left,
306
- :wrap => :wordchar,
307
- :justify => false,
308
- :spacing => 0,
309
- :color => nil,
310
- :markup => nil
311
- }
312
- end
313
-
314
314
  # renders a pango layout onto our main context
315
315
  # based on a function of the same name found in the text2.rb sample file
316
316
  # distributed with rcairo - it's still black magic to me and has a few edge
@@ -0,0 +1,53 @@
1
+ # coding: utf-8
2
+
3
+ module PDF
4
+ class Wrapper
5
+ class TextCell
6
+
7
+ attr_reader :data, :min_width, :natural_width, :max_width, :wrapper
8
+ attr_accessor :width, :height
9
+ attr_writer :options
10
+
11
+ def initialize(str)
12
+ @data = str.to_s
13
+ @options = {}
14
+ end
15
+
16
+ def draw(wrapper, x, y)
17
+ @wrapper = wrapper
18
+
19
+ wrapper.cell(self.data, x, y, self.width, self.height, self.options)
20
+ end
21
+
22
+ def calculate_width_range(wrapper)
23
+ @wrapper = wrapper
24
+
25
+ padding = options[:padding] || 3
26
+ if options[:markup] == :pango
27
+ str = self.data.dup.gsub(/<.+?>/,"").gsub("&amp;","&").gsub("&lt;","<").gsub("&gt;",">")
28
+ else
29
+ str = self.data.dup
30
+ end
31
+ @min_width = wrapper.text_width(str.gsub(/\b|\B/,"\n"), text_options) + (padding * 4)
32
+ @natural_width = wrapper.text_width(str, text_options) + (padding * 4)
33
+ end
34
+
35
+ def calculate_height(wrapper)
36
+ raise "Cannot calculate height until cell width is set" if self.width.nil?
37
+
38
+ @wrapper = wrapper
39
+
40
+ padding = options[:padding] || 3
41
+ @height = wrapper.text_height(self.data, self.width - (padding * 2), text_options) + (padding * 2)
42
+ end
43
+
44
+ def options
45
+ @options ||= {}
46
+ end
47
+
48
+ def text_options
49
+ self.options.only(wrapper.default_text_options.keys)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,68 @@
1
+ # coding: utf-8
2
+
3
+ module PDF
4
+ class Wrapper
5
+ class TextImageCell
6
+
7
+ attr_reader :text, :min_width, :natural_width, :max_width, :wrapper
8
+ attr_accessor :width, :height
9
+ attr_writer :options
10
+
11
+ def initialize(str, filename, width, height)
12
+ @text = str.to_s
13
+ @filename = filename
14
+ @min_width = width
15
+ @natural_width = width
16
+ @max_width = width
17
+ @height = height
18
+ @options = {}
19
+ end
20
+
21
+ def draw(wrapper, x, y)
22
+ @wrapper = wrapper
23
+
24
+ wrapper.cell(self.text, x, y, self.width, self.height, self.options)
25
+ wrapper.image(@filename, image_options(x,y))
26
+ end
27
+
28
+ def calculate_width_range(wrapper)
29
+ # nothing required, width range set in constructor
30
+ end
31
+
32
+ def calculate_height(wrapper)
33
+ # nothing required, height set in constructor
34
+ end
35
+
36
+ def options
37
+ @options ||= {}
38
+ end
39
+
40
+ private
41
+
42
+ def image_offset
43
+ @image_offset ||= text_height + 4
44
+ end
45
+
46
+ def image_options(x, y)
47
+ {
48
+ :left => x,
49
+ :top => y + image_offset,
50
+ :width => self.width,
51
+ :height => self.height - image_offset,
52
+ :proportional => true,
53
+ :center => true
54
+ }
55
+ end
56
+
57
+ def text_height
58
+ padding = options[:padding] || 3
59
+ wrapper.text_height(self.text, self.width - (padding * 2), text_options) + (padding * 2)
60
+ end
61
+
62
+ def text_options
63
+ self.options.only(wrapper.default_text_options.keys)
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -18,8 +18,8 @@ context "The PDF::Wrapper class" do
18
18
  # the begin_new_subpath command specifies the start of the line, append line specifies the end
19
19
  receiver.count(:begin_new_subpath).should eql(1)
20
20
  receiver.count(:append_line).should eql(1)
21
- receiver.first_occurance_of(:begin_new_subpath)[:args].should eql([x0.to_f, 741.89])
22
- receiver.first_occurance_of(:append_line)[:args].should eql([x1.to_f, 641.89])
21
+ receiver.first_occurance_of(:begin_new_subpath)[:args].should eql([x0, 741.89])
22
+ receiver.first_occurance_of(:append_line)[:args].should eql([x1, 641.89])
23
23
  end
24
24
 
25
25
  specify "should be able to draw a single line onto the canvas with a width of 5" do
@@ -36,9 +36,9 @@ context "The PDF::Wrapper class" do
36
36
  receiver.count(:set_line_width).should eql(1)
37
37
  receiver.count(:begin_new_subpath).should eql(1)
38
38
  receiver.count(:append_line).should eql(1)
39
- receiver.first_occurance_of(:set_line_width)[:args].should eql([width.to_f])
40
- receiver.first_occurance_of(:begin_new_subpath)[:args].should eql([x0.to_f, 741.89])
41
- receiver.first_occurance_of(:append_line)[:args].should eql([x1.to_f, 641.89])
39
+ receiver.first_occurance_of(:set_line_width)[:args].should eql([width])
40
+ receiver.first_occurance_of(:begin_new_subpath)[:args].should eql([x0, 741.89])
41
+ receiver.first_occurance_of(:append_line)[:args].should eql([x1, 641.89])
42
42
  end
43
43
 
44
44
  specify "should be able to draw a cubic bezier spline onto the canvas"
@@ -57,7 +57,7 @@ context "The PDF::Wrapper class" do
57
57
  callbacks.size.should eql(2)
58
58
  # don't care about the first rectangel, it just goes around the outside of the page
59
59
  callbacks.shift
60
- callbacks.shift[:args].should eql([100.0, 741.89, 200.0, -200.0])
60
+ callbacks.shift[:args].should eql([100, 741.89, 200, -200])
61
61
  end
62
62
 
63
63
  specify "should be able to draw an empty rectangle onto the canvas with a line width of 5" do
@@ -72,14 +72,14 @@ context "The PDF::Wrapper class" do
72
72
 
73
73
  # ensure the line width was set correctly
74
74
  receiver.count(:set_line_width).should eql(1)
75
- receiver.first_occurance_of(:set_line_width)[:args].should eql([width.to_f])
75
+ receiver.first_occurance_of(:set_line_width)[:args].should eql([width])
76
76
 
77
77
  # the begin_new_subpath command specifies the start of the line, append line specifies the end
78
78
  callbacks = receiver.all(:append_rectangle)
79
79
  callbacks.size.should eql(2)
80
80
  # don't care about the first rectangel, it just goes around the outside of the page
81
81
  callbacks.shift
82
- callbacks.shift[:args].should eql([100.0, 741.89, 200.0, -200.0])
82
+ callbacks.shift[:args].should eql([100, 741.89, 200, -200])
83
83
  end
84
84
 
85
85
  specify "should be able to draw a filled rectangle onto the canvas"
data/specs/tables_spec.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require File.dirname(__FILE__) + '/spec_helper'
4
4
 
5
- context "The PDF::Wrapper class" do
5
+ context PDF::Wrapper do
6
6
 
7
7
  before(:each) { create_pdf }
8
8
 
@@ -20,6 +20,41 @@ context "The PDF::Wrapper class" do
20
20
  receiver.content.first.include?("data4").should be_true
21
21
  end
22
22
 
23
+ specify "should be able to draw a table on the canvas using an array of TextCells" do
24
+ data = [
25
+ [ PDF::Wrapper::TextCell.new("data1"), PDF::Wrapper::TextCell.new("data2")],
26
+ [ PDF::Wrapper::TextCell.new("data3"), PDF::Wrapper::TextCell.new("data4")]
27
+ ]
28
+ @pdf.table(data)
29
+ @pdf.finish
30
+
31
+ receiver = PageTextReceiver.new
32
+ reader = PDF::Reader.string(@output.string, receiver)
33
+
34
+ receiver.content.first.include?("data1").should be_true
35
+ receiver.content.first.include?("data2").should be_true
36
+ receiver.content.first.include?("data3").should be_true
37
+ receiver.content.first.include?("data4").should be_true
38
+ end
39
+
40
+ specify "should be able to draw a table on the canvas using an array of TextImageCells" do
41
+ filename = File.dirname(__FILE__) + "/data/orc.svg"
42
+ data = [
43
+ [ "data1", PDF::Wrapper::TextImageCell.new("data2", filename, 100, 100)],
44
+ [ "data3", PDF::Wrapper::TextImageCell.new("data4", filename, 100, 100)]
45
+ ]
46
+ @pdf.table(data)
47
+ @pdf.finish
48
+
49
+ receiver = PageTextReceiver.new
50
+ reader = PDF::Reader.string(@output.string, receiver)
51
+
52
+ receiver.content.first.include?("data1").should be_true
53
+ receiver.content.first.include?("data2").should be_true
54
+ receiver.content.first.include?("data3").should be_true
55
+ receiver.content.first.include?("data4").should be_true
56
+ end
57
+
23
58
  specify "should be able to draw a table on the canvas using a PDF::Wrapper::Table object" do
24
59
  table = PDF::Wrapper::Table.new do |t|
25
60
  t.data = [%w{data1 data2}, %w{data3 data4}]
@@ -110,3 +145,201 @@ context "The PDF::Wrapper class" do
110
145
  end
111
146
 
112
147
  end
148
+
149
+ context PDF::Wrapper, "data= method" do
150
+
151
+ specify "should raise an exception if given rows of uneven size" do
152
+ data = [%w{head1 head2},%w{data1}]
153
+ table = PDF::Wrapper::Table.new
154
+ lambda { table.data = data }.should raise_error(ArgumentError)
155
+ end
156
+
157
+ specify "should convert all non cell objects to TextCells" do
158
+ data = [%w{head1 head2},%w{data1 data2}]
159
+ table = PDF::Wrapper::Table.new
160
+ table.data = data
161
+ table.each_cell do |cell|
162
+ cell.should be_a_kind_of(PDF::Wrapper::TextCell)
163
+ end
164
+ end
165
+
166
+ specify "should leave existing TextCells unchanged" do
167
+ manual_cell_one = PDF::Wrapper::TextCell.new("data1")
168
+ manual_cell_two = PDF::Wrapper::TextCell.new("data2")
169
+ data = [[manual_cell_one, manual_cell_two]]
170
+
171
+ table = PDF::Wrapper::Table.new
172
+ table.data = data
173
+
174
+ cells = []
175
+ table.each_cell do |cell|
176
+ cells << cell
177
+ end
178
+ (cells[0] === manual_cell_one).should be_true
179
+ (cells[1] === manual_cell_two).should be_true
180
+ end
181
+
182
+ specify "should leave existing TextImageCells unchanged" do
183
+ manual_cell_one = PDF::Wrapper::TextImageCell.new("data1", "image.png", 100, 100)
184
+ manual_cell_two = PDF::Wrapper::TextImageCell.new("data2", "image.png", 100, 100)
185
+ data = [[manual_cell_one, manual_cell_two]]
186
+
187
+ table = PDF::Wrapper::Table.new
188
+ table.data = data
189
+
190
+ cells = []
191
+ table.each_cell do |cell|
192
+ cells << cell
193
+ end
194
+ (cells[0] === manual_cell_one).should be_true
195
+ (cells[1] === manual_cell_two).should be_true
196
+ end
197
+
198
+ specify "should set the default table options on all cells" do
199
+ data = [%w{head1 head2},%w{data1 data2}]
200
+ table = PDF::Wrapper::Table.new(:markup => :pango)
201
+
202
+ table.data = data
203
+
204
+ table.each_cell do |cell|
205
+ cell.options.should eql(:markup => :pango)
206
+ end
207
+ end
208
+ end
209
+
210
+ context PDF::Wrapper, "headers method" do
211
+
212
+ specify "should raise an exception if given cell count does not match existing data" do
213
+ data = [%w{data1 data2},%w{data1 data2}]
214
+ headers = %w{head1}
215
+
216
+ table = PDF::Wrapper::Table.new
217
+ table.data = data
218
+
219
+ lambda { table.headers(headers) }.should raise_error(ArgumentError)
220
+ end
221
+
222
+ specify "should wrap non-cell objects in a TextCell" do
223
+ headers = [["head1","head2"]]
224
+
225
+ table = PDF::Wrapper::Table.new
226
+ table.headers(headers)
227
+
228
+ set_headers = table.instance_variable_get("@headers")
229
+ set_headers.each do |cell|
230
+ cell.should be_a_kind_of(PDF::Wrapper::TextCell)
231
+ end
232
+ end
233
+
234
+ specify "should leave TextCell objects untouched" do
235
+ manual_cell_one = PDF::Wrapper::TextCell.new("data1")
236
+ manual_cell_two = PDF::Wrapper::TextCell.new("data2")
237
+ headers = [manual_cell_one, manual_cell_two]
238
+
239
+ table = PDF::Wrapper::Table.new
240
+ table.headers(headers)
241
+
242
+ set_headers = table.instance_variable_get("@headers")
243
+ (set_headers[0] === manual_cell_one).should be_true
244
+ (set_headers[1] === manual_cell_two).should be_true
245
+ end
246
+
247
+ specify "should leave TextImageCell objects untouched" do
248
+ manual_cell_one = PDF::Wrapper::TextImageCell.new("data1", "image.png", 100, 100)
249
+ manual_cell_two = PDF::Wrapper::TextImageCell.new("data2", "image.png", 100, 100)
250
+ headers = [manual_cell_one, manual_cell_two]
251
+
252
+ table = PDF::Wrapper::Table.new
253
+ table.headers(headers)
254
+
255
+ set_headers = table.instance_variable_get("@headers")
256
+ (set_headers[0] === manual_cell_one).should be_true
257
+ (set_headers[1] === manual_cell_two).should be_true
258
+ end
259
+
260
+ specify "should set options on all cells" do
261
+ headers = ["head1","head2"]
262
+
263
+ table = PDF::Wrapper::Table.new
264
+ table.headers(headers, :markup => :pango)
265
+
266
+ set_headers = table.instance_variable_get("@headers")
267
+ set_headers.each do |cell|
268
+ cell.options.should eql(:markup => :pango)
269
+ end
270
+ end
271
+
272
+ specify "should set default table options on all cells" do
273
+ headers = ["head1","head2"]
274
+
275
+ table = PDF::Wrapper::Table.new(:markup => :pango)
276
+ table.headers(headers)
277
+
278
+ set_headers = table.instance_variable_get("@headers")
279
+ set_headers.each do |cell|
280
+ cell.options.should eql(:markup => :pango)
281
+ end
282
+ end
283
+ end
284
+
285
+ context PDF::Wrapper, "cell method" do
286
+
287
+ specify "should return the appropriate cell" do
288
+ data = [%w{data1 data2},%w{data3 data4}]
289
+ headers = %w{head1}
290
+
291
+ table = PDF::Wrapper::Table.new
292
+ table.data = data
293
+
294
+ table.cell(0,0).should be_a_kind_of(PDF::Wrapper::TextCell)
295
+ table.cell(0,0).data.should eql("data1")
296
+
297
+ table.cell(1,1).should be_a_kind_of(PDF::Wrapper::TextCell)
298
+ table.cell(1,1).data.should eql("data4")
299
+ end
300
+ end
301
+
302
+ context PDF::Wrapper, "cell_options method" do
303
+
304
+ specify "should set options on the appropriate cell" do
305
+ data = [%w{data1 data2},%w{data3 data4}]
306
+
307
+ table = PDF::Wrapper::Table.new
308
+ table.data = data
309
+ table.cell_options(0,0, :markup => :pango)
310
+
311
+ table.cell(0,0).options.should eql(:markup => :pango)
312
+ end
313
+ end
314
+
315
+ context PDF::Wrapper, "col_options method" do
316
+
317
+ specify "should set options on all cells in the appropriate column" do
318
+ data = [%w{data1 data2},%w{data3 data4}]
319
+
320
+ table = PDF::Wrapper::Table.new
321
+ table.data = data
322
+ table.col_options(0, :markup => :pango)
323
+
324
+ table.cell(0,0).options.should eql(:markup => :pango)
325
+ table.cell(0,1).options.should eql(:markup => :pango)
326
+ table.cell(1,0).options.should eql({})
327
+ table.cell(1,1).options.should eql({})
328
+ end
329
+ end
330
+
331
+ context PDF::Wrapper, "row_options method" do
332
+
333
+ specify "should set options on all cells in the appropriate row" do
334
+ data = [%w{data1 data2},%w{data3 data4}]
335
+
336
+ table = PDF::Wrapper::Table.new
337
+ table.data = data
338
+ table.row_options(0, :markup => :pango)
339
+
340
+ table.cell(0,0).options.should eql(:markup => :pango)
341
+ table.cell(1,0).options.should eql(:markup => :pango)
342
+ table.cell(0,1).options.should eql({})
343
+ table.cell(1,1).options.should eql({})
344
+ end
345
+ end