Canvas Interact with Canvas

Introduction

A canvas is the area on which all painting occurs. The SetaPDF-Core component comes with a low level interface that allows you to access the canvas of a page or a form XObject.  

A pages canvas coordinate space is called user space and the positive x axis extends horizontally to the right and the positive y axis vertically upward. The coordinate space of a form XObject may depend on its Matrix entry and is called form space. You have to take care about the origins of the bounding box of a page or a form XObject when working with coordinates. 

Please notice that the offered interface is mostly a direct mapping to PDF operators which can be used in content streams and it is up to you to use them in the correct manner. For more details please refer to the PDF specification.  

Get an Instance

A canvas instance can be created from a page instance or from a form XObject: 

PHP
$pageOne = $document->getCatalog()->getPages()->getPage(1);
$canvas = $pageOne->getCanvas();
// ...or
$xObject = \SetaPDF_Core_XObject_Form::create($document, [0, 0, 100, 200]);
$canvas = $xObject->getCanvas();

Working with Existing Pages or Form XObjects

Behind the canvas instance the component accesses the content streams of a page or the content stream of a form XObject. If they are empty and under your control you're aware of the current graphic state. If you access existing pages or form XObjects you have to take care about their current graphic state, page rotation or individual form space in form XObjects. 

While a form XObject has only a single content stream a page can be created by several individual streams which are concatenated at rendering time. You can access both through the implementation of the getStreamProxy() method of the SetaPDF_Core_Canvas_ContainerInterface interface in both page or form XObject instances.

To omit to parse the whole content stream of a page it is possible to encapsulate the existing data in a separate graphic state with the help of the contents object which is returned by the getStreamProxy() method of a page.  

You need to do this, to get rid of existing graphic state definitions such as scaling, rotation, color definition and so on. 

Let's take this graphic state which sets a stroking color to red, the non-stroking color to green, and scales the graphic state by the factor 2:

1 0 0 RG
0 1 0 rg
2 0 0 2 0 0 cm
% do some drawing...

If you would add additional content to this stream it would be affected by this definitions and it would result in (maybe for you) unexpected results.

Because of this you will need to encapsulate it in an separate graphic state (q ... Q operators) which can be done by: 

PHP
$page->getStreamProxy()->encapsulateExistingContentInGraphicState();

This will result in:

q
1 0 0 RG
0 1 0 rg
2 0 0 2 0 0 cm
% do some drawing...
Q

When you add new content then, you will start with a clean default graphic state which is fully under your control.

There are more things you should be aware of: page rotation and the page boundary origin. By rotating a page you just rotate its visible representation. The coordinate system is still the same. The canvas implementation comes with a helper method that allows you to counter rotate the coordinate system and shifts its origin so that you can work on a rotated page or a page with a shifted page boundary origin the same way as you would do on a page without rotation and its lower left origin at [0,0]:

PHP
$canvas->normalizeRotationAndOrigin($page->getRotation(), $page->getBoundary());

The Canvas Instance

The canvas comes with several default methods which will affect the graphic state or content stream. Nearly all methods will return the canvas instance so that you can build a fluid interface with them:

addCurrentTransformationMatrix()

Add a transformation matrix to the matrix stack of the current graphic state.

clear()

Clears the complete canvas content.

drawXObject()

Draw an external object.

normalizeRotation()

Normalize the graphic state in view to an outer rotation (e.g. page rotation).

normalizeRotationAndOrigin()

Normalize the graphic state in view to an outer rotation (e.g. page rotation) and shifted origin.

restoreGraphicState()

Restore the last graphic state and pop all matrices of the current graphic state out of the matrix stack.

rotate()

Rotate the transformation matrix by $angle degrees at the origin defined by $x and $y.

saveGraphicState()

Open a new graphic state and copy the entire graphic state onto the stack of the new graphic state.

scale()

Scale the transformation matrix by the factor $scaleX and $scaleY.

setColor()

Set the color.

setColorSpace()

Set the current color space.

setGraphicState()

Set a named graphic state.

setNonStrokingColor()

Set the non-stroking color.

setNonStrokingColorSpace()

Set the non-stroking color space.

setStrokingColor()

Set the stroking color.

setStrokingColorSpace()

Set the stroking color space.

skew()

Skew the transformation matrix.

translate()

Move the transformation matrix by $shiftX and $shiftY on x-axis and y-axis.

write()

Writes bytes to the canvas content stream.

The canvas object additionally offers a graphic state object, which keeps track of all called operations. This way you're able to re-map transformed coordiantes into the main user space. By default the graphic state only records changes to the transformation matrix. You can use and control this behaviour through these methods: 

getGraphicStateSync()

Get the bitmask that defines which values should be synced with the graphic state object.

graphicState()

Return the graphic state object if no graphic state is defined an new instance will be initialized.

setGraphicStateSync()

Set the bitmask defining, which values should be synced with the graphic state object.

toUserSpace()

Returns the user space coordinates vector.

The detail level is defined by following class constants:

Currently only the transformation matrix and text is synced. 

Resources of a canvas can be accessed through these methods: 

addResource()

Add a resource to the pages/xobjects resources dictionary.

getResources()

Returns the resources dictionary or an entry of it.

setResource()

Set a resource for the canvas.

Helpers

The canvas offers several helpers which each deals with special content stream operators. They all offer standard operators and methods which are defined in the SetaPDF_Core_Canvas_StandardOperators class.

Additionally each helper has methods to get access to the other helper instances such as the canvas class has, too: 

draw()

Get the draw helper.

path()

Get the path helper.

text()

Get the text helper.

Path Helper

This helper allows you to write path operators to the content stream: 

PHP
$path = $canvas->path();

clip()

Clip the current path.

clipEvenOdd()

Clip the current path using even-odd rule.

close()

Close the current subpath by appending a straight line segment from the current point to the starting point of the subpath.

closeAndStroke()

Close and stroke the path.

closeFillAndStroke()

Close, fill and stroke the path.

closeFillAndStrokeEvenOdd()

Close, fill and stroke the path using even-odd rule.

curveTo()

Append a cubic Bézier curve to the current path.

endPath()

End the path object without filling or stroking it.

fill()

Fill the path.

fillAndStroke()

Fill and stroke the path.

fillAndStrokeEvenOdd()

Fill and stroke the path using even-odd rule.

fillEvenOdd()

Fill the path using even-odd rule.

lineTo()

Append a straight line segment.

moveTo()

Begin a new subpath at a specific position.

rect()

Append a rectangle to the current path as a complete subpath.

setDashPattern()

Set the dash pattern.

setLineCap()

Set the line cap style.

setLineJoin()

Set the line join type.

setLineWidth()

Set the line width.

setMiterLimit()

Set the miter limit.

stroke()

Stroke the path.

A very simple example: 

PHP
<?php
require_once('library/SetaPDF/Autoload.php');

// get a document instance
$writer = new \SetaPDF_Core_Writer_Http('path.pdf', true);
$document = new \SetaPDF_Core_Document($writer);

// create a blank page
$page = $document->getCatalog()->getPages()->create(\SetaPDF_Core_PageFormats::A4);

// get the canvas object
$canvas = $page->getCanvas();

// set some colors
$canvas->setNonStrokingColor([1, 0, 0])
    ->setStrokingColor([0, 1, 0]);

// get the path helper
$path = $canvas->path();

// let's draw a simple triangle
$path->setLineWidth(10)
    ->moveTo(50, 600)
    ->lineTo(200, 680)
    ->lineTo(350, 620)
    ->closeFillAndStroke();

// let's draw a simple rect
$path->setNonStrokingColor(.4)
    ->setStrokingColor(.9)
    ->rect(30, 650, 100, 100)->closeFillAndStroke();

// let's draw a rectangle with rounded corners
$llx = 300;
$lly = 600;
$urx = 500;
$ury = 700;
$radius = 10;

// set another line width and colors
$path->setLineWidth(3)
    ->setStrokingColor([0, 0, 1])
    ->setNonStrokingColor([.5, .25, .5]);

// draw lower line
$path->moveTo($llx + $radius, $lly)
    ->lineTo($urx - $radius, $lly);

// draw lower right corner
$path->curveTo($urx, $lly, $urx, $lly + $radius);

// draw right line
$path->lineTo($urx, $ury - $radius);

// draw upper right corner
$path->curveTo($urx, $ury, $urx - $radius, $ury);

// draw top line
$path->lineTo($llx + $radius, $ury);

// draw upper left corner
$path->curveTo($llx, $ury, $llx, $ury - $radius);

// draw left line
$path->lineTo($llx, $lly + $radius);

// draw lower left corner
$path->curveTo($llx, $lly, $llx + $radius, $lly);

// close the path, fill and stroke it
$path->closeFillAndStroke();

// save and finish the document instance
$document->save()->finish();

Draw Helper

The draw helper offers some higher level methods to draw e.g. rectangles or circles: 

PHP
$draw = $canvas->draw();

circle()

Draws a circle on the canvas.

ellipse()

Draws an ellipse on the canvas.

line()

Draws a line on the canvas.

rect()

Draws a rectangle on the canvas.

A very simple example: 

PHP
<?php
require_once('library/SetaPDF/Autoload.php');

// get a document instance
$writer = new \SetaPDF_Core_Writer_Http('draw.pdf', true);
$document = new \SetaPDF_Core_Document($writer);

// create a blank page
$page = $document->getCatalog()->getPages()->create(\SetaPDF_Core_PageFormats::A4);

// get the canvas object
$canvas = $page->getCanvas();

// set the line width
$canvas->path()->setLineWidth(2);

// get the drawhelper
$draw = $canvas->draw();

// set some colors
$draw->setNonStrokingColor([1, 1, 0])
    ->setStrokingColor([0, 1, 1]);

// let's draw a simple rectangle
$draw->rect(30, 650, 100, 100, \SetaPDF_Core_Canvas_Draw::STYLE_DRAW_AND_FILL);

// set some colors
$draw->setNonStrokingColor([.25, .5, 0])
    ->setStrokingColor([0, .5, 1]);

// draw a circle
$draw->circle(150, 750, 50, \SetaPDF_Core_Canvas_Draw::STYLE_DRAW_AND_FILL);

// set another non-stroking color
$draw->setNonStrokingColor([.8, .5, 0]);

// draw an elipse
$draw->ellipse(250, 710, 100, 50, \SetaPDF_Core_Canvas_Draw::STYLE_FILL);

// save and finish the document instance
$document->save()->finish();

Text Helper

The text helper allows you to write text to the content stream: 

PHP
$text = $canvas->text();

Methods that affect the text state: 

setCharSpacing()

Alias for setCharacterSpacing()

setCharacterSpacing()

Set the char spacing.

setFont()

Set the font.

setLeading()

Set the leading.

setRenderingMode()

Set the rendering mode.

setRise()

Set text rise.

setScaling()

Set the horizontal scaling.

setWordSpacing()

Set the word spacing.

Methods for text object operators: 

begin()

Begin a text object.

end()

End a text object.

Methods for text-positioning operators: 

moveToNextLine()

Move to the next line.

moveToNextLineAndShowText()

Move to the next line and show text.

moveToStartOfNextLine()

Move to the start of the next line.

setTextMatrix()

Set the text matrix.

Methods for text-showing operators: 

moveToNextLineAndShowText()

Move to the next line and show text.

showText()

Show text.

showTextStrings()

Shows text strings.

A very simple example: 

PHP
<?php
require_once('library/SetaPDF/Autoload.php');

// get a document instance
$writer = new \SetaPDF_Core_Writer_Http('text.pdf', true);
$document = new \SetaPDF_Core_Document($writer);

// create a blank page
$page = $document->getCatalog()->getPages()->create(\SetaPDF_Core_PageFormats::A4);

// create a font instance
$font = \SetaPDF_Core_Font_Standard_HelveticaBold::create($document);

// get the canvas object
$canvas = $page->getCanvas();

// get the text helper
$text = $canvas->text();

// set non stroking color
$text->setNonStrokingColor([1, 0, 0]);

// begin text
$text->begin()
    // set the font and size
    ->setFont($font, 26)
    // set a leading value
    ->setLeading(26 * 1.2)
    // move to the next line
    ->moveToNextLine(50, 600)
    // show text
    ->showText($font->getCharCodes('A simple test!', 'UTF-8'))
    // change the color
    ->setNonStrokingColor([0, 1, 0]);

// move to the next line and show text
$text->moveToNextLineAndShowText($font->getCharCodes('With several lines.', 'UTF-8'))
    // end text
    ->end();

// save and finish the document instance
$document->save()->finish();