# encoding: utf-8
# cell.rb : Table support functions
#
# 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
# Builds and renders a Table::Cell. A cell is essentially a
# special-purpose bounding box designed for flowing text within a bordered
# area. For available options, see Table::Cell#new.
#
# Prawn::Document.generate("cell.pdf") do
# cell [100,500],
# :width => 200,
# :text => "The rain in Spain falls mainly on the plains"
# end
#
def cell(point, options={})
Prawn::Table::Cell.new(
options.merge(:document => self, :point => point)).draw
end
end
class Table
# A cell is a special-purpose bounding box designed to flow text within a
# bordered area. This is used by Prawn's Document::Table implementation but
# can also be used standalone for drawing text boxes via Document#cell
#
class Cell
# Creates a new cell object. Generally used indirectly via Document#cell
#
# Of the available options listed below, :point, :width,
# and :text must be provided. If you are not using the
# Document#cell shortcut, the :document must also be provided.
#
# :point:: Absolute [x,y] coordinate of the top-left corner of the cell.
# :document:: The Prawn::Document object to render on.
# :text:: The text to be flowed within the cell
# :text_color:: The color of the text to be displayed
# :width:: The width in PDF points of the cell.
# :height:: The height in PDF points of the cell.
# :horizontal_padding:: The horizontal padding in PDF points
# :vertical_padding:: The vertical padding in PDF points
# :padding:: Overrides both horizontal and vertical padding
# :align:: One of :left, :right, :center
# :borders:: An array of sides which should have a border. Any of :top, :left, :right, :bottom
# :border_width:: The border line width. Defaults to 1.
# :border_style:: One of :all, :no_top, :no_bottom, :sides, :none, :bottom_only. Defaults to :all.
# :border_color:: The color of the cell border.
# :font_size:: The font size for the cell text.
# :font_style:: The font style for the cell text.
#
def initialize(options={})
@point = options[:point]
@document = options[:document]
@text = options[:text].to_s
@text_color = options[:text_color]
@width = options[:width]
@height = options[:height]
@borders = options[:borders]
@border_width = options[:border_width] || 1
@border_style = options[:border_style] || :all
@border_color = options[:border_color]
@background_color = options[:background_color]
@align = options[:align] || :left
@font_size = options[:font_size]
@font_style = options[:font_style]
@horizontal_padding = options[:horizontal_padding] || 0
@vertical_padding = options[:vertical_padding] || 0
if options[:padding]
@horizontal_padding = @vertical_padding = options[:padding]
end
end
attr_accessor :point, :border_style, :border_width, :background_color,
:document, :horizontal_padding, :vertical_padding, :align,
:borders, :text_color, :border_color, :font_size, :font_style
attr_writer :height, :width #:nodoc:
# Returns the cell's text as a string.
#
def to_s
@text
end
# The width of the text area excluding the horizonal padding
#
def text_area_width
width - 2*@horizontal_padding
end
# The width of the cell in PDF points
#
def width
@width || (@document.width_of(@text, :size => @font_size)) + 2*@horizontal_padding
end
# The height of the cell in PDF points
#
def height
@height || text_area_height + 2*@vertical_padding
end
# The height of the text area excluding the vertical padding
#
def text_area_height
text_height = 0
if @font_size
@document.font_size(@font_size) do
text_height = @document.height_of(@text, :width => text_area_width)
end
else
text_height = @document.height_of(@text, :width => text_area_width)
end
text_height
end
# Draws the cell onto the PDF document
#
def draw
margin = @border_width / 2.0
if @background_color
old_color = @document.fill_color || "000000"
@document.fill_color(@background_color)
h = borders.include?(:bottom) ?
height - ( 2 * margin ) : height + margin
@document.fill_rectangle [x, y ], width, h
@document.fill_color(old_color)
end
if @border_width > 0
@document.mask(:line_width) do
@document.line_width = @border_width
@document.mask(:stroke_color) do
@document.stroke_color @border_color if @border_color
if borders.include?(:left)
@document.stroke_line [x, y + margin],
[x, y - height - margin ]
end
if borders.include?(:right)
@document.stroke_line(
[x + width, y + margin],
[x + width, y - height - margin] )
end
if borders.include?(:top)
@document.stroke_line(
[ x, y ],
[ x + width, y ])
end
if borders.include?(:bottom)
@document.stroke_line [x, y - height ],
[x + width, y - height]
end
end
end
borders
end
@document.bounding_box( [x + @horizontal_padding,
y - @vertical_padding],
:width => text_area_width,
:height => height - @vertical_padding) do
@document.move_down((@document.font.line_gap +
@document.font.descender) / 2)
options = {:align => @align, :final_gap => false}
options[:size] = @font_size if @font_size
options[:style] = @font_style if @font_style
old_color = @document.fill_color || "000000"
@document.fill_color @text_color if @text_color
@document.text @text, options
@document.fill_color old_color
end
end
private
# x-position of the cell
def x
@point[0]
end
# y-position of the cell
def y
@point[1]
end
def borders
@borders ||= case @border_style
when :all
[:top,:left,:right,:bottom]
when :sides
[:left,:right]
when :no_top
[:left,:right,:bottom]
when :no_bottom
[:left,:right,:top]
when :bottom_only
[:bottom]
when :none
[]
end
end
end
class CellBlock #:nodoc:
# Not sure if this class is something I want to expose in the public API.
def initialize(document)
@document = document
@cells = []
@width = 0
@height = 0
end
attr_reader :width, :height, :cells
attr_accessor :background_color, :text_color, :border_color
def <<(cell)
@cells << cell
@height = cell.height if cell.height > @height
@width += cell.width
self
end
def draw
y = @document.y
x = @document.bounds.left_side
@cells.each do |e|
e.point = [x - @document.bounds.absolute_left,
y - @document.bounds.absolute_bottom]
e.height = @height
e.background_color ||= @background_color
e.text_color ||= @text_color
e.border_color ||= @border_color
e.draw
x += e.width
end
@document.y = y - @height
end
def border_width
@cells[0].border_width
end
def border_style=(s)
@cells.each { |e| e.border_style = s }
end
def align=(align)
@cells.each { |e| e.align = align }
end
def border_style
@cells[0].border_style
end
end
end
end