# encoding: utf-8
#
# transformation.rb: Implements rotate, translate, skew, scale and a generic
# transformation_matrix
#
# Copyright January 2010, Michael Witrant. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
module Prawn
module Graphics
module Transformation
# Rotate the user space. If a block is not provided, then you must save
# and restore the graphics state yourself.
#
# == Options
# :origin:: [number, number]. The point around which to
# rotate. A block must be provided if using the :origin
#
# raises Prawn::Errors::BlockRequired if an :origin option is
# provided, but no block is given
#
# Example without a block:
#
# save_graphics_state
# rotate 30
# text "rotated text"
# restore_graphics_state
#
# Example with a block: rotating a rectangle around its upper-left corner
#
# x = 300
# y = 300
# width = 150
# height = 200
# angle = 30
# pdf.rotate(angle, :origin => [x, y]) do
# pdf.stroke_rectangle([x, y], width, height)
# end
#
def rotate(angle, options={}, &block)
Prawn.verify_options(:origin, options)
rad = degree_to_rad(angle)
cos = Math.cos(rad)
sin = Math.sin(rad)
if options[:origin].nil?
transformation_matrix(cos, sin, -sin, cos, 0, 0, &block)
else
raise Prawn::Errors::BlockRequired unless block_given?
x = options[:origin][0] + bounds.absolute_left
y = options[:origin][1] + bounds.absolute_bottom
x_prime = x * cos - y * sin
y_prime = x * sin + y * cos
translate(x - x_prime, y - y_prime) do
transformation_matrix(cos, sin, -sin, cos, 0, 0, &block)
end
end
end
# Translate the user space. If a block is not provided, then you must save
# and restore the graphics state yourself.
#
# Example without a block: move the text up and over 10
#
# save_graphics_state
# translate(10, 10)
# text "scaled text"
# restore_graphics_state
#
# Example with a block: draw a rectangle with its upper-left corner at
# x + 10, y + 10
#
# x = 300
# y = 300
# width = 150
# height = 200
# pdf.translate(10, 10) do
# pdf.stroke_rectangle([x, y], width, height)
# end
#
def translate(x, y, &block)
transformation_matrix(1, 0, 0, 1, x, y, &block)
end
# Scale the user space. If a block is not provided, then you must save
# and restore the graphics state yourself.
#
# == Options
# :origin:: [number, number]. The point from which to
# scale. A block must be provided if using the :origin
#
# raises Prawn::Errors::BlockRequired if an :origin option is
# provided, but no block is given
#
# Example without a block:
#
# save_graphics_state
# scale 1.5
# text "scaled text"
# restore_graphics_state
#
# Example with a block: scale a rectangle from its upper-left corner
#
# x = 300
# y = 300
# width = 150
# height = 200
# factor = 1.5
# pdf.scale(angle, :origin => [x, y]) do
# pdf.stroke_rectangle([x, y], width, height)
# end
#
def scale(factor, options={}, &block)
Prawn.verify_options(:origin, options)
if options[:origin].nil?
transformation_matrix(factor, 0, 0, factor, 0, 0, &block)
else
raise Prawn::Errors::BlockRequired unless block_given?
x = options[:origin][0] + bounds.absolute_left
y = options[:origin][1] + bounds.absolute_bottom
x_prime = factor * x
y_prime = factor * y
translate(x - x_prime, y - y_prime) do
transformation_matrix(factor, 0, 0, factor, 0, 0, &block)
end
end
end
# The following definition of skew would only work in a clearly
# predicatable manner when if the document had no margin. don't provide
# this shortcut until it behaves in a clearly understood manner
#
# def skew(a, b, &block)
# transformation_matrix(1,
# Math.tan(degree_to_rad(a)),
# Math.tan(degree_to_rad(b)),
# 1, 0, 0, &block)
# end
# Transform the user space (see notes for rotate regarding graphics state)
# Generally, one would use the rotate, scale, translate, and skew
# convenience methods instead of calling transformation_matrix directly
def transformation_matrix(a, b, c, d, e, f)
values = [a, b, c, d, e, f].map { |x| "%.5f" % x }.join(" ")
save_graphics_state if block_given?
add_content "#{values} cm"
if block_given?
yield
restore_graphics_state
end
end
end
end
end