src/reportlab/graphics/renderSVG.py
branchpy33
changeset 3721 0c93dd8ff567
parent 3644 8072bd965473
child 3723 99aa837b6703
equal deleted inserted replaced
3720:7a059dde5bf5 3721:0c93dd8ff567
    81         n = transformNode(doc, "node1", node0, x="x0", y="x0", zoo=bar())
    81         n = transformNode(doc, "node1", node0, x="x0", y="x0", zoo=bar())
    82         -> DOM node for <node1 x="[node0.x0]" y="[node0.y0]" zoo="[bar()]"/>
    82         -> DOM node for <node1 x="[node0.x0]" y="[node0.y0]" zoo="[bar()]"/>
    83     """
    83     """
    84 
    84 
    85     newNode = doc.createElement(newTag)
    85     newNode = doc.createElement(newTag)
    86     for newAttr, attr in attrDict.items():
    86     for newAttr, attr in list(attrDict.items()):
    87         sattr =  str(attr)
    87         sattr =  str(attr)
    88         if not node:
    88         if not node:
    89             newNode.setAttribute(newAttr, sattr)
    89             newNode.setAttribute(newAttr, sattr)
    90         else:
    90         else:
    91             attrVal = node.getAttribute(sattr)
    91             attrVal = node.getAttribute(sattr)
   112         self.encoding = encoding = codecs.lookup(encoding).name
   112         self.encoding = encoding = codecs.lookup(encoding).name
   113         if bom and '16' in encoding or '32' in encoding:
   113         if bom and '16' in encoding or '32' in encoding:
   114             self.write(self.BOMS[encoding])
   114             self.write(self.BOMS[encoding])
   115 
   115 
   116     def write(self,s):
   116     def write(self,s):
   117         if isinstance(s,unicode):
   117         if isinstance(s,str):
   118             s = s.encode(self.encoding)
   118             s = s.encode(self.encoding)
   119         elif isinstance(s,str):
   119         elif isinstance(s,str):
   120             try:
   120             try:
   121                  u = s.decode('utf-8')
   121                  u = s.decode('utf-8')
   122             except:
   122             except:
   192                     xmlns="http://www.w3.org/2000/svg",
   192                     xmlns="http://www.w3.org/2000/svg",
   193                     version="1.0",
   193                     version="1.0",
   194                     )
   194                     )
   195         svgAttrs["xmlns:xlink"] = "http://www.w3.org/1999/xlink"
   195         svgAttrs["xmlns:xlink"] = "http://www.w3.org/1999/xlink"
   196         svgAttrs.update(kwds.pop('svgAttrs',{}))
   196         svgAttrs.update(kwds.pop('svgAttrs',{}))
   197         for k,v in svgAttrs.iteritems():
   197         for k,v in svgAttrs.items():
   198             self.svg.setAttribute(k,v)
   198             self.svg.setAttribute(k,v)
   199 
   199 
   200         title = self.doc.createElement('title')
   200         title = self.doc.createElement('title')
   201         text = self.doc.createTextNode('...')
   201         text = self.doc.createTextNode('...')
   202         title.appendChild(text)
   202         title.appendChild(text)
   240 
   240 
   241     def save(self, fn=None):
   241     def save(self, fn=None):
   242         writer = EncodedWriter(self.encoding,bom=self.bom)
   242         writer = EncodedWriter(self.encoding,bom=self.bom)
   243         self.doc.writexml(writer,addindent="\t",newl="\n",encoding=self.encoding)
   243         self.doc.writexml(writer,addindent="\t",newl="\n",encoding=self.encoding)
   244 
   244 
   245         if type(fn) in types.StringTypes:
   245         if type(fn) in str:
   246             f = open(fn, 'w')
   246             f = open(fn, 'w')
   247         else:
   247         else:
   248             f = fn
   248             f = fn
   249         svg = writer.getvalue()
   249         svg = writer.getvalue()
   250         exd = self.extraXmlDecl
   250         exd = self.extraXmlDecl
   266         return stringWidth(s, font, fontSize)
   266         return stringWidth(s, font, fontSize)
   267 
   267 
   268     def _formatStyle(self, include=[], exclude='',**kwds):
   268     def _formatStyle(self, include=[], exclude='',**kwds):
   269         style = self.style.copy()
   269         style = self.style.copy()
   270         style.update(kwds)
   270         style.update(kwds)
   271         keys = style.keys()
   271         keys = list(style.keys())
   272         if include:
   272         if include:
   273             keys = [k for k in keys if k in include]
   273             keys = [k for k in keys if k in include]
   274         if exclude:
   274         if exclude:
   275             exclude = exclude.split()
   275             exclude = exclude.split()
   276             items = [k+': '+str(style[k]) for k in keys if k not in exclude]
   276             items = [k+': '+str(style[k]) for k in keys if k not in exclude]
   385             for k in TEXT_STYLES:
   385             for k in TEXT_STYLES:
   386                 if k in style:
   386                 if k in style:
   387                     del style[k]
   387                     del style[k]
   388             svgAttrs = self.fontHacks[font] if font in self.fontHacks else {}
   388             svgAttrs = self.fontHacks[font] if font in self.fontHacks else {}
   389             if isinstance(font,RLString):
   389             if isinstance(font,RLString):
   390                 svgAttrs.update(font.svgAttrs.iteritems())
   390                 svgAttrs.update(iter(font.svgAttrs.items()))
   391             if svgAttrs:
   391             if svgAttrs:
   392                 for k,v in svgAttrs.iteritems():
   392                 for k,v in svgAttrs.items():
   393                     a = 'font-'+k
   393                     a = 'font-'+k
   394                     if a in TEXT_STYLES:
   394                     if a in TEXT_STYLES:
   395                         style[a] = v
   395                         style[a] = v
   396             if 'font-family' not in style:
   396             if 'font-family' not in style:
   397                 style['font-family'] = font
   397                 style['font-family'] = font
   405 
   405 
   406     ### shapes ###
   406     ### shapes ###
   407     def rect(self, x1,y1, x2,y2, rx=8, ry=8, link_info=None, **_svgAttrs):
   407     def rect(self, x1,y1, x2,y2, rx=8, ry=8, link_info=None, **_svgAttrs):
   408         "Draw a rectangle between x1,y1 and x2,y2."
   408         "Draw a rectangle between x1,y1 and x2,y2."
   409 
   409 
   410         if self.verbose: print "+++ SVGCanvas.rect"
   410         if self.verbose: print("+++ SVGCanvas.rect")
   411 
   411 
   412         x = min(x1,x2)
   412         x = min(x1,x2)
   413         y = min(y1,y2)
   413         y = min(y1,y2)
   414         kwds = {}
   414         kwds = {}
   415         rect = transformNode(self.doc, "rect",
   415         rect = transformNode(self.doc, "rect",
   437             rect = self._add_link(rect, link_info)
   437             rect = self._add_link(rect, link_info)
   438 
   438 
   439         self.currGroup.appendChild(rect)
   439         self.currGroup.appendChild(rect)
   440 
   440 
   441     def drawString(self, s, x, y, angle=0, link_info=None,**_svgAttrs):
   441     def drawString(self, s, x, y, angle=0, link_info=None,**_svgAttrs):
   442         if self.verbose: print "+++ SVGCanvas.drawString"
   442         if self.verbose: print("+++ SVGCanvas.drawString")
   443 
   443 
   444         if self._fillColor != None:
   444         if self._fillColor != None:
   445             self.setColor(self._fillColor)
   445             self.setColor(self._fillColor)
   446             s = self._escape(s)
   446             s = self._escape(s)
   447             st = self._formatStyle(TEXT_STYLES)
   447             st = self._formatStyle(TEXT_STYLES)
   460                 text = self._add_link(text, link_info)
   460                 text = self._add_link(text, link_info)
   461     
   461     
   462             self.currGroup.appendChild(text)
   462             self.currGroup.appendChild(text)
   463 
   463 
   464     def drawCentredString(self, s, x, y, angle=0, text_anchor='middle', link_info=None):
   464     def drawCentredString(self, s, x, y, angle=0, text_anchor='middle', link_info=None):
   465         if self.verbose: print "+++ SVGCanvas.drawCentredString"
   465         if self.verbose: print("+++ SVGCanvas.drawCentredString")
   466 
   466 
   467         if self._fillColor != None:
   467         if self._fillColor != None:
   468             if not text_anchor in ['start', 'inherited']:
   468             if not text_anchor in ['start', 'inherited']:
   469                 textLen = stringWidth(s,self._font,self._fontSize)
   469                 textLen = stringWidth(s,self._font,self._fontSize)
   470                 if text_anchor=='end':
   470                 if text_anchor=='end':
   472                 elif text_anchor=='middle':
   472                 elif text_anchor=='middle':
   473                     x -= textLen/2.
   473                     x -= textLen/2.
   474                 elif text_anchor=='numeric':
   474                 elif text_anchor=='numeric':
   475                     x -= numericXShift(text_anchor,s,textLen,self._font,self._fontSize)
   475                     x -= numericXShift(text_anchor,s,textLen,self._font,self._fontSize)
   476                 else:
   476                 else:
   477                     raise ValueError, 'bad value for text_anchor ' + str(text_anchor)
   477                     raise ValueError('bad value for text_anchor ' + str(text_anchor))
   478         self.drawString(x,y,text,angle=angle, link_info=link_info)
   478         self.drawString(x,y,text,angle=angle, link_info=link_info)
   479 
   479 
   480     def drawRightString(self, text, x, y, angle=0, link_info=None):
   480     def drawRightString(self, text, x, y, angle=0, link_info=None):
   481         self.drawCentredString(text,x,y,angle=angle,text_anchor='end', link_info=link_info)
   481         self.drawCentredString(text,x,y,angle=angle,text_anchor='end', link_info=link_info)
   482 
   482 
   578         assert len(points) >= 2, 'Polygon must have 2 or more points'
   578         assert len(points) >= 2, 'Polygon must have 2 or more points'
   579 
   579 
   580         if self._strokeColor != None:
   580         if self._strokeColor != None:
   581             self.setColor(self._strokeColor)
   581             self.setColor(self._strokeColor)
   582             pairs = []
   582             pairs = []
   583             for i in xrange(len(points)):
   583             for i in range(len(points)):
   584                 pairs.append("%f %f" % (points[i]))
   584                 pairs.append("%f %f" % (points[i]))
   585             pts = ', '.join(pairs)
   585             pts = ', '.join(pairs)
   586             polyline = transformNode(self.doc, "polygon",
   586             polyline = transformNode(self.doc, "polygon",
   587                 points=pts, style=self._formatStyle(AREA_STYLES))
   587                 points=pts, style=self._formatStyle(AREA_STYLES))
   588 
   588 
   607         assert len(points) >= 1, 'Polyline must have 1 or more points'
   607         assert len(points) >= 1, 'Polyline must have 1 or more points'
   608 
   608 
   609         if self._strokeColor != None:
   609         if self._strokeColor != None:
   610             self.setColor(self._strokeColor)
   610             self.setColor(self._strokeColor)
   611             pairs = []
   611             pairs = []
   612             for i in xrange(len(points)):
   612             for i in range(len(points)):
   613                 pairs.append("%f %f" % (points[i]))
   613                 pairs.append("%f %f" % (points[i]))
   614             pts = ', '.join(pairs)
   614             pts = ', '.join(pairs)
   615             polyline = transformNode(self.doc, "polyline",
   615             polyline = transformNode(self.doc, "polyline",
   616                 points=pts, style=self._formatStyle(AREA_STYLES,fill=None))
   616                 points=pts, style=self._formatStyle(AREA_STYLES,fill=None))
   617             self.currGroup.appendChild(polyline)
   617             self.currGroup.appendChild(polyline)
   618 
   618 
   619     ### groups ###
   619     ### groups ###
   620     def startGroup(self):
   620     def startGroup(self):
   621         if self.verbose: print "+++ begin SVGCanvas.startGroup"
   621         if self.verbose: print("+++ begin SVGCanvas.startGroup")
   622         currGroup, group = self.currGroup, transformNode(self.doc, "g", transform="")
   622         currGroup, group = self.currGroup, transformNode(self.doc, "g", transform="")
   623         currGroup.appendChild(group)
   623         currGroup.appendChild(group)
   624         self.currGroup = group
   624         self.currGroup = group
   625         if self.verbose: print "+++ end SVGCanvas.startGroup"
   625         if self.verbose: print("+++ end SVGCanvas.startGroup")
   626         return currGroup
   626         return currGroup
   627 
   627 
   628     def endGroup(self,currGroup):
   628     def endGroup(self,currGroup):
   629         if self.verbose: print "+++ begin SVGCanvas.endGroup"
   629         if self.verbose: print("+++ begin SVGCanvas.endGroup")
   630         self.currGroup = currGroup
   630         self.currGroup = currGroup
   631         if self.verbose: print "+++ end SVGCanvas.endGroup"
   631         if self.verbose: print("+++ end SVGCanvas.endGroup")
   632 
   632 
   633     def transform(self, a, b, c, d, e, f):
   633     def transform(self, a, b, c, d, e, f):
   634         if self.verbose: print "!!! begin SVGCanvas.transform", a, b, c, d, e, f
   634         if self.verbose: print("!!! begin SVGCanvas.transform", a, b, c, d, e, f)
   635         tr = self.currGroup.getAttribute("transform")
   635         tr = self.currGroup.getAttribute("transform")
   636         t = 'matrix(%f, %f, %f, %f, %f, %f)' % (a,b,c,d,e,f)
   636         t = 'matrix(%f, %f, %f, %f, %f, %f)' % (a,b,c,d,e,f)
   637         if (a, b, c, d, e, f) != (1, 0, 0, 1, 0, 0):
   637         if (a, b, c, d, e, f) != (1, 0, 0, 1, 0, 0):
   638             self.currGroup.setAttribute("transform", "%s %s" % (tr, t))
   638             self.currGroup.setAttribute("transform", "%s %s" % (tr, t))
   639 
   639 
   640     def translate(self, x, y):
   640     def translate(self, x, y):
   641         # probably never used
   641         # probably never used
   642         print "!!! begin SVGCanvas.translate"
   642         print("!!! begin SVGCanvas.translate")
   643         return
   643         return
   644 
   644 
   645         tr = self.currGroup.getAttribute("transform")
   645         tr = self.currGroup.getAttribute("transform")
   646         t = 'translate(%f, %f)' % (x, y)
   646         t = 'translate(%f, %f)' % (x, y)
   647         self.currGroup.setAttribute("transform", "%s %s" % (tr, t))
   647         self.currGroup.setAttribute("transform", "%s %s" % (tr, t))
   648 
   648 
   649     def scale(self, x, y):
   649     def scale(self, x, y):
   650         # probably never used
   650         # probably never used
   651         print "!!! begin SVGCanvas.scale"
   651         print("!!! begin SVGCanvas.scale")
   652         return
   652         return
   653 
   653 
   654         tr = self.groups[-1].getAttribute("transform")
   654         tr = self.groups[-1].getAttribute("transform")
   655         t = 'scale(%f, %f)' % (x, y)
   655         t = 'scale(%f, %f)' % (x, y)
   656         self.currGroup.setAttribute("transform", "%s %s" % (tr, t))
   656         self.currGroup.setAttribute("transform", "%s %s" % (tr, t))
   684 
   684 
   685     def drawNode(self, node):
   685     def drawNode(self, node):
   686         """This is the recursive method called for each node in the tree.
   686         """This is the recursive method called for each node in the tree.
   687         """
   687         """
   688 
   688 
   689         if self.verbose: print "### begin _SVGRenderer.drawNode(%r)" % node
   689         if self.verbose: print("### begin _SVGRenderer.drawNode(%r)" % node)
   690 
   690 
   691         self._canvas.comment('begin node %r'%node)
   691         self._canvas.comment('begin node %r'%node)
   692         color = self._canvas._color
   692         color = self._canvas._color
   693         style = self._canvas.style.copy()
   693         style = self._canvas.style.copy()
   694         if not (isinstance(node, Path) and node.isClipPath):
   694         if not (isinstance(node, Path) and node.isClipPath):
   707             pass #self._canvas.restoreState()
   707             pass #self._canvas.restoreState()
   708         self._canvas.comment('end node %r'%node)
   708         self._canvas.comment('end node %r'%node)
   709         self._canvas._color = color
   709         self._canvas._color = color
   710 
   710 
   711         #restore things we might have lost (without actually doing anything).
   711         #restore things we might have lost (without actually doing anything).
   712         for k, v in rDeltas.items():
   712         for k, v in list(rDeltas.items()):
   713             if k in self._restores:
   713             if k in self._restores:
   714                 setattr(self._canvas,self._restores[k],v)
   714                 setattr(self._canvas,self._restores[k],v)
   715         self._canvas.style = style
   715         self._canvas.style = style
   716 
   716 
   717         if self.verbose: print "### end _SVGRenderer.drawNode(%r)" % node
   717         if self.verbose: print("### end _SVGRenderer.drawNode(%r)" % node)
   718 
   718 
   719     _restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap',
   719     _restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap',
   720                 'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font',
   720                 'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font',
   721                 'fontSize':'_fontSize'}
   721                 'fontSize':'_fontSize'}
   722 
   722 
   750             #"_top" as used here?
   750             #"_top" as used here?
   751         else :
   751         else :
   752             return None
   752             return None
   753 
   753 
   754     def drawGroup(self, group):
   754     def drawGroup(self, group):
   755         if self.verbose: print "### begin _SVGRenderer.drawGroup"
   755         if self.verbose: print("### begin _SVGRenderer.drawGroup")
   756 
   756 
   757         currGroup = self._canvas.startGroup()
   757         currGroup = self._canvas.startGroup()
   758         a, b, c, d, e, f = self._tracker.getState()['transform']
   758         a, b, c, d, e, f = self._tracker.getState()['transform']
   759         for childNode in group.getContents():
   759         for childNode in group.getContents():
   760             if isinstance(childNode, UserNode):
   760             if isinstance(childNode, UserNode):
   763                 node2 = childNode
   763                 node2 = childNode
   764             self.drawNode(node2)
   764             self.drawNode(node2)
   765         self._canvas.transform(a, b, c, d, e, f)
   765         self._canvas.transform(a, b, c, d, e, f)
   766         self._canvas.endGroup(currGroup)
   766         self._canvas.endGroup(currGroup)
   767 
   767 
   768         if self.verbose: print "### end _SVGRenderer.drawGroup"
   768         if self.verbose: print("### end _SVGRenderer.drawGroup")
   769 
   769 
   770     def drawRect(self, rect):
   770     def drawRect(self, rect):
   771         link_info = self._get_link_info_dict(rect)
   771         link_info = self._get_link_info_dict(rect)
   772         svgAttrs = getattr(rect,'_svgAttrs',{})
   772         svgAttrs = getattr(rect,'_svgAttrs',{})
   773         if rect.rx == rect.ry == 0:
   773         if rect.rx == rect.ry == 0:
   796                 elif text_anchor=='middle':
   796                 elif text_anchor=='middle':
   797                     x -= textLen/2
   797                     x -= textLen/2
   798                 elif text_anchor=='numeric':
   798                 elif text_anchor=='numeric':
   799                     x -= numericXShift(text_anchor,text,textLen,font,fontSize)
   799                     x -= numericXShift(text_anchor,text,textLen,font,fontSize)
   800                 else:
   800                 else:
   801                     raise ValueError, 'bad value for text_anchor ' + str(text_anchor)
   801                     raise ValueError('bad value for text_anchor ' + str(text_anchor))
   802             self._canvas.drawString(text,x,y,link_info=self._get_link_info_dict(stringObj),**getattr(stringObj,'_svgAttrs',{}))
   802             self._canvas.drawString(text,x,y,link_info=self._get_link_info_dict(stringObj),**getattr(stringObj,'_svgAttrs',{}))
   803 
   803 
   804     def drawLine(self, line):
   804     def drawLine(self, line):
   805         if self._canvas._strokeColor:
   805         if self._canvas._strokeColor:
   806             self._canvas.line(line.x1, line.y1, line.x2, line.y2)
   806             self._canvas.line(line.x1, line.y1, line.x2, line.y2)
   848 
   848 
   849     def applyStateChanges(self, delta, newState):
   849     def applyStateChanges(self, delta, newState):
   850         """This takes a set of states, and outputs the operators
   850         """This takes a set of states, and outputs the operators
   851         needed to set those properties"""
   851         needed to set those properties"""
   852 
   852 
   853         for key, value in delta.items():
   853         for key, value in list(delta.items()):
   854             if key == 'transform':
   854             if key == 'transform':
   855                 pass
   855                 pass
   856                 #self._canvas.transform(value[0], value[1], value[2], value[3], value[4], value[5])
   856                 #self._canvas.transform(value[0], value[1], value[2], value[3], value[4], value[5])
   857             elif key == 'strokeColor':
   857             elif key == 'strokeColor':
   858                 self._canvas.setStrokeColor(value)
   858                 self._canvas.setStrokeColor(value)