Parent

Included Modules

Files

Prawn::Table

This class implements simple PDF table generation.

Prawn tables have the following features:

  * Can be generated with or without headers
  * Can tweak horizontal and vertical padding of text
  * Minimal styling support (borders / row background colors)
  * Can be positioned by bounding boxes (left/center aligned) or an
    absolute x position
  * Automated page-breaking as needed
  * Column widths can be calculated automatically or defined explictly on a 
    column by column basis
  * Text alignment can be set for the whole table or by column

The current implementation is a bit barebones, but covers most of the basic needs for PDF table generation. If you have feature requests, please share them at: groups.google.com/group/prawn-ruby

Tables will be revisited before the end of the Ruby Mendicant project and the most commonly needed functionality will likely be added.

Public Class Methods

new(data, document, options={}) click to toggle source

Creates a new Document::Table object. This is generally called indirectly through Document#table but can also be used explictly.

The data argument is a two dimensional array of strings, organized by row, e.g. [[“r1-col1”,“r1-col2”],[“r2-col2”,“r2-col2”]]. As with all Prawn text drawing operations, strings must be UTF-8 encoded.

The following options are available for customizing your tables, with defaults shown in [] at the end of each description.

:headers

An array of table headers, either strings or Cells. [Empty]

:align_headers

Alignment of header text. Specify for entire header (:left) or by column ({ 0 => :right, 1 => :left}). If omitted, the header alignment is the same as the column alignment.

:header_text_color

Sets the text color of the headers

:header_color

Manually sets the header color

:font_size

The font size for the text cells . [12]

:horizontal_padding

The horizontal cell padding in PDF points [5]

:vertical_padding

The vertical cell padding in PDF points [5]

:padding

Horizontal and vertical cell padding (overrides both)

:border_width

With of border lines in PDF points [1]

:border_style

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

:border_color

Sets the color of the borders.

:position

One of :left, :center or n, where n is an x-offset from the left edge of the current bounding box

:width

A set width for the table, defaults to the sum of all column widths

:column_widths

A hash of indices and widths in PDF points. E.g. { 0 => 50, 1 => 100 }

:row_colors

Used to specify background colors for rows. See below for usage.

:align

Alignment of text in columns, for entire table (:center) or by column ({ 0 => :left, 1 => :center})

Row colors (:row_colors) are specified as HTML hex color values, e.g., “ccaaff”. They can take several forms:

  • An array of colors, used cyclically to “zebra stripe” the table: ['ffffff', 'cccccc', '336699'].

  • A hash taking 0-based row numbers to colors: { 0 => 'ffffff', 2 => 'cccccc'}.

  • The symbol :pdf_writer, for PDF::Writer’s default color scheme.

See Document#table for typical usage, as directly using this class is not recommended unless you know why you want to do it.

     # File lib/prawn/table.rb, line 122
122:     def initialize(data, document, options={})     
123:       unless data.all? { |e| Array === e }
124:         raise Prawn::Errors::InvalidTableData,
125:           "data must be a two dimensional array of Prawn::Cells or strings"
126:       end
127:       
128:       @data     = data        
129:       @document = document
130:       
131:       Prawn.verify_options [:font_size,:border_style, :border_width,
132:        :position, :headers, :row_colors, :align, :align_headers, 
133:        :header_text_color, :border_color, :horizontal_padding, 
134:        :vertical_padding, :padding, :column_widths, :width, :header_color ], 
135:        options     
136:                                           
137:       configuration.update(options)  
138: 
139:       if padding = options[:padding]
140:         C(:horizontal_padding => padding, :vertical_padding => padding) 
141:       end
142:        
143:       if options[:row_colors] == :pdf_writer 
144:         C(:row_colors => ["ffffff","cccccc"])  
145:       end
146:       
147:       if options[:row_colors]
148:         C(:original_row_colors => C(:row_colors)) 
149:       end
150: 
151:       calculate_column_widths(options[:column_widths], options[:width])
152:     end

Public Instance Methods

draw() click to toggle source

Draws the table onto the PDF document

     # File lib/prawn/table.rb, line 164
164:     def draw  
165:       @parent_bounds = @document.bounds  
166:       case C(:position) 
167:       when :center
168:         x = (@document.bounds.width - width) / 2.0
169:         dy = @document.bounds.absolute_top - @document.y
170:         final_pos = nil
171: 
172:         @document.bounding_box [x, @parent_bounds.top], :width => width do 
173:           @document.move_down(dy)
174:           generate_table
175:           final_pos = @document.y
176:         end
177: 
178:         @document.y = final_pos
179:       when Numeric     
180:         x, y = C(:position), @document.cursor
181:         final_pos = nil
182: 
183:         @document.bounding_box([x,y], :width => width) do
184:           generate_table
185:           final_pos = @document.y 
186:         end
187: 
188:         @document.y = final_pos
189:       else
190:         generate_table
191:       end
192:     end
width() click to toggle source

Width of the table in PDF points

     # File lib/prawn/table.rb, line 158
158:     def width
159:        @column_widths.inject(0) { |s,r| s + r }
160:     end

Private Instance Methods

calculate_column_widths(manual_widths=nil, width=nil) click to toggle source
     # File lib/prawn/table.rb, line 204
204:     def calculate_column_widths(manual_widths=nil, width=nil)
205:       @column_widths = [0] * @data[0].inject(0){ |acc, e| 
206:         acc += (e.is_a?(Hash) && e.has_key?(:colspan)) ? e[:colspan] : 1 }
207: 
208:       renderable_data.each do |row|
209:         colspan = 0
210:         row.each_with_index do |cell,i|
211:           cell_text = cell.is_a?(Hash) ? cell[:text] : cell.to_s
212:           length = cell_text.lines.map { |e|
213:             @document.width_of(e, :size => C(:font_size)) }.max.to_f +
214:               2*C(:horizontal_padding)
215:           if length > @column_widths[i+colspan]
216:             @column_widths[i+colspan] = length.ceil
217:           end
218: 
219:           if cell.is_a?(Hash) && cell[:colspan]
220:             colspan += cell[:colspan] - 1
221:           end
222:         end
223:       end  
224: 
225:       fit_within_bounds(manual_widths, width)
226:     end
default_configuration() click to toggle source
     # File lib/prawn/table.rb, line 196
196:     def default_configuration     
197:       { :font_size           => 12, 
198:         :border_width        => 1, 
199:         :position            => :left,
200:         :horizontal_padding  => 5,
201:         :vertical_padding    => 5 } 
202:     end
draw_page(contents) click to toggle source
     # File lib/prawn/table.rb, line 361
361:     def draw_page(contents)
362:       return if contents.empty?
363: 
364:       if C(:border_style) == :underline_header
365:         contents.each { |e| e.border_style = :none }
366:         contents.first.border_style = :bottom_only if C(:headers)
367:       elsif C(:border_style) == :grid || contents.length == 1
368:         contents.each { |e| e.border_style = :all }
369:       else
370:         contents.first.border_style = C(:headers) ? :all : :no_bottom
371:         contents.last.border_style = :no_top
372:       end
373:       
374:       if C(:headers)
375:         contents.first.cells.each_with_index do |e,i|
376:           if C(:align_headers)
377:             case C(:align_headers)
378:               when Hash
379:                 align = C(:align_headers)[i]
380:               else
381:                 align = C(:align_headers)
382:               end
383:           end
384:           e.align = align if align
385:           e.text_color = C(:header_text_color) if C(:header_text_color)
386:           e.background_color = C(:header_color) if C(:header_color)
387:         end
388:       end
389:       
390:       contents.each do |x|
391:         unless x.background_color
392:           x.background_color = next_row_color if C(:row_colors)
393:         end
394:         x.border_color = C(:border_color) if C(:border_color)
395:         
396:         x.draw 
397:       end
398: 
399:       reset_row_colors
400:     end
fit_within_bounds(manual_widths, width) click to toggle source
     # File lib/prawn/table.rb, line 228
228:     def fit_within_bounds(manual_widths, width)
229:       manual_width = 0
230:       manual_widths.each { |k,v| 
231:         @column_widths[k] = v; manual_width += v } if manual_widths           
232: 
233:       #Ensures that the maximum width of the document is not exceeded
234:       #Takes into consideration the manual widths specified (With full manual 
235:       # widths specified, the width can exceed the document width as manual 
236:       # widths are taken as gospel)
237:       max_width = width || @document.margin_box.width
238:       calculated_width = @column_widths.inject {|sum,e| sum += e }
239: 
240:       if calculated_width > max_width
241:         shrink_by = (max_width - manual_width).to_f / 
242:           (calculated_width - manual_width)
243:         @column_widths.each_with_index { |c,i| 
244:           @column_widths[i] = c * shrink_by if manual_widths.nil? || 
245:             manual_widths[i].nil? 
246:         }
247:       elsif width && calculated_width < width
248:         grow_by = (width - manual_width).to_f / 
249:           (calculated_width - manual_width)
250:         @column_widths.each_with_index { |c,i| 
251:           @column_widths[i] = c * grow_by if manual_widths.nil? || 
252:             manual_widths[i].nil? 
253:         }
254:       end
255:     end
generate_table() click to toggle source
     # File lib/prawn/table.rb, line 262
262:     def generate_table    
263:       page_contents = []
264:       y_pos = @document.y 
265: 
266:       @document.font_size C(:font_size) do
267:         renderable_data.each_with_index do |row,index|
268:           c = Prawn::Table::CellBlock.new(@document)
269:           
270:           if C(:row_colors).is_a?(Hash)
271:             real_index = index
272:             real_index -= 1 if C(:headers)
273:             color = C(:row_colors)[real_index]
274:             c.background_color = color if color
275:           end
276:           
277:           col_index = 0
278:           row.each do |e|
279:             case C(:align)
280:             when Hash
281:               align            = C(:align)[col_index]
282:             else
283:               align            = C(:align)
284:             end   
285:             
286:             
287:             align ||= e.to_s =~ NUMBER_PATTERN ? :right : :left 
288:             
289:             case e
290:             when Prawn::Table::Cell
291:               e.document = @document
292:               e.width    = @column_widths[col_index]
293:               e.horizontal_padding = C(:horizontal_padding)
294:               e.vertical_padding   = C(:vertical_padding)    
295:               e.border_width       = C(:border_width)
296:               e.border_style       = :sides
297:               e.align              = align 
298:               c << e
299:             else
300:               text = e.is_a?(Hash) ? e[:text] : e.to_s
301:               width = if e.is_a?(Hash) && e.has_key?(:colspan)
302:                 @column_widths.slice(col_index, e[:colspan]).inject { 
303:                   |sum, width| sum + width }
304:               else
305:                 @column_widths[col_index]
306:               end
307:               
308:               cell_options = {
309:                 :document => @document, 
310:                 :text     => text,
311:                 :width    => width,
312:                 :horizontal_padding => C(:horizontal_padding),
313:                 :vertical_padding   => C(:vertical_padding),
314:                 :border_width       => C(:border_width),
315:                 :border_style       => :sides,
316:                 :align              => align 
317:               }
318: 
319:               if e.is_a?(Hash) 
320:                 opts = e.dup
321:                 opts.delete(:colspan)
322:                 cell_options.update(opts)
323:               end
324: 
325:               c << Prawn::Table::Cell.new(cell_options)
326:             end
327:             
328:             col_index += (e.is_a?(Hash) && e.has_key?(:colspan)) ? e[:colspan] : 1
329:           end
330:                                               
331:           bbox = @parent_bounds.stretchy? ? @document.margin_box : @parent_bounds
332:           if c.height > y_pos - bbox.absolute_bottom
333:             if C(:headers) && page_contents.length == 1
334:               @parent_bounds.move_past_bottom
335:               y_pos = @document.y
336:             else
337:               draw_page(page_contents)
338:               @parent_bounds.move_past_bottom
339:               if C(:headers) && page_contents.any?
340:                 page_contents = [page_contents[0]]
341:                 y_pos = @document.y - page_contents[0].height
342:               else
343:                 page_contents = []
344:                 y_pos = @document.y
345:               end
346:             end
347:           end
348: 
349:           page_contents << c
350: 
351:           y_pos -= c.height
352: 
353:           if index == renderable_data.length - 1
354:             draw_page(page_contents)
355:           end
356: 
357:         end
358:       end
359:     end
next_row_color() click to toggle source
     # File lib/prawn/table.rb, line 403
403:     def next_row_color
404:       return if C(:row_colors).is_a?(Hash)
405: 
406:       color = C(:row_colors).shift
407:       C(:row_colors).push(color)
408:       color
409:     end
renderable_data() click to toggle source
     # File lib/prawn/table.rb, line 258
258:     def renderable_data
259:       C(:headers) ? [C(:headers)] + @data : @data
260:     end
reset_row_colors() click to toggle source
     # File lib/prawn/table.rb, line 411
411:     def reset_row_colors    
412:       C(:row_colors => C(:original_row_colors).dup) if C(:row_colors)
413:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.