def draw(self, drawing, canvas, x=0, y=0, showBoundary=rl_config._unset_): """This is the top level function, which draws the drawing at the given location. The recursive part is handled by drawNode.""" self._tracker = StateTracker(defaultObj=drawing) #stash references for ease of communication if showBoundary is rl_config._unset_: showBoundary = rl_config.showBoundary self._canvas = canvas canvas.__dict__['_drawing'] = self._drawing = drawing drawing._parent = None try: #bounding box if showBoundary: canvas.rect(x, y, drawing.width, drawing.height) canvas.saveState() self.initState(x, y) #this is the push() self.drawNode(drawing) self.pop() canvas.restoreState() finally: #remove any circular references try: del self._canvas, self._drawing, canvas._drawing, drawing._parent, self._tracker except: pass
def __init__(self): self._tracker = StateTracker() self.verbose = 0
class _SVGRenderer(Renderer): """This draws onto an SVG document. """ def __init__(self): self._tracker = StateTracker() self.verbose = 0 def drawNode(self, node): """This is the recursive method called for each node in the tree. """ if self.verbose: print "### begin _SVGRenderer.drawNode" self._canvas.comment("begin node %s" % ` node `) color = self._canvas._color if not (isinstance(node, Path) and node.isClipPath): pass # self._canvas.saveState() # apply state changes deltas = getStateDelta(node) self._tracker.push(deltas) self.applyStateChanges(deltas, {}) # draw the object, or recurse self.drawNodeDispatcher(node) rDeltas = self._tracker.pop() if not (isinstance(node, Path) and node.isClipPath): pass # self._canvas.restoreState() self._canvas.comment("end node %s" % ` node `) self._canvas._color = color # restore things we might have lost (without actually doing anything). for k, v in rDeltas.items(): if self._restores.has_key(k): setattr(self._canvas, self._restores[k], v) if self.verbose: print "### end _SVGRenderer.drawNode" _restores = { "strokeColor": "_strokeColor", "strokeWidth": "_lineWidth", "strokeLineCap": "_lineCap", "strokeLineJoin": "_lineJoin", "fillColor": "_fillColor", "fontName": "_font", "fontSize": "_fontSize", } def drawGroup(self, group): if self.verbose: print "### begin _SVGRenderer.drawGroup" currGroup = self._canvas.startGroup() a, b, c, d, e, f = self._tracker.getCTM() for childNode in group.getContents(): if isinstance(childNode, UserNode): node2 = childNode.provideNode() else: node2 = childNode self.drawNode(node2) self._canvas.transform(a, b, c, d, e, f) self._canvas.endGroup(currGroup) if self.verbose: print "### end _SVGRenderer.drawGroup" def drawRect(self, rect): if rect.rx == rect.ry == 0: # plain old rectangle self._canvas.rect(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height) else: # cheat and assume ry = rx; better to generalize # pdfgen roundRect function. TODO self._canvas.roundRect(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.rx, rect.ry) def drawString(self, stringObj): if self._canvas._fillColor: S = self._tracker.getState() text_anchor, x, y, text = S["textAnchor"], stringObj.x, stringObj.y, stringObj.text if not text_anchor in ["start", "inherited"]: font, fontSize = S["fontName"], S["fontSize"] textLen = stringWidth(text, font, fontSize) if text_anchor == "end": x = x - textLen elif text_anchor == "middle": x = x - textLen / 2 else: raise ValueError, "bad value for text_anchor " + str(text_anchor) self._canvas.drawString(text, x, y) def drawLine(self, line): if self._canvas._strokeColor: self._canvas.line(line.x1, line.y1, line.x2, line.y2) def drawCircle(self, circle): self._canvas.circle(circle.cx, circle.cy, circle.r) def drawWedge(self, wedge): centerx, centery, radius, startangledegrees, endangledegrees = ( wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees, ) yradius = wedge.yradius or wedge.radius (x1, y1) = (centerx - radius, centery - yradius) (x2, y2) = (centerx + radius, centery + yradius) extent = endangledegrees - startangledegrees self._canvas.drawArc(x1, y1, x2, y2, startangledegrees, extent, fromcenter=1) def drawPolyLine(self, p): if self._canvas._strokeColor: self._canvas.polyLine(_pointsFromList(p.points)) 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) def drawPolygon(self, p): self._canvas.polygon(_pointsFromList(p.points), closed=1) def drawPath(self, path): # print "### drawPath", path.points from reportlab.graphics.shapes import _renderPath c = self._canvas drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath) isClosed = _renderPath(path, drawFuncs) if not isClosed: c._fillColor = None c._fillAndStroke([], clip=path.isClipPath) def applyStateChanges(self, delta, newState): """This takes a set of states, and outputs the operators needed to set those properties""" for key, value in delta.items(): if key == "transform": pass # self._canvas.transform(value[0], value[1], value[2], value[3], value[4], value[5]) elif key == "strokeColor": 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 == "strokeDashArray": if value: self._canvas.setDash(value) else: self._canvas.setDash() elif key == "fillColor": self._canvas.setFillColor(value) elif key in ["fontSize", "fontName"]: fontname = delta.get("fontName", self._canvas._font) fontsize = delta.get("fontSize", self._canvas._fontSize) self._canvas.setFont(fontname, fontsize)
def __init__(self): self._tracker = StateTracker()
class _PSRenderer(Renderer): """This draws onto a EPS document. It needs to be a class rather than a function, as some EPS-specific state tracking is needed outside of the state info in the SVG model.""" def __init__(self): self._tracker = StateTracker() def drawNode(self, node): """This is the recursive method called for each node in the tree""" self._canvas.comment('begin node %r'%node) color = self._canvas._color 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) rDeltas = self._tracker.pop() if not (isinstance(node, Path) and node.isClipPath): self._canvas.restoreState() self._canvas.comment('end node %r'%node) self._canvas._color = color #restore things we might have lost (without actually doing anything). for k, v in rDeltas.items(): if k in self._restores: setattr(self._canvas,self._restores[k],v) ## _restores = {'stroke':'_stroke','stroke_width': '_lineWidth','stroke_linecap':'_lineCap', ## 'stroke_linejoin':'_lineJoin','fill':'_fill','font_family':'_font', ## 'font_size':'_fontSize'} _restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap', 'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font', 'fontSize':'_fontSize'} def drawRect(self, rect): if rect.rx == rect.ry == 0: #plain old rectangle self._canvas.rect( rect.x, rect.y, rect.x+rect.width, rect.y+rect.height) else: #cheat and assume ry = rx; better to generalize #pdfgen roundRect function. TODO self._canvas.roundRect( rect.x, rect.y, rect.x+rect.width, rect.y+rect.height, rect.rx, rect.ry ) def drawLine(self, line): if self._canvas._strokeColor: self._canvas.line(line.x1, line.y1, line.x2, line.y2) def drawCircle(self, circle): self._canvas.circle( circle.cx, circle.cy, circle.r) def drawWedge(self, wedge): yradius, radius1, yradius1 = wedge._xtraRadii() if (radius1==0 or radius1 is None) and (yradius1==0 or yradius1 is None): startangledegrees = wedge.startangledegrees endangledegrees = wedge.endangledegrees centerx= wedge.centerx centery = wedge.centery radius = wedge.radius extent = endangledegrees - startangledegrees self._canvas.drawArc(centerx-radius, centery-yradius, centerx+radius, centery+yradius, startangledegrees, extent, fromcenter=1) else: self.drawPolygon(wedge.asPolygon()) def drawPolyLine(self, p): if self._canvas._strokeColor: self._canvas.polyLine(_pointsFromList(p.points)) 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) def drawPolygon(self, p): self._canvas.polygon(_pointsFromList(p.points), closed=1) def drawString(self, stringObj): if self._canvas._fillColor: S = self._tracker.getState() text_anchor, x, y, text = S['textAnchor'], stringObj.x,stringObj.y,stringObj.text if not text_anchor in ['start','inherited']: font, fontSize = S['fontName'], S['fontSize'] textLen = stringWidth(text, font,fontSize) if text_anchor=='end': x -= textLen elif text_anchor=='middle': x -= textLen/2 elif text_anchor=='numeric': x -= numericXShift(text_anchor,text,textLen,font,fontSize,encoding='winansi') else: raise ValueError, 'bad value for text_anchor '+str(text_anchor) self._canvas.drawString(x,y,text) def drawPath(self, path): from reportlab.graphics.shapes import _renderPath c = self._canvas drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath) isClosed = _renderPath(path, drawFuncs) if not isClosed: c._fillColor = None c._fillAndStroke([], clip=path.isClipPath) def applyStateChanges(self, delta, newState): """This takes a set of states, and outputs the 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 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 == 'strokeDashArray': if value: self._canvas.setDash(value) else: self._canvas.setDash() ## elif key == 'stroke_opacity': ## warnOnce('Stroke Opacity not supported yet') 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 self._canvas.setFillColor(value) ## elif key == 'fill_rule': ## warnOnce('Fill rules not done yet') ## elif key == 'fill_opacity': ## warnOnce('Fill opacity not done yet') 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._font) fontsize = delta.get('fontSize', self._canvas._fontSize) self._canvas.setFont(fontname, fontsize) def drawImage(self, image): from reportlab.lib.utils import ImageReader im = ImageReader(image.path) x0 = image.x y0 = image.y x1 = image.width if x1 is not None: x1 += x0 y1 = image.height if y1 is not None: y1 += y0 self._canvas.drawImage(im._image,x0,y0,x1,y1)
class _PMRenderer(Renderer): """This draws onto a pix map image. It needs to be a class rather than a function, as some image-specific state tracking is needed outside of the state info in the SVG model.""" def __init__(self): self._tracker = StateTracker() def pop(self): self._tracker.pop() self.applyState() def push(self,node): deltas = getStateDelta(node) self._tracker.push(deltas) self.applyState() def applyState(self): s = self._tracker.getState() self._canvas.ctm = s['ctm'] self._canvas.strokeWidth = s['strokeWidth'] alpha = s['strokeOpacity'] if alpha is not None: self._canvas.strokeOpacity = alpha self._canvas.setStrokeColor(s['strokeColor']) self._canvas.lineCap = s['strokeLineCap'] self._canvas.lineJoin = s['strokeLineJoin'] da = s['strokeDashArray'] da = da and (0,da) or None self._canvas.dashArray = da alpha = s['fillOpacity'] if alpha is not None: self._canvas.fillOpacity = alpha self._canvas.setFillColor(s['fillColor']) self._canvas.setFont(s['fontName'], s['fontSize']) def initState(self,x,y): deltas = STATE_DEFAULTS.copy() deltas['transform'] = self._canvas._baseCTM[0:4]+(x,y) self._tracker.push(deltas) self.applyState() def drawNode(self, node): """This is the recursive method called for each node in the tree""" #apply state changes self.push(node) #draw the object, or recurse self.drawNodeDispatcher(node) # restore the state self.pop() def drawRect(self, rect): c = self._canvas if rect.rx == rect.ry == 0: #plain old rectangle, draw clockwise (x-axis to y-axis) direction c.rect(rect.x,rect.y, rect.width, rect.height) else: c.roundRect(rect.x,rect.y, rect.width, rect.height, rect.rx, rect.ry) def drawLine(self, line): self._canvas.line(line.x1,line.y1,line.x2,line.y2) def drawImage(self, image): if image.path and os.path.exists(image.path): if type(image.path) is type(''): im = _getImage().open(image.path).convert('RGB') else: im = image.path.convert('RGB') srcW, srcH = im.size dstW, dstH = image.width, image.height if dstW is None: dstW = srcW if dstH is None: dstH = srcH self._canvas._aapixbuf( image.x, image.y, dstW, dstH, im.tostring(), srcW, srcH, 3, ) def drawCircle(self, circle): c = self._canvas c.circle(circle.cx,circle.cy, circle.r) c.fillstrokepath() def drawPolyLine(self, polyline, _doClose=0): P = polyline.points assert len(P) >= 2, 'Polyline must have 1 or more points' c = self._canvas c.pathBegin() c.moveTo(P[0], P[1]) for i in range(2, len(P), 2): c.lineTo(P[i], P[i+1]) if _doClose: c.pathClose() c.pathFill() c.pathStroke() def drawEllipse(self, ellipse): c=self._canvas c.ellipse(ellipse.cx, ellipse.cy, ellipse.rx,ellipse.ry) c.fillstrokepath() def drawPolygon(self, polygon): self.drawPolyLine(polygon,_doClose=1) def drawString(self, stringObj): canv = self._canvas fill = canv.fillColor if fill is not None: S = self._tracker.getState() text_anchor = S['textAnchor'] fontName = S['fontName'] fontSize = S['fontSize'] text = stringObj.text x = stringObj.x y = stringObj.y if not text_anchor in ['start','inherited']: textLen = stringWidth(text, fontName,fontSize) if text_anchor=='end': x -= textLen elif text_anchor=='middle': x -= textLen/2 elif text_anchor=='numeric': x -= numericXShift(text_anchor,text,textLen,fontName,fontSize,stringObj.encoding) else: raise ValueError, 'bad value for textAnchor '+str(text_anchor) canv.drawString(x,y,text,_fontInfo=(fontName,fontSize)) def drawPath(self, path): c = self._canvas if path is EmptyClipPath: del c._clipPaths[-1] if c._clipPaths: P = c._clipPaths[-1] icp = P.isClipPath P.isClipPath = 1 self.drawPath(P) P.isClipPath = icp else: c.clipPathClear() return c.pathBegin() drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.pathClose) from reportlab.graphics.shapes import _renderPath isClosed = _renderPath(path, drawFuncs) if path.isClipPath: c.clipPathSet() c._clipPaths.append(path) else: if isClosed: c.pathFill() c.pathStroke()
class _SVGRenderer(Renderer): """This draws onto an SVG document. """ def __init__(self): self._tracker = StateTracker() self.verbose = 0 def drawNode(self, node): """This is the recursive method called for each node in the tree. """ if self.verbose: print "### begin _SVGRenderer.drawNode(%r)" % node self._canvas.comment('begin node %r'%node) color = self._canvas._color style = self._canvas.style.copy() if not (isinstance(node, Path) and node.isClipPath): pass # self._canvas.saveState() #apply state changes deltas = getStateDelta(node) self._tracker.push(deltas) self.applyStateChanges(deltas, {}) #draw the object, or recurse self.drawNodeDispatcher(node) rDeltas = self._tracker.pop() if not (isinstance(node, Path) and node.isClipPath): pass #self._canvas.restoreState() self._canvas.comment('end node %r'%node) self._canvas._color = color #restore things we might have lost (without actually doing anything). for k, v in rDeltas.items(): if k in self._restores: setattr(self._canvas,self._restores[k],v) self._canvas.style = style if self.verbose: print "### end _SVGRenderer.drawNode(%r)" % node _restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap', 'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font', 'fontSize':'_fontSize'} def _get_link_info_dict(self, obj): #We do not want None or False as the link, even if it is the #attribute's value - use the empty string instead. url = getattr(obj, "hrefURL", "") or "" title = getattr(obj, "hrefTitle", "") or "" if url : #Is it valid to have a link with no href? The XML requires #the xlink:href to be present, but you might just want a #tool tip shown (via the xlink:title attribute). Note that #giving an href of "" is equivalent to "the current page" #(a relative link saying go nowhere). return {"xlink:href":url, "xlink:title":title, "target":"_top"} #Currently of all the mainstream browsers I have tested, only Safari/webkit #will show SVG images embedded in HTML using a simple <img src="..." /> tag. #However, the links don't work (Safari 3.2.1 on the Mac). # #Therefore I use the following, which also works for Firefox, Opera, and #IE 6.0 with Adobe SVG Viewer 6 beta: #<object data="..." type="image/svg+xml" width="430" height="150" class="img"> # #Once displayed, Firefox and Safari treat the SVG like a frame, and #by default clicking on links acts "in frame" and replaces the image. #Opera does what I expect, and replaces the whole page with the link. # #Therefore I use target="_top" to force the links to replace the whole page. #This now works as expected on Safari 3.2.1, Firefox 3.0.6, Opera 9.20. #Perhaps the target attribute should be an option, perhaps defaulting to #"_top" as used here? else : return None def drawGroup(self, group): if self.verbose: print "### begin _SVGRenderer.drawGroup" currGroup = self._canvas.startGroup() a, b, c, d, e, f = self._tracker.getState()['transform'] for childNode in group.getContents(): if isinstance(childNode, UserNode): node2 = childNode.provideNode() else: node2 = childNode self.drawNode(node2) self._canvas.transform(a, b, c, d, e, f) self._canvas.endGroup(currGroup) if self.verbose: print "### end _SVGRenderer.drawGroup" def drawRect(self, rect): link_info = self._get_link_info_dict(rect) if rect.rx == rect.ry == 0: #plain old rectangle self._canvas.rect( rect.x, rect.y, rect.x+rect.width, rect.y+rect.height, link_info=link_info) else: #cheat and assume ry = rx; better to generalize #pdfgen roundRect function. TODO self._canvas.roundRect( rect.x, rect.y, rect.x+rect.width, rect.y+rect.height, rect.rx, rect.ry, link_info=link_info) def drawString(self, stringObj): if self._canvas._fillColor: S = self._tracker.getState() text_anchor, x, y, text = S['textAnchor'], stringObj.x, stringObj.y, stringObj.text if not text_anchor in ('start', 'inherited'): font, fontSize = S['fontName'], S['fontSize'] textLen = stringWidth(text, font,fontSize) if text_anchor=='end': x -= textLen elif text_anchor=='middle': x -= textLen/2 elif text_anchor=='numeric': x -= numericXShift(text_anchor,text,textLen,font,fontSize) else: raise ValueError, 'bad value for text_anchor ' + str(text_anchor) self._canvas.drawString(text,x,y,link_info=self._get_link_info_dict(stringObj)) def drawLine(self, line): if self._canvas._strokeColor: self._canvas.line(line.x1, line.y1, line.x2, line.y2) def drawCircle(self, circle): self._canvas.circle( circle.cx, circle.cy, circle.r, link_info=self._get_link_info_dict(circle)) def drawWedge(self, wedge): centerx, centery, radius, startangledegrees, endangledegrees = \ wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees yradius = wedge.yradius or wedge.radius (x1, y1) = (centerx-radius, centery-yradius) (x2, y2) = (centerx+radius, centery+yradius) extent = endangledegrees - startangledegrees self._canvas.drawArc(x1, y1, x2, y2, startangledegrees, extent, fromcenter=1) def drawPolyLine(self, p): if self._canvas._strokeColor: self._canvas.polyLine(_pointsFromList(p.points)) 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, link_info=self._get_link_info_dict(ellipse)) def drawPolygon(self, p): self._canvas.polygon(_pointsFromList(p.points), closed=1, link_info=self._get_link_info_dict(p)) def drawPath(self, path): # print "### drawPath", path.points from reportlab.graphics.shapes import _renderPath c = self._canvas drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath) isClosed = _renderPath(path, drawFuncs) if isClosed: #Only try and add links to closed paths... link_info = self._get_link_info_dict(path) else : c._fillColor = None link_info = None c._fillAndStroke([], clip=path.isClipPath, link_info=link_info) def applyStateChanges(self, delta, newState): """This takes a set of states, and outputs the operators needed to set those properties""" for key, value in delta.items(): if key == 'transform': pass #self._canvas.transform(value[0], value[1], value[2], value[3], value[4], value[5]) elif key == 'strokeColor': 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 == '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': self._canvas.setFillColor(value) elif key in ['fontSize', 'fontName']: fontname = delta.get('fontName', self._canvas._font) fontsize = delta.get('fontSize', self._canvas._fontSize) self._canvas.setFont(fontname, fontsize)
class _PSRenderer(Renderer): """This draws onto a EPS document. It needs to be a class rather than a function, as some EPS-specific state tracking is needed outside of the state info in the SVG model.""" def __init__(self): self._tracker = StateTracker() def drawNode(self, node): """This is the recursive method called for each node in the tree""" self._canvas.comment('begin node %r' % node) color = self._canvas._color 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) rDeltas = self._tracker.pop() if not (isinstance(node, Path) and node.isClipPath): self._canvas.restoreState() self._canvas.comment('end node %r' % node) self._canvas._color = color #restore things we might have lost (without actually doing anything). for k, v in rDeltas.items(): if k in self._restores: setattr(self._canvas, self._restores[k], v) ## _restores = {'stroke':'_stroke','stroke_width': '_lineWidth','stroke_linecap':'_lineCap', ## 'stroke_linejoin':'_lineJoin','fill':'_fill','font_family':'_font', ## 'font_size':'_fontSize'} _restores = { 'strokeColor': '_strokeColor', 'strokeWidth': '_lineWidth', 'strokeLineCap': '_lineCap', 'strokeLineJoin': '_lineJoin', 'fillColor': '_fillColor', 'fontName': '_font', 'fontSize': '_fontSize' } def drawRect(self, rect): if rect.rx == rect.ry == 0: #plain old rectangle self._canvas.rect(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height) else: #cheat and assume ry = rx; better to generalize #pdfgen roundRect function. TODO self._canvas.roundRect(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.rx, rect.ry) def drawLine(self, line): if self._canvas._strokeColor: self._canvas.line(line.x1, line.y1, line.x2, line.y2) def drawCircle(self, circle): self._canvas.circle(circle.cx, circle.cy, circle.r) def drawWedge(self, wedge): yradius, radius1, yradius1 = wedge._xtraRadii() if (radius1 == 0 or radius1 is None) and (yradius1 == 0 or yradius1 is None): startangledegrees = wedge.startangledegrees endangledegrees = wedge.endangledegrees centerx = wedge.centerx centery = wedge.centery radius = wedge.radius extent = endangledegrees - startangledegrees self._canvas.drawArc(centerx - radius, centery - yradius, centerx + radius, centery + yradius, startangledegrees, extent, fromcenter=1) else: self.drawPolygon(wedge.asPolygon()) def drawPolyLine(self, p): if self._canvas._strokeColor: self._canvas.polyLine(_pointsFromList(p.points)) 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) def drawPolygon(self, p): self._canvas.polygon(_pointsFromList(p.points), closed=1) def drawString(self, stringObj): if self._canvas._fillColor: S = self._tracker.getState() text_anchor, x, y, text = S[ 'textAnchor'], stringObj.x, stringObj.y, stringObj.text if not text_anchor in ['start', 'inherited']: font, fontSize = S['fontName'], S['fontSize'] textLen = stringWidth(text, font, fontSize) if text_anchor == 'end': x -= textLen elif text_anchor == 'middle': x -= textLen / 2 elif text_anchor == 'numeric': x -= numericXShift(text_anchor, text, textLen, font, fontSize, encoding='winansi') else: raise ValueError('bad value for text_anchor ' + str(text_anchor)) self._canvas.drawString(x, y, text) def drawPath(self, path): from reportlab.graphics.shapes import _renderPath c = self._canvas drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath) isClosed = _renderPath(path, drawFuncs) if not isClosed: c._fillColor = None c._fillAndStroke([], clip=path.isClipPath) def applyStateChanges(self, delta, newState): """This takes a set of states, and outputs the 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 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 == '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 == 'stroke_opacity': ## warnOnce('Stroke Opacity not supported yet') 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 self._canvas.setFillColor(value) ## elif key == 'fill_rule': ## warnOnce('Fill rules not done yet') ## elif key == 'fill_opacity': ## warnOnce('Fill opacity not done yet') 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._font) fontsize = delta.get('fontSize', self._canvas._fontSize) self._canvas.setFont(fontname, fontsize) def drawImage(self, image): from reportlab.lib.utils import ImageReader im = ImageReader(image.path) x0 = image.x y0 = image.y x1 = image.width if x1 is not None: x1 += x0 y1 = image.height if y1 is not None: y1 += y0 self._canvas.drawImage(im._image, x0, y0, x1, y1)
class _SVGRenderer(Renderer): """This draws onto an SVG document. """ def __init__(self): self._tracker = StateTracker() self.verbose = 0 def drawNode(self, node): """This is the recursive method called for each node in the tree. """ if self.verbose: print("### begin _SVGRenderer.drawNode(%r)" % node) self._canvas.comment('begin node %r' % node) color = self._canvas._color style = self._canvas.style.copy() if not (isinstance(node, Path) and node.isClipPath): pass # self._canvas.saveState() #apply state changes deltas = getStateDelta(node) self._tracker.push(deltas) self.applyStateChanges(deltas, {}) #draw the object, or recurse self.drawNodeDispatcher(node) rDeltas = self._tracker.pop() if not (isinstance(node, Path) and node.isClipPath): pass #self._canvas.restoreState() self._canvas.comment('end node %r' % node) self._canvas._color = color #restore things we might have lost (without actually doing anything). for k, v in rDeltas.items(): if k in self._restores: setattr(self._canvas, self._restores[k], v) self._canvas.style = style if self.verbose: print("### end _SVGRenderer.drawNode(%r)" % node) _restores = { 'strokeColor': '_strokeColor', 'strokeWidth': '_lineWidth', 'strokeLineCap': '_lineCap', 'strokeLineJoin': '_lineJoin', 'fillColor': '_fillColor', 'fontName': '_font', 'fontSize': '_fontSize' } def _get_link_info_dict(self, obj): #We do not want None or False as the link, even if it is the #attribute's value - use the empty string instead. url = getattr(obj, "hrefURL", "") or "" title = getattr(obj, "hrefTitle", "") or "" if url: #Is it valid to have a link with no href? The XML requires #the xlink:href to be present, but you might just want a #tool tip shown (via the xlink:title attribute). Note that #giving an href of "" is equivalent to "the current page" #(a relative link saying go nowhere). return {"xlink:href": url, "xlink:title": title, "target": "_top"} #Currently of all the mainstream browsers I have tested, only Safari/webkit #will show SVG images embedded in HTML using a simple <img src="..." /> tag. #However, the links don't work (Safari 3.2.1 on the Mac). # #Therefore I use the following, which also works for Firefox, Opera, and #IE 6.0 with Adobe SVG Viewer 6 beta: #<object data="..." type="image/svg+xml" width="430" height="150" class="img"> # #Once displayed, Firefox and Safari treat the SVG like a frame, and #by default clicking on links acts "in frame" and replaces the image. #Opera does what I expect, and replaces the whole page with the link. # #Therefore I use target="_top" to force the links to replace the whole page. #This now works as expected on Safari 3.2.1, Firefox 3.0.6, Opera 9.20. #Perhaps the target attribute should be an option, perhaps defaulting to #"_top" as used here? else: return None def drawGroup(self, group): if self.verbose: print("### begin _SVGRenderer.drawGroup") currGroup = self._canvas.startGroup() a, b, c, d, e, f = self._tracker.getState()['transform'] for childNode in group.getContents(): if isinstance(childNode, UserNode): node2 = childNode.provideNode() else: node2 = childNode self.drawNode(node2) self._canvas.transform(a, b, c, d, e, f) self._canvas.endGroup(currGroup) if self.verbose: print("### end _SVGRenderer.drawGroup") def drawRect(self, rect): link_info = self._get_link_info_dict(rect) svgAttrs = getattr(rect, '_svgAttrs', {}) if rect.rx == rect.ry == 0: #plain old rectangle self._canvas.rect(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, link_info=link_info, **svgAttrs) else: #cheat and assume ry = rx; better to generalize #pdfgen roundRect function. TODO self._canvas.roundRect(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.rx, rect.ry, link_info=link_info, **svgAttrs) def drawString(self, stringObj): if self._canvas._fillColor: S = self._tracker.getState() text_anchor, x, y, text = S[ 'textAnchor'], stringObj.x, stringObj.y, stringObj.text if not text_anchor in ('start', 'inherited'): font, fontSize = S['fontName'], S['fontSize'] textLen = stringWidth(text, font, fontSize) if text_anchor == 'end': x -= textLen elif text_anchor == 'middle': x -= textLen / 2 elif text_anchor == 'numeric': x -= numericXShift(text_anchor, text, textLen, font, fontSize) else: raise ValueError('bad value for text_anchor ' + str(text_anchor)) self._canvas.drawString( text, x, y, link_info=self._get_link_info_dict(stringObj), **getattr(stringObj, '_svgAttrs', {})) def drawLine(self, line): if self._canvas._strokeColor: self._canvas.line(line.x1, line.y1, line.x2, line.y2) def drawCircle(self, circle): self._canvas.circle(circle.cx, circle.cy, circle.r, link_info=self._get_link_info_dict(circle)) def drawWedge(self, wedge): centerx, centery, radius, startangledegrees, endangledegrees = \ wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees yradius = wedge.yradius or wedge.radius (x1, y1) = (centerx - radius, centery - yradius) (x2, y2) = (centerx + radius, centery + yradius) extent = endangledegrees - startangledegrees self._canvas.drawArc(x1, y1, x2, y2, startangledegrees, extent, fromcenter=1) def drawPolyLine(self, p): if self._canvas._strokeColor: self._canvas.polyLine(_pointsFromList(p.points)) 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, link_info=self._get_link_info_dict(ellipse)) def drawPolygon(self, p): self._canvas.polygon(_pointsFromList(p.points), closed=1, link_info=self._get_link_info_dict(p)) def drawPath(self, path): # print "### drawPath", path.points from reportlab.graphics.shapes import _renderPath c = self._canvas drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath) isClosed = _renderPath(path, drawFuncs) if isClosed: #Only try and add links to closed paths... link_info = self._get_link_info_dict(path) else: c._fillColor = None link_info = None c._fillAndStroke([], clip=path.isClipPath, link_info=link_info) def applyStateChanges(self, delta, newState): """This takes a set of states, and outputs the operators needed to set those properties""" for key, value in delta.items(): if key == 'transform': pass #self._canvas.transform(value[0], value[1], value[2], value[3], value[4], value[5]) elif key == 'strokeColor': 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 == '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': self._canvas.setFillColor(value) elif key in ['fontSize', 'fontName']: fontname = delta.get('fontName', self._canvas._font) fontsize = delta.get('fontSize', self._canvas._fontSize) self._canvas.setFont(fontname, fontsize)
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): 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 _SVGRenderer(Renderer): """This draws onto an SVG document. """ def __init__(self): self._tracker = StateTracker() self.verbose = 0 def drawNode(self, node): """This is the recursive method called for each node in the tree. """ if self.verbose: print "### begin _SVGRenderer.drawNode" self._canvas.comment('begin node %s'%`node`) color = self._canvas._color if not (isinstance(node, Path) and node.isClipPath): pass # self._canvas.saveState() #apply state changes deltas = getStateDelta(node) self._tracker.push(deltas) self.applyStateChanges(deltas, {}) #draw the object, or recurse self.drawNodeDispatcher(node) rDeltas = self._tracker.pop() if not (isinstance(node, Path) and node.isClipPath): pass # self._canvas.restoreState() self._canvas.comment('end node %s'%`node`) self._canvas._color = color #restore things we might have lost (without actually doing anything). for k, v in rDeltas.items(): if self._restores.has_key(k): setattr(self._canvas,self._restores[k],v) if self.verbose: print "### end _SVGRenderer.drawNode" _restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap', 'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font', 'fontSize':'_fontSize'} def drawGroup(self, group): if self.verbose: print "### begin _SVGRenderer.drawGroup" currGroup = self._canvas.startGroup() a, b, c, d, e, f = self._tracker.getState()['transform'] for childNode in group.getContents(): if isinstance(childNode, UserNode): node2 = childNode.provideNode() else: node2 = childNode self.drawNode(node2) self._canvas.transform(a, b, c, d, e, f) self._canvas.endGroup(currGroup) if self.verbose: print "### end _SVGRenderer.drawGroup" def drawRect(self, rect): if rect.rx == rect.ry == 0: #plain old rectangle self._canvas.rect( rect.x, rect.y, rect.x+rect.width, rect.y+rect.height) else: #cheat and assume ry = rx; better to generalize #pdfgen roundRect function. TODO self._canvas.roundRect( rect.x, rect.y, rect.x+rect.width, rect.y+rect.height, rect.rx, rect.ry ) def drawString(self, stringObj): if self._canvas._fillColor: S = self._tracker.getState() text_anchor, x, y, text = S['textAnchor'], stringObj.x, stringObj.y, stringObj.text if not text_anchor in ['start', 'inherited']: font, fontSize = S['fontName'], S['fontSize'] textLen = stringWidth(text, font,fontSize) if text_anchor=='end': x = x-textLen elif text_anchor=='middle': x = x - textLen/2 else: raise ValueError, 'bad value for text_anchor ' + str(text_anchor) self._canvas.drawString(text,x,y) def drawLine(self, line): if self._canvas._strokeColor: self._canvas.line(line.x1, line.y1, line.x2, line.y2) def drawCircle(self, circle): self._canvas.circle( circle.cx, circle.cy, circle.r) def drawWedge(self, wedge): centerx, centery, radius, startangledegrees, endangledegrees = \ wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees yradius = wedge.yradius or wedge.radius (x1, y1) = (centerx-radius, centery-yradius) (x2, y2) = (centerx+radius, centery+yradius) extent = endangledegrees - startangledegrees self._canvas.drawArc(x1, y1, x2, y2, startangledegrees, extent, fromcenter=1) def drawPolyLine(self, p): if self._canvas._strokeColor: self._canvas.polyLine(_pointsFromList(p.points)) 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) def drawPolygon(self, p): self._canvas.polygon(_pointsFromList(p.points), closed=1) def drawPath(self, path): # print "### drawPath", path.points from reportlab.graphics.shapes import _renderPath c = self._canvas drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath) isClosed = _renderPath(path, drawFuncs) if not isClosed: c._fillColor = None c._fillAndStroke([], clip=path.isClipPath) def applyStateChanges(self, delta, newState): """This takes a set of states, and outputs the operators needed to set those properties""" for key, value in delta.items(): if key == 'transform': pass #self._canvas.transform(value[0], value[1], value[2], value[3], value[4], value[5]) elif key == 'strokeColor': 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 == 'strokeDashArray': if value: self._canvas.setDash(value) else: self._canvas.setDash() elif key == 'fillColor': self._canvas.setFillColor(value) elif key in ['fontSize', 'fontName']: fontname = delta.get('fontName', self._canvas._font) fontsize = delta.get('fontSize', self._canvas._fontSize) self._canvas.setFont(fontname, fontsize)