lib/prawn/document/table.rb in prawn-0.1.2 vs lib/prawn/document/table.rb in prawn-0.2.0
- old
+ new
@@ -5,12 +5,12 @@
# Copyright June 2008, Gregory Brown. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
module Prawn
- class Document
-
+ class Document
+
# Builds and renders a Document::Table object from raw data.
# For details on the options that can be passed, see
# Document::Table.new
#
# data = [["Gregory","Brown"],["James","Healy"],["Jia","Wu"]]
@@ -35,11 +35,11 @@
# table data, :border_style => :grid,
# :widths => { 0 => 100, 1 => 150 }
#
# end
#
- def table(data,options={})
+ def table(data,options={})
Prawn::Document::Table.new(data,self,options).draw
end
# This class implements simple PDF table generation.
#
@@ -51,21 +51,26 @@
# * 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: http://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.
#
- class Table
+ class Table
+
+ include Prawn::Configurable
- attr_reader :col_widths # :nodoc:
+ attr_reader :col_widths # :nodoc:
+
+ NUMBER_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d+(?:[eE][+-]?\d+)?)?$/ #:nodoc:
# Creates a new Document::Table object. This is generally called
# indirectly through Document#table but can also be used explictly.
#
# The <tt>data</tt> argument is a two dimensional array of strings,
@@ -77,138 +82,158 @@
#
# <tt>:font_size</tt>:: The font size for the text cells . [12]
# <tt>:horizontal_padding</tt>:: The horizontal cell padding in PDF points [5]
# <tt>:vertical_padding</tt>:: The vertical cell padding in PDF points [5]
# <tt>:padding</tt>:: Horizontal and vertical cell padding (overrides both)
- # <tt>:border</tt>:: With of border lines in PDF points [1]
+ # <tt>:border_width</tt>:: With of border lines in PDF points [1]
# <tt>:border_style</tt>:: If set to :grid, fills in all borders. Otherwise, borders are drawn on columns only, not rows
# <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
# <tt>:widths:</tt> A hash of indices and widths in PDF points. E.g. <tt>{ 0 => 50, 1 => 100 }</tt>
# <tt>:row_colors</tt>:: An array of row background colors which are used cyclicly.
- # <tt>:align</tt>:: Alignment of text in columns [:left]
+ # <tt>:align</tt>:: Alignment of text in columns, for entire table (<tt>:center</tt>) or by column (<tt>{ 0 => :left, 1 => :center}</tt>)
+ # <tt>:align_headers</tt>:: Alignment of header text.
#
# Row colors are specified as html encoded values, e.g.
# ["ffffff","aaaaaa","ccaaff"]. You can also specify
# <tt>:row_colors => :pdf_writer</tt> if you wish to use the default color
# scheme from the PDF::Writer library.
#
# See Document#table for typical usage, as directly using this class is
# not recommended unless you know why you want to do it.
#
- def initialize(data, document,options={})
- @data = data
- @document = document
- @font_size = options[:font_size] || 12
- @border_style = options[:border_style]
- @border = options[:border] || 1
- @position = options[:position] || :left
- @headers = options[:headers]
- @row_colors = options[:row_colors]
- @align = options[:align]
-
- @horizontal_padding = options[:horizontal_padding] || 5
- @vertical_padding = options[:vertical_padding] || 5
-
- if options[:padding]
- @horizontal_padding = @vertical_padding = options[:padding]
+ def initialize(data, document,options={})
+ unless data.all? { |e| Array === e }
+ raise Prawn::Errors::InvalidTableData,
+ "data must be a two dimensional array of Prawn::Cells or strings"
end
-
- @row_colors = ["ffffff","cccccc"] if @row_colors == :pdf_writer
+ @data = data
+ @document = document
+
+ Prawn.verify_options [:font_size,:border_style, :border_width,
+ :position, :headers, :row_colors, :align, :align_headers,
+ :horizontal_padding, :vertical_padding, :padding, :widths ], options
+
+ configuration.update(options)
- @original_row_colors = @row_colors.dup if @row_colors
+ if padding = options[:padding]
+ C(:horizontal_padding => padding, :vertical_padding => padding)
+ end
+
+ if options[:row_colors] == :pdf_writer
+ C(:row_colors => ["ffffff","cccccc"])
+ end
+ if options[:row_colors]
+ C(:original_row_colors => C(:row_colors))
+ end
+
calculate_column_widths(options[:widths])
- end
+ end
+ attr_reader :col_widths #:nodoc:
+
# Width of the table in PDF points
#
def width
@col_widths.inject(0) { |s,r| s + r }
end
# Draws the table onto the PDF document
#
- def draw
- case(@position)
+ def draw
+ @parent_bounds = @document.bounds
+ case C(:position)
when :center
x = (@document.bounds.width - width) / 2.0
- y = @document.y - @document.bounds.absolute_bottom
- @document.bounding_box [x, y], :width => width do
+ dy = @document.bounds.absolute_top - @document.y
+ @document.bounding_box [x, @parent_bounds.top], :width => width do
+ @document.move_down(dy)
generate_table
end
when Numeric
- x = @position
- y = @document.y - @document.bounds.absolute_bottom
- @document.bounding_box [x,y], :width => width do
- generate_table
- end
+ x, y = C(:position), @document.y - @document.bounds.absolute_bottom
+ @document.bounding_box([x,y], :width => width) { generate_table }
else
generate_table
end
end
private
+
+ def default_configuration
+ { :font_size => 12,
+ :border_width => 1,
+ :position => :left,
+ :horizontal_padding => 5,
+ :vertical_padding => 5 }
+ end
def calculate_column_widths(manual_widths=nil)
@col_widths = [0] * @data[0].length
renderable_data.each do |row|
row.each_with_index do |cell,i|
length = cell.to_s.lines.map { |e|
- @document.font_metrics.string_width(e,@font_size) }.max.to_f +
- 2*@horizontal_padding
- @col_widths[i] = length if length > @col_widths[i]
+ @document.font.metrics.string_width(e,C(:font_size)) }.max.to_f +
+ 2*C(:horizontal_padding)
+ @col_widths[i] = length.ceil if length > @col_widths[i]
end
end
- # TODO: Could optimize here
manual_widths.each { |k,v| @col_widths[k] = v } if manual_widths
end
def renderable_data
- if @headers
- [@headers] + @data
- else
- @data
- end
+ C(:headers) ? [C(:headers)] + @data : @data
end
- def generate_table
+ def generate_table
page_contents = []
- y_pos = @document.y
+ y_pos = @document.y
- @document.font_size(@font_size) do
+ @document.font.size C(:font_size) do
renderable_data.each_with_index do |row,index|
c = Prawn::Graphics::CellBlock.new(@document)
- row.each_with_index do |e,i|
- case(e)
+ row.each_with_index do |e,i|
+ case C(:align)
+ when Hash
+ align = C(:align)[i]
+ else
+ align = C(:align)
+ end
+
+
+ align ||= e.to_s =~ NUMBER_PATTERN ? :right : :left
+
+ case e
when Prawn::Graphics::Cell
e.document = @document
e.width = @col_widths[i]
- e.horizontal_padding = @horizontal_padding
- e.vertical_padding = @vertical_padding
- e.border = @border
+ e.horizontal_padding = C(:horizontal_padding)
+ e.vertical_padding = C(:vertical_padding)
+ e.border_width = C(:border_width)
e.border_style = :sides
- e.align = @align
+ e.align = align
c << e
else
c << Prawn::Graphics::Cell.new(
:document => @document,
:text => e.to_s,
:width => @col_widths[i],
- :horizontal_padding => @horizontal_padding,
- :vertical_padding => @vertical_padding,
- :border => @border,
- :border_style => :sides,
- :align => @align )
+ :horizontal_padding => C(:horizontal_padding),
+ :vertical_padding => C(:vertical_padding),
+ :border_width => C(:border_width),
+ :border_style => :sides,
+ :align => align )
end
end
-
- if c.height > y_pos - @document.margin_box.absolute_bottom
+
+ bbox = @parent_bounds.stretchy? ? @document.margin_box : @parent_bounds
+ if c.height > y_pos - bbox.absolute_bottom
draw_page(page_contents)
@document.start_new_page
- if @headers
+ if C(:headers)
page_contents = [page_contents[0]]
y_pos = @document.y - page_contents[0].height
else
page_contents = []
y_pos = @document.y
@@ -222,37 +247,41 @@
if index == renderable_data.length - 1
draw_page(page_contents)
end
end
- @document.y -= @vertical_padding
end
end
def draw_page(contents)
return if contents.empty?
- if @border_style == :grid || contents.length == 1
+ if C(:border_style) == :grid || contents.length == 1
contents.each { |e| e.border_style = :all }
- else
- contents.first.border_style = @headers ? :all : :no_bottom
+ else
+ if C(:headers)
+ contents.first.border_style = :all
+ contents.first.align = C(:align_headers) || :left
+ else
+ contents.first.border_style = :no_bottom
+ end
contents.last.border_style = :no_top
end
contents.each do |x|
- x.background_color = next_row_color if @row_colors
+ x.background_color = next_row_color if C(:row_colors)
x.draw
end
reset_row_colors
end
def next_row_color
- @row_colors.unshift(@row_colors.pop).last
+ C(:row_colors).unshift(C(:row_colors).pop).last
end
- def reset_row_colors
- @row_colors = @original_row_colors.dup if @row_colors
+ def reset_row_colors
+ C(:row_colors => C(:original_row_colors).dup) if C(:row_colors)
end
end
end
end