jerryvos-prawn-layout 0.2.0.3 → 0.2.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,515 @@
1
+ # encoding: utf-8
2
+ #
3
+ # table.rb : Simple table drawing functionality
4
+ #
5
+ # Copyright June 2008, Gregory Brown. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ require "prawn/table/cell"
10
+
11
+ module Prawn
12
+ class Document
13
+
14
+ # Builds and renders a Document::Table object from raw data.
15
+ # For details on the options that can be passed, see
16
+ # Document::Table.new
17
+ #
18
+ # data = [["Gregory","Brown"],["James","Healy"],["Jia","Wu"]]
19
+ #
20
+ # Prawn::Document.generate("table.pdf") do
21
+ #
22
+ # # Default table, without headers
23
+ # table(data)
24
+ #
25
+ # # Default table with headers
26
+ # table data, :headers => ["First Name", "Last Name"]
27
+ #
28
+ # # Very close to PDF::Writer's default SimpleTable output
29
+ # table data, :headers => ["First Name", "Last Name"],
30
+ # :font_size => 10,
31
+ # :vertical_padding => 2,
32
+ # :horizontal_padding => 5,
33
+ # :position => :center,
34
+ # :row_colors => :pdf_writer,
35
+ #
36
+ # # Grid border style with explicit column widths.
37
+ # table data, :border_style => :grid,
38
+ # :column_widths => { 0 => 100, 1 => 150 }
39
+ #
40
+ # end
41
+ #
42
+ # Will raise <tt>Prawn::Errors::EmptyTable</tt> given
43
+ # a nil or empty <tt>data</tt> paramater.
44
+ #
45
+ def table(data, options={})
46
+ if data.nil? || data.empty?
47
+ raise Prawn::Errors::EmptyTable,
48
+ "data must be a non-empty, non-nil, two dimensional array of Prawn::Cells or strings"
49
+ end
50
+ Prawn::Table.new(data,self,options).draw
51
+ end
52
+ end
53
+
54
+
55
+ # This class implements simple PDF table generation.
56
+ #
57
+ # Prawn tables have the following features:
58
+ #
59
+ # * Can be generated with or without headers
60
+ # * Can tweak horizontal and vertical padding of text
61
+ # * Minimal styling support (borders / row background colors)
62
+ # * Can be positioned by bounding boxes (left/center aligned) or an
63
+ # absolute x position
64
+ # * Automated page-breaking as needed
65
+ # * Column widths can be calculated automatically or defined explictly on a
66
+ # column by column basis
67
+ # * Text alignment can be set for the whole table or by column
68
+ #
69
+ # The current implementation is a bit barebones, but covers most of the
70
+ # basic needs for PDF table generation. If you have feature requests,
71
+ # please share them at: http://groups.google.com/group/prawn-ruby
72
+ #
73
+ # Tables will be revisited before the end of the Ruby Mendicant project and
74
+ # the most commonly needed functionality will likely be added.
75
+ #
76
+ class Table
77
+
78
+ include Prawn::Configurable
79
+
80
+ attr_reader :column_widths # :nodoc:
81
+
82
+ NUMBER_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d+(?:[eE][+-]?\d+)?)?$/ #:nodoc:
83
+
84
+ # Creates a new Document::Table object. This is generally called
85
+ # indirectly through Document#table but can also be used explictly.
86
+ #
87
+ # The <tt>data</tt> argument is a two dimensional array of strings,
88
+ # organized by row, e.g. [["r1-col1","r1-col2"],["r2-col2","r2-col2"]].
89
+ # As with all Prawn text drawing operations, strings must be UTF-8 encoded.
90
+ #
91
+ # The following options are available for customizing your tables, with
92
+ # defaults shown in [] at the end of each description.
93
+ #
94
+ # <tt>:headers</tt>:: An array of table headers, either strings or Cells. [Empty]
95
+ # <tt>:align_headers</tt>:: Alignment of header text. Specify for entire header (<tt>:left</tt>) or by column (<tt>{ 0 => :right, 1 => :left}</tt>). If omitted, the header alignment is the same as the column alignment.
96
+ # <tt>:header_text_color</tt>:: Sets the text color of the headers
97
+ # <tt>:header_color</tt>:: Manually sets the header color
98
+ # <tt>:font_size</tt>:: The font size for the text cells . [12]
99
+ # <tt>:horizontal_padding</tt>:: The horizontal cell padding in PDF points [5]
100
+ # <tt>:vertical_padding</tt>:: The vertical cell padding in PDF points [5]
101
+ # <tt>:padding</tt>:: Horizontal and vertical cell padding (overrides both)
102
+ # <tt>:border_width</tt>:: With of border lines in PDF points [1]
103
+ # <tt>:border_style</tt>:: If set to :grid, fills in all borders. If set to :underline_header, underline header only. Otherwise, borders are drawn on columns only, not rows
104
+ # <tt>:border_color</tt>:: Sets the color of the borders.
105
+ # <tt>:position</tt>:: One of <tt>:left</tt>, <tt>:center</tt> or <tt>n</tt>, where <tt>n</tt> is an x-offset from the left edge of the current bounding box
106
+ # <tt>:width:</tt> A set width for the table, defaults to the sum of all column widths
107
+ # <tt>:max_width:</tt> The maximum width of the table, defaults to the width or the document's margin box width.
108
+ # <tt>:column_widths:</tt> A hash of indices and widths in PDF points. E.g. <tt>{ 0 => 50, 1 => 100 }</tt>
109
+ # <tt>:row_colors</tt>:: An array of row background colors which are used cyclicly.
110
+ # <tt>:align</tt>:: Alignment of text in columns, for entire table (<tt>:center</tt>) or by column (<tt>{ 0 => :left, 1 => :center}</tt>)
111
+ #
112
+ # Row colors are specified as html encoded values, e.g.
113
+ # ["ffffff","aaaaaa","ccaaff"]. You can also specify
114
+ # <tt>:row_colors => :pdf_writer</tt> if you wish to use the default color
115
+ # scheme from the PDF::Writer library.
116
+ #
117
+ # See Document#table for typical usage, as directly using this class is
118
+ # not recommended unless you know why you want to do it.
119
+ #
120
+ def initialize(data, document, options={})
121
+ unless data.all? { |e| Array === e }
122
+ raise Prawn::Errors::InvalidTableData,
123
+ "data must be a two dimensional array of Prawn::Cells or strings"
124
+ end
125
+
126
+ @data = data
127
+ @document = document
128
+
129
+ Prawn.verify_options [:font_size,:border_style, :border_width,
130
+ :position, :headers, :row_colors, :align, :align_headers,
131
+ :header_text_color, :border_color, :horizontal_padding,
132
+ :vertical_padding, :padding, :column_widths, :width, :max_width, :header_color ],
133
+ options
134
+
135
+ configuration.update(options)
136
+
137
+ if padding = options[:padding]
138
+ C(:horizontal_padding => padding, :vertical_padding => padding)
139
+ end
140
+
141
+ if options[:row_colors] == :pdf_writer
142
+ C(:row_colors => ["ffffff","cccccc"])
143
+ end
144
+
145
+ if options[:row_colors]
146
+ C(:original_row_colors => C(:row_colors))
147
+ end
148
+
149
+ # Once we have all configuration setted...
150
+ normalize_data
151
+ check_rows_lengths
152
+ calculate_column_widths(options[:column_widths], options[:width], options[:max_width])
153
+ end
154
+
155
+ attr_reader :column_widths #:nodoc:
156
+
157
+ # Width of the table in PDF points
158
+ #
159
+ def width
160
+ @column_widths.inject(0) { |s,r| s + r }
161
+ end
162
+
163
+ # Draws the table onto the PDF document
164
+ #
165
+ def draw
166
+ @parent_bounds = @document.bounds
167
+ case C(:position)
168
+ when :center
169
+ x = (@document.bounds.width - width) / 2.0
170
+ dy = @document.bounds.absolute_top - @document.y
171
+ @document.bounding_box [x, @parent_bounds.top], :width => width do
172
+ @document.move_down(dy)
173
+ generate_table
174
+ end
175
+ when Numeric
176
+ x, y = C(:position), @document.y - @document.bounds.absolute_bottom
177
+ @document.bounding_box([x,y], :width => width) { generate_table }
178
+ else
179
+ generate_table
180
+ end
181
+ end
182
+
183
+ private
184
+
185
+ def default_configuration
186
+ { :font_size => 12,
187
+ :border_width => 1,
188
+ :position => :left,
189
+ :horizontal_padding => 5,
190
+ :vertical_padding => 5 }
191
+ end
192
+
193
+ # Check that all rows are well formed with the same length.
194
+ #
195
+ # Will raise an <tt>Prawn::Errors::InvalidTableData</tt> exception
196
+ # in case that a bad formed row is found
197
+ def check_rows_lengths
198
+ tables_width = nil
199
+ actual_row = 0
200
+ old_index = -1
201
+ check_last_row = lambda {
202
+ tables_width ||= old_index # only setted the first time
203
+ if tables_width != nil && tables_width != old_index
204
+ raise Prawn::Errors::InvalidTableData
205
+ "The row #{actual_row} has a length of #{old_index + 1}, " +
206
+ "it should be of #{tables_width + 1} according to the previous rows"
207
+ end
208
+ }
209
+ each_cell_with_index do |cell, i, n_row|
210
+ if actual_row != n_row # is new row
211
+ check_last_row.call
212
+ actual_row = n_row
213
+ end
214
+ old_index = i + cell.colspan - 1
215
+ end
216
+ check_last_row.call
217
+ end
218
+
219
+ # An iterator method around renderable_data method.
220
+ #
221
+ # The issue using renderable_data is that in each iteration you don't know
222
+ # the real index for that cell, due to colspan & rowspan values of the
223
+ # previous cells.
224
+ #
225
+ # So this method yields every cell (Prawn::Table::Cell) with its column
226
+ # index.
227
+ #
228
+ # Example:
229
+ # +-----------+
230
+ # | A | B |
231
+ # +-------+---+
232
+ # | C | D | E |
233
+ # +---+---+---+
234
+ # The values in each iteration will be:
235
+ # * Cell A, 0, 0
236
+ # * Cell B, 2, 0
237
+ # * Cell C, 0, 1
238
+ # * Cell D, 1, 1
239
+ # * Cell E, 2, 1
240
+ #
241
+ def each_cell_with_index
242
+ rowspan_cells = {}
243
+ n_row = 0
244
+ renderable_data.each do |row|
245
+ index = 0
246
+ rowspan_cells.each_value { |v| v[:rowspan] -= 1 }
247
+ rowspan_cells.delete_if { |k, v| v[:rowspan] == 0 }
248
+ row.each do |cell|
249
+ while rowspan_cells[ index ] do
250
+ index += rowspan_cells[ index ][:colspan]
251
+ end
252
+
253
+ yield cell, index, n_row
254
+
255
+ if cell.rowspan > 1
256
+ rowspan_cells[ index ] = { :rowspan => cell.rowspan,
257
+ :colspan => cell.colspan }
258
+ end
259
+ index += cell.colspan
260
+ end # row.each
261
+ n_row += 1
262
+ end # renderable_data.each
263
+ end
264
+
265
+ def cells_width( cell )
266
+ width = 2 * C(:horizontal_padding) + cell.to_s.lines.map do |e|
267
+ @document.width_of(e, :size => C(:font_size))
268
+ end.max.to_f
269
+ width.ceil
270
+ end
271
+
272
+ def calculate_column_widths(manual_widths=nil, width=nil, max_width=nil)
273
+ @column_widths = [0] * @data[0].inject(0){ |total, e| total + e.colspan }
274
+
275
+ # Firstly, calculate column widths for cells without colspan attribute
276
+ colspan_cell_to_proccess = []
277
+ each_cell_with_index do |cell, index|
278
+ if cell.colspan <= 1
279
+ length = cells_width( cell )
280
+ @column_widths[ index ] = length if length > @column_widths[ index ]
281
+ else
282
+ colspan_cell_to_proccess << [ cell, index ]
283
+ end
284
+ end
285
+
286
+ # Secondly, calculate column width for cells with colspan attribute
287
+ # and update @column_widths properly
288
+ colspan_cell_to_proccess.each do |cell, index|
289
+ current_colspan = cell.colspan
290
+ calculate_width = @column_widths.slice( index, current_colspan ).
291
+ inject( 0 ) { |t, w| t + w }
292
+ length = cells_width( cell )
293
+ if length > calculate_width
294
+ # This is a little tricky, we have to increase each column
295
+ # that the actual colspan cell use, by a proportional part
296
+ # so the sum of these widths will be equal to the actual width
297
+ # of our colspan cell
298
+ difference = length - calculate_width
299
+ increase = ( difference / current_colspan ).floor
300
+ increase_by = [ increase ] * current_colspan
301
+ # it's important to sum, in total, the difference, so if
302
+ # difference is, e.g., 3 and current_colspan is 2, increase_by
303
+ # will be [ 1, 1 ], but actually we want to be [ 2, 1 ]
304
+ extra_dif = difference - increase * current_colspan
305
+ extra_dif.times { |n| increase_by[n] += 1 }
306
+ current_colspan.times do |j|
307
+ @column_widths[ index + j ] += increase_by[j]
308
+ end
309
+ end
310
+ end
311
+
312
+ # Thridly, establish manual column widths
313
+ manual_width = 0
314
+ manual_widths.each { |k,v|
315
+ @column_widths[k] = v; manual_width += v } if manual_widths
316
+
317
+ # Finally, ensures that the maximum width of the document is not exceeded.
318
+ # Takes into consideration the manual widths specified (With full manual
319
+ # widths specified, the width can exceed the document width as manual
320
+ # widths are taken as gospel)
321
+ max_width ||= width || @document.margin_box.width
322
+ calculated_width = @column_widths.inject {|sum,e| sum += e }
323
+
324
+ if calculated_width > max_width
325
+ shrink_by = (max_width - manual_width).to_f /
326
+ (calculated_width - manual_width)
327
+ @column_widths.each_with_index { |c,i|
328
+ @column_widths[i] = c * shrink_by if manual_widths.nil? ||
329
+ manual_widths[i].nil?
330
+ }
331
+ elsif width && calculated_width < width
332
+ grow_by = (width - manual_width).to_f /
333
+ (calculated_width - manual_width)
334
+ @column_widths.each_with_index { |c,i|
335
+ @column_widths[i] = c * grow_by if manual_widths.nil? ||
336
+ manual_widths[i].nil?
337
+ }
338
+ end
339
+ end
340
+
341
+
342
+ def renderable_data
343
+ C(:headers) ? C(:headers) + @data : @data
344
+ end
345
+
346
+ # Transform all items from @data into Prawn::Table::Cell objects
347
+ def normalize_data
348
+ normalize = lambda { |data|
349
+ data.map do |row|
350
+ row.map do |cell|
351
+ unless cell.is_a?( Hash ) || cell.is_a?( Prawn::Table::Cell )
352
+ cell = { :text => cell.to_s }
353
+ end
354
+ if cell.is_a?( Hash )
355
+ cell = Prawn::Table::Cell.new( cell )
356
+ end
357
+ cell.document = @document
358
+ cell
359
+ end
360
+ end
361
+ }
362
+ @data = normalize.call( @data )
363
+ # C is an alias to configuration method, which is a wrapper around @config
364
+ @config[:headers] = normalize.call( [ C(:headers) ] ) if C(:headers)
365
+
366
+ end
367
+
368
+ def generate_table
369
+ page_contents = []
370
+ y_pos = @document.y
371
+ rowspan_cells = {}
372
+
373
+ @document.font_size C(:font_size) do
374
+ renderable_data.each_with_index do |row, index|
375
+ c = Prawn::Table::CellBlock.new(@document)
376
+
377
+ rowspan_cells.each_value { |v| v[:rowspan] -= 1 }
378
+ rowspan_cells.delete_if { |k, v| v[:rowspan] == 0 }
379
+
380
+ col_index = 0
381
+ row.each do |e|
382
+ align = case C(:align)
383
+ when Hash
384
+ C(:align)[ col_index ]
385
+ else
386
+ C(:align)
387
+ end
388
+ align ||= e.to_s =~ NUMBER_PATTERN ? :right : :left
389
+
390
+ while rowspan_cells[ col_index ] do
391
+ c << rowspan_cells[ col_index ][:cell_fake]
392
+ col_index += rowspan_cells[ col_index ][:colspan]
393
+ end
394
+
395
+ colspan = e.colspan
396
+ rowspan = e.rowspan
397
+
398
+ width = @column_widths.
399
+ slice( col_index, colspan ).
400
+ inject { |sum, width| sum + width }
401
+
402
+ e.width = width
403
+ e.horizontal_padding = C(:horizontal_padding)
404
+ e.vertical_padding = C(:vertical_padding)
405
+ e.border_width = C(:border_width)
406
+ e.align ||= align
407
+
408
+ if rowspan > 1
409
+ cell_fake = Prawn::Table::CellFake.new( :width => width )
410
+ rowspan_cells[ col_index ] = {
411
+ :rowspan => rowspan,
412
+ :colspan => colspan,
413
+ :cell_fake => cell_fake
414
+ }
415
+ end
416
+ c << e
417
+ col_index += colspan
418
+ end # row.each do |e|
419
+
420
+ bbox = @parent_bounds.stretchy? ? @document.margin_box : @parent_bounds
421
+ fit_in_current_page = c.height <= y_pos - bbox.absolute_bottom
422
+ if ! fit_in_current_page then
423
+ if C(:headers) && page_contents.length == 1
424
+ @document.start_new_page
425
+ y_pos = @document.y
426
+ else
427
+ draw_page(page_contents)
428
+ @document.start_new_page
429
+ if C(:headers) && page_contents.any?
430
+ page_contents = [page_contents[0]]
431
+ y_pos = @document.y - page_contents[0].height
432
+ else
433
+ page_contents = []
434
+ y_pos = @document.y
435
+ end
436
+ end
437
+ end
438
+
439
+ page_contents << c
440
+
441
+ y_pos -= c.height
442
+
443
+ if index == renderable_data.length - 1
444
+ draw_page(page_contents)
445
+ end
446
+
447
+ end
448
+ end
449
+ end
450
+
451
+ def draw_page(contents)
452
+ return if contents.empty?
453
+
454
+ if C(:border_style) == :underline_header
455
+ contents.each { |e| e.border_style = :none }
456
+ contents.first.border_style = :bottom_only if C(:headers)
457
+ elsif C(:border_style) == :grid || contents.length == 1
458
+ contents.each { |e| e.border_style = :all }
459
+ else
460
+ contents.first.border_style = C(:headers) ? :all : :no_bottom
461
+ contents.last.border_style = :no_top
462
+ end
463
+
464
+ if C(:headers)
465
+ contents.first.cells.each_with_index do |e,i|
466
+ if C(:align_headers)
467
+ case C(:align_headers)
468
+ when Hash
469
+ align = C(:align_headers)[i]
470
+ else
471
+ align = C(:align_headers)
472
+ end
473
+ end
474
+ e.align = align if align
475
+ e.text_color = C(:header_text_color) if C(:header_text_color)
476
+ e.background_color = C(:header_color) if C(:header_color)
477
+ end
478
+ end
479
+
480
+ # modified the height of the cells with rowspan attribute
481
+ contents.each_with_index do |x, i|
482
+ x.cells.each do |cell|
483
+ if cell.rowspan > 1
484
+ heights_per_row ||= contents.map { |x| x.height }
485
+ cell.height = heights_per_row.
486
+ slice( i, cell.rowspan ).inject(0){ |sum, h| sum + h }
487
+ end
488
+ end
489
+ end
490
+
491
+ contents.each do |x|
492
+ unless x.background_color
493
+ x.background_color = next_row_color if C(:row_colors)
494
+ end
495
+ x.border_color = C(:border_color) if C(:border_color)
496
+
497
+ x.draw
498
+ end
499
+
500
+ reset_row_colors
501
+ end
502
+
503
+
504
+ def next_row_color
505
+ color = C(:row_colors).shift
506
+ C(:row_colors).push(color)
507
+ color
508
+ end
509
+
510
+ def reset_row_colors
511
+ C(:row_colors => C(:original_row_colors).dup) if C(:row_colors)
512
+ end
513
+
514
+ end
515
+ end
data/spec/grid_spec.rb ADDED
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
3
+
4
+ describe "A document's grid" do
5
+ before do
6
+ @pdf = Prawn::Document.new
7
+ end
8
+
9
+ it "should allow definition of a grid" do
10
+ @pdf.define_grid(:columns => 5, :rows => 8, :gutter => 0.1)
11
+ @pdf.grid.columns.should == 5
12
+ @pdf.grid.rows.should == 8
13
+ @pdf.grid.gutter.should == 0.1
14
+ end
15
+
16
+ describe "when a grid is defined" do
17
+ before do
18
+ @num_columns = 5
19
+ @num_rows = 8
20
+ @gutter = 10.0
21
+ @pdf.define_grid(
22
+ :columns => @num_columns,
23
+ :rows => @num_rows,
24
+ :gutter => @gutter
25
+ )
26
+ end
27
+
28
+ it "should compute the column width" do
29
+ (@pdf.grid.column_width * @num_columns.to_f +
30
+ @gutter * (@num_columns - 1).to_f).should == @pdf.bounds.width
31
+ end
32
+
33
+ it "should compute the row height" do
34
+ (@pdf.grid.row_height * @num_rows.to_f +
35
+ @gutter * (@num_rows - 1).to_f).should == @pdf.bounds.height
36
+ end
37
+
38
+ it "should give the edges of a grid box" do
39
+ grid_width = (@pdf.bounds.width.to_f -
40
+ (@gutter * (@num_columns - 1).to_f )) / @num_columns.to_f
41
+ grid_height = (@pdf.bounds.height.to_f -
42
+ (@gutter * (@num_rows - 1).to_f ))/ @num_rows.to_f
43
+
44
+ exp_tl_x = (grid_width + @gutter.to_f) * 4.0
45
+ exp_tl_y = @pdf.bounds.height.to_f - (grid_height + @gutter.to_f)
46
+
47
+ @pdf.grid(1,4).top_left.should == [exp_tl_x, exp_tl_y]
48
+ @pdf.grid(1,4).top_right.should == [exp_tl_x + grid_width, exp_tl_y]
49
+ @pdf.grid(1,4).bottom_left.should == [exp_tl_x, exp_tl_y - grid_height]
50
+ @pdf.grid(1,4).bottom_right.should == [exp_tl_x + grid_width, exp_tl_y - grid_height]
51
+ end
52
+
53
+ it "should give the edges of a multiple grid boxes" do
54
+ # Hand verified. Cheating a bit. Don't tell.
55
+ @pdf.grid([1,3], [2,5]).top_left.should == [330.0, 628.75]
56
+ @pdf.grid([1,3], [2,5]).top_right.should == [650.0, 628.75]
57
+ @pdf.grid([1,3], [2,5]).bottom_left.should == [330.0, 456.25]
58
+ @pdf.grid([1,3], [2,5]).bottom_right.should == [650.0, 456.25]
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
3
+
4
+ describe "When beginning each new page" do
5
+
6
+ it "should execute codeblock given to Document#header" do
7
+ call_count = 0
8
+
9
+ pdf = Prawn::Document.new
10
+ pdf.header(pdf.margin_box.top_left) do
11
+ call_count += 1
12
+ end
13
+
14
+ pdf.start_new_page
15
+ pdf.start_new_page
16
+ pdf.render
17
+
18
+ call_count.should == 3
19
+ end
20
+
21
+ end
22
+
23
+ describe "When ending each page" do
24
+
25
+ it "should execute codeblock given to Document#footer" do
26
+
27
+ call_count = 0
28
+
29
+ pdf = Prawn::Document.new
30
+ pdf.footer([pdf.margin_box.left, pdf.margin_box.bottom + 50]) do
31
+ call_count += 1
32
+ end
33
+
34
+ pdf.start_new_page
35
+ pdf.start_new_page
36
+ pdf.render
37
+
38
+ call_count.should == 3
39
+ end
40
+
41
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+
3
+ puts "Prawn specs: Running on Ruby Version: #{RUBY_VERSION}"
4
+
5
+ require "rubygems"
6
+ require "test/spec"
7
+ require "mocha"
8
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
9
+
10
+ require "prawn"
11
+ require "prawn/layout"
12
+ $LOAD_PATH << File.join(Prawn::BASEDIR, 'vendor','pdf-inspector','lib')
13
+
14
+ Prawn.debug = true
15
+
16
+ gem 'pdf-reader', ">=0.7.3"
17
+ require "pdf/reader"
18
+ require "pdf/inspector"
19
+
20
+ def create_pdf(klass=Prawn::Document)
21
+ @pdf = klass.new(:left_margin => 0,
22
+ :right_margin => 0,
23
+ :top_margin => 0,
24
+ :bottom_margin => 0)
25
+ end
26
+
27
+ def width_table_for( pdf, hpad, fs, cells )
28
+ cells.inject( 2 * cells.size * hpad ) do |ret, cell|
29
+ ret + pdf.width_of( cell, :size => fs ).ceil
30
+ end
31
+ end