1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
# frozen_string_literal: true
# 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
# @group Stable API
# Rotate the user space. If a block is not provided, then you must save
# and restore the graphics state yourself.
#
# == Options
# <tt>:origin</tt>:: <tt>[number, number]</tt>. The point around which to
# rotate. A block must be provided if using the :origin
#
# raises <tt>Prawn::Errors::BlockRequired</tt> 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
# <tt>:origin</tt>:: <tt>[number, number]</tt>. The point from which to
# scale. A block must be provided if using the :origin
#
# raises <tt>Prawn::Errors::BlockRequired</tt> 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(*matrix)
if matrix.length != 6
raise ArgumentError,
'Transformation matrix must have exacty 6 elements'
end
values = matrix.map { |x| x.to_f.round(5) }.join(' ')
save_graphics_state if block_given?
add_to_transformation_stack(*matrix)
renderer.add_content "#{values} cm"
if block_given?
yield
restore_graphics_state
end
end
end
end
end
|