~toolpart/openobject-server/toolpart

« back to all changes in this revision

Viewing changes to bin/reportlab/graphics/renderPDF.py

  • Committer: pinky
  • Date: 2006-12-07 13:41:40 UTC
  • Revision ID: pinky-3f10ee12cea3c4c75cef44ab04ad33ef47432907
New trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
5
"""Usage:
 
6
    import renderpdf
 
7
    renderpdf.draw(drawing, canvas, x, y)
 
8
Execute the script to see some test drawings.
 
9
changed
 
10
"""
 
11
__version__=''' $Id$ '''
 
12
 
 
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
 
18
 
 
19
# the main entry point for users...
 
20
def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_):
 
21
    """As it says"""
 
22
    R = _PDFRenderer()
 
23
    R.draw(drawing, canvas, x, y, showBoundary=showBoundary)
 
24
 
 
25
from renderbase import Renderer, StateTracker, getStateDelta
 
26
 
 
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."""
 
31
 
 
32
    def __init__(self):
 
33
        self._stroke = 0
 
34
        self._fill = 0
 
35
        self._tracker = StateTracker()
 
36
 
 
37
    def drawNode(self, node):
 
38
        """This is the recursive method called for each node
 
39
        in the tree"""
 
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()
 
44
 
 
45
        #apply state changes
 
46
        deltas = getStateDelta(node)
 
47
        self._tracker.push(deltas)
 
48
        self.applyStateChanges(deltas, {})
 
49
 
 
50
        #draw the object, or recurse
 
51
        self.drawNodeDispatcher(node)
 
52
 
 
53
        self._tracker.pop()
 
54
        if not (isinstance(node, Path) and node.isClipPath):
 
55
            self._canvas.restoreState()
 
56
 
 
57
    def drawRect(self, rect):
 
58
        if rect.rx == rect.ry == 0:
 
59
            #plain old rectangle
 
60
            self._canvas.rect(
 
61
                    rect.x, rect.y,
 
62
                    rect.width, rect.height,
 
63
                    stroke=self._stroke,
 
64
                    fill=self._fill
 
65
                    )
 
66
        else:
 
67
            #cheat and assume ry = rx; better to generalize
 
68
            #pdfgen roundRect function.  TODO
 
69
            self._canvas.roundRect(
 
70
                    rect.x, rect.y,
 
71
                    rect.width, rect.height, rect.rx,
 
72
                    fill=self._fill,
 
73
                    stroke=self._stroke
 
74
                    )
 
75
 
 
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(
 
80
                    image.path,
 
81
                    image.x, image.y,
 
82
                    image.width, image.height
 
83
                    )
 
84
 
 
85
    def drawLine(self, line):
 
86
        if self._stroke:
 
87
            self._canvas.line(line.x1, line.y1, line.x2, line.y2)
 
88
 
 
89
    def drawCircle(self, circle):
 
90
            self._canvas.circle(
 
91
                    circle.cx, circle.cy, circle.r,
 
92
                    fill=self._fill,
 
93
                    stroke=self._stroke
 
94
                    )
 
95
 
 
96
    def drawPolyLine(self, polyline):
 
97
        if self._stroke:
 
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)
 
105
 
 
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)
 
117
        else:
 
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)
 
122
        path.close()
 
123
        self._canvas.drawPath(path,
 
124
                    fill=self._fill,
 
125
                    stroke=self._stroke)
 
126
 
 
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)
 
134
 
 
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])
 
142
        path.close()
 
143
        self._canvas.drawPath(
 
144
                            path,
 
145
                            stroke=self._stroke,
 
146
                            fill=self._fill
 
147
                            )
 
148
 
 
149
    def drawString(self, stringObj):
 
150
        if self._fill:
 
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':
 
157
                    x = x-textLen
 
158
                elif text_anchor=='middle':
 
159
                    x = x - textLen/2
 
160
                else:
 
161
                    raise ValueError, 'bad value for textAnchor '+str(text_anchor)
 
162
            t = self._canvas.beginText(x,y)
 
163
            t.textLine(text)
 
164
            self._canvas.drawText(t)
 
165
 
 
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)
 
171
        if isClosed:
 
172
            fill = self._fill
 
173
        else:
 
174
            fill = 0
 
175
        if path.isClipPath:
 
176
            self._canvas.clipPath(pdfPath, fill=fill, stroke=self._stroke)
 
177
        else:
 
178
            self._canvas.drawPath(pdfPath,
 
179
                        fill=fill,
 
180
                        stroke=self._stroke)
 
181
 
 
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
 
193
                if value is None:
 
194
                    self._stroke = 0
 
195
                else:
 
196
                    self._stroke = 1
 
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':
 
207
                if value:
 
208
                    self._canvas.setDash(value)
 
209
                else:
 
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
 
215
                if value is None:
 
216
                    self._fill = 0
 
217
                else:
 
218
                    self._fill = 1
 
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)
 
227
 
 
228
from reportlab.platypus import Flowable
 
229
 
 
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
 
236
 
 
237
    def draw(self):
 
238
        draw(self.drawing, self.canv, 0, 0)
 
239
 
 
240
def drawToFile(d, fn, msg="", showBoundary=rl_config._unset_, autoSize=1):
 
241
    """Makes a one-page PDF with just the drawing.
 
242
 
 
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
 
246
    if too big."""
 
247
    c = Canvas(fn)
 
248
    c.setFont('Times-Roman', 36)
 
249
    c.drawString(80, 750, msg)
 
250
    c.setTitle(msg)
 
251
 
 
252
    if autoSize:
 
253
        c.setPageSize((d.width, d.height))
 
254
        draw(d, c, 0, 0, showBoundary=showBoundary)
 
255
    else:
 
256
    #show with a title
 
257
        c.setFont('Times-Roman', 12)
 
258
        y = 740
 
259
        i = 1
 
260
        y = y - d.height
 
261
        draw(d, c, 80, y, showBoundary=showBoundary)
 
262
 
 
263
    c.showPage()
 
264
    c.save()
 
265
    if sys.platform=='mac' and not hasattr(fn, "write"):
 
266
        try:
 
267
            import macfs, macostools
 
268
            macfs.FSSpec(fn).SetCreatorType("CARO", "PDF ")
 
269
            macostools.touched(fn)
 
270
        except:
 
271
            pass
 
272
 
 
273
 
 
274
def drawToString(d, msg="", showBoundary=rl_config._unset_,autoSize=1):
 
275
    "Returns a PDF as a string in memory, without touching the disk"
 
276
    s = getStringIO()
 
277
    drawToFile(d, s, msg=msg, showBoundary=showBoundary,autoSize=autoSize)
 
278
    return s.getvalue()
 
279
 
 
280
 
 
281
#########################################################
 
282
#
 
283
#   test code.  First, defin a bunch of drawings.
 
284
#   Routine to draw them comes at the end.
 
285
#
 
286
#########################################################
 
287
 
 
288
 
 
289
def test():
 
290
    c = Canvas('renderPDF.pdf')
 
291
    c.setFont('Times-Roman', 36)
 
292
    c.drawString(80, 750, 'Graphics Test')
 
293
 
 
294
    # print all drawings and their doc strings from the test
 
295
    # file
 
296
 
 
297
    #grab all drawings from the test module
 
298
    from reportlab.graphics import testshapes
 
299
    drawings = []
 
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))
 
305
 
 
306
    #print in a loop, with their doc strings
 
307
    c.setFont('Times-Roman', 12)
 
308
    y = 740
 
309
    i = 1
 
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
 
313
            c.showPage()
 
314
            y = 740
 
315
        # draw a title
 
316
        y = y - 30
 
317
        c.setFont('Times-BoldItalic',12)
 
318
        c.drawString(80, y, 'Drawing %d' % i)
 
319
        c.setFont('Times-Roman',12)
 
320
        y = y - 14
 
321
        textObj = c.beginText(80, y)
 
322
        textObj.textLines(docstring)
 
323
        c.drawText(textObj)
 
324
        y = textObj.getY()
 
325
        y = y - drawing.height
 
326
        draw(drawing, c, 80, y)
 
327
        i = i + 1
 
328
    if y!=740: c.showPage()
 
329
 
 
330
    c.save()
 
331
    print 'saved renderPDF.pdf'
 
332
 
 
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']
 
339
##
 
340
##    doc = SimpleDocTemplate('test_flowable.pdf')
 
341
##    story = []
 
342
##    story.append(Paragraph("This sees is a drawing can work as a flowable", styNormal))
 
343
##
 
344
##    import testdrawings
 
345
##    drawings = []
 
346
##
 
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))
 
355
##
 
356
##    doc.build(story)
 
357
##    print 'saves test_flowable.pdf'
 
358
 
 
359
if __name__=='__main__':
 
360
    test()
 
361
    #testFlowable()