This module contains graphics primitives. Objects that include this module must respond to add_content.
The PDF::Writer coordinate system is in PDF userspace units. The coordinate system in PDF::Writer is slightly different than might be expected, in that (0, 0) is at the lower left-hand corner of the canvas (page), not the normal top left-hand corner of the canvas. (See the diagram below.)
Y Y 0+-----+X | | | | | | 0+-----+X 0 0
Each primitive provided below indicates the New Point, or the coordinates new drawing point at the completion of the drawing operation. Drawing operations themselves do not draw or fill the path. This must be done by one of the stroke or fill operators, stroke, close_stroke, fill, close_fill, fill_stroke, or close_fill_stroke.
Drawing operations return self (the canvas) so that operations may be chained.
This constant is used to approximate a symmetrical arc using a cubic Bezier curve.
Add an image from a loaded image (JPEG or PNG) resource at position (x, y) (the lower left-hand corner of the image) and scaled to width by height units. If provided, image_info is a PDF::Writer::Graphics::ImageInfo object.
In PDF::Writer 1.1 or later, the new link parameter is a hash with two keys:
:type |
The type of link, either :internal or :external. |
:target |
The destination of the link. For an :internal link, this is an internal cross-reference destination. For an :external link, this is an URI. |
This will automatically make the image a clickable link if set.
# File lib/pdf/writer/graphics.rb, line 568 def add_image(image, x, y, width = nil, height = nil, image_info = nil, link = nil) if image.kind_of?(PDF::Writer::External::Image) label = image.label image_obj = image image_info ||= image.image_info else image_info ||= PDF::Writer::Graphics::ImageInfo.new(image) tt = Time.now @images << tt id = @images.index(tt) label = "I#{id}" image_obj = PDF::Writer::External::Image.new(self, image, image_info, label) @images[id] = image_obj end if width.nil? and height.nil? width = image_info.width height = image_info.height end width ||= height / image_info.height.to_f * image_info.width height ||= width * image_info.height / image_info.width.to_f tt = "\nq\n%.3f 0 0 %.3f %.3f %.3f cm\n/%s Do\nQ" add_content(tt % [ width, height, x, y, label ]) if link case link[:type] when :internal add_internal_link(link[:target], x, y, x + width, y + height) when :external add_link(link[:target], x, y, x + width, y + height) end end image_obj end
Add an image from a file to the current page at position (x, y) (the lower left-hand corner of the image). The image will be scaled to width by height units. The image may be a PNG or JPEG image.
The image parameter may be a filename or an object that returns the full image data when read is called with no parameters (such as an IO object). If ‘open-uri’ is loaded, then the image name may be an URI.
In PDF::Writer 1.1 or later, the new link parameter is a hash with two keys:
:type |
The type of link, either :internal or :external. |
:target |
The destination of the link. For an :internal link, this is an internal cross-reference destination. For an :external link, this is an URI. |
This will automatically make the image a clickable link if set.
# File lib/pdf/writer/graphics.rb, line 540 def add_image_from_file(image, x, y, width = nil, height = nil, link = nil) data = nil if image.respond_to?(:read) data = image.read else open(image, 'rb') { |ff| data = ff.read } end add_image(data, x, y, width, height, nil, link) end
Draws a circle of radius r with the centre-point at (x, y) as a complete subpath. The drawing point will be moved to the centre-point upon completion of the drawing the circle.
# File lib/pdf/writer/graphics.rb, line 227 def circle_at(x, y, r) ellipse_at(x, y, r, r) end
Close the current path by appending a straight line segment from the drawing point to the starting point of the path. If the path is closed, this does nothing. This operator terminates the current subpath.
# File lib/pdf/writer/graphics.rb, line 50 def close add_content(" h") self end
Close the current path by appending a straight line segment from the drawing point to the starting point of the path, and then fill it. This does the same as close followed by fill.
See fill for more information on fill rules.
# File lib/pdf/writer/graphics.rb, line 92 def close_fill(rule = nil) close fill(rule) self end
Closes, fills and then strokes the path. Open subpaths are explicitly closed before being filled (as if close and then fill_stroke had been called). This is the same as constructing two identical path objects, calling fill on one and stroke on the other. Paths filled and stroked in this manner are treated as if they were one object for PDF transparency purposes (PDF transparency is not yet supported by PDF::Writer).
See fill for more information on fill rules.
# File lib/pdf/writer/graphics.rb, line 124 def close_fill_stroke(rule = nil) if :even_odd == rule add_content(" b*") else add_content(" b") end self end
Draw a cubic Bezier curve from (x0, y0) to (x3, y3) using (x1, y1) and (x2, y2) as control points.
New Point |
(x3, y3) |
Subpath |
New |
# File lib/pdf/writer/graphics.rb, line 198 def curve(x0, y0, x1, y1, x2, y2, x3, y3) move_to(x0, y0).curve_to(x1, y1, x2, y2, x3, y3) end
Draws a cubic Bezier curve from the drawing point to (x2, y2) using (x0, y0) and (x1, y1) as the control points for the curve.
New Point |
(x2, y2) |
Subpath |
Current |
# File lib/pdf/writer/graphics.rb, line 157 def curve_to(x0, y0, x1, y1, x2, y2) add_content("\n%.3f %.3f %.3f %.3f %.3f %.3f c" % [ x0, y0, x1, y1, x2, y2 ]) self end
Draw a cubic Bezier curve from (x0, y0) to (x2, y2) using (x1, y1) and (x2, y2) as control points.
New Point |
(x2, y2) |
Subpath |
New |
# File lib/pdf/writer/graphics.rb, line 216 def ecurve(x0, y0, x1, y1, x2, y2) move_to(x0, y0).ecurve_to(x1, y1, x2, y2) end
Draws a cubic Bezier curve from the drawing point to (x1, y1) using (x0, y0) and (x1, y1) as the control points for the curve.
New Point |
(x1, y1) |
Subpath |
Current |
# File lib/pdf/writer/graphics.rb, line 179 def ecurve_to(x0, y0, x1, y1) add_content("\n%.3f %.3f %.3f %.3f y" % [ x0, y0, x1, y1 ]) self end
Draw an ellipse centered at (x, y) with x radius r1 and y radius r2. A partial ellipse can be drawn by specifying the starting and finishing angles.
New Point |
(x, y) |
Subpath |
New |
# File lib/pdf/writer/graphics.rb, line 256 def ellipse2_at(x, y, r1, r2 = r1, start = 0, stop = 359.99, segments = 8) segments = 2 if segments < 2 start = PDF::Math.deg2rad(start) stop = PDF::Math.deg2rad(stop) arc = stop - start segarc = arc / segments.to_f dtm = segarc / 3.0 theta = start a0 = x + r1 * Math.cos(theta) b0 = y + r2 * Math.sin(theta) c0 = -r1 * Math.sin(theta) d0 = r2 * Math.cos(theta) move_to(a0, b0) (1..segments).each do |ii| theta = ii * segarc + start a1 = x + r1 * Math.cos(theta) b1 = y + r2 * Math.sin(theta) c1 = -r1 * Math.sin(theta) d1 = r2 * Math.cos(theta) curve_to(a0 + (c0 * dtm), b0 + (d0 * dtm), a1 - (c1 * dtm), b1 - (d1 * dtm), a1, b1) a0 = a1 b0 = b1 c0 = c1 d0 = d1 end move_to(x, y) self end
Draws an ellipse of x radius r1 and y radius r2 with the centre-point at (x, y) as a complete subpath. The drawing point will be moved to the centre-point upon completion of the drawing the ellipse.
# File lib/pdf/writer/graphics.rb, line 235 def ellipse_at(x, y, r1, r2 = r1) l1 = r1 * KAPPA l2 = r2 * KAPPA move_to(x + r1, y) # Upper right hand corner curve_to(x + r1, y + l1, x + l2, y + r2, x, y + r2) # Upper left hand corner curve_to(x - l2, y + r2, x - r1, y + l1, x - r1, y) # Lower left hand corner curve_to(x - r1, y - l1, x - l2, y - r2, x, y - r2) # Lower right hand corner curve_to(x + l2, y - r2, x + r1, y - l1, x + r1, y) move_to(x, y) end
Fills the path. Open subpaths are implicitly closed before being filled. PDF offers two methods for determining the fill region. The first is called the “nonzero winding number” and is the default fill. The second is called “even-odd”.
Use the even-odd rule (called with #fill(:even_odd)) with caution, as this will cause certain portions of the path to be considered outside of the fill region, resulting in interesting cutout patterns.
# File lib/pdf/writer/graphics.rb, line 78 def fill(rule = nil) if :even_odd == rule add_content(" f*") else add_content(" f") end self end
Sets the color for fill operations.
# File lib/pdf/writer/graphics.rb, line 483 def fill_color(color) fill_color!(color) if @current_fill_color.nil? or color != @current_fill_color end
Forces the color for fill operations to be set, even if the color is the same as the current color. Does nothing if nil is provided.
# File lib/pdf/writer/graphics.rb, line 489 def fill_color!(color = nil) if color @current_fill_color = color add_content "\n#{@current_fill_color.pdf_fill}" end end
Returns the current fill color.
# File lib/pdf/writer/graphics.rb, line 497 def fill_color? @current_fill_color end
Fills and then strokes the path. Open subpaths are implicitly closed before being filled. This is the same as constructing two identical path objects, calling fill on one and stroke on the other. Paths filled and stroked in this manner are treated as if they were one object for PDF transparency purposes (the PDF transparency model is not yet supported by PDF::Writer).
See fill for more information on fill rules.
# File lib/pdf/writer/graphics.rb, line 106 def fill_stroke(rule = nil) if :even_odd == rule add_content(" B*") else add_content(" B") end self end
Add an image easily to a PDF document. image is the name of a JPG or PNG image. options is a Hash:
:pad |
The number of PDF userspace units that will be on all sides of the image. The default is 5 units. |
:width |
The desired width of the image. The image will be resized to this width with the aspect ratio kept. If unspecified, the image’s natural width will be used. |
:resize |
How to resize the image, either :width (resizes the image to be as wide as the margins) or :full (resizes the image to be as large as possible). May be a numeric value, used as a multiplier for the image size (e.g., 0.5 will shrink the image to half-sized). If this and :width are unspecified, the image’s natural size will be used. Mutually exclusive with the <tt>:width<tt> option. |
:justification |
The placement of the image. May be :center, :right, or :left. Defaults to :left. |
:border |
The border options. No default border. If specified, must be either true, which uses the default border, or a Hash. |
:link |
Makes the image a clickable link. |
Image borders are specified as a hash with two options:
:color |
The colour of the border. Defaults to 50% grey. |
:style |
The stroke style of the border. This must be a StrokeStyle object and defaults to the default line. |
Image links are defined as a hash with two options:
:type |
The type of link, either :internal or :external. |
:target |
The destination of the link. For an :internal link, this is an internal cross-reference destination. For an :external link, this is an URI. |
# File lib/pdf/writer/graphics.rb, line 648 def image(image, options = {}) width = options[:width] pad = options[:pad] || 5 resize = options[:resize] just = options[:justification] || :left border = options[:border] link = options[:link] if image.kind_of?(PDF::Writer::External::Image) info = image.image_info image_data = image else if image.respond_to?(:read) image_data = image.read else image_data = open(image, "rb") { |file| file.read } end info = PDF::Writer::Graphics::ImageInfo.new(image_data) end raise "Unsupported Image Type" unless %(JPEG PNG).include?(info.format) width = info.width if width.nil? aspect = info.width.to_f / info.height.to_f # Get the maximum width of the image on insertion. if @columns_on max_width = @columns[:width] - (pad * 2) else max_width = @page_width - (pad * 2) - @left_margin - @right_margin end if resize == :full or resize == :width or width > max_width width = max_width end # Keep the height in an appropriate aspect ratio of the width. height = (width / aspect.to_f) # Resize the image. if resize.kind_of?(Numeric) width *= resize height *= resize end # Resize the image *again*, if it is wider than what is available. if width > max_width height = (width / aspect.to_f) end # If the height is greater than the available space: havail = @y - @bottom_margin - (pad * 2) if height > havail # If the image is to be resized to :full (remaining space # available), adjust the image size appropriately. Otherwise, start # a new page and flow to the next page. if resize == :full height = havail width = (height * aspect) else start_new_page end end # Find the x and y positions. y = @y - pad - height x = @left_margin + pad if (width < max_width) case just when :center x += (max_width - width) / 2.0 when :right x += (max_width - width) end end image_obj = add_image(image_data, x, y, width, height, info) if border border = {} if true == border border[:color] ||= Color::RGB::Grey50 border[:style] ||= PDF::Writer::StrokeStyle::DEFAULT save_state stroke_color border[:color] stroke_style border[:style] rectangle(x, y - pad, width, height - pad).stroke restore_state end if link case link[:type] when :internal add_internal_link(link[:target], x, y - pad, x + width, y + height - pad) when :external add_link(link[:target], x, y - pad, x + width, y + height - pad) end end @y = @y - pad - height image_obj end
Draw a straight line from (x0, y0) to (x1, y1). The line is a new subpath.
New Point |
(x1, y1). |
Subpath |
New |
# File lib/pdf/writer/graphics.rb, line 189 def line(x0, y0, x1, y1) move_to(x0, y0).line_to(x1, y1) end
Draw a straight line from the drawing point to (x, y).
New Point |
(x, y) |
Subpath |
Current |
# File lib/pdf/writer/graphics.rb, line 146 def line_to(x, y) add_content("\n%.3f %.3f l" % [ x, y ]) self end
Move the drawing point to the specified coordinates (x, y).
New Point |
(x, y) |
Subpath |
New |
# File lib/pdf/writer/graphics.rb, line 137 def move_to(x, y) add_content("\n%.3f %.3f m" % [ x, y ]) self end
Draw a polygon. points is an array of PolygonPoint objects, or an array that can be converted to an array of PolygonPoint objects with PDF::Writer::PolygonPoint.new(*value).
New Point |
(points[-1].x, points[-1].y) |
Subpath |
New |
# File lib/pdf/writer/graphics.rb, line 325 def polygon(points) points = points.map { |pp| pp.kind_of?(Array) ? PDF::Writer::PolygonPoint.new(*pp) : pp } point = points.shift move_to(point.x, point.y) while not points.empty? point = points.shift case point.connector when :curve c1 = point c2 = points.shift point = points.shift curve_to(c1.x, c1.y, c2.x, c2.y, point.x, point.y) when :scurve c1 = point point = points.shift scurve_to(c1.x, c1.y, point.x, point.y) when :ecurve c1 = point point = points.shift ecurve_to(c1.x, c1.y, point.x, point.y) else line_to(point.x, point.y) end end self end
Draw a rectangle. The first corner is (x, y) and the second corner is (x + w, y - h).
New Point |
(x + w, y - h) |
Subpath |
Current |
# File lib/pdf/writer/graphics.rb, line 365 def rectangle(x, y, w, h = w) add_content("\n%.3f %.3f %.3f %.3f re" % [ x, y, w, h ]) self end
Rotate the axis of the coordinate system by the specified clockwise angle.
# File lib/pdf/writer/graphics.rb, line 762 def rotate_axis(angle) rad = PDF::Math.deg2rad(angle) tt = "\n%.3f %.3f %.3f %.3f 0 0 cm" tx = [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad) ] add_content(tt % tx) self end
Draw a rounded rectangle with corners (x, y) and (x + w, y - h) and corner radius r. The radius should be significantly smaller than h and w.
New Point |
(x + w, y - h) |
Subpath |
New |
# File lib/pdf/writer/graphics.rb, line 376 def rounded_rectangle(x, y, w, h, r) x1 = x x2 = x1 + w y1 = y y2 = y1 - h r1 = r r2 = r / 2.0 points = [ [ x1 + r1, y1, :line ], [ x2 - r1, y1, :line ], [ x2 - r2, y1, :curve ], # cp1 [ x2, y1 - r2, ], # cp2 [ x2, y1 - r1, ], # ep [ x2, y2 + r1, :line ], [ x2, y2 + r2, :curve ], # cp1 [ x2 - r2, y2, ], # cp2 [ x2 - r1, y2, ], # ep [ x1 + r1, y2, :line ], [ x1 + r2, y2, :curve ], # cp1 [ x1, y2 + r2, ], # cp2 [ x1, y2 + r1, ], # ep [ x1, y1 - r1, :line ], [ x1, y1 - r2, :curve ], # cp1 [ x1 + r2, y1, ], # cp2 [ x1 + r1, y1, ], # ep ] polygon(points) move_to(x2, y2) self end
Scale the coordinate system axis by the specified factors.
# File lib/pdf/writer/graphics.rb, line 771 def scale_axis(x = 1, y = 1) add_content("\n%.3f 0 0 %.3f 0 0 cm" % [ x, y ]) self end
Draw a cubic Bezier curve from (x0, y0) to (x2, y2) using (x0, y0) and (x1, y1) as control points.
New Point |
(x2, y2) |
Subpath |
New |
# File lib/pdf/writer/graphics.rb, line 207 def scurve(x0, y0, x1, y1, x2, y2) move_to(x0, y0).scurve_to(x1, y1, x2, y2) end
Draws a cubic Bezier curve from the drawing point to (x1, y1) using the drawing point and (x0, y0) as the control points for the curve.
New Point |
(x1, y1) |
Subpath |
Current |
# File lib/pdf/writer/graphics.rb, line 168 def scurve_to(x0, y0, x1, y1) add_content("\n%.3f %.3f %.3f %.3f v" % [ x0, y0, x1, y1 ]) self end
Draws an ellipse segment. Draws a closed partial ellipse.
New Point |
(x, y) |
Subpath |
New |
# File lib/pdf/writer/graphics.rb, line 301 def segment_at(x, y, r1, r2 = r1, start = 0, stop = 360, segments = 8) ellipse2_at(x, y, r1, r2, start, stop, segments) start = PDF::Math.deg2rad(start) stop = PDF::Math.deg2rad(stop) ax = x + r1 * Math.cos(start) ay = y + r2 * Math.sin(start) bx = x + r1 * Math.cos(stop) by = y + r2 * Math.sin(stop) move_to(ax, ay) line_to(x, y) line_to(bx, by) move_to(x, y) self end
Skew the coordinate system axis by the specified angles.
# File lib/pdf/writer/graphics.rb, line 777 def skew_axis(xangle = 0, yangle = 0) xr = PDF::Math.deg2rad(xangle) yr = PDF::Math.deg2rad(yangle) xr = Math.tan(xr) if xangle != 0 yr = Math.tan(yr) if yangle != 0 add_content("\n1 %.3f %.3f 1 0 0 cm" % [ xr, yr ]) self end
Draws a star centered on (x, y) with rays portions of length from the centre. Stars with an odd number of rays should have the top ray pointing toward the top of the document. This will not create a “star” with fewer than four points.
New Point |
(cx, cy) |
Subpath |
New |
# File lib/pdf/writer/graphics.rb, line 416 def star(cx, cy, length, rays = 5) rays = 4 if rays < 4 points = [] part = Math::PI / rays.to_f 0.step((rays * 4), 2) do |ray| if ((ray / 2) % 2 == 0) dist = length / 2.0 else dist = length end x = cx + Math.cos((1.5 + ray / 2.0) * part) * dist y = cy + Math.sin((1.5 + ray / 2.0) * part) * dist points << [ x, y ] end polygon(points) move_to(cx, cy) self end
Stroke the path. This operation terminates a path object and draws it.
# File lib/pdf/writer/graphics.rb, line 56 def stroke add_content(" S") self end
Sets the color for stroke operations.
# File lib/pdf/writer/graphics.rb, line 502 def stroke_color(color) stroke_color!(color) if @current_stroke_color.nil? or color != @current_stroke_color end
Forces the color for stroke operations to be set, even if the color is the same as the current color. Does nothing if nil is provided.
# File lib/pdf/writer/graphics.rb, line 508 def stroke_color!(color = nil) if color @current_stroke_color = color add_content "\n#{@current_stroke_color.pdf_stroke}" end end
Returns the current stroke color.
# File lib/pdf/writer/graphics.rb, line 516 def stroke_color? @current_stroke_color end
This sets the line drawing style. This must be a PDF::Writer::StrokeStyle object.
# File lib/pdf/writer/graphics.rb, line 440 def stroke_style(style) stroke_style!(style) if @current_stroke_style.nil? or style != @current_stroke_style end
Forces the line drawing style to be set, even if it’s the same as the current color. Emits the current stroke style if nil is provided.
# File lib/pdf/writer/graphics.rb, line 446 def stroke_style!(style = nil) @current_stroke_style = style if style add_content "\n#{@current_stroke_style.render}" if @current_stroke_style end
Returns the current stroke style.
# File lib/pdf/writer/graphics.rb, line 452 def stroke_style? @current_stroke_style end
Set the text rendering style. This may be one of the following options:
0 |
fill |
1 |
stroke |
2 |
fill then stroke |
3 |
invisible |
4 |
fill and add to clipping path |
5 |
stroke and add to clipping path |
6 |
fill and stroke and add to clipping path |
7 |
add to clipping path |
# File lib/pdf/writer/graphics.rb, line 467 def text_render_style(style) text_render_style!(style) unless @current_text_render_style and style == @current_text_render_style end
Forces the text rendering style to be set, even if it’s the same as the current style.
# File lib/pdf/writer/graphics.rb, line 473 def text_render_style!(style) @current_text_render_style = style end
Reutnrs the current text rendering style.
# File lib/pdf/writer/graphics.rb, line 478 def text_render_style? @current_text_render_style end
Transforms the coordinate axis with the appended matrix. All transformations (including those above) are performed with this matrix. The transformation matrix is:
+- -+ | a c e | | b d f | | 0 0 1 | +- -+
The six values are represented as a six-digit vector: [ a b c d e f ]
Axis translation uses [ 1 0 0 1 x y ] where x and y are the new (0,0) coordinates in the old axis system.
Scaling uses [ sx 0 0 sy 0 0 ] where sx and sy are the scaling factors.
Rotation uses [ cos(a) sin(a) -sin(a) cos(a) 0 0 ] where a is the angle, measured in radians.
X axis skewing uses [ 1 0 tan(a) 1 0 0 ] where a is the angle, measured in radians.
Y axis skewing uses [ 1 tan(a) 0 1 0 0 ] where a is the angle, measured in radians.
# File lib/pdf/writer/graphics.rb, line 810 def transform_matrix(a, b, c, d, e, f) add_content("\n%.3f %.3f %.3f %.3f %.3f %.3f cm" % [ a, b, c, d, e, f ]) end
Generated with the Darkfish Rdoc Generator 2.