Module: Prawn::Graphics

Includes:
BlendMode, CapStyle, Color, Dash, JoinStyle, Patterns, Transformation, Transparency
Included in:
Document
Defined in:
lib/prawn/graphics.rb,
lib/prawn/graphics/dash.rb,
lib/prawn/graphics/color.rb,
lib/prawn/graphics/patterns.rb,
lib/prawn/graphics/cap_style.rb,
lib/prawn/graphics/blend_mode.rb,
lib/prawn/graphics/join_style.rb,
lib/prawn/graphics/transparency.rb,
lib/prawn/graphics/transformation.rb

Overview

Implements the drawing facilities for Prawn::Document. Use this to draw the most beautiful imaginable things.

This file lifts and modifies several of PDF::Writer's graphics functions ruby-pdf.rubyforge.org

Defined Under Namespace

Modules: BlendMode, CapStyle, Color, Dash, JoinStyle, Patterns, Transformation, Transparency

Stable API collapse

KAPPA =

This constant is used to approximate a symmetrical arc using a cubic Bezier curve.

4.0 * ((Math.sqrt(2) - 1.0) / 3.0)

Constants included from JoinStyle

JoinStyle::JOIN_STYLES

Constants included from CapStyle

CapStyle::CAP_STYLES

Stable API collapse

Methods included from Patterns

#fill_gradient, #stroke_gradient

Methods included from Transformation

#rotate, #scale, #transformation_matrix, #translate

Methods included from Transparency

#transparent

Methods included from JoinStyle

#join_style

Methods included from CapStyle

#cap_style

Methods included from Dash

#dash, #dashed?, #undash

Methods included from Color

#fill_color, hex2rgb, rgb2hex, #stroke_color

Methods included from BlendMode

#blend_mode

Instance Method Details

#circle(center, radius) ⇒ Object

Draws a circle of radius radius with the centre-point at point as a complete subpath. The drawing point will be moved to the centre-point upon completion of the drawing the circle.

pdf.circle [100,100], 25

195
196
197
# File 'lib/prawn/graphics.rb', line 195

def circle(center, radius)
  ellipse(center, radius, radius)
end

#close_and_strokeObject

Closes and strokes the current path. If a block is provided, yields to the block before closing the path. See Graphics::Color for color details.


298
299
300
301
# File 'lib/prawn/graphics.rb', line 298

def close_and_stroke
  yield if block_given?
  renderer.add_content 's'
end

#close_pathObject

Closes the current path.


417
418
419
# File 'lib/prawn/graphics.rb', line 417

def close_path
  renderer.add_content 'h'
end

#curve(origin, dest, options = {}) ⇒ Object

Draws a Bezier curve between two points, bounded by two additional points

pdf.curve [50,100], [100,100], :bounds => [[90,90],[75,75]]

179
180
181
182
# File 'lib/prawn/graphics.rb', line 179

def curve(origin, dest, options = {})
  move_to(*origin)
  curve_to(dest, options)
end

#curve_to(dest, options = {}) ⇒ Object

Draws a Bezier curve from the current drawing position to the specified point, bounded by two additional points.

pdf.curve_to [100,100], :bounds => [[90,90],[75,75]]

68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/prawn/graphics.rb', line 68

def curve_to(dest, options = {})
  options[:bounds] || raise(
    Prawn::Errors::InvalidGraphicsPath,
    'Bounding points for bezier curve must be specified ' \
    'as :bounds => [[x1,y1],[x2,y2]]'
  )

  curve_points = PDF::Core.real_params(
    (options[:bounds] << dest).flat_map { |e| map_to_absolute(e) }
  )

  renderer.add_content("#{curve_points} c")
end

#ellipse(point, radius1, radius2 = radius1) ⇒ Object

Draws an ellipse of x radius radius1 and y radius radius2 with the centre-point at point as a complete subpath. The drawing point will be moved to the centre-point upon completion of the drawing the ellipse.

# draws an ellipse with x-radius 25 and y-radius 50
pdf.ellipse [100,100], 25, 50

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/prawn/graphics.rb', line 207

def ellipse(point, radius1, radius2 = radius1)
  x, y = point
  l1 = radius1 * KAPPA
  l2 = radius2 * KAPPA

  move_to(x + radius1, y)

  # Upper right hand corner
  curve_to [x,  y + radius2],
    bounds: [[x + radius1, y + l2], [x + l1, y + radius2]]

  # Upper left hand corner
  curve_to [x - radius1, y],
    bounds: [[x - l1, y + radius2], [x - radius1, y + l2]]

  # Lower left hand corner
  curve_to [x, y - radius2],
    bounds: [[x - radius1, y - l2], [x - l1, y - radius2]]

  # Lower right hand corner
  curve_to [x + radius1, y],
    bounds: [[x + l1, y - radius2], [x + radius1, y - l2]]

  move_to(x, y)
end

#fill(options = {}) ⇒ Object

Closes and fills the current path. See Graphics::Color for color details.

If the option :fill_rule => :even_odd is specified, Prawn will use the even-odd rule to fill the path. Otherwise, the nonzero winding number rule will be used. See the PDF reference, “Graphics -> Path Construction and Painting -> Clipping Path Operators” for details on the difference.


396
397
398
399
# File 'lib/prawn/graphics.rb', line 396

def fill(options = {})
  yield if block_given?
  renderer.add_content(options[:fill_rule] == :even_odd ? 'f*' : 'f')
end

#fill_and_stroke(options = {}) ⇒ Object

Closes, fills, and strokes the current path. If a block is provided, yields to the block before closing the path. See Graphics::Color for color details.

If the option :fill_rule => :even_odd is specified, Prawn will use the even-odd rule to fill the path. Otherwise, the nonzero winding number rule will be used. See the PDF reference, “Graphics -> Path Construction and Painting -> Clipping Path Operators” for details on the difference.


410
411
412
413
# File 'lib/prawn/graphics.rb', line 410

def fill_and_stroke(options = {})
  yield if block_given?
  renderer.add_content(options[:fill_rule] == :even_odd ? 'b*' : 'b')
end

#horizontal_line(x1, x2, options = {}) ⇒ Object

Draws a horizontal line from x1 to x2 at the current y position, or the position specified by the :at option.

# draw a line from [25, 75] to [100, 75]
horizontal_line 25, 100, :at => 75

152
153
154
155
156
# File 'lib/prawn/graphics.rb', line 152

def horizontal_line(x1, x2, options = {})
  y1 = options[:at] || y - bounds.absolute_bottom

  line(x1, y1, x2, y1)
end

#horizontal_ruleObject

Draws a horizontal line from the left border to the right border of the bounding box at the current y position.


161
162
163
# File 'lib/prawn/graphics.rb', line 161

def horizontal_rule
  horizontal_line(bounds.left, bounds.right)
end

#line(*points) ⇒ Object

Draws a line from one point to another. Points may be specified as tuples or flattened argument list:

pdf.line [100,100], [200,250]
pdf.line(100,100,200,250)

140
141
142
143
144
# File 'lib/prawn/graphics.rb', line 140

def line(*points)
  x0, y0, x1, y1 = points.flatten
  move_to(x0, y0)
  line_to(x1, y1)
end

#line_to(*point) ⇒ Object

Draws a line from the current drawing position to the specified point. The destination may be described as a tuple or a flattened list:

pdf.line_to [50,50]
pdf.line_to(50,50)

58
59
60
61
# File 'lib/prawn/graphics.rb', line 58

def line_to(*point)
  xy = PDF::Core.real_params(map_to_absolute(point))
  renderer.add_content("#{xy} l")
end

#line_width(width = nil) ⇒ Object

When called without an argument, returns the current line thickness. When called with an argument, sets the line thickness to the specified value (in PDF points)

pdf.line_width #=> 1
pdf.line_width(5)
pdf.line_width #=> 5

126
127
128
129
130
131
132
# File 'lib/prawn/graphics.rb', line 126

def line_width(width = nil)
  if width
    self.line_width = width
  else
    current_line_width
  end
end

#line_width=(width) ⇒ Object

Sets line thickness to the width specified.


113
114
115
116
# File 'lib/prawn/graphics.rb', line 113

def line_width=(width)
  self.current_line_width = width
  write_line_width
end

#move_to(*point) ⇒ Object

Moves the drawing position to a given point. The point can be specified as a tuple or a flattened argument list

pdf.move_to [100,50]
pdf.move_to(100,50)

47
48
49
50
# File 'lib/prawn/graphics.rb', line 47

def move_to(*point)
  xy = PDF::Core.real_params(map_to_absolute(point))
  renderer.add_content("#{xy} m")
end

#polygon(*points) ⇒ Object

Draws a polygon from the specified points.

# draws a snazzy triangle
pdf.polygon [100,100], [100,200], [200,200]

238
239
240
241
242
243
244
245
# File 'lib/prawn/graphics.rb', line 238

def polygon(*points)
  move_to points[0]
  (points[1..-1] << points[0]).each do |point|
    line_to(*point)
  end
  # close the path
  renderer.add_content 'h'
end

#rectangle(point, width, height) ⇒ Object

Draws a rectangle given point, width and height. The rectangle is bounded by its upper-left corner.

pdf.rectangle [300,300], 100, 200

87
88
89
90
91
92
# File 'lib/prawn/graphics.rb', line 87

def rectangle(point, width, height)
  x, y = map_to_absolute(point)
  box = PDF::Core.real_params([x, y - height, width, height])

  renderer.add_content("#{box} re")
end

#rounded_polygon(radius, *points) ⇒ Object

Draws a rounded polygon from specified points using the radius to define bezier curves

# draws a rounded filled in polygon
pdf.fill_and_stroke_rounded_polygon(
  10, [100, 250], [200, 300], [300, 250], [300, 150], [200, 100],
  [100, 150]
)

255
256
257
258
259
260
261
262
263
264
# File 'lib/prawn/graphics.rb', line 255

def rounded_polygon(radius, *points)
  move_to point_on_line(radius, points[1], points[0])
  sides = points.size
  points << points[0] << points[1]
  sides.times do |i|
    rounded_vertex(radius, points[i], points[i + 1], points[i + 2])
  end
  # close the path
  renderer.add_content 'h'
end

#rounded_rectangle(point, width, height, radius) ⇒ Object

Draws a rounded rectangle given point, width and height and radius for the rounded corner. The rectangle is bounded by its upper-left corner.

pdf.rounded_rectangle [300,300], 100, 200, 10

100
101
102
103
104
105
# File 'lib/prawn/graphics.rb', line 100

def rounded_rectangle(point, width, height, radius)
  x, y = point
  rounded_polygon(
    radius, point, [x + width, y], [x + width, y - height], [x, y - height]
  )
end

#rounded_vertex(radius, *points) ⇒ Object

Creates a rounded vertex for a line segment used for building a rounded polygon requires a radius to define bezier curve and three points. The first two points define the line segment and the third point helps define the curve for the vertex.


270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/prawn/graphics.rb', line 270

def rounded_vertex(radius, *points)
  radial_point1 = point_on_line(radius, points[0], points[1])
  bezier_point1 = point_on_line(
    (radius - radius * KAPPA),
    points[0],
    points[1]
  )
  radial_point2 = point_on_line(radius, points[2], points[1])
  bezier_point2 = point_on_line(
    (radius - radius * KAPPA),
    points[2],
    points[1]
  )
  line_to(radial_point1)
  curve_to(radial_point2, bounds: [bezier_point1, bezier_point2])
end

#strokeObject

Strokes the current path. If a block is provided, yields to the block before closing the path. See Graphics::Color for color details.


290
291
292
293
# File 'lib/prawn/graphics.rb', line 290

def stroke
  yield if block_given?
  renderer.add_content 'S'
end

#stroke_axis(options = {}) ⇒ Object

Draws and strokes X and Y axes rulers beginning at the current bounding box origin (or at a custom location).

Options

:at

Origin of the X and Y axes (default: [0, 0] = origin of the bounding box)

:width

Length of the X axis (default: width of the bounding box)

:height

Length of the Y axis (default: height of the bounding box)

:step_length

Length of the step between markers (default: 100)

:negative_axes_length

Length of the negative parts of the axes (default: 20)

:color:

The color of the axes and the text.

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/prawn/graphics.rb', line 333

def stroke_axis(options = {})
  options = {
    at: [0, 0],
    height: bounds.height.to_i - (options[:at] || [0, 0])[1],
    width: bounds.width.to_i - (options[:at] || [0, 0])[0],
    step_length: 100,
    negative_axes_length: 20,
    color: '000000'
  }.merge(options)

  Prawn.verify_options(
    %i[at width height step_length negative_axes_length color],
    options
  )

  save_graphics_state do
    fill_color(options[:color])
    stroke_color(options[:color])

    dash(1, space: 4)
    stroke_horizontal_line(
      options[:at][0] - options[:negative_axes_length],
      options[:at][0] + options[:width],
      at: options[:at][1]
    )
    stroke_vertical_line(
      options[:at][1] - options[:negative_axes_length],
      options[:at][1] + options[:height],
      at: options[:at][0]
    )
    undash

    fill_circle(options[:at], 1)

    (options[:step_length]..options[:width])
      .step(options[:step_length]) do |point|
      fill_circle([options[:at][0] + point, options[:at][1]], 1)
      draw_text(
        point,
        at: [options[:at][0] + point - 5, options[:at][1] - 10],
        size: 7
      )
    end

    (options[:step_length]..options[:height])
      .step(options[:step_length]) do |point|
      fill_circle([options[:at][0], options[:at][1] + point], 1)
      draw_text(
        point,
        at: [options[:at][0] - 17, options[:at][1] + point - 2],
        size: 7
      )
    end
  end
end

#stroke_boundsObject

Draws and strokes a rectangle represented by the current bounding box


305
306
307
# File 'lib/prawn/graphics.rb', line 305

def stroke_bounds
  stroke_rectangle bounds.top_left, bounds.width, bounds.height
end

#vertical_line(y1, y2, params) ⇒ Object

Draws a vertical line at the x cooordinate given by :at from y1 to y2.

# draw a line from [25, 100] to [25, 300]
vertical_line 100, 300, :at => 25

170
171
172
# File 'lib/prawn/graphics.rb', line 170

def vertical_line(y1, y2, params)
  line(params[:at], y1, params[:at], y2)
end