summaryrefslogtreecommitdiff
path: root/bin/reportlab/graphics/renderPDF.py
diff options
context:
space:
mode:
authorpinky <>2007-01-07 21:26:16 +0000
committerpinky <>2007-01-07 21:26:16 +0000
commitdb39be37af8e1f7947c186a2b4688f5963ed7cc7 (patch)
tree00228b8f6f6f2d14e30181376b2406d557b6e6de /bin/reportlab/graphics/renderPDF.py
parenteacf3d2a7007d9abe4fb00245d6fe833241fe283 (diff)
Reportlab 2.0
bzr revid: pinky-42646ca164f938abd312f6b2f8dccf73970f0c59
Diffstat (limited to 'bin/reportlab/graphics/renderPDF.py')
-rw-r--r--bin/reportlab/graphics/renderPDF.py360
1 files changed, 360 insertions, 0 deletions
diff --git a/bin/reportlab/graphics/renderPDF.py b/bin/reportlab/graphics/renderPDF.py
new file mode 100644
index 00000000000..e1da60c0896
--- /dev/null
+++ b/bin/reportlab/graphics/renderPDF.py
@@ -0,0 +1,360 @@
+#Copyright ReportLab Europe Ltd. 2000-2004
+#see license.txt for license details
+#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/renderPDF.py
+# renderPDF - draws Drawings onto a canvas
+"""Usage:
+ import renderpdf
+ renderpdf.draw(drawing, canvas, x, y)
+Execute the script to see some test drawings.
+changed
+"""
+__version__=''' $Id: renderPDF.py 2830 2006-04-05 15:18:32Z rgbecker $ '''
+
+from reportlab.graphics.shapes import *
+from reportlab.pdfgen.canvas import Canvas
+from reportlab.pdfbase.pdfmetrics import stringWidth
+from reportlab.lib.utils import getStringIO
+from reportlab import rl_config
+from renderbase import Renderer, StateTracker, getStateDelta, renderScaledDrawing
+
+# the main entry point for users...
+def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_):
+ """As it says"""
+ R = _PDFRenderer()
+ R.draw(renderScaledDrawing(drawing), canvas, x, y, showBoundary=showBoundary)
+
+class _PDFRenderer(Renderer):
+ """This draws onto a PDF document. It needs to be a class
+ rather than a function, as some PDF-specific state tracking is
+ needed outside of the state info in the SVG model."""
+
+ def __init__(self):
+ self._stroke = 0
+ self._fill = 0
+ self._tracker = StateTracker()
+
+ def drawNode(self, node):
+ """This is the recursive method called for each node
+ in the tree"""
+ #print "pdf:drawNode", self
+ #if node.__class__ is Wedge: stop
+ if not (isinstance(node, Path) and node.isClipPath):
+ self._canvas.saveState()
+
+ #apply state changes
+ deltas = getStateDelta(node)
+ self._tracker.push(deltas)
+ self.applyStateChanges(deltas, {})
+
+ #draw the object, or recurse
+ self.drawNodeDispatcher(node)
+
+ self._tracker.pop()
+ if not (isinstance(node, Path) and node.isClipPath):
+ self._canvas.restoreState()
+
+ def drawRect(self, rect):
+ if rect.rx == rect.ry == 0:
+ #plain old rectangle
+ self._canvas.rect(
+ rect.x, rect.y,
+ rect.width, rect.height,
+ stroke=self._stroke,
+ fill=self._fill
+ )
+ else:
+ #cheat and assume ry = rx; better to generalize
+ #pdfgen roundRect function. TODO
+ self._canvas.roundRect(
+ rect.x, rect.y,
+ rect.width, rect.height, rect.rx,
+ fill=self._fill,
+ stroke=self._stroke
+ )
+
+ def drawImage(self, image):
+ # currently not implemented in other renderers
+ if image.path and os.path.exists(image.path):
+ self._canvas.drawInlineImage(
+ image.path,
+ image.x, image.y,
+ image.width, image.height
+ )
+
+ def drawLine(self, line):
+ if self._stroke:
+ self._canvas.line(line.x1, line.y1, line.x2, line.y2)
+
+ def drawCircle(self, circle):
+ self._canvas.circle(
+ circle.cx, circle.cy, circle.r,
+ fill=self._fill,
+ stroke=self._stroke
+ )
+
+ def drawPolyLine(self, polyline):
+ if self._stroke:
+ assert len(polyline.points) >= 2, 'Polyline must have 2 or more points'
+ head, tail = polyline.points[0:2], polyline.points[2:],
+ path = self._canvas.beginPath()
+ path.moveTo(head[0], head[1])
+ for i in range(0, len(tail), 2):
+ path.lineTo(tail[i], tail[i+1])
+ self._canvas.drawPath(path)
+
+ def drawWedge(self, wedge):
+ centerx, centery, radius, startangledegrees, endangledegrees = \
+ wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees
+ yradius, radius1, yradius1 = wedge._xtraRadii()
+ if yradius is None: yradius = radius
+ angle = endangledegrees-startangledegrees
+ path = self._canvas.beginPath()
+ if (radius1==0 or radius1 is None) and (yradius1==0 or yradius1 is None):
+ path.moveTo(centerx, centery)
+ path.arcTo(centerx-radius, centery-yradius, centerx+radius, centery+yradius,
+ startangledegrees, angle)
+ else:
+ path.arc(centerx-radius, centery-yradius, centerx+radius, centery+yradius,
+ startangledegrees, angle)
+ path.arcTo(centerx-radius1, centery-yradius1, centerx+radius1, centery+yradius1,
+ endangledegrees, -angle)
+ path.close()
+ self._canvas.drawPath(path,
+ fill=self._fill,
+ stroke=self._stroke)
+
+ def drawEllipse(self, ellipse):
+ #need to convert to pdfgen's bounding box representation
+ x1 = ellipse.cx - ellipse.rx
+ x2 = ellipse.cx + ellipse.rx
+ y1 = ellipse.cy - ellipse.ry
+ y2 = ellipse.cy + ellipse.ry
+ self._canvas.ellipse(x1,y1,x2,y2,fill=self._fill,stroke=self._stroke)
+
+ def drawPolygon(self, polygon):
+ assert len(polygon.points) >= 2, 'Polyline must have 2 or more points'
+ head, tail = polygon.points[0:2], polygon.points[2:],
+ path = self._canvas.beginPath()
+ path.moveTo(head[0], head[1])
+ for i in range(0, len(tail), 2):
+ path.lineTo(tail[i], tail[i+1])
+ path.close()
+ self._canvas.drawPath(
+ path,
+ stroke=self._stroke,
+ fill=self._fill
+ )
+
+ def drawString(self, stringObj):
+ if self._fill:
+ S = self._tracker.getState()
+ text_anchor, x, y, text, enc = S['textAnchor'], stringObj.x,stringObj.y,stringObj.text, stringObj.encoding
+ if not text_anchor in ['start','inherited']:
+ font, font_size = S['fontName'], S['fontSize']
+ textLen = stringWidth(text, font, font_size, enc)
+ if text_anchor=='end':
+ x = x-textLen
+ elif text_anchor=='middle':
+ x = x - textLen/2
+ else:
+ raise ValueError, 'bad value for textAnchor '+str(text_anchor)
+ t = self._canvas.beginText(x,y)
+ t.textLine(text)
+ self._canvas.drawText(t)
+
+ def drawPath(self, path):
+ from reportlab.graphics.shapes import _renderPath
+ pdfPath = self._canvas.beginPath()
+ drawFuncs = (pdfPath.moveTo, pdfPath.lineTo, pdfPath.curveTo, pdfPath.close)
+ isClosed = _renderPath(path, drawFuncs)
+ if isClosed:
+ fill = self._fill
+ else:
+ fill = 0
+ if path.isClipPath:
+ self._canvas.clipPath(pdfPath, fill=fill, stroke=self._stroke)
+ else:
+ self._canvas.drawPath(pdfPath,
+ fill=fill,
+ stroke=self._stroke)
+
+ def applyStateChanges(self, delta, newState):
+ """This takes a set of states, and outputs the PDF operators
+ needed to set those properties"""
+ for key, value in delta.items():
+ if key == 'transform':
+ self._canvas.transform(value[0], value[1], value[2],
+ value[3], value[4], value[5])
+ elif key == 'strokeColor':
+ #this has different semantics in PDF to SVG;
+ #we always have a color, and either do or do
+ #not apply it; in SVG one can have a 'None' color
+ if value is None:
+ self._stroke = 0
+ else:
+ self._stroke = 1
+ self._canvas.setStrokeColor(value)
+ elif key == 'strokeWidth':
+ self._canvas.setLineWidth(value)
+ elif key == 'strokeLineCap': #0,1,2
+ self._canvas.setLineCap(value)
+ elif key == 'strokeLineJoin':
+ self._canvas.setLineJoin(value)
+# elif key == 'stroke_dasharray':
+# self._canvas.setDash(array=value)
+ elif key == 'strokeDashArray':
+ if value:
+ self._canvas.setDash(value)
+ else:
+ self._canvas.setDash()
+ elif key == 'fillColor':
+ #this has different semantics in PDF to SVG;
+ #we always have a color, and either do or do
+ #not apply it; in SVG one can have a 'None' color
+ if value is None:
+ self._fill = 0
+ else:
+ self._fill = 1
+ self._canvas.setFillColor(value)
+ elif key in ['fontSize', 'fontName']:
+ # both need setting together in PDF
+ # one or both might be in the deltas,
+ # so need to get whichever is missing
+ fontname = delta.get('fontName', self._canvas._fontname)
+ fontsize = delta.get('fontSize', self._canvas._fontsize)
+ self._canvas.setFont(fontname, fontsize)
+
+from reportlab.platypus import Flowable
+class GraphicsFlowable(Flowable):
+ """Flowable wrapper around a Pingo drawing"""
+ def __init__(self, drawing):
+ self.drawing = drawing
+ self.width = self.drawing.width
+ self.height = self.drawing.height
+
+ def draw(self):
+ draw(self.drawing, self.canv, 0, 0)
+
+def drawToFile(d, fn, msg="", showBoundary=rl_config._unset_, autoSize=1):
+ """Makes a one-page PDF with just the drawing.
+
+ If autoSize=1, the PDF will be the same size as
+ the drawing; if 0, it will place the drawing on
+ an A4 page with a title above it - possibly overflowing
+ if too big."""
+ d = renderScaledDrawing(d)
+ c = Canvas(fn)
+ c.setFont('Times-Roman', 36)
+ c.drawString(80, 750, msg)
+ c.setTitle(msg)
+
+ if autoSize:
+ c.setPageSize((d.width, d.height))
+ draw(d, c, 0, 0, showBoundary=showBoundary)
+ else:
+ #show with a title
+ c.setFont('Times-Roman', 12)
+ y = 740
+ i = 1
+ y = y - d.height
+ draw(d, c, 80, y, showBoundary=showBoundary)
+
+ c.showPage()
+ c.save()
+ if sys.platform=='mac' and not hasattr(fn, "write"):
+ try:
+ import macfs, macostools
+ macfs.FSSpec(fn).SetCreatorType("CARO", "PDF ")
+ macostools.touched(fn)
+ except:
+ pass
+
+
+def drawToString(d, msg="", showBoundary=rl_config._unset_,autoSize=1):
+ "Returns a PDF as a string in memory, without touching the disk"
+ s = getStringIO()
+ drawToFile(d, s, msg=msg, showBoundary=showBoundary,autoSize=autoSize)
+ return s.getvalue()
+
+
+#########################################################
+#
+# test code. First, defin a bunch of drawings.
+# Routine to draw them comes at the end.
+#
+#########################################################
+
+
+def test():
+ c = Canvas('renderPDF.pdf')
+ c.setFont('Times-Roman', 36)
+ c.drawString(80, 750, 'Graphics Test')
+
+ # print all drawings and their doc strings from the test
+ # file
+
+ #grab all drawings from the test module
+ from reportlab.graphics import testshapes
+ drawings = []
+ for funcname in dir(testshapes):
+ if funcname[0:10] == 'getDrawing':
+ drawing = eval('testshapes.' + funcname + '()') #execute it
+ docstring = eval('testshapes.' + funcname + '.__doc__')
+ drawings.append((drawing, docstring))
+
+ #print in a loop, with their doc strings
+ c.setFont('Times-Roman', 12)
+ y = 740
+ i = 1
+ for (drawing, docstring) in drawings:
+ assert (docstring is not None), "Drawing %d has no docstring!" % i
+ if y < 300: #allows 5-6 lines of text
+ c.showPage()
+ y = 740
+ # draw a title
+ y = y - 30
+ c.setFont('Times-BoldItalic',12)
+ c.drawString(80, y, 'Drawing %d' % i)
+ c.setFont('Times-Roman',12)
+ y = y - 14
+ textObj = c.beginText(80, y)
+ textObj.textLines(docstring)
+ c.drawText(textObj)
+ y = textObj.getY()
+ y = y - drawing.height
+ draw(drawing, c, 80, y)
+ i = i + 1
+ if y!=740: c.showPage()
+
+ c.save()
+ print 'saved renderPDF.pdf'
+
+##def testFlowable():
+## """Makes a platypus document"""
+## from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
+## from reportlab.lib.styles import getSampleStyleSheet
+## styles = getSampleStyleSheet()
+## styNormal = styles['Normal']
+##
+## doc = SimpleDocTemplate('test_flowable.pdf')
+## story = []
+## story.append(Paragraph("This sees is a drawing can work as a flowable", styNormal))
+##
+## import testdrawings
+## drawings = []
+##
+## for funcname in dir(testdrawings):
+## if funcname[0:10] == 'getDrawing':
+## drawing = eval('testdrawings.' + funcname + '()') #execute it
+## docstring = eval('testdrawings.' + funcname + '.__doc__')
+## story.append(Paragraph(docstring, styNormal))
+## story.append(Spacer(18,18))
+## story.append(drawing)
+## story.append(Spacer(36,36))
+##
+## doc.build(story)
+## print 'saves test_flowable.pdf'
+
+if __name__=='__main__':
+ test()
+ #testFlowable()