def __init__(self): self._stroke = 0 self._fill = 0 self._tracker = StateTracker()
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): path = image.path # currently not implemented in other renderers if path and (hasattr(path,'mode') or os.path.exists(image.path)): self._canvas.drawInlineImage( 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 -= textLen elif text_anchor=='middle': x -= textLen*0.5 elif text_anchor=='numeric': x -= numericXShift(text_anchor,text,textLen,font,font_size,enc) 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 setStrokeColor(self,c): self._canvas.setStrokeColor(c) def setFillColor(self,c): self._canvas.setFillColor(c) 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.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: if isinstance(value,(list,tuple)) and len(value)==2 and isinstance(value[1],(tuple,list)): phase = value[0] value = value[1] else: phase = 0 self._canvas.setDash(value,phase) 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.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) elif key=='fillOpacity': if value is not None: self._canvas.setFillAlpha(value) elif key=='strokeOpacity': if value is not None: self._canvas.setStrokeAlpha(value) elif key=='fillOverprint': self._canvas.setFillOverprint(value) elif key=='strokeOverprint': self._canvas.setStrokeOverprint(value) elif key=='overprintMask': self._canvas.setOverprintMask(value)
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)