Graphics System in PDF

From
Jump to: navigation, search

Overview

Graphics system in PDF is built around a few concepts:

  • Coordinate space, which defines the canvas on which all painting occurs. It determines the position, orientation, and scale of the text, graphics, and images that appear on a page. Paths and positions shall be defined in terms of pairs of coordinates on the Cartesian plane. A coordinate pair is a pair of real numbers x and y that locate a point horizontally and vertically within a two-dimensional coordinate space. A coordinate space is determined by the following properties with respect to the current page: the location of the origin, the orientation of the x and y axes, the lengths of the units along each axis.
  • Graphics state, which defines the current state of the drawing system: current stroking and non-stroking colors, masks, transformations, clipping, alpha etc. See section 8.4 ”Graphics State” of the PDF specification.
  • Drawing commands, which use current coordinate space and graphics state and may affect them if designed to do so. They often use graphics paths as an input, which in turn consist of atomic operations for combining lines and curves into a single figure.
  • ColorSpace, which defines the current color representation and transformation

Implementation in Fixed layout API

Coordinate spaces

PDF defines several coordinate spaces in which the coordinates specifying graphics objects should be interpreted. Transformations between these spaces should be defined by the transformation matrices which can define any linear transformation of two dimensional coordinates, including translation, scaling, rotation, reflection, and skewing.

Device and User coordinate spaces

The content of a page gets presented on a raster output device such as computer display or printer. Such devices may have differ in the built-in coordinate systems they use to address pixels within their visible areas. A particular device’s coordinate system is called a device space. It's a device dependend space and is not used as a default PDF coordinate space.

To avoid the device-dependent effects of specifying graphic objects in device space, PDF defines a device-independent coordinate system that always has the same relationship to the current page, regardless of the output device on which printing or displaying occurs. This device-independent coordinate system is called user space. It's possible to specify the custom length unit (UserUnit) used for a particular page, but by default it equals 1/72 inch and such coordinate space is called default user space and its origin corresponds to the lower left corner of the output medium.

User space maps to the device space using the current transformation matrix (CTM) defined in the current graphics state, if an identity matrix is used user space maps directly to the device space. Technically user space is an infinit plane, and only a portion of it limited by the page's cropbox gets presented to the reader.

ClippedContent class instances are being used as command containers and can be nested into each other providing a way to create combined clippings and other visual effects. Page class is inherited from ClippedContent thus providing the same functionality. Page maps directly to the document's page it represents and its coordinate space corresponds by default to the doc's page coordinate space.

Drawing commands

One may draw complex and simple figures by using the Path class that represents the PDF path object. It's possible to draw lines, curves, standard primitives, clipped shapes and more using this object. Please check the samples below demonstrating its functionality. The approach for using it is as follows: build the path, set container properties, add path to the container invoking desired operation(stroke, fill, stroke and fill).

Drawing a line

We create a Path object and add line to it by calling the self-explaining method Path.AppendLine. After that we set line width and color and stroke this path object by calling ClippedContent.StrokePath method.

// create output PDF file
using (FileStream outputStream = new FileStream("outfile.pdf", FileMode.Create, FileAccess.Write))
{
    // create new document
    using(FixedDocument document = new FixedDocument())
    {
        // create new page
        Page page = new Page(Boundaries.A4);

        // create new path representing a line
        Path path = new Path(10, 820);
        path.AppendLine(300,750);

        // set current non-stroking color             
        page.Content.SetDeviceStrokingColor(new double[] { 1, 0, 1 });                
        // set line width                
        page.Content.SetLineWidth(2.0);
        // stroke path
        page.Content.StrokePath(path);

        // add page to the document
        document.Pages.Add(page);

        // save to output stream
        document.Save(outputStream);
    }
}
 

The results produced by this code are below:

Drawing a line


Drawing a Bézier curve

Drawing a curve has lots in common with drawing a line, except we have to define control points for it as described in section 8.5.2.2 “Cubic Bézier Curves” of the PDF specification. The code is almost the same as for drawing a line except that AppendCubicBezier method is being used instead of AppendLine to define a Bézier curve.

// create output PDF file
using (FileStream outputStream = new FileStream("outfile.pdf", FileMode.Create, FileAccess.Write))
{
    // create new document and add empty page
    using(FixedDocument document = new FixedDocument())
    {
        Page page = new Page(Boundaries.A4);

        // create new path representing a line
        Path path = new Path(10, 750);
        path.AppendCubicBezier(100,700,200,720,300,840);
        // set current non-stroking color             
        page.Content. SetDeviceStrokingColor (new double[] {0.3, 0.5, 1 });

        // set line width                
        page.Content.SetLineWidth(2.0);
        // stroke path
        page.Content.StrokePath(path);

        document.Pages.Add(page);

        // save to output stream
        document.Save(outputStream);
    }
}
 

Here are the results:

Drawing bézier curve


Drawing a circle using Bézier curves

Now we'll show how to draw a complex but very common figure, a circle. In order to draw it we will combine four Bézier curves to one path and stroke it. The code for drawing a circle is as follows:

// create output PDF file
using (FileStream outputStream = new FileStream("outfile.pdf", FileMode.Create, FileAccess.Write))
{
    // radius of the circle
    double radius = 100;
    // circle constant
    double r_c = 0.5522847498 * radius;

    // create new document
    using(FixedDocument document = new FixedDocument())
    {
        // create new page
        Page page = new Page(Boundaries.A4);

        // create new path representing a circle
        Path path = new Path(0, radius);                
        path.AppendCubicBezier(r_c, radius, radius, r_c, radius, 0);
        path.AppendCubicBezier(radius, -r_c, r_c, -radius, 0, -radius);
        path.AppendCubicBezier(-r_c, -radius, -radius, -r_c, -radius, 0);
        path.AppendCubicBezier(-radius, r_c, -r_c, radius, 0, radius);

        // set current non-stroking color             
        page.Content.SetDeviceStrokingColor(new double[] { 0.3, 0.5, 1 });
        // set line width                
        page.Content.SetLineWidth(2.0);

        page.Content.ModifyCurrentTransformationMatrix(1,0,0,1,250,730);
        // stroke path
        page.Content.StrokePath(path);

        // add page to the document
        document.Pages.Add(page);

        // save to output stream
        document.Save(outputStream);
    }
}
 

The resulting circle it creates is shown below:

Drawing circle using bézier curves

This sample demonstrated how to create complex paths and combine several drawing commands together. Next sample will show how to draw filled shapes and apply clipping to achieve various effects.


Drawing a filled shape with clipping

In order to draw a clipped and filled shape on PDF page you have to set clipping path and draw using it. PDF specification defines several filling and clipping rules described in sections 8.5.3.3 "Filling" and 8.5.4 “Clipping Path Operators” respectively. These sections contain very detailed description of what areas of the path should be considered “fillable” or “clippable”, it’s important to understand what EvenOdd and NonZeroWinding rules are and reading of these sections is highly recommended.

// create output PDF file
using (FileStream outputStream = new FileStream("outfile.pdf", FileMode.Create, FileAccess.Write))
{
    // radius of the circle
    double radius = 100;
    // circle constant
    double r_c = 0.5522847498*radius;

    // create new document
    using(FixedDocument document = new FixedDocument())
    {
        // create new page
        Page page = new Page(Boundaries.A4);

        // create and construct new clipping path
        Path clippingPath = new Path(radius,-radius);   
                
        // outer rect
        clippingPath.AppendLine(radius, radius);
        clippingPath.AppendLine(-radius,radius);
        clippingPath.AppendLine(-radius, -radius); 
        clippingPath.AppendLine(radius, -radius);

        //inner small rect
        clippingPath.MoveTo(-20, 20);
        clippingPath.AppendLine(20, 20);
        clippingPath.AppendLine(20, -20);
        clippingPath.AppendLine(-20, -20);
        clippingPath.AppendLine(-20, 20);     
                                
        // create clipped content using non-zero winding rule and our clipping path,
        // it will clip everything that hits the inner small rect and we will get a "hole"
        // because rectangles in clipping path are drawn in opposite direction
        ClippedContent clippedContent = new ClippedContent(clippingPath, FillRule.Nonzero);                                  

        // create new path representing a circle
        Path path = new Path(0, radius);
        path.AppendCubicBezier(r_c, radius, radius, r_c, radius, 0);
        path.AppendCubicBezier(radius, -r_c, r_c, -radius, 0, -radius);
        path.AppendCubicBezier(-r_c, -radius, -radius, -r_c, -radius, 0);
        path.AppendCubicBezier(-radius, r_c, -r_c, radius, 0, radius);

        // set current non-stroking color             
        clippedContent.SetDeviceStrokingColor (new double[] { 0.3, 0.5, 1 });
        clippedContent.SetDeviceNonStrokingColor(new double[] { 0.3, 0.5, 1 });
        // set line width                
        clippedContent.SetLineWidth(2.0);
        // stroke path
        clippedContent.FillAndStrokePath(path);                

        // set current tranform
        page.Content.ModifyCurrentTransformationMatrix(1, 0, 0, 1, 250, 730);
        // append clipped content
        page.Content.AppendContent(clippedContent);             

        // add page to the document
        document.Pages.Add(page);

        // save to output stream
        document.Save(outputStream);
    }
}
 

The results are shown below:

Filled shape with clipping applied


Drawing standard primitives

A number of the static methods provided by Path class can be used to draw standard primitives like circle, ellipse, rect etc. See the code below:

<nowiki>

using (Stream outputStream = File.Create("primitives.pdf")) {

   // create

Implementation in Flow layout API

There are no direct mappings to the graphic objects defined by the PDF specification in Flow layout API because it uses a different approach for documents generation, nevertheless Form XObjects can be included into the final document using the ContentReference elements.