Vector Graphics in PDFKit

An introduction to vector graphics

Unlike images which are defined by pixels, vector graphics are defined througha series of drawing commands. This makes vector graphics scalable to any sizewithout a reduction in quality (pixelization). The PDF format was designedwith vector graphics in mind, so creating vector drawings is very easy. ThePDFKit vector graphics APIs are very similar to that of the HTML5 canvaselement, so if you are familiar at all with that API, you will find PDFKiteasy to pick up.

Creating basic shapes

Shapes are defined by a series of lines and curves. lineTo, bezierCurveToand quadraticCurveTo all draw from the current point (which you can set withmoveTo) to the specified point (always the last two arguments). Beziercurves use two control points and quadratic curves use just one. Here is anexample that illustrates defining a path.

  1. doc.moveTo(0, 20) # set the current point
  2. .lineTo(100, 160) # draw a line
  3. .quadraticCurveTo(130, 200, 150, 120) # draw a quadratic curve
  4. .bezierCurveTo(190, -40, 200, 200, 300, 150) # draw a bezier curve
  5. .lineTo(400, 90) # draw another line
  6. .stroke() # stroke the path

The output of this example looks like this:

Vector Graphics  - 图1

One thing to notice about this example is the use of method chaining. Allmethods in PDFKit are chainable, meaning that you can call one method rightafter the other without referencing the doc variable again. Of course, thisis an option, so if you don't like how the code looks when chained, you don'thave to write it that way.

SVG paths

PDFKit includes an SVG path parser, so you can include paths written in theSVG path syntax in your PDF documents. This makes it simple to include vectorgraphics elements produced in many popular editors such as Inkscape or AdobeIllustrator. The previous example could also be written using the SVG pathsyntax like this.

  1. doc.path('M 0,20 L 100,160 Q 130,200 150,120 C 190,-40 200,200 300,150 L 400,90')
  2. .stroke()

Vector Graphics  - 图2

The PDFKit SVG parser supports all of the command types supported by SVG, soany valid SVG path you throw at it should work as expected.

Shape helpers

PDFKit also includes some helpers that make defining common shapes mucheasier. Here is a list of the helpers.

  • rect(x, y, width, height)
  • roundedRect(x, y, width, height, cornerRadius)
  • ellipse(centerX, centerY, radiusX, radiusY = radiusX)
  • circle(centerX, centerY, radius)
  • polygon(points…)
    The last one, polygon, allows you to pass in a list of points (arrays of x,ypairs), and it will create the shape by moving to the first point, and thendrawing lines to each consecutive point. Here is how you'd draw a trianglewith the polygon helper.
  1. doc.polygon [100, 0], [50, 100], [150, 100]
  2. doc.stroke()

The output of this example looks like this:

Vector Graphics  - 图3

Fill and stroke styles

So far we have only been stroking our paths, but you can also fill them withthe fill method, and both fill and stroke the same path with thefillAndStroke method. Note that calling fill and then strokeconsecutively will not work because of a limitation in the PDF spec. Use thefillAndStroke method if you want to accomplish both operations on the samepath.

In order to make our drawings interesting, we really need to give them somestyle. PDFKit has many methods designed to do just that.

  • lineWidth
  • lineCap
  • lineJoin
  • miterLimit
  • dash
  • fillColor
  • strokeColor
  • opacity
  • fillOpacity
  • strokeOpacity
    Some of these are pretty self explanatory, but let's go through a few of them.

Line cap and line join

The lineCap and lineJoin properties accept constants describing what theyshould do. This is best illustrated by an example.

  1. # these examples are easier to see with a large line width
  2. doc.lineWidth(25)
  3. # line cap settings
  4. doc.lineCap('butt')
  5. .moveTo(50, 20)
  6. .lineTo(100, 20)
  7. .stroke()
  8. doc.lineCap('round')
  9. .moveTo(150, 20)
  10. .lineTo(200, 20)
  11. .stroke()
  12. # square line cap shown with a circle instead of a line so you can see it
  13. doc.lineCap('square')
  14. .moveTo(250, 20)
  15. .circle(275, 30, 15)
  16. .stroke()
  17. # line join settings
  18. doc.lineJoin('miter')
  19. .rect(50, 100, 50, 50)
  20. .stroke()
  21. doc.lineJoin('round')
  22. .rect(150, 100, 50, 50)
  23. .stroke()
  24. doc.lineJoin('bevel')
  25. .rect(250, 100, 50, 50)
  26. .stroke()

The output of this example looks like this.

Vector Graphics  - 图4

Dashed lines

The dash method allows you to create non-continuous dashed lines. It takes alength specifying how long each dash should be, as well as an optional hashdescribing the additional properties space and phase.

The space option defines the length of the space between each dash, and the phase optiondefines the starting point of the sequence of dashes. By default the spaceattribute is equal to the length and the phase attribute is set to 0.You can use the undash method to make the line solid again.

The following example draws a circle with a dashed line where the spacebetween the dashes is double the length of each dash.

  1. doc.circle(100, 50, 50)
  2. .dash(5, space: 10)
  3. .stroke()

The output of this example looks like this:

Vector Graphics  - 图5

Color

What is a drawing without color? PDFKit makes it simple to set the fill andstroke color and opacity. You can pass an array specifying an RGB or CMYKcolor, a hex color string, or use any of the named CSS colors.

The fillColor and strokeColor methods accept an optional second argument as a shortcut forsetting the fillOpacity and strokeOpacity. Finally, the opacity methodis a convenience method that sets both the fill and stroke opacity to the samevalue.

The fill and stroke methods also accept a color as an argument sothat you don't have to call fillColor or strokeColor beforehand. ThefillAndStroke method accepts both fill and stroke colors as arguments.

  1. doc.circle(100, 50, 50)
  2. .lineWidth(3)
  3. .fillOpacity(0.8)
  4. .fillAndStroke("red", "#900")

This example produces the following output:

Vector Graphics  - 图6

Gradients

PDFKit also supports gradient fills. Gradients can be used just like color fills,and are applied with the same methods (e.g. fillColor, or just fill). Beforeyou can apply a gradient with these methods, however, you must create a gradient object.

There are two types of gradients: linear and radial. They are created by the linearGradientand radialGradient methods. Their function signatures are listed below:

  • linearGradient(x1, y1, x2, y2) - x1,y1 is the start point, x2,y2 is the end point
  • radialGradient(x1, y2, r1, x2, y2, r2) - r1 is the inner radius, r2 is the outer radius
    Once you have a gradient object, you need to create color stops at points along that gradient.Stops are defined at percentage values (0 to 1), and take a color value (any usable by the fillColor method), and an optional opacity.

You can see both linear and radial gradients in the following example:

  1. # Create a linear gradient
  2. grad = doc.linearGradient(50, 0, 150, 100)
  3. grad.stop(0, 'green')
  4. .stop(1, 'red')
  5. doc.rect 50, 0, 100, 100
  6. doc.fill grad
  7. # Create a radial gradient
  8. grad = doc.radialGradient(300, 50, 0, 300, 50, 50)
  9. grad.stop(0, 'orange', 0)
  10. .stop(1, 'orange', 1)
  11. doc.circle 300, 50, 50
  12. doc.fill grad

Here is the output from the this example:

Vector Graphics  - 图7

Winding rules

Winding rules define how a path is filled and are best illustrated by anexample. The winding rule is an optional attribute to the fill andfillAndStroke methods, and there are two values to choose from: non-zeroand even-odd.

  1. # Initial setup
  2. doc.fillColor('red')
  3. .translate(-100, -50)
  4. .scale(0.8)
  5. # Draw the path with the non-zero winding rule
  6. doc.path('M 250,75 L 323,301 131,161 369,161 177,301 z')
  7. .fill('non-zero')
  8. # Draw the path with the even-odd winding rule
  9. doc.translate(280, 0)
  10. .path('M 250,75 L 323,301 131,161 369,161 177,301 z')
  11. .fill('even-odd')

You'll notice that I used the scale and translate transformations in thisexample. We'll cover those in a minute. The output of this example, with someadded labels, is below.

Vector Graphics  - 图8

Saving and restoring the graphics stack

Once you start producing more complex vector drawings, you will want to beable to save and restore the state of the graphics context. The graphics stateis basically a snapshot of all the styles and transformations (see below) thathave been applied, and many states can be created and stored on a stack. Everytime the save method is called, the current graphics state is pushed ontothe stack, and when you call restore, the last state on the stack is appliedto the context again. This way, you can save the state, change some styles,and then restore it to how it was before you made those changes.

Transformations

Transformations allow you to modify the look of a drawing without modifyingthe drawing itself. There are three types of transformations available, aswell as a method for setting the transformation matrix yourself. They aretranslate, rotate and scale.

The translate transformation takes two arguments, x and y, and effectivelymoves the origin of the document which is (0, 0) by default, to the left andright x and y units.

The rotate transformation takes an angle and optionally, an object with anorigin property. It rotates the document angle degrees around the passedorigin or by default, the center of the page.

The scale transformation takes a scale factor and an optional originpassed in an options hash as with the rotate transformation. It is used toincrease or decrease the size of the units in the drawing, or change it'ssize. For example, applying a scale of 0.5 would make the drawing appear athalf size, and a scale of 2 would make it appear twice as large.

If you are feeling particularly smart, you can modify the transformationmatrix yourself using the transform method.

We used the scale and translate transformations above, so here is anexample of using the rotate transformation. We'll set the origin of therotation to the center of the rectangle.

  1. doc.rotate(20, origin: [150, 70])
  2. .rect(100, 20, 100, 100)
  3. .fill('gray')

This example produces the following effect.

Vector Graphics  - 图9

Clipping

A clipping path is a path defined using the normal path creation methods, butinstead of being filled or stroked, it becomes a mask that hides unwantedparts of the drawing. Everything falling inside the clipping path after it iscreated is visible, and everything outside the path is invisible. Here is anexample that clips a checkerboard pattern to the shape of a circle.

  1. # Create a clipping path
  2. doc.circle(100, 100, 100)
  3. .clip()
  4. # Draw a checkerboard pattern
  5. for row in [0...10]
  6. for col in [0...10]
  7. color = if (col % 2) - (row % 2) then '#eee' else '#4183C4'
  8. doc.rect(row * 20, col * 20, 20, 20)
  9. .fill(color)

The result of this example is the following:

Vector Graphics  - 图10

That's it for vector graphics in PDFKit. Now let's move on to learning aboutPDFKit's text support!

PreviousNext

原文: http://pdfkit.org/docs/vector.html