1
#Copyright ReportLab Europe Ltd. 2000-2004
2
#see license.txt for license details
3
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/renderPDF.py
4
# renderPDF - draws Drawings onto a canvas
7
renderpdf.draw(drawing, canvas, x, y)
8
Execute the script to see some test drawings.
11
__version__=''' $Id$ '''
13
from reportlab.graphics.shapes import *
14
from reportlab.pdfgen.canvas import Canvas
15
from reportlab.pdfbase.pdfmetrics import stringWidth
16
from reportlab.lib.utils import getStringIO
17
from reportlab import rl_config
19
# the main entry point for users...
20
def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_):
23
R.draw(drawing, canvas, x, y, showBoundary=showBoundary)
25
from renderbase import Renderer, StateTracker, getStateDelta
27
class _PDFRenderer(Renderer):
28
"""This draws onto a PDF document. It needs to be a class
29
rather than a function, as some PDF-specific state tracking is
30
needed outside of the state info in the SVG model."""
35
self._tracker = StateTracker()
37
def drawNode(self, node):
38
"""This is the recursive method called for each node
40
#print "pdf:drawNode", self
41
#if node.__class__ is Wedge: stop
42
if not (isinstance(node, Path) and node.isClipPath):
43
self._canvas.saveState()
46
deltas = getStateDelta(node)
47
self._tracker.push(deltas)
48
self.applyStateChanges(deltas, {})
50
#draw the object, or recurse
51
self.drawNodeDispatcher(node)
54
if not (isinstance(node, Path) and node.isClipPath):
55
self._canvas.restoreState()
57
def drawRect(self, rect):
58
if rect.rx == rect.ry == 0:
62
rect.width, rect.height,
67
#cheat and assume ry = rx; better to generalize
68
#pdfgen roundRect function. TODO
69
self._canvas.roundRect(
71
rect.width, rect.height, rect.rx,
76
def drawImage(self, image):
77
# currently not implemented in other renderers
78
if image.path and os.path.exists(image.path):
79
self._canvas.drawInlineImage(
82
image.width, image.height
85
def drawLine(self, line):
87
self._canvas.line(line.x1, line.y1, line.x2, line.y2)
89
def drawCircle(self, circle):
91
circle.cx, circle.cy, circle.r,
96
def drawPolyLine(self, polyline):
98
assert len(polyline.points) >= 2, 'Polyline must have 2 or more points'
99
head, tail = polyline.points[0:2], polyline.points[2:],
100
path = self._canvas.beginPath()
101
path.moveTo(head[0], head[1])
102
for i in range(0, len(tail), 2):
103
path.lineTo(tail[i], tail[i+1])
104
self._canvas.drawPath(path)
106
def drawWedge(self, wedge):
107
centerx, centery, radius, startangledegrees, endangledegrees = \
108
wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees
109
yradius, radius1, yradius1 = wedge._xtraRadii()
110
if yradius is None: yradius = radius
111
angle = endangledegrees-startangledegrees
112
path = self._canvas.beginPath()
113
if (radius1==0 or radius1 is None) and (yradius1==0 or yradius1 is None):
114
path.moveTo(centerx, centery)
115
path.arcTo(centerx-radius, centery-yradius, centerx+radius, centery+yradius,
116
startangledegrees, angle)
118
path.arc(centerx-radius, centery-yradius, centerx+radius, centery+yradius,
119
startangledegrees, angle)
120
path.arcTo(centerx-radius1, centery-yradius1, centerx+radius1, centery+yradius1,
121
endangledegrees, -angle)
123
self._canvas.drawPath(path,
127
def drawEllipse(self, ellipse):
128
#need to convert to pdfgen's bounding box representation
129
x1 = ellipse.cx - ellipse.rx
130
x2 = ellipse.cx + ellipse.rx
131
y1 = ellipse.cy - ellipse.ry
132
y2 = ellipse.cy + ellipse.ry
133
self._canvas.ellipse(x1,y1,x2,y2,fill=self._fill,stroke=self._stroke)
135
def drawPolygon(self, polygon):
136
assert len(polygon.points) >= 2, 'Polyline must have 2 or more points'
137
head, tail = polygon.points[0:2], polygon.points[2:],
138
path = self._canvas.beginPath()
139
path.moveTo(head[0], head[1])
140
for i in range(0, len(tail), 2):
141
path.lineTo(tail[i], tail[i+1])
143
self._canvas.drawPath(
149
def drawString(self, stringObj):
151
S = self._tracker.getState()
152
text_anchor, x, y, text = S['textAnchor'], stringObj.x,stringObj.y,stringObj.text
153
if not text_anchor in ['start','inherited']:
154
font, font_size = S['fontName'], S['fontSize']
155
textLen = stringWidth(text, font,font_size)
156
if text_anchor=='end':
158
elif text_anchor=='middle':
161
raise ValueError, 'bad value for textAnchor '+str(text_anchor)
162
t = self._canvas.beginText(x,y)
164
self._canvas.drawText(t)
166
def drawPath(self, path):
167
from reportlab.graphics.shapes import _renderPath
168
pdfPath = self._canvas.beginPath()
169
drawFuncs = (pdfPath.moveTo, pdfPath.lineTo, pdfPath.curveTo, pdfPath.close)
170
isClosed = _renderPath(path, drawFuncs)
176
self._canvas.clipPath(pdfPath, fill=fill, stroke=self._stroke)
178
self._canvas.drawPath(pdfPath,
182
def applyStateChanges(self, delta, newState):
183
"""This takes a set of states, and outputs the PDF operators
184
needed to set those properties"""
185
for key, value in delta.items():
186
if key == 'transform':
187
self._canvas.transform(value[0], value[1], value[2],
188
value[3], value[4], value[5])
189
elif key == 'strokeColor':
190
#this has different semantics in PDF to SVG;
191
#we always have a color, and either do or do
192
#not apply it; in SVG one can have a 'None' color
197
self._canvas.setStrokeColor(value)
198
elif key == 'strokeWidth':
199
self._canvas.setLineWidth(value)
200
elif key == 'strokeLineCap': #0,1,2
201
self._canvas.setLineCap(value)
202
elif key == 'strokeLineJoin':
203
self._canvas.setLineJoin(value)
204
# elif key == 'stroke_dasharray':
205
# self._canvas.setDash(array=value)
206
elif key == 'strokeDashArray':
208
self._canvas.setDash(value)
210
self._canvas.setDash()
211
elif key == 'fillColor':
212
#this has different semantics in PDF to SVG;
213
#we always have a color, and either do or do
214
#not apply it; in SVG one can have a 'None' color
219
self._canvas.setFillColor(value)
220
elif key in ['fontSize', 'fontName']:
221
# both need setting together in PDF
222
# one or both might be in the deltas,
223
# so need to get whichever is missing
224
fontname = delta.get('fontName', self._canvas._fontname)
225
fontsize = delta.get('fontSize', self._canvas._fontsize)
226
self._canvas.setFont(fontname, fontsize)
228
from reportlab.platypus import Flowable
230
class GraphicsFlowable(Flowable):
231
"""Flowable wrapper around a Pingo drawing"""
232
def __init__(self, drawing):
233
self.drawing = drawing
234
self.width = self.drawing.width
235
self.height = self.drawing.height
238
draw(self.drawing, self.canv, 0, 0)
240
def drawToFile(d, fn, msg="", showBoundary=rl_config._unset_, autoSize=1):
241
"""Makes a one-page PDF with just the drawing.
243
If autoSize=1, the PDF will be the same size as
244
the drawing; if 0, it will place the drawing on
245
an A4 page with a title above it - possibly overflowing
248
c.setFont('Times-Roman', 36)
249
c.drawString(80, 750, msg)
253
c.setPageSize((d.width, d.height))
254
draw(d, c, 0, 0, showBoundary=showBoundary)
257
c.setFont('Times-Roman', 12)
261
draw(d, c, 80, y, showBoundary=showBoundary)
265
if sys.platform=='mac' and not hasattr(fn, "write"):
267
import macfs, macostools
268
macfs.FSSpec(fn).SetCreatorType("CARO", "PDF ")
269
macostools.touched(fn)
274
def drawToString(d, msg="", showBoundary=rl_config._unset_,autoSize=1):
275
"Returns a PDF as a string in memory, without touching the disk"
277
drawToFile(d, s, msg=msg, showBoundary=showBoundary,autoSize=autoSize)
281
#########################################################
283
# test code. First, defin a bunch of drawings.
284
# Routine to draw them comes at the end.
286
#########################################################
290
c = Canvas('renderPDF.pdf')
291
c.setFont('Times-Roman', 36)
292
c.drawString(80, 750, 'Graphics Test')
294
# print all drawings and their doc strings from the test
297
#grab all drawings from the test module
298
from reportlab.graphics import testshapes
300
for funcname in dir(testshapes):
301
if funcname[0:10] == 'getDrawing':
302
drawing = eval('testshapes.' + funcname + '()') #execute it
303
docstring = eval('testshapes.' + funcname + '.__doc__')
304
drawings.append((drawing, docstring))
306
#print in a loop, with their doc strings
307
c.setFont('Times-Roman', 12)
310
for (drawing, docstring) in drawings:
311
assert (docstring is not None), "Drawing %d has no docstring!" % i
312
if y < 300: #allows 5-6 lines of text
317
c.setFont('Times-BoldItalic',12)
318
c.drawString(80, y, 'Drawing %d' % i)
319
c.setFont('Times-Roman',12)
321
textObj = c.beginText(80, y)
322
textObj.textLines(docstring)
325
y = y - drawing.height
326
draw(drawing, c, 80, y)
328
if y!=740: c.showPage()
331
print 'saved renderPDF.pdf'
333
##def testFlowable():
334
## """Makes a platypus document"""
335
## from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
336
## from reportlab.lib.styles import getSampleStyleSheet
337
## styles = getSampleStyleSheet()
338
## styNormal = styles['Normal']
340
## doc = SimpleDocTemplate('test_flowable.pdf')
342
## story.append(Paragraph("This sees is a drawing can work as a flowable", styNormal))
344
## import testdrawings
347
## for funcname in dir(testdrawings):
348
## if funcname[0:10] == 'getDrawing':
349
## drawing = eval('testdrawings.' + funcname + '()') #execute it
350
## docstring = eval('testdrawings.' + funcname + '.__doc__')
351
## story.append(Paragraph(docstring, styNormal))
352
## story.append(Spacer(18,18))
353
## story.append(drawing)
354
## story.append(Spacer(36,36))
357
## print 'saves test_flowable.pdf'
359
if __name__=='__main__':