Graphics System in PDF

From
Revision as of 16:50, 13 February 2018 by Adminko (talk | contribs) (Drawing commands)
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 space

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

Graphics state

Implementation in Flow layout API

There is no direct access to the graphics system, however existing or created using Fixed layout API FormXObjects can be included into the final document using ContentReference element.