author | robin |
Thu, 24 Oct 2019 16:07:15 +0100 | |
changeset 4551 | d357e2acc856 |
parent 4474 | 066480f7a206 |
child 4586 | 2fd83707e011 |
permissions | -rw-r--r-- |
3032 | 1 |
__doc__="""An experimental SVG renderer for the ReportLab graphics framework. |
1607 | 2 |
|
3 |
This will create SVG code from the ReportLab Graphics API (RLG). |
|
1670 | 4 |
To read existing SVG code and convert it into ReportLab graphics |
5 |
objects download the svglib module here: |
|
6 |
||
7 |
http://python.net/~gherman/#svglib |
|
1607 | 8 |
""" |
9 |
||
4303
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
10 |
import math, types, sys, os, codecs, base64 |
1607 | 11 |
from operator import getitem |
12 |
||
13 |
from reportlab.pdfbase.pdfmetrics import stringWidth # for font info |
|
3781 | 14 |
from reportlab.lib.rl_accel import fp_str |
1607 | 15 |
from reportlab.lib.colors import black |
4303
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
16 |
from reportlab.lib.utils import asNative, getBytesIO |
2544
a6b9aa99b3c3
graphics: added Drawing.renderScale hack for renderer terminal drawing scales
rgbecker
parents:
2360
diff
changeset
|
17 |
from reportlab.graphics.renderbase import StateTracker, getStateDelta, Renderer, renderScaledDrawing |
1607 | 18 |
from reportlab.graphics.shapes import STATE_DEFAULTS, Path, UserNode |
19 |
from reportlab.graphics.shapes import * # (only for test0) |
|
4367
9960d82643bf
remove ascii, cmp & xrange builtins abuse; version-->3.4.15
robin <robin@reportlab.com>
parents:
4325
diff
changeset
|
20 |
from reportlab import rl_config, ascii |
4283
50081cf927c3
fix problems with svg drawToString contrib by Eric Gillet & Johann Du Toit
robin
parents:
4274
diff
changeset
|
21 |
from reportlab.lib.utils import getStringIO, RLString, isPy3, isUnicode, isBytes |
4306
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
22 |
from reportlab.pdfgen.canvas import FILL_EVEN_ODD, FILL_NON_ZERO |
1607 | 23 |
|
1617
c449e678a126
Removed dependency on PyXML. Now should use only std. lib. XML modules.
dinu_gherman
parents:
1614
diff
changeset
|
24 |
from xml.dom import getDOMImplementation |
1607 | 25 |
|
26 |
### some constants ### |
|
27 |
||
28 |
sin = math.sin |
|
29 |
cos = math.cos |
|
30 |
pi = math.pi |
|
31 |
||
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
32 |
AREA_STYLES = 'stroke-width stroke-linecap stroke stroke-opacity fill fill-opacity stroke-dasharray id'.split() |
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
33 |
LINE_STYLES = 'stroke-width stroke-linecap stroke stroke-opacity stroke-dasharray id'.split() |
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
34 |
TEXT_STYLES = 'font-family font-weight font-style font-variant font-size id'.split() |
1607 | 35 |
|
36 |
### top-level user function ### |
|
3644 | 37 |
def drawToString(d, showBoundary=rl_config.showBoundary,**kwds): |
1743 | 38 |
"Returns a SVG as a string in memory, without touching the disk" |
4283
50081cf927c3
fix problems with svg drawToString contrib by Eric Gillet & Johann Du Toit
robin
parents:
4274
diff
changeset
|
39 |
s = getStringIO() |
3644 | 40 |
drawToFile(d, s, showBoundary=showBoundary,**kwds) |
1743 | 41 |
return s.getvalue() |
42 |
||
3644 | 43 |
def drawToFile(d, fn, showBoundary=rl_config.showBoundary,**kwds): |
2544
a6b9aa99b3c3
graphics: added Drawing.renderScale hack for renderer terminal drawing scales
rgbecker
parents:
2360
diff
changeset
|
44 |
d = renderScaledDrawing(d) |
3644 | 45 |
c = SVGCanvas((d.width, d.height),**kwds) |
1607 | 46 |
draw(d, c, 0, 0, showBoundary=showBoundary) |
47 |
c.save(fn) |
|
48 |
||
49 |
def draw(drawing, canvas, x=0, y=0, showBoundary=rl_config.showBoundary): |
|
50 |
"""As it says.""" |
|
51 |
r = _SVGRenderer() |
|
2553
a880f43d10bd
reprotlab/graphics: fix so renderScale is used properly
rgbecker
parents:
2544
diff
changeset
|
52 |
r.draw(renderScaledDrawing(drawing), canvas, x, y, showBoundary=showBoundary) |
1607 | 53 |
|
54 |
### helper functions ### |
|
55 |
def _pointsFromList(L): |
|
56 |
""" |
|
57 |
given a list of coordinates [x0, y0, x1, y1....] |
|
58 |
produce a list of points [(x0,y0), (y1,y0),....] |
|
59 |
""" |
|
60 |
||
61 |
P=[] |
|
62 |
for i in range(0,len(L), 2): |
|
63 |
P.append((L[i], L[i+1])) |
|
64 |
||
65 |
return P |
|
66 |
||
67 |
def transformNode(doc, newTag, node=None, **attrDict): |
|
68 |
"""Transform a DOM node into new node and copy selected attributes. |
|
69 |
||
1683 | 70 |
Creates a new DOM node with tag name 'newTag' for document 'doc' |
1607 | 71 |
and copies selected attributes from an existing 'node' as provided |
72 |
in 'attrDict'. The source 'node' can be None. Attribute values will |
|
73 |
be converted to strings. |
|
74 |
||
1683 | 75 |
E.g. |
76 |
||
1607 | 77 |
n = transformNode(doc, "node1", x="0", y="1") |
78 |
-> DOM node for <node1 x="0" y="1"/> |
|
79 |
||
80 |
n = transformNode(doc, "node1", x=0, y=1+1) |
|
81 |
-> DOM node for <node1 x="0" y="2"/> |
|
82 |
||
83 |
n = transformNode(doc, "node1", node0, x="x0", y="x0", zoo=bar()) |
|
84 |
-> DOM node for <node1 x="[node0.x0]" y="[node0.y0]" zoo="[bar()]"/> |
|
85 |
""" |
|
86 |
||
87 |
newNode = doc.createElement(newTag) |
|
3723
99aa837b6703
second stage of port to Python 3.3; working hello world
rptlab
parents:
3721
diff
changeset
|
88 |
for newAttr, attr in attrDict.items(): |
1607 | 89 |
sattr = str(attr) |
1683 | 90 |
if not node: |
1607 | 91 |
newNode.setAttribute(newAttr, sattr) |
92 |
else: |
|
93 |
attrVal = node.getAttribute(sattr) |
|
94 |
newNode.setAttribute(newAttr, attrVal or sattr) |
|
95 |
||
96 |
return newNode |
|
97 |
||
3620
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
98 |
class EncodedWriter(list): |
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
99 |
''' |
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
100 |
EncodedWriter(encoding) assumes .write will be called with |
3924 | 101 |
either unicode or utf8 encoded bytes. it will accumulate |
3765 | 102 |
unicode |
3620
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
103 |
''' |
3621 | 104 |
BOMS = { |
105 |
'utf-32':codecs.BOM_UTF32, |
|
106 |
'utf-32-be':codecs.BOM_UTF32_BE, |
|
107 |
'utf-32-le':codecs.BOM_UTF32_LE, |
|
108 |
'utf-16':codecs.BOM_UTF16, |
|
109 |
'utf-16-be':codecs.BOM_UTF16_BE, |
|
110 |
'utf-16-le':codecs.BOM_UTF16_LE, |
|
111 |
} |
|
112 |
def __init__(self,encoding,bom=False): |
|
3620
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
113 |
list.__init__(self) |
3621 | 114 |
self.encoding = encoding = codecs.lookup(encoding).name |
115 |
if bom and '16' in encoding or '32' in encoding: |
|
116 |
self.write(self.BOMS[encoding]) |
|
3620
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
117 |
|
3765 | 118 |
def write(self,u): |
3924 | 119 |
if isBytes(u): |
3620
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
120 |
try: |
3765 | 121 |
u = u.decode('utf-8') |
3620
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
122 |
except: |
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
123 |
et, ev, tb = sys.exc_info() |
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
124 |
ev = str(ev) |
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
125 |
del et, tb |
3765 | 126 |
raise ValueError("String %r not encoded as 'utf-8'\nerror=%s" % (u,ev)) |
3924 | 127 |
elif not isUnicode(u): |
128 |
raise ValueError("EncodedWriter.write(%s) argument should be 'utf-8' bytes or str" % ascii(u)) |
|
3765 | 129 |
self.append(u) |
3620
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
130 |
|
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
131 |
def getvalue(self): |
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
132 |
r = ''.join(self) |
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
133 |
del self[:] |
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
134 |
return r |
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
135 |
|
4325
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
136 |
_fillRuleMap = { |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
137 |
FILL_NON_ZERO: 'nonzero', |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
138 |
'non-zero': 'nonzero', |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
139 |
'nonzero': 'nonzero', |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
140 |
FILL_EVEN_ODD: 'evenodd', |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
141 |
'even-odd': 'evenodd', |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
142 |
'evenodd': 'evenodd', |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
143 |
} |
4306
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
144 |
|
1607 | 145 |
### classes ### |
146 |
class SVGCanvas: |
|
3644 | 147 |
def __init__(self, size=(300,300), encoding='utf-8', verbose=0, bom=False, **kwds): |
3639
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
148 |
''' |
3644 | 149 |
verbose = 0 >0 means do verbose stuff |
150 |
useClip = False True means don't use a clipPath definition put the global clip into the clip property |
|
3639
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
151 |
to get around an issue with safari |
3644 | 152 |
extraXmlDecl = '' use to add extra xml declarations |
153 |
scaleGroupId = '' id of an extra group to add around the drawing to allow easy scaling |
|
154 |
svgAttrs = {} dictionary of attributes to be applied to the svg tag itself |
|
3639
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
155 |
''' |
3607
97d0b7cfa37f
renderSVG.py: allow for encoding (default utf8) and verbose=0; Guillaume's changes
rgbecker
parents:
3606
diff
changeset
|
156 |
self.verbose = verbose |
3621 | 157 |
self.encoding = codecs.lookup(encoding).name |
158 |
self.bom = bom |
|
3644 | 159 |
useClip = kwds.pop('useClip',False) |
160 |
self.fontHacks = kwds.pop('fontHacks',{}) |
|
161 |
self.extraXmlDecl = kwds.pop('extraXmlDecl','') |
|
162 |
scaleGroupId = kwds.pop('scaleGroupId','') |
|
4306
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
163 |
self._fillMode = FILL_EVEN_ODD |
3644 | 164 |
|
1607 | 165 |
self.width, self.height = self.size = size |
166 |
# self.height = size[1] |
|
167 |
self.code = [] |
|
168 |
self.style = {} |
|
169 |
self.path = '' |
|
170 |
self._strokeColor = self._fillColor = self._lineWidth = \ |
|
171 |
self._font = self._fontSize = self._lineCap = \ |
|
4325
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
172 |
self._lineJoin = None |
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
173 |
if kwds.pop('use_fp_str',False): |
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
174 |
self.fp_str = fp_str |
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
175 |
else: |
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
176 |
self.fp_str = lambda *args: (' '.join(len(args)*['%f'])) % args |
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
177 |
self.cfp_str = lambda *args: self.fp_str(*args).replace(' ',',') |
1607 | 178 |
|
1642 | 179 |
implementation = getDOMImplementation('minidom') |
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
180 |
#Based on official example here http://www.w3.org/TR/SVG10/linking.html want: |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
181 |
#<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
182 |
# "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
183 |
#Thus, |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
184 |
#doctype = implementation.createDocumentType("svg", |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
185 |
# "-//W3C//DTD SVG 20010904//EN", |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
186 |
# "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd") |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
187 |
# |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
188 |
#However, putting that example through http://validator.w3.org/ recommends: |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
189 |
#<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
190 |
# "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
191 |
#So we'll use that for our SVG 1.0 output. |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
192 |
doctype = implementation.createDocumentType("svg", |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
193 |
"-//W3C//DTD SVG 1.0//EN", |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
194 |
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd") |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
195 |
self.doc = implementation.createDocument(None,"svg",doctype) |
1617
c449e678a126
Removed dependency on PyXML. Now should use only std. lib. XML modules.
dinu_gherman
parents:
1614
diff
changeset
|
196 |
self.svg = self.doc.documentElement |
3644 | 197 |
svgAttrs = dict( |
198 |
width = str(size[0]), |
|
199 |
height=str(self.height), |
|
200 |
preserveAspectRatio="xMinYMin meet", |
|
201 |
viewBox="0 0 %d %d" % (self.width, self.height), |
|
202 |
#baseProfile = "full", #disliked in V 1.0 |
|
1607 | 203 |
|
3644 | 204 |
#these suggested by Tim Roberts, as updated by peter@maubp.freeserve.co.uk |
205 |
xmlns="http://www.w3.org/2000/svg", |
|
206 |
version="1.0", |
|
207 |
) |
|
4325
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
208 |
svgAttrs['fill-rule'] = _fillRuleMap[self._fillMode] |
3644 | 209 |
svgAttrs["xmlns:xlink"] = "http://www.w3.org/1999/xlink" |
210 |
svgAttrs.update(kwds.pop('svgAttrs',{})) |
|
3721 | 211 |
for k,v in svgAttrs.items(): |
3644 | 212 |
self.svg.setAttribute(k,v) |
3605
bb3c9e12a4db
added viewBox attribute to SVG drawings. This means they can be resized by setting the width and eheight in the embed tag.
andy
parents:
3592
diff
changeset
|
213 |
|
1607 | 214 |
title = self.doc.createElement('title') |
215 |
text = self.doc.createTextNode('...') |
|
216 |
title.appendChild(text) |
|
1683 | 217 |
self.svg.appendChild(title) |
218 |
||
1607 | 219 |
desc = self.doc.createElement('desc') |
220 |
text = self.doc.createTextNode('...') |
|
221 |
desc.appendChild(text) |
|
1683 | 222 |
self.svg.appendChild(desc) |
1607 | 223 |
|
224 |
self.setFont(STATE_DEFAULTS['fontName'], STATE_DEFAULTS['fontSize']) |
|
225 |
self.setStrokeColor(STATE_DEFAULTS['strokeColor']) |
|
226 |
self.setLineCap(2) |
|
227 |
self.setLineJoin(0) |
|
228 |
self.setLineWidth(1) |
|
229 |
||
3644 | 230 |
if not useClip: |
3639
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
231 |
# Add a rectangular clipping path identical to view area. |
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
232 |
clipPath = transformNode(self.doc, "clipPath", id="clip") |
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
233 |
clipRect = transformNode(self.doc, "rect", x=0, y=0, |
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
234 |
width=self.width, height=self.height) |
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
235 |
clipPath.appendChild(clipRect) |
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
236 |
self.svg.appendChild(clipPath) |
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
237 |
gtkw = dict(style="clip-path: url(#clip)") |
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
238 |
else: |
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
239 |
gtkw = dict(clip="0 0 %d %d" % (self.width,self.height)) |
1661 | 240 |
|
1683 | 241 |
self.groupTree = transformNode(self.doc, "g", |
242 |
id="group", |
|
1661 | 243 |
transform="scale(1,-1) translate(0,-%d)" % self.height, |
3639
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
244 |
**gtkw |
22763fe00fab
renderSVG.py: allow way to use clip property rather than <clipPath>
rptlab
parents:
3635
diff
changeset
|
245 |
) |
3644 | 246 |
|
247 |
if scaleGroupId: |
|
248 |
self.scaleTree = transformNode(self.doc, "g", id=scaleGroupId, transform="scale(1,1)") |
|
249 |
self.scaleTree.appendChild(self.groupTree) |
|
250 |
self.svg.appendChild(self.scaleTree) |
|
251 |
else: |
|
252 |
self.svg.appendChild(self.groupTree) |
|
1607 | 253 |
self.currGroup = self.groupTree |
254 |
||
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
255 |
def save(self, fn=None): |
3621 | 256 |
writer = EncodedWriter(self.encoding,bom=self.bom) |
3620
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
257 |
self.doc.writexml(writer,addindent="\t",newl="\n",encoding=self.encoding) |
7b2b21af8759
renderSVG.py: use a properly checking EndodedWriter class
rgbecker
parents:
3608
diff
changeset
|
258 |
|
3765 | 259 |
if hasattr(fn,'write'): |
260 |
f = fn |
|
1607 | 261 |
else: |
3765 | 262 |
if isPy3: |
263 |
f = open(fn, 'w',encoding=self.encoding) |
|
264 |
else: |
|
265 |
f = open(fn, 'w') |
|
266 |
||
3641 | 267 |
svg = writer.getvalue() |
268 |
exd = self.extraXmlDecl |
|
269 |
if exd: |
|
270 |
svg = svg.replace('?>','?>'+exd) |
|
3924 | 271 |
f.write(svg if isPy3 else svg.encode(self.encoding)) |
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
272 |
if f is not fn: |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
273 |
f.close() |
1607 | 274 |
|
275 |
### helpers ### |
|
276 |
def NOTUSED_stringWidth(self, s, font=None, fontSize=None): |
|
277 |
"""Return the logical width of the string if it were drawn |
|
278 |
in the current font (defaults to self.font). |
|
279 |
""" |
|
280 |
||
281 |
font = font or self._font |
|
282 |
fontSize = fontSize or self._fontSize |
|
283 |
||
284 |
return stringWidth(s, font, fontSize) |
|
285 |
||
3631 | 286 |
def _formatStyle(self, include=[], exclude='',**kwds): |
3370
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
287 |
style = self.style.copy() |
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
288 |
style.update(kwds) |
3721 | 289 |
keys = list(style.keys()) |
1607 | 290 |
if include: |
3631 | 291 |
keys = [k for k in keys if k in include] |
3370
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
292 |
if exclude: |
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
293 |
exclude = exclude.split() |
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
294 |
items = [k+': '+str(style[k]) for k in keys if k not in exclude] |
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
295 |
else: |
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
296 |
items = [k+': '+str(style[k]) for k in keys] |
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
297 |
return '; '.join(items) + ';' |
1607 | 298 |
|
299 |
def _escape(self, s): |
|
4243
a1dd7de04817
remove unwanted escaping in renderSVG, version --> 3.2.14
robin
parents:
4132
diff
changeset
|
300 |
'''I don't think this was ever needed; seems to have been copied from renderPS''' |
a1dd7de04817
remove unwanted escaping in renderSVG, version --> 3.2.14
robin
parents:
4132
diff
changeset
|
301 |
return s |
1607 | 302 |
|
303 |
def _genArcCode(self, x1, y1, x2, y2, startAng, extent): |
|
1683 | 304 |
"""Calculate the path for an arc inscribed in rectangle defined |
1607 | 305 |
by (x1,y1),(x2,y2).""" |
306 |
||
307 |
return |
|
308 |
||
309 |
#calculate semi-minor and semi-major axes of ellipse |
|
310 |
xScale = abs((x2-x1)/2.0) |
|
311 |
yScale = abs((y2-y1)/2.0) |
|
312 |
#calculate centre of ellipse |
|
313 |
x, y = (x1+x2)/2.0, (y1+y2)/2.0 |
|
314 |
||
315 |
codeline = 'matrix currentmatrix %s %s translate %s %s scale 0 0 1 %s %s %s setmatrix' |
|
316 |
||
317 |
if extent >= 0: |
|
318 |
arc='arc' |
|
319 |
else: |
|
320 |
arc='arcn' |
|
321 |
data = (x,y, xScale, yScale, startAng, startAng+extent, arc) |
|
322 |
||
323 |
return codeline % data |
|
324 |
||
4306
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
325 |
def _fillAndStroke(self, code, clip=0, link_info=None,styles=AREA_STYLES,fillMode=None): |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
326 |
xtra = {} |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
327 |
if fillMode: |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
328 |
xtra['fill-rule'] = _fillRuleName(fillMode) |
1683 | 329 |
path = transformNode(self.doc, "path", |
4306
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
330 |
d=self.path, style=self._formatStyle(styles), |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
331 |
) |
3128
28e7abf47067
reportlab: renderSVG.py add in PJACock's (<peter@maubp.freeserve.co.uk>) path linkability patch
rgbecker
parents:
3115
diff
changeset
|
332 |
if link_info : |
28e7abf47067
reportlab: renderSVG.py add in PJACock's (<peter@maubp.freeserve.co.uk>) path linkability patch
rgbecker
parents:
3115
diff
changeset
|
333 |
path = self._add_link(path, link_info) |
1607 | 334 |
self.currGroup.appendChild(path) |
335 |
self.path = '' |
|
336 |
||
337 |
||
338 |
### styles ### |
|
339 |
def setLineCap(self, v): |
|
340 |
vals = {0:'butt', 1:'round', 2:'square'} |
|
341 |
if self._lineCap != v: |
|
342 |
self._lineCap = v |
|
343 |
self.style['stroke-linecap'] = vals[v] |
|
344 |
||
345 |
def setLineJoin(self, v): |
|
346 |
vals = {0:'miter', 1:'round', 2:'bevel'} |
|
347 |
if self._lineJoin != v: |
|
348 |
self._lineJoin = v |
|
349 |
self.style['stroke-linecap'] = vals[v] |
|
1683 | 350 |
|
1607 | 351 |
def setDash(self, array=[], phase=0): |
352 |
"""Two notations. Pass two numbers, or an array and phase.""" |
|
353 |
||
3502 | 354 |
if isinstance(array,(float,int)): |
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
355 |
self.style['stroke-dasharray'] = ', '.join(map(str, ([array, phase]))) |
3502 | 356 |
elif isinstance(array,(tuple,list)) and len(array) > 0: |
1607 | 357 |
assert phase >= 0, "phase is a length in user space" |
4551 | 358 |
self.style['stroke-dasharray'] = ', '.join(map(str, (list(array)+[phase]))) |
1607 | 359 |
|
360 |
def setStrokeColor(self, color): |
|
361 |
self._strokeColor = color |
|
362 |
if color == None: |
|
363 |
self.style['stroke'] = 'none' |
|
364 |
else: |
|
365 |
r, g, b = color.red, color.green, color.blue |
|
366 |
self.style['stroke'] = 'rgb(%d%%,%d%%,%d%%)' % (r*100, g*100, b*100) |
|
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
367 |
alpha = color.normalizedAlpha |
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
368 |
if alpha!=1: |
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
369 |
self.style['stroke-opacity'] = '%s' % alpha |
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
370 |
elif 'stroke-opacity' in self.style: |
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
371 |
del self.style['stroke-opacity'] |
1607 | 372 |
|
373 |
def setFillColor(self, color): |
|
374 |
self._fillColor = color |
|
375 |
if color == None: |
|
376 |
self.style['fill'] = 'none' |
|
377 |
else: |
|
378 |
r, g, b = color.red, color.green, color.blue |
|
379 |
self.style['fill'] = 'rgb(%d%%,%d%%,%d%%)' % (r*100, g*100, b*100) |
|
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
380 |
alpha = color.normalizedAlpha |
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
381 |
if alpha!=1: |
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
382 |
self.style['fill-opacity'] = '%s' % alpha |
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
383 |
elif 'fill-opacity' in self.style: |
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
384 |
del self.style['fill-opacity'] |
1607 | 385 |
|
4325
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
386 |
def setFillMode(self, v): |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
387 |
self._fillMode = v |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
388 |
self.style['fill-rule'] = _fillRuleMap[v] |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
389 |
|
1607 | 390 |
def setLineWidth(self, width): |
391 |
if width != self._lineWidth: |
|
392 |
self._lineWidth = width |
|
393 |
self.style['stroke-width'] = width |
|
394 |
||
395 |
def setFont(self, font, fontSize): |
|
396 |
if self._font != font or self._fontSize != fontSize: |
|
3631 | 397 |
self._font = font |
398 |
self._fontSize = fontSize |
|
399 |
style = self.style |
|
400 |
for k in TEXT_STYLES: |
|
401 |
if k in style: |
|
402 |
del style[k] |
|
3640 | 403 |
svgAttrs = self.fontHacks[font] if font in self.fontHacks else {} |
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
404 |
if isinstance(font,RLString): |
3721 | 405 |
svgAttrs.update(iter(font.svgAttrs.items())) |
3640 | 406 |
if svgAttrs: |
3721 | 407 |
for k,v in svgAttrs.items(): |
3640 | 408 |
a = 'font-'+k |
409 |
if a in TEXT_STYLES: |
|
410 |
style[a] = v |
|
3631 | 411 |
if 'font-family' not in style: |
412 |
style['font-family'] = font |
|
413 |
style['font-size'] = '%spx' % fontSize |
|
1607 | 414 |
|
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
415 |
def _add_link(self, dom_object, link_info) : |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
416 |
assert isinstance(link_info, dict) |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
417 |
link = transformNode(self.doc, "a", **link_info) |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
418 |
link.appendChild(dom_object) |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
419 |
return link |
1607 | 420 |
|
421 |
### shapes ### |
|
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
422 |
def rect(self, x1,y1, x2,y2, rx=8, ry=8, link_info=None, **_svgAttrs): |
1607 | 423 |
"Draw a rectangle between x1,y1 and x2,y2." |
1683 | 424 |
|
3721 | 425 |
if self.verbose: print("+++ SVGCanvas.rect") |
1607 | 426 |
|
3358
1c3e05c2cf09
renderSVG.py: attempt to fix negative dimensions for rect
rgbecker
parents:
3326
diff
changeset
|
427 |
x = min(x1,x2) |
1c3e05c2cf09
renderSVG.py: attempt to fix negative dimensions for rect
rgbecker
parents:
3326
diff
changeset
|
428 |
y = min(y1,y2) |
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
429 |
kwds = {} |
1683 | 430 |
rect = transformNode(self.doc, "rect", |
3358
1c3e05c2cf09
renderSVG.py: attempt to fix negative dimensions for rect
rgbecker
parents:
3326
diff
changeset
|
431 |
x=x, y=y, width=max(x1,x2)-x, height=max(y1,y2)-y, |
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
432 |
style=self._formatStyle(AREA_STYLES),**_svgAttrs) |
1607 | 433 |
|
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
434 |
if link_info : |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
435 |
rect = self._add_link(rect, link_info) |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
436 |
|
1607 | 437 |
self.currGroup.appendChild(rect) |
438 |
||
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
439 |
def roundRect(self, x1,y1, x2,y2, rx=8, ry=8, link_info=None, **_svgAttrs): |
1607 | 440 |
"""Draw a rounded rectangle between x1,y1 and x2,y2. |
1683 | 441 |
|
442 |
Corners inset as ellipses with x-radius rx and y-radius ry. |
|
1607 | 443 |
These should have x1<x2, y1<y2, rx>0, and ry>0. |
444 |
""" |
|
445 |
||
1683 | 446 |
rect = transformNode(self.doc, "rect", |
447 |
x=x1, y=y1, width=x2-x1, height=y2-y1, rx=rx, ry=ry, |
|
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
448 |
style=self._formatStyle(AREA_STYLES), **_svgAttrs) |
1607 | 449 |
|
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
450 |
if link_info: |
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
451 |
rect = self._add_link(rect, link_info) |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
452 |
|
1607 | 453 |
self.currGroup.appendChild(rect) |
454 |
||
3635 | 455 |
def drawString(self, s, x, y, angle=0, link_info=None,**_svgAttrs): |
3986 | 456 |
s = asNative(s) |
3721 | 457 |
if self.verbose: print("+++ SVGCanvas.drawString") |
1607 | 458 |
|
459 |
if self._fillColor != None: |
|
460 |
s = self._escape(s) |
|
461 |
st = self._formatStyle(TEXT_STYLES) |
|
462 |
if angle != 0: |
|
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
463 |
st = st + " rotate(%s);" % self.fp_str(angle, x, y) |
1607 | 464 |
st = st + " fill: %s;" % self.style['fill'] |
1683 | 465 |
text = transformNode(self.doc, "text", |
466 |
x=x, y=y, style=st, |
|
3635 | 467 |
transform="translate(0,%d) scale(1,-1)" % (2*y), |
468 |
**_svgAttrs |
|
469 |
) |
|
1607 | 470 |
content = self.doc.createTextNode(s) |
471 |
text.appendChild(content) |
|
472 |
||
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
473 |
if link_info: |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
474 |
text = self._add_link(text, link_info) |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
475 |
|
1607 | 476 |
self.currGroup.appendChild(text) |
477 |
||
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
478 |
def drawCentredString(self, s, x, y, angle=0, text_anchor='middle', link_info=None): |
3721 | 479 |
if self.verbose: print("+++ SVGCanvas.drawCentredString") |
2574 | 480 |
|
481 |
if self._fillColor != None: |
|
482 |
if not text_anchor in ['start', 'inherited']: |
|
483 |
textLen = stringWidth(s,self._font,self._fontSize) |
|
484 |
if text_anchor=='end': |
|
485 |
x -= textLen |
|
486 |
elif text_anchor=='middle': |
|
487 |
x -= textLen/2. |
|
3219
b28d6eef8227
reportlab: add support for String anchor 'numeric'
rgbecker
parents:
3128
diff
changeset
|
488 |
elif text_anchor=='numeric': |
b28d6eef8227
reportlab: add support for String anchor 'numeric'
rgbecker
parents:
3128
diff
changeset
|
489 |
x -= numericXShift(text_anchor,s,textLen,self._font,self._fontSize) |
2574 | 490 |
else: |
3721 | 491 |
raise ValueError('bad value for text_anchor ' + str(text_anchor)) |
4474
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
492 |
self.drawString(s,x,y,angle=angle, link_info=link_info) |
2574 | 493 |
|
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
494 |
def drawRightString(self, text, x, y, angle=0, link_info=None): |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
495 |
self.drawCentredString(text,x,y,angle=angle,text_anchor='end', link_info=link_info) |
1607 | 496 |
|
497 |
def comment(self, data): |
|
498 |
"Add a comment." |
|
499 |
||
500 |
comment = self.doc.createComment(data) |
|
501 |
# self.currGroup.appendChild(comment) |
|
502 |
||
4303
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
503 |
def drawImage(self, image, x, y, width, height, embed=True): |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
504 |
buf = getBytesIO() |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
505 |
image.save(buf,'png') |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
506 |
buf = asNative(base64.b64encode(buf.getvalue())) |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
507 |
self.currGroup.appendChild( |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
508 |
transformNode(self.doc,'image', |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
509 |
x=x,y=y,width=width,height=height, |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
510 |
href="data:image/png;base64,"+buf, |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
511 |
) |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
512 |
) |
1607 | 513 |
|
514 |
def line(self, x1, y1, x2, y2): |
|
515 |
if self._strokeColor != None: |
|
516 |
if 0: # something is wrong with line in my SVG viewer... |
|
1683 | 517 |
line = transformNode(self.doc, "line", |
518 |
x=x1, y=y1, x2=x2, y2=y2, |
|
1607 | 519 |
style=self._formatStyle(LINE_STYLES)) |
520 |
self.currGroup.appendChild(line) |
|
1683 | 521 |
path = transformNode(self.doc, "path", |
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
522 |
d="M %s L %s Z" % (self.cfp_str(x1,y1),self.cfp_str(x2,y2)), |
1607 | 523 |
style=self._formatStyle(LINE_STYLES)) |
524 |
self.currGroup.appendChild(path) |
|
525 |
||
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
526 |
def ellipse(self, x1, y1, x2, y2, link_info=None): |
1607 | 527 |
"""Draw an orthogonal ellipse inscribed within the rectangle x1,y1,x2,y2. |
1683 | 528 |
|
1607 | 529 |
These should have x1<x2 and y1<y2. |
530 |
""" |
|
1683 | 531 |
ellipse = transformNode(self.doc, "ellipse", |
532 |
cx=(x1+x2)/2.0, cy=(y1+y2)/2.0, rx=(x2-x1)/2.0, ry=(y2-y1)/2.0, |
|
3370
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
533 |
style=self._formatStyle(AREA_STYLES)) |
1607 | 534 |
|
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
535 |
if link_info: |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
536 |
ellipse = self._add_link(ellipse, link_info) |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
537 |
|
1607 | 538 |
self.currGroup.appendChild(ellipse) |
539 |
||
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
540 |
def circle(self, xc, yc, r, link_info=None): |
1683 | 541 |
circle = transformNode(self.doc, "circle", |
542 |
cx=xc, cy=yc, r=r, |
|
3370
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
543 |
style=self._formatStyle(AREA_STYLES)) |
1607 | 544 |
|
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
545 |
if link_info: |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
546 |
circle = self._add_link(circle, link_info) |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
547 |
|
1607 | 548 |
self.currGroup.appendChild(circle) |
549 |
||
550 |
def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, closed=0): |
|
551 |
pass |
|
552 |
return |
|
553 |
||
554 |
codeline = '%s m %s curveto' |
|
555 |
data = (fp_str(x1, y1), fp_str(x2, y2, x3, y3, x4, y4)) |
|
556 |
if self._fillColor != None: |
|
557 |
self.code.append((codeline % data) + ' eofill') |
|
558 |
if self._strokeColor != None: |
|
559 |
self.code.append((codeline % data) |
|
560 |
+ ((closed and ' closepath') or '') |
|
561 |
+ ' stroke') |
|
562 |
||
563 |
def drawArc(self, x1,y1, x2,y2, startAng=0, extent=360, fromcenter=0): |
|
564 |
"""Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2. |
|
565 |
||
1683 | 566 |
Starting at startAng degrees and covering extent degrees. Angles |
1607 | 567 |
start with 0 to the right (+x) and increase counter-clockwise. |
568 |
These should have x1<x2 and y1<y2. |
|
569 |
""" |
|
570 |
||
571 |
cx, cy = (x1+x2)/2.0, (y1+y2)/2.0 |
|
572 |
rx, ry = (x2-x1)/2.0, (y2-y1)/2.0 |
|
573 |
mx = rx * cos(startAng*pi/180) + cx |
|
574 |
my = ry * sin(startAng*pi/180) + cy |
|
575 |
ax = rx * cos((startAng+extent)*pi/180) + cx |
|
576 |
ay = ry * sin((startAng+extent)*pi/180) + cy |
|
577 |
||
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
578 |
cfp_str = self.cfp_str |
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
579 |
s = [].append |
1607 | 580 |
if fromcenter: |
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
581 |
s("M %s L %s" % (cfp_str(cx, cy), cfp_str(ax, ay))) |
1607 | 582 |
|
583 |
if fromcenter: |
|
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
584 |
s("A %s %d %d %d %s" % \ |
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
585 |
(cfp_str(rx, ry), 0, extent>=180, 0, cfp_str(mx, my))) |
1607 | 586 |
else: |
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
587 |
s("M %s A %s %d %d %d %s Z" % \ |
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
588 |
(cfp_str(mx, my), cfp_str(rx, ry), 0, extent>=180, 0, cfp_str(mx, my))) |
1607 | 589 |
|
590 |
if fromcenter: |
|
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
591 |
s("L %s Z" % cfp_str(cx, cy)) |
1607 | 592 |
|
1683 | 593 |
path = transformNode(self.doc, "path", |
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
594 |
d=' '.join(s.__self__), style=self._formatStyle()) |
1607 | 595 |
self.currGroup.appendChild(path) |
596 |
||
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
597 |
def polygon(self, points, closed=0, link_info=None): |
1607 | 598 |
assert len(points) >= 2, 'Polygon must have 2 or more points' |
599 |
||
4325
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
600 |
if self._strokeColor!=None or self._fillColor!=None: |
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
601 |
pts = ', '.join([fp_str(*p) for p in points]) |
1683 | 602 |
polyline = transformNode(self.doc, "polygon", |
3370
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
603 |
points=pts, style=self._formatStyle(AREA_STYLES)) |
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
604 |
|
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
605 |
if link_info: |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
606 |
polyline = self._add_link(polyline, link_info) |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
607 |
|
1607 | 608 |
self.currGroup.appendChild(polyline) |
609 |
||
610 |
# self._fillAndStroke(polyCode) |
|
611 |
||
612 |
def lines(self, lineList, color=None, width=None): |
|
613 |
# print "### lineList", lineList |
|
614 |
return |
|
615 |
||
616 |
if self._strokeColor != None: |
|
617 |
codeline = '%s m %s l stroke' |
|
618 |
for line in lineList: |
|
619 |
self.code.append(codeline % (fp_str(line[0]), fp_str(line[1]))) |
|
620 |
||
621 |
def polyLine(self, points): |
|
622 |
assert len(points) >= 1, 'Polyline must have 1 or more points' |
|
623 |
||
624 |
if self._strokeColor != None: |
|
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
625 |
pts = ', '.join([fp_str(*p) for p in points]) |
1683 | 626 |
polyline = transformNode(self.doc, "polyline", |
3370
0783797d9871
graphics.py: adjust renderPM test and fix renderSVG PolyLine
rgbecker
parents:
3358
diff
changeset
|
627 |
points=pts, style=self._formatStyle(AREA_STYLES,fill=None)) |
1607 | 628 |
self.currGroup.appendChild(polyline) |
629 |
||
630 |
### groups ### |
|
4474
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
631 |
def startGroup(self,attrDict=dict(transform="")): |
3721 | 632 |
if self.verbose: print("+++ begin SVGCanvas.startGroup") |
4474
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
633 |
currGroup = self.currGroup |
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
634 |
group = transformNode(self.doc, "g", **attrDict) |
1648 | 635 |
currGroup.appendChild(group) |
1607 | 636 |
self.currGroup = group |
3721 | 637 |
if self.verbose: print("+++ end SVGCanvas.startGroup") |
1648 | 638 |
return currGroup |
1607 | 639 |
|
1648 | 640 |
def endGroup(self,currGroup): |
3721 | 641 |
if self.verbose: print("+++ begin SVGCanvas.endGroup") |
1648 | 642 |
self.currGroup = currGroup |
3721 | 643 |
if self.verbose: print("+++ end SVGCanvas.endGroup") |
1607 | 644 |
|
645 |
def transform(self, a, b, c, d, e, f): |
|
3721 | 646 |
if self.verbose: print("!!! begin SVGCanvas.transform", a, b, c, d, e, f) |
1607 | 647 |
tr = self.currGroup.getAttribute("transform") |
648 |
if (a, b, c, d, e, f) != (1, 0, 0, 1, 0, 0): |
|
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
649 |
t = 'matrix(%s)' % self.cfp_str(a,b,c,d,e,f) |
1607 | 650 |
self.currGroup.setAttribute("transform", "%s %s" % (tr, t)) |
651 |
||
652 |
def translate(self, x, y): |
|
4474
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
653 |
if (x,y) != (0,0): |
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
654 |
self.currGroup.setAttribute("transform", "%s %s" |
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
655 |
% (self.currGroup.getAttribute("transform"), |
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
656 |
'translate(%s)' % self.cfp_str(x,y))) |
1607 | 657 |
|
4474
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
658 |
def scale(self, sx, sy): |
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
659 |
if (sx,sy) != (1,1): |
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
660 |
self.currGroup.setAttribute("transform", "%s %s" |
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
661 |
% (self.groups[-1].getAttribute("transform"), |
066480f7a206
improve log axis ticking; version --> 3.5.11
robin <robin@reportlab.com>
parents:
4385
diff
changeset
|
662 |
'scale(%s)' % self.cfp_str(sx, sy))) |
1607 | 663 |
|
664 |
### paths ### |
|
665 |
def moveTo(self, x, y): |
|
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
666 |
self.path = self.path + 'M %s ' % self.fp_str(x, y) |
1607 | 667 |
|
668 |
def lineTo(self, x, y): |
|
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
669 |
self.path = self.path + 'L %s ' % self.fp_str(x, y) |
1607 | 670 |
|
671 |
def curveTo(self, x1, y1, x2, y2, x3, y3): |
|
4385
e4da72e381d5
allow fp_str rendering in renderSVG
robin <robin@reportlab.com>
parents:
4367
diff
changeset
|
672 |
self.path = self.path + 'C %s ' % self.fp_str(x1, y1, x2, y2, x3, y3) |
1607 | 673 |
|
674 |
def closePath(self): |
|
675 |
self.path = self.path + 'Z ' |
|
676 |
||
2360
0fbaee224de1
rl_config add _unset_, graphics.renderxxx refactoring
rgbecker
parents:
1744
diff
changeset
|
677 |
def saveState(self): |
0fbaee224de1
rl_config add _unset_, graphics.renderxxx refactoring
rgbecker
parents:
1744
diff
changeset
|
678 |
pass |
1607 | 679 |
|
2360
0fbaee224de1
rl_config add _unset_, graphics.renderxxx refactoring
rgbecker
parents:
1744
diff
changeset
|
680 |
def restoreState(self): |
0fbaee224de1
rl_config add _unset_, graphics.renderxxx refactoring
rgbecker
parents:
1744
diff
changeset
|
681 |
pass |
1607 | 682 |
|
683 |
class _SVGRenderer(Renderer): |
|
684 |
"""This draws onto an SVG document. |
|
685 |
""" |
|
686 |
||
687 |
def __init__(self): |
|
688 |
self.verbose = 0 |
|
689 |
||
690 |
def drawNode(self, node): |
|
691 |
"""This is the recursive method called for each node in the tree. |
|
692 |
""" |
|
693 |
||
3721 | 694 |
if self.verbose: print("### begin _SVGRenderer.drawNode(%r)" % node) |
1607 | 695 |
|
3326 | 696 |
self._canvas.comment('begin node %r'%node) |
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
697 |
style = self._canvas.style.copy() |
1607 | 698 |
if not (isinstance(node, Path) and node.isClipPath): |
699 |
pass # self._canvas.saveState() |
|
700 |
||
701 |
#apply state changes |
|
702 |
deltas = getStateDelta(node) |
|
703 |
self._tracker.push(deltas) |
|
704 |
self.applyStateChanges(deltas, {}) |
|
705 |
||
706 |
#draw the object, or recurse |
|
707 |
self.drawNodeDispatcher(node) |
|
708 |
||
709 |
rDeltas = self._tracker.pop() |
|
710 |
if not (isinstance(node, Path) and node.isClipPath): |
|
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
711 |
pass #self._canvas.restoreState() |
3326 | 712 |
self._canvas.comment('end node %r'%node) |
1607 | 713 |
|
714 |
#restore things we might have lost (without actually doing anything). |
|
3723
99aa837b6703
second stage of port to Python 3.3; working hello world
rptlab
parents:
3721
diff
changeset
|
715 |
for k, v in rDeltas.items(): |
3326 | 716 |
if k in self._restores: |
1607 | 717 |
setattr(self._canvas,self._restores[k],v) |
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
718 |
self._canvas.style = style |
1607 | 719 |
|
3721 | 720 |
if self.verbose: print("### end _SVGRenderer.drawNode(%r)" % node) |
1607 | 721 |
|
722 |
_restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap', |
|
723 |
'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font', |
|
724 |
'fontSize':'_fontSize'} |
|
725 |
||
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
726 |
def _get_link_info_dict(self, obj): |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
727 |
#We do not want None or False as the link, even if it is the |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
728 |
#attribute's value - use the empty string instead. |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
729 |
url = getattr(obj, "hrefURL", "") or "" |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
730 |
title = getattr(obj, "hrefTitle", "") or "" |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
731 |
if url : |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
732 |
#Is it valid to have a link with no href? The XML requires |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
733 |
#the xlink:href to be present, but you might just want a |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
734 |
#tool tip shown (via the xlink:title attribute). Note that |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
735 |
#giving an href of "" is equivalent to "the current page" |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
736 |
#(a relative link saying go nowhere). |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
737 |
return {"xlink:href":url, "xlink:title":title, "target":"_top"} |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
738 |
#Currently of all the mainstream browsers I have tested, only Safari/webkit |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
739 |
#will show SVG images embedded in HTML using a simple <img src="..." /> tag. |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
740 |
#However, the links don't work (Safari 3.2.1 on the Mac). |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
741 |
# |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
742 |
#Therefore I use the following, which also works for Firefox, Opera, and |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
743 |
#IE 6.0 with Adobe SVG Viewer 6 beta: |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
744 |
#<object data="..." type="image/svg+xml" width="430" height="150" class="img"> |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
745 |
# |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
746 |
#Once displayed, Firefox and Safari treat the SVG like a frame, and |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
747 |
#by default clicking on links acts "in frame" and replaces the image. |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
748 |
#Opera does what I expect, and replaces the whole page with the link. |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
749 |
# |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
750 |
#Therefore I use target="_top" to force the links to replace the whole page. |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
751 |
#This now works as expected on Safari 3.2.1, Firefox 3.0.6, Opera 9.20. |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
752 |
#Perhaps the target attribute should be an option, perhaps defaulting to |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
753 |
#"_top" as used here? |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
754 |
else : |
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
755 |
return None |
1607 | 756 |
|
757 |
def drawGroup(self, group): |
|
3721 | 758 |
if self.verbose: print("### begin _SVGRenderer.drawGroup") |
1607 | 759 |
|
1648 | 760 |
currGroup = self._canvas.startGroup() |
3056 | 761 |
a, b, c, d, e, f = self._tracker.getState()['transform'] |
1607 | 762 |
for childNode in group.getContents(): |
763 |
if isinstance(childNode, UserNode): |
|
764 |
node2 = childNode.provideNode() |
|
765 |
else: |
|
766 |
node2 = childNode |
|
767 |
self.drawNode(node2) |
|
768 |
self._canvas.transform(a, b, c, d, e, f) |
|
1648 | 769 |
self._canvas.endGroup(currGroup) |
1607 | 770 |
|
3721 | 771 |
if self.verbose: print("### end _SVGRenderer.drawGroup") |
1607 | 772 |
|
773 |
def drawRect(self, rect): |
|
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
774 |
link_info = self._get_link_info_dict(rect) |
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
775 |
svgAttrs = getattr(rect,'_svgAttrs',{}) |
1607 | 776 |
if rect.rx == rect.ry == 0: |
777 |
#plain old rectangle |
|
778 |
self._canvas.rect( |
|
779 |
rect.x, rect.y, |
|
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
780 |
rect.x+rect.width, rect.y+rect.height, link_info=link_info, **svgAttrs) |
1607 | 781 |
else: |
782 |
#cheat and assume ry = rx; better to generalize |
|
783 |
#pdfgen roundRect function. TODO |
|
784 |
self._canvas.roundRect( |
|
785 |
rect.x, rect.y, |
|
1683 | 786 |
rect.x+rect.width, rect.y+rect.height, |
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
787 |
rect.rx, rect.ry, |
3632
401542b8679b
support for passing attributes into the svg renderer
rgbecker
parents:
3631
diff
changeset
|
788 |
link_info=link_info, **svgAttrs) |
1607 | 789 |
|
790 |
def drawString(self, stringObj): |
|
791 |
if self._canvas._fillColor: |
|
792 |
S = self._tracker.getState() |
|
793 |
text_anchor, x, y, text = S['textAnchor'], stringObj.x, stringObj.y, stringObj.text |
|
3219
b28d6eef8227
reportlab: add support for String anchor 'numeric'
rgbecker
parents:
3128
diff
changeset
|
794 |
if not text_anchor in ('start', 'inherited'): |
1683 | 795 |
font, fontSize = S['fontName'], S['fontSize'] |
1607 | 796 |
textLen = stringWidth(text, font,fontSize) |
797 |
if text_anchor=='end': |
|
3219
b28d6eef8227
reportlab: add support for String anchor 'numeric'
rgbecker
parents:
3128
diff
changeset
|
798 |
x -= textLen |
1607 | 799 |
elif text_anchor=='middle': |
3219
b28d6eef8227
reportlab: add support for String anchor 'numeric'
rgbecker
parents:
3128
diff
changeset
|
800 |
x -= textLen/2 |
b28d6eef8227
reportlab: add support for String anchor 'numeric'
rgbecker
parents:
3128
diff
changeset
|
801 |
elif text_anchor=='numeric': |
b28d6eef8227
reportlab: add support for String anchor 'numeric'
rgbecker
parents:
3128
diff
changeset
|
802 |
x -= numericXShift(text_anchor,text,textLen,font,fontSize) |
1607 | 803 |
else: |
3721 | 804 |
raise ValueError('bad value for text_anchor ' + str(text_anchor)) |
3635 | 805 |
self._canvas.drawString(text,x,y,link_info=self._get_link_info_dict(stringObj),**getattr(stringObj,'_svgAttrs',{})) |
1607 | 806 |
|
807 |
def drawLine(self, line): |
|
808 |
if self._canvas._strokeColor: |
|
809 |
self._canvas.line(line.x1, line.y1, line.x2, line.y2) |
|
810 |
||
811 |
def drawCircle(self, circle): |
|
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
812 |
self._canvas.circle( circle.cx, circle.cy, circle.r, link_info=self._get_link_info_dict(circle)) |
1607 | 813 |
|
814 |
def drawWedge(self, wedge): |
|
4132
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
815 |
yradius, radius1, yradius1 = wedge._xtraRadii() |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
816 |
if (radius1==0 or radius1 is None) and (yradius1==0 or yradius1 is None) and not wedge.annular: |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
817 |
centerx, centery, radius, startangledegrees, endangledegrees = \ |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
818 |
wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
819 |
yradius = wedge.yradius or wedge.radius |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
820 |
(x1, y1) = (centerx-radius, centery-yradius) |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
821 |
(x2, y2) = (centerx+radius, centery+yradius) |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
822 |
extent = endangledegrees - startangledegrees |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
823 |
self._canvas.drawArc(x1, y1, x2, y2, startangledegrees, extent, fromcenter=1) |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
824 |
else: |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
825 |
P = wedge.asPolygon() |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
826 |
if isinstance(P,Path): |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
827 |
self.drawPath(P) |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
828 |
else: |
28379377462b
fix doughnut chart special case and how it is drawn; bump to 3.1.31
robin
parents:
3986
diff
changeset
|
829 |
self.drawPolygon(P) |
1607 | 830 |
|
831 |
def drawPolyLine(self, p): |
|
832 |
if self._canvas._strokeColor: |
|
833 |
self._canvas.polyLine(_pointsFromList(p.points)) |
|
834 |
||
835 |
def drawEllipse(self, ellipse): |
|
836 |
#need to convert to pdfgen's bounding box representation |
|
837 |
x1 = ellipse.cx - ellipse.rx |
|
838 |
x2 = ellipse.cx + ellipse.rx |
|
839 |
y1 = ellipse.cy - ellipse.ry |
|
840 |
y2 = ellipse.cy + ellipse.ry |
|
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
841 |
self._canvas.ellipse(x1,y1,x2,y2, link_info=self._get_link_info_dict(ellipse)) |
1607 | 842 |
|
843 |
def drawPolygon(self, p): |
|
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
844 |
self._canvas.polygon(_pointsFromList(p.points), closed=1, link_info=self._get_link_info_dict(p)) |
1607 | 845 |
|
4306
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
846 |
def drawPath(self, path, fillMode=None): |
1607 | 847 |
# print "### drawPath", path.points |
848 |
from reportlab.graphics.shapes import _renderPath |
|
849 |
c = self._canvas |
|
850 |
drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath) |
|
4306
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
851 |
if fillMode is None: |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
852 |
fillMode = getattr(path,'fillMode',None) |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
853 |
link_info = self._get_link_info_dict(path) |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
854 |
autoclose = getattr(path,'autoclose','') |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
855 |
def rP(**kwds): |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
856 |
return _renderPath(path, drawFuncs, **kwds) |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
857 |
if autoclose=='svg': |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
858 |
rP() |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
859 |
c._fillAndStroke([], clip=path.isClipPath, link_info=link_info, fillMode=fillMode) |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
860 |
elif autoclose=='pdf': |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
861 |
rP(forceClose=True) |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
862 |
c._fillAndStroke([], clip=path.isClipPath, link_info=link_info, fillMode=fillMode) |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
863 |
else: |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
864 |
isClosed = rP() |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
865 |
if not isClosed: |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
866 |
ofc = c._fillColor |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
867 |
c.setFillColor(None) |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
868 |
try: |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
869 |
link_info = None |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
870 |
c._fillAndStroke([], clip=path.isClipPath, link_info=link_info, fillMode=fillMode) |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
871 |
finally: |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
872 |
c.setFillColor(ofc) |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
873 |
else: |
8ffb2ffc283b
support for Path autoclose & fillMode; version --> 3.3.29
robin
parents:
4303
diff
changeset
|
874 |
c._fillAndStroke([], clip=path.isClipPath, link_info=link_info, fillMode=fillMode) |
1607 | 875 |
|
4303
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
876 |
def drawImage(self, image): |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
877 |
path = image.path |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
878 |
if isinstance(path,str): |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
879 |
if not (path and os.path.isfile(path)): return |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
880 |
im = _getImage().open(path) |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
881 |
elif hasattr(path,'convert'): |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
882 |
im = path |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
883 |
else: |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
884 |
return |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
885 |
srcW, srcH = im.size |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
886 |
dstW, dstH = image.width, image.height |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
887 |
if dstW is None: dstW = srcW |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
888 |
if dstH is None: dstH = srcH |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
889 |
self._canvas.drawImage(im, image.x, image.y, dstW, dstH, embed=True) |
1dbe6269e831
improved support for images in renderPM/renderSVG bug report from Claude Paroz; version --> 3.3.26
robin
parents:
4283
diff
changeset
|
890 |
|
1607 | 891 |
def applyStateChanges(self, delta, newState): |
892 |
"""This takes a set of states, and outputs the operators |
|
893 |
needed to set those properties""" |
|
894 |
||
3723
99aa837b6703
second stage of port to Python 3.3; working hello world
rptlab
parents:
3721
diff
changeset
|
895 |
for key, value in delta.items(): |
1607 | 896 |
if key == 'transform': |
1683 | 897 |
pass |
1648 | 898 |
#self._canvas.transform(value[0], value[1], value[2], value[3], value[4], value[5]) |
1607 | 899 |
elif key == 'strokeColor': |
900 |
self._canvas.setStrokeColor(value) |
|
901 |
elif key == 'strokeWidth': |
|
902 |
self._canvas.setLineWidth(value) |
|
903 |
elif key == 'strokeLineCap': #0,1,2 |
|
904 |
self._canvas.setLineCap(value) |
|
905 |
elif key == 'strokeLineJoin': |
|
906 |
self._canvas.setLineJoin(value) |
|
907 |
elif key == 'strokeDashArray': |
|
908 |
if value: |
|
3502 | 909 |
if isinstance(value,(list,tuple)) and len(value)==2 and isinstance(value[1],(tuple,list)): |
910 |
phase = value[0] |
|
911 |
value = value[1] |
|
912 |
else: |
|
913 |
phase = 0 |
|
914 |
self._canvas.setDash(value,phase) |
|
1607 | 915 |
else: |
916 |
self._canvas.setDash() |
|
917 |
elif key == 'fillColor': |
|
918 |
self._canvas.setFillColor(value) |
|
919 |
elif key in ['fontSize', 'fontName']: |
|
920 |
fontname = delta.get('fontName', self._canvas._font) |
|
921 |
fontsize = delta.get('fontSize', self._canvas._fontSize) |
|
922 |
self._canvas.setFont(fontname, fontsize) |
|
4325
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
923 |
elif key == 'fillMode': |
fc03e6396da5
add support for fillMode in drawing stack; version-->3.3.34
robin <robin@reportlab.com>
parents:
4306
diff
changeset
|
924 |
self._canvas.setFillMode(value) |
1607 | 925 |
|
3986 | 926 |
def test(outDir='out-svg'): |
1607 | 927 |
# print all drawings and their doc strings from the test |
928 |
# file |
|
3986 | 929 |
if not os.path.isdir(outDir): |
930 |
os.mkdir(outDir) |
|
1607 | 931 |
#grab all drawings from the test module |
932 |
from reportlab.graphics import testshapes |
|
933 |
drawings = [] |
|
934 |
||
935 |
for funcname in dir(testshapes): |
|
936 |
if funcname[0:10] == 'getDrawing': |
|
4551 | 937 |
func = getattr(testshapes,funcname) |
938 |
drawing = func() |
|
939 |
docstring = getattr(func,'__doc__','') |
|
1607 | 940 |
drawings.append((drawing, docstring)) |
941 |
||
942 |
i = 0 |
|
943 |
for (d, docstring) in drawings: |
|
3986 | 944 |
filename = os.path.join(outDir,'renderSVG_%d.svg' % i) |
1607 | 945 |
drawToFile(d, filename) |
3114
3e5d70c382c3
renderSVG.py: add in PJACock's pacth and fix a couple of bugs
rgbecker
parents:
3096
diff
changeset
|
946 |
i += 1 |
1607 | 947 |
|
948 |
from reportlab.graphics.testshapes import getDrawing01 |
|
949 |
d = getDrawing01() |
|
3986 | 950 |
drawToFile(d, os.path.join(outDir,"test.svg")) |
1607 | 951 |
|
952 |
from reportlab.lib.corp import RL_CorpLogo |
|
953 |
from reportlab.graphics.shapes import Drawing |
|
954 |
||
955 |
rl = RL_CorpLogo() |
|
956 |
d = Drawing(rl.width,rl.height) |
|
957 |
d.add(rl) |
|
3986 | 958 |
drawToFile(d, os.path.join(outDir,"corplogo.svg")) |
1607 | 959 |
|
960 |
if __name__=='__main__': |
|
3986 | 961 |
test() |