author andy_robinson
Mon, 10 Jul 2000 14:20:15 +0000
changeset 341 27c68d524504
child 347 35eda3272102
permissions -rw-r--r--
Broke up the user guide into chapters.

from genuserguide import *

heading1("Graphics and Text with $pdfgen$")

heading2("Basic Concepts")
The $pdfgen$ package is the lowest level interface for
generating PDF documents.  A $pdfgen$ program is essentially
a sequence of instructions for "painting" a document onto
a sequence of pages.  The interface object which provides the
painting operations is the $pdfgen canvas$.  

The canvas should be thought of as a sheet of white paper
with points on the sheet identified using Cartesian ^(X,Y)^ coordinates
which by default have the ^(0,0)^ origin point at the lower
left corner of the page.  Furthermore the first coordinate ^x^
goes to the right and the second coordinate ^y^ goes up, by

A simple example
program that uses a canvas follows.

    from reportlab.pdfgen import canvas
    c = canvas.Canvas("hello.pdf")

The above code creates a $canvas$ object which will generate
a PDF file named $hello.pdf$ in the current working directory.
It then calls the $hello$ function passing the $canvas$ as an argument.
Finally the $showPage$ method saves the current page of the canvas
and the $save$ method stores the file and closes the canvas.""")

The $showPage$ method causes the $canvas$ to stop drawing on the
current page and any further operations will draw on a subsequent
page (if there are any further operations -- if not no
new page is created).  The $save$ method must be called after the
construction of the document is complete -- it generates the PDF
document, which is the whole purpose of the $canvas$ object.

heading2("More about the Canvas")
Before describing the drawing operations, we will digress to cover
some of the things which can be done to configure a canvas.  There
are many different settings available.""")
todo("""Cover all constructor arguments, and setAuthor etc.""")

heading2("Drawing Operations")
Suppose the $hello$ function referenced above is implemented as
follows (we will not explain each of the operations in detail


Examining this code notice that there are essentially two types
of operations performed using a canvas.  The first type draws something
on the page such as a text string or a rectangle or a line.  The second
type changes the state of the canvas such as
changing the current fill or stroke color or changing the current font
type and size.  

If we imagine the program as a painter working on
the canvas the "draw" operations apply paint to the canvas using
the current set of tools (colors, line styles, fonts, etcetera)
and the "state change" operations change one of the current tools
(changing the fill color from whatever it was to blue, or changing
the current font to $Times-Roman$ in 15 points, for example).

The document generated by the "hello world" program listed above would contain
the following graphics.

illust(examples.hello, '"Hello World" in pdfgen')

heading3("About the demos in this document")

This document contains demonstrations of the code discussed like the one shown
in the rectangle above.  These demos are drawn on a "tiny page" embedded
within the real pages of the guide.  The tiny pages are %s inches wide
and %s inches tall. The demo displays show the actual output of the demo code.
""" % (examplefunctionxinches, examplefunctionyinches))

heading2('The tools: the "draw" operations')

This section briefly lists the tools available to the program
for painting information onto a page using the canvas interface.
These will be discussed in detail in later sections.  They are listed
here for easy reference and for summary purposes.

heading3("Line methods")


The line methods draw straight line segments on the canvas.

heading3("Shape methods")

eg("""canvas.grid(xlist, ylist) """)
eg("""canvas.bezier(x1, y1, x2, y2, x3, y3, x4, y4)""")
eg("""canvas.arc(x1,y1,x2,y2) """)
eg("""canvas.rect(x, y, width, height, stroke=1, fill=0) """)
eg("""canvas.ellipse(x, y, width, height, stroke=1, fill=0)""")
eg("""canvas.wedge(x1,y1, x2,y2, startAng, extent, stroke=1, fill=0) """)
eg(""", y_cen, r, stroke=1, fill=0)""")
eg("""canvas.roundRect(x, y, width, height, radius, stroke=1, fill=0) """)

The shape methods draw common complex shapes on the canvas.

heading3("String drawing methods")

eg("""canvas.drawString(x, y, text):""")
eg("""canvas.drawRightString(x, y, text) """)
eg("""canvas.drawCentredString(x, y, text)""")

The draw string methods draw single lines of text on the canvas.

heading3("The text object methods")
eg("""textobject = canvas.beginText(x, y) """)
eg("""canvas.drawText(textobject) """)

Text objects are used to format text in ways that
are not supported directly by the canvas interface.
A program creates a text object from the canvas using beginText
and then formats text by invoking textobject methods.
Finally the textobject is drawn onto the canvas using

heading3("The path object methods")

eg("""path = canvas.beginPath() """)
eg("""canvas.drawPath(path, stroke=1, fill=0) """)
eg("""canvas.clipPath(path, stroke=1, fill=0) """)

heading3("Image methods")

eg("""canvas.drawInlineImage(self, image, x,y, width=None,height=None) """)

heading3("Ending a page")


disc("""The showPage method finishes the current page.  All additional drawing will
be done on another page.""")


disc("""Warning!  All state changes (font changes, color settings, geometry transforms, etcetera)
are FORGOTTEN when you advance to a new page in $pdfgen$.  Any state settings you wish to preserve
must be set up again before the program proceeds with drawing!""")

heading2('The toolbox: the "state change" operations')

This section briefly lists the ways to switch the tools used by the
for painting information onto a page using the canvas interface.
These too will be discussed in detail in later sections.

heading3("Changing Colors")
eg("""canvas.setFillColorCMYK(c, m, y, k) """)
eg("""canvas.setStrikeColorCMYK(c, m, y, k) """)
eg("""canvas.setFillColorRGB(r, g, b) """)
eg("""canvas.setStrokeColorRGB(r, g, b) """)
eg("""canvas.setFillColor(acolor) """)
eg("""canvas.setStrokeColor(acolor) """)
eg("""canvas.setFillGray(gray) """)
eg("""canvas.setStrokeGray(gray) """)

heading3("Changing Fonts")
eg("""canvas.setFont(psfontname, size, leading = None) """)

heading3("Changing Graphical Styles")

eg("""canvas.setLineWidth(width) """)
eg("""canvas.setLineCap(mode) """)
eg("""canvas.setLineJoin(mode) """)
eg("""canvas.setMiterLimit(limit) """)
eg("""canvas.setDash(self, array=[], phase=0) """)

heading3("Changing Geometry")

eg("""canvas.setPageSize(pair) """)
eg("""canvas.transform(a,b,c,d,e,f): """)
eg("""canvas.translate(dx, dy) """)
eg("""canvas.scale(x, y) """)
eg("""canvas.rotate(theta) """)
eg("""canvas.skew(alpha, beta) """)

heading3("State control")

eg("""canvas.saveState() """)
eg("""canvas.restoreState() """)

heading2("Other canvas methods.")

Not all methods of the canvas object fit into the "tool" or "toolbox"
categories.  Below are some of the misfits, included here for completeness.

 canvas.addOutlineEntry(title, key, level=0, closed=None)
 canvas.bookmarkHorizontalAbsolute(name, yhorizontal)
 canvas.beginForm(name, lowerx=0, lowery=0, upperx=None, uppery=None)
 canvas.linkAbsolute(contents, destinationname, Rect=None, addtopage=1, name=None, **kw)
 canvas.stringWidth(self, text, fontName, fontSize, encoding=None)
 canvas.setPageTransition(self, effectname=None, duration=1, 

heading2('Coordinates (default user space)')

By default locations on a page are identified by a pair of numbers.
For example the pair $(4.5*inch, 1*inch)$ identifies the location
found on the page by starting at the lower left corner and moving to
the right 4.5 inches and up one inch.

disc("""For example, the following function draws
a number of elements on a canvas.""")


disc("""In the default user space the "origin" ^(0,0)^ point is at the lower
left corner.  Executing the $coords$ function in the default user space
(for the "demo minipage") we obtain the following.""")

illust(examples.coords, 'The Coordinate System')

heading3("Moving the origin: the $translate$ method")

disc("""Often it is useful to "move the origin" to a new point off
the lower left corner.  The $canvas.translate(^x,y^)$ method moves the origin
for the current page to the point currently identified by ^(x,y)^.""")

disc("""For example the following translate function first moves
the origin before drawing the same objects as shown above.""")


disc("""This produces the following.""")

illust(examples.translate, "Moving the origin: the $translate$ method")

#illust(NOP) # execute some code


<i>Note:</i> As illustrated in the example it is perfectly possible to draw objects 
or parts of objects "off the page".
In particular a common confusing bug is a translation operation that translates the
entire drawing off the visible area of the page.  If a program produces a blank page
it is possible that all the drawn objects are off the page.

heading3("Shrinking and growing: the scale operation")

disc("""Another important operation is scaling.  The scaling operation $canvas.scale(^dx,dy^)$
stretches or shrinks the ^x^ and ^y^ dimensions by the ^dx^, ^dy^ factors respectively.  Often
^dx^ and ^dy^ are the same -- for example to reduce a drawing by half in all dimensions use
$dx = dy = 0.5$.  However for the purposes of illustration we show an example where
$dx$ and $dy$ are different.


disc("""This produces a "short and fat" reduced version of the previously displayed operations.""")

illust(examples.scale, "Scaling the coordinate system")

#illust(NOP) # execute some code


disc("""<i>Note:</i> scaling may also move objects or parts of objects off the page,
or may cause objects to "shrink to nothing." """)

disc("""Scaling and translation can be combined, but the order of the
operations are important.""")


disc("""This example function first saves the current canvas state
and then does a $scale$ followed by a $translate$.  Afterward the function
restores the state (effectively removing the effects of the scaling and
translation) and then does the <i>same</i> operations in a different order.
Observe the effect below.""")

illust(examples.scaletranslate, "Scaling and Translating")

#illust(NOP) # execute some code


disc("""<em>Note:</em> scaling shrinks or grows everything including line widths
so using the canvas.scale method to render a microscopic drawing in 
scaled microscopic units
may produce a blob (because all line widths will get expanded a huge amount).  
Also rendering an aircraft wing in meters scaled to centimeters may cause the lines
to shrink to the point where they disappear.  For engineering or scientific purposes
such as these scale and translate
the units externally before rendering them using the canvas.""")

heading3("Saving and restoring the canvas state: $saveState$ and $restoreState$")

The $scaletranslate$ function used an important feature of the canvas object:
the ability to save and restore the current parameters of the canvas.
By enclosing a sequence of operations in a matching pair of $canvas.saveState()$
an $canvas.restoreState()$ operations all changes of font, color, line style,
scaling, translation, or other aspects of the canvas graphics state can be
restored to the state at the point of the $saveState()$.  Remember that the save/restore
calls must match: a stray save or restore operation may cause unexpected
and undesirable behavior.  Also, remember that <i>no</i> canvas state is
preserved across page breaks, and the save/restore mechanism does not work
across page breaks.

heading3("Mirror image")

It is interesting although perhaps not terribly useful to note that
scale factors can be negative.  For example the following function


creates a mirror image of the elements drawn by the $coord$ function.

illust(examples.mirror, "Mirror Images")

Notice that the text strings are painted backwards.


There are four way to specify colors in $pdfgen$: by name (using the $color$
module, by red/green/blue (additive, $RGB$) value,
by cyan/magenta/yellow/darkness (subtractive, $CMYK$), or by gray level.
The $colors$ function below exercises each of the four methods.


The $RGB$ or additive color specification follows the way a computer
screen adds different levels of the red, green, or blue light to make
any color, where white is formed by turning all three lights on full

disc("""The $CMYK$ or subtractive method follows the way a printer
mixes three pigments (cyan, magenta, and yellow) to form colors.
Because mixing chemicals is more difficult than combining light there
is a fourth parameter for darkness.  For example a chemical
combination of the $CMY$ pigments generally never makes a perfect
black -- instead producing a muddy color -- so, to get black printers
don't use the $CMY$ pigments but use a direct black ink.  Because
$CMYK$ maps more directly to the way printer hardware works it may
be the case that colors specified in $CMYK$ will provide better fidelity
and better control when printed.

illust(examples.colors, "Color Models")

heading2('Painting back to front')

Objects may be painted over other objects to good effect in $pdfgen$.  As
in painting with oils the object painted last will show up on top.  For
example, the $spumoni$ function below paints up a base of colors and then
paints a white text over the base.


The word "SPUMONI" is painted in white over the colored rectangles,
with the apparent effect of "removing" the color inside the body of
the word.

illust(examples.spumoni, "Painting over colors")

The last letters of the word are not visible because the default canvas
background is white and painting white letters over a white background
leaves no visible effect.

This method of building up complex paintings in layers can be done
in very many layers in $pdfgen$ -- there are fewer physical limitations
than there are when dealing with physical paints.


The $spumoni2$ function layers an ice cream cone over the
$spumoni$ drawing.  Note that different parts of the cone
and scoops layer over eachother as well.
illust(examples.spumoni2, "building up a drawing in layers")

heading2('Fonts and text objects')

Text may be drawn in many different colors, fonts, and sizes in $pdfgen$.
The $textsize$ function demonstrates how to change the color and font and
size of text and how to place text on the page.


The $textsize$ function generates the following page.

illust(examples.textsize, "text in different fonts and sizes")

A number of different fonts are always available in $pdfgen$.


The $fonts$ function lists the fonts that are always available.
These don;t need to be stored in a PDF document, since they
are guaranteed to be present in Acrobat Reader.

illust(examples.fonts, "the 14 standard fonts")

In the near future we will add the ability to embed other fonts
within a PDF file.  However, this will add a little to file size.

heading2("Text object methods")

For the dedicated presentation of text in a PDF document, use a text object.
The text object interface provides detailed control of text layout parameters
not available directly at the canvas level.  In addition, it results in smaller
PDF that will render faster than many separate calls to the $drawString$ methods.



eg("""textobject.moveCursor(dx, dy) # from start of current LINE""")

eg("""(x,y) = textobject.getCursor()""")

eg("""x = textobject.getX(); y = textobject.getY()""")

eg("""textobject.setFont(psfontname, size, leading = None)""")



eg("""textobject.textLines(stuff, trim=1)""")

The text object methods shown above relate to basic text geometry.

A text object maintains a text cursor which moves about the page when 
text is drawn.  For example the $setTextOrigin$ places the cursor
in a known position and the $textLine$ and $textLines$ methods move
the text cursor down past the lines that have been missing.


The $cursormoves$ function relies on the automatic
movement of the text cursor for placing text after the origin
has been set.

illust(examples.cursormoves1, "How the text cursor moves")

It is also possible to control the movement of the cursor
more explicitly by using the $moveCursor$ method (which moves
the cursor as an offset from the start of the current <i>line</i>
NOT the current cursor, and which also has positive ^y^ offsets
move <i>down</i> (in contrast to the normal geometry where
positive ^y^ usually moves up.


Here the $textOut$ does not move the down a line in contrast
to the $textLine$ function which does move down.

illust(examples.cursormoves2, "How the text cursor moves again")

heading3("Character Spacing")


disc("""The $setCharSpace$ method adjusts one of the parameters of text -- the inter-character


$charspace$ function exercises various spacing settings.
It produces the following page.""")

illust(examples.charspace, "Adjusting inter-character spacing")

heading3("Word Spacing")


disc("The $setWordSpace$ method adjusts the space between word.")


disc("""The $wordspace$ function shows what various word space settings
look like below.""")

illust(examples.wordspace, "Adjusting word spacing")

heading3("Horizontal Scaling")


disc("""Lines of text can be stretched or shrunken horizontally by the 
$setHorizScale$ method.""")


disc("""The horizontal scaling parameter ^horizScale^
is given in percentages (with 100 as the default), so the 80 setting
shown below looks skinny.
illust(examples.horizontalscale, "adjusting horizontal text scaling")

heading3("Interline spacing (Leading)")


disc("""The vertical offset between the point at which one
line starts and where the next starts is called the leading
offset.  The $setLeading$ method adjusts the leading offset.


disc("""As shown below if the leading offset is set too small
characters of one line my write over the bottom parts of characters
in the previous line.""")

illust(examples.leading, "adjusting the leading")

heading3("Other text object methods")


disc("""The $setTextRenderMode$ method allows text to be used
as a forground for clipping background drawings, for example.""")


The $setRise$ method <super>raises</super> or <sub>lowers</sub> text on the line
(for creating superscripts or subscripts, for example).

textobject.setStrokeColor(self, aColor) 
# and similar""")

These color change operations change the <font color=darkviolet>color</font> of the text and are otherwise
similar to the color methods for the canvas object.""")

heading2('Paths and Lines')

disc("""Just as textobjects are designed for the dedicated presentation
of text, path objects are designed for the dedicated construction of
graphical figures.  When path objects are drawn onto a canvas they are
are drawn as one figure (like a rectangle) and the mode of drawing
for the entire figure can be adjusted: the lines of the figure can
be drawn (stroked) or not; the interior of the figure can be filled or
not; and so forth.""")

For example the $star$ function uses a path object
to draw a star


The $star$ function has been designed to be useful in illustrating
various line style parameters supported by $pdfgen$.

illust(, "line style parameters")

heading3("Line join settings")

The $setLineJoin$ method can adjust whether line segments meet in a point
a square or a rounded vertex.


The line join setting is only really of interest for thick lines because
it cannot be seen clearly for thin lines.

illust(examples.joins, "different line join styles")

heading3("Line cap settings")

disc("""The line cap setting, adjusted using the $setLineCap$ method,
determines whether a terminating line
ends in a square exactly at the vertex, a square over the vertex
or a half circle over the vertex.


disc("""The line cap setting, like the line join setting, is only
visible when the lines are thick.""")

illust(examples.caps, "line cap settings")

heading3("Dashes and broken lines")

The $setDash$ method allows lines to be broken into dots or dashes.


The patterns for the dashes or dots can be in a simple on/off repeating pattern
or they can be specified in a complex repeating pattern.

illust(examples.dashes, "some dash patterns")

heading3("Creating complex figures with path objects")

Combinations of lines, curves, arcs and other figures
can be combined into a single figure using path objects.
For example the function shown below constructs two path
objects using lines and curves.  
This function will be used later on as part of a
pencil icon construction.


Note that the interior of the pencil tip is filled
as one object even though it is constructed from
several lines and curves.  The pencil lead is then
drawn over it using a new path object.

illust(examples.penciltip, "a pencil tip")

heading2('Rectangles, circles, ellipses')

The $pdfgen$ module supports a number of generally useful shapes
such as rectangles, rounded rectangles, ellipses, and circles.
Each of these figures can be used in path objects or can be drawn
directly on a canvas.  For example the $pencil$ function below
draws a pencil icon using rectangles and rounded rectangles with
various fill colors and a few other annotations.



Note that this function is used to create the "margin pencil" to the left.
Also note that the order in which the elements are drawn are important
because, for example, the white rectangles "erase" parts of a black rectangle
and the "tip" paints over part of the yellow rectangle.

illust(examples.pencil, "a whole pencil")

heading2('Bezier curves')

Programs that wish to construct figures with curving borders
generally use Bezier curves to form the borders.


A Bezier curve is specified by four control points 
$(x1,y1)$, $(x2,y2)$, $(x3,y3)$, $(x4,y4)$.
The curve starts at $(x1,y1)$ and ends at $(x4,y4)$
and the line segment from $(x1,y1)$ to $(x2,y2)$
and the line segment from $(x3,y3)$ to $(x4,y4)$
both form tangents to the curve.  Furthermore the
curve is entirely contained in the convex figure with vertices
at the control points.

illust(examples.bezier, "basic bezier curves")

The drawing above (the output of $testbezier$) shows
a bezier curves, the tangent lines defined by the control points
and the convex figure with vertices at the control points.

heading3("Smoothly joining bezier curve sequences")

It is often useful to join several bezier curves to form a
single smooth curve.  To construct a larger smooth curve from
several bezier curves make sure that the tangent lines to adjacent
bezier curves that join at a control point lie on the same line.


The figure created by $testbezier2$ describes a smooth
complex curve because adjacent tangent lines "line up" as
illustrated below.

illust(examples.bezier2, "bezier curves")

heading2("Path object methods")



eg("""pathobject.curveTo(x1, y1, x2, y2, x3, y3) """)

eg("""pathobject.arc(x1,y1, x2,y2, startAng=0, extent=90) """)

eg("""pathobject.arcTo(x1,y1, x2,y2, startAng=0, extent=90) """)

eg("""pathobject.rect(x, y, width, height) """)

eg("""pathobject.ellipse(x, y, width, height)""")

eg(""", y_cen, r) """)

eg("""pathobject.close() """)


illust(examples.hand, "an outline of a hand using bezier curves")


illust(examples.hand2, "the finished hand, filled")