pdf-wrapper 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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