1# Copyright (C) 2000  Greg Landrum
2#
3# This library is free software; you can redistribute it and/or
4# modify it under the terms of the GNU Lesser General Public
5# License as published by the Free Software Foundation; either
6# version 2 of the License, or (at your option) any later version.
7#
8# This library is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11# Lesser General Public License for more details.
12#
13# You should have received a copy of the GNU Lesser General Public
14# License along with this library; if not, write to the Free Software
15# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16"""piddleSVG
17
18This module implements an SVG PIDDLE canvas.
19In other words, this is a PIDDLE backend that renders into a
20SVG file.
21
22Bits have been shamelessly cobbled from piddlePDF.py and/or
23piddlePS.py
24
25Greg Landrum (greglandrum@earthlink.net) 3/10/2000
26"""
27"""
28  Functionality implemented:
29  -drawLine
30  -drawPolygon
31  -drawEllipse
32  -drawArc
33  -drawCurve
34  -drawString (rotated text is, mostly, fine... see below)
35  -drawFigure
36  -drawImage
37
38  Known problems:
39   -Rotated text is right in either IBM's SVGView or Adobe's plugin.  This
40    problem is explained in drawString()
41   -The font/string handling is not perfect.  There are definite problems
42    with getting the widths of strings.  Thus far heights seem to work okay
43    in the tests that I've done, but those could well be broken as well.
44
45"""
46
47from rdkit.sping.pid import *
48from rdkit.sping.PDF import pdfmetrics  # for font info
49from math import *
50
51#SVG_HEADER = """<?xml version="1.0" encoding="iso-8859-1"?>
52#<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
53#"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
54#"""
55SVG_HEADER = """<?xml version="1.0" encoding="iso-8859-1"?>
56"""
57
58
59def _ColorToSVG(color):
60  """ convenience function for converting a sping.pid color to an SVG color
61
62  """
63  if color == transparent:
64    return 'none'
65  else:
66    return 'rgb(%d,%d,%d)' % (int(color.red * 255), int(color.green * 255), int(color.blue * 255))
67
68
69def _PointListToSVG(points, dupFirst=0):
70  """ convenience function for converting a list of points to a string
71      suitable for passing to SVG path operations
72
73  """
74  outStr = ''
75  for i in range(len(points)):
76    outStr = outStr + '%.2f,%.2f ' % (points[i][0], points[i][1])
77  # add back on the first point.  This is not required in the spec,
78  #  but Adobe's beta-quality viewer seems to not like it being skipped
79  if dupFirst == 1:
80    outStr = outStr + '%.2f,%.2f' % (points[0][0], points[0][1])
81  return outStr
82
83
84class SVGCanvas(Canvas):
85
86  def __init__(self, size=(300, 300), name='SVGCanvas', includeXMLHeader=True, extraHeaderText=''):
87    self._nImages = 1
88    # I'd rather do this as PNG, but IBM's SVGView doesn't support those
89    #  yet. Adobe's plugin works just fine with them, however.
90    self._imageFormat = 'GIF'
91    self.size = size
92    self._initOutput(includeXMLHeader=includeXMLHeader, extraHeaderText=extraHeaderText)
93    Canvas.__init__(self, size, name)
94
95  def _initOutput(self, includeXMLHeader=True, extraHeaderText=''):
96    if includeXMLHeader:
97      self._txt = SVG_HEADER
98    else:
99      self._txt = ""
100    self._txt += """<svg:svg version="1.1" baseProfile="full"
101        xmlns:svg="http://www.w3.org/2000/svg"
102        xmlns:xlink="http://www.w3.org/1999/xlink"
103        xml:space="preserve" width="%dpx" height="%dpx" %s>\n""" % (self.size[0], self.size[1],
104                                                                    extraHeaderText)
105
106  def _findExternalFontName(self, font):  #copied from piddlePDF by cwl- hack away!
107    """Attempts to return proper font name.
108        PDF uses a standard 14 fonts referred to
109        by name. Default to self.defaultFont('Helvetica').
110        The dictionary allows a layer of indirection to
111        support a standard set of sping.pid font names."""
112
113    piddle_font_map = {
114      'Times': 'Times',
115      'times': 'Times',
116      'Courier': 'Courier',
117      'courier': 'Courier',
118      'helvetica': 'Helvetica',
119      'Helvetica': 'Helvetica',
120      'symbol': 'Symbol',
121      'Symbol': 'Symbol',
122      'monospaced': 'Courier',
123      'serif': 'Times',
124      'sansserif': 'Helvetica',
125      'ZapfDingbats': 'ZapfDingbats',
126      'zapfdingbats': 'ZapfDingbats',
127      'arial': 'Helvetica'
128    }
129
130    try:
131      face = piddle_font_map[font.face.lower()]
132    except Exception:
133      return piddle_font_map['sansserif']
134
135    name = face + '-'
136    if font.bold and face in ['Courier', 'Helvetica', 'Times']:
137      name = name + 'Bold'
138    if font.italic and face in ['Courier', 'Helvetica']:
139      name = name + 'Oblique'
140    elif font.italic and face == 'Times':
141      name = name + 'Italic'
142
143    if name == 'Times-':
144      name = name + 'Roman'
145    # symbol and ZapfDingbats cannot be modified!
146
147    #trim and return
148    if name[-1] == '-':
149      name = name[0:-1]
150    return name
151
152  def _FormFontStr(self, font):
153    """ form what we hope is a valid SVG font string.
154      Defaults to 'sansserif'
155      This should work when an array of font faces are passed in.
156    """
157    fontStr = ''
158    if font.face is None:
159      font.__dict__['face'] = 'sansserif'  # quick hack -cwl
160    if isinstance(font.face, str):
161      if len(font.face.split()) > 1:
162        familyStr = '\'%s\'' % font.face
163      else:
164        familyStr = font.face
165    else:
166      face = font.face[0]
167      if len(face.split()) > 1:
168        familyStr = '\'%s\'' % (face)
169      else:
170        familyStr = face
171      for i in range(1, len(font.face)):
172        face = font.face[i]
173        if len(face.split()) > 1:
174          familyStr = ', \'%s\'' % (face)
175        else:
176          familyStr = familyStr + ', %s' % face
177    if font.italic:
178      styleStr = 'font-style="italic"'
179    else:
180      styleStr = ''
181    if font.bold:
182      weightStr = 'font-weight="bold"'
183    else:
184      weightStr = ''
185    if font.size:
186      sizeStr = 'font-size="%.2f"' % font.size
187    else:
188      sizeStr = ''
189
190    fontStr = 'font-family="%s" %s %s %s' % (familyStr, styleStr, weightStr, sizeStr)
191    return fontStr
192
193  def _FormArcStr(self, x1, y1, x2, y2, theta1, extent):
194    """ Forms an arc specification for SVG
195
196    """
197    if abs(extent) > 360:
198      if extent < 0:
199        extent = -abs(extent) % 360
200      else:
201        extent = extent % 360
202
203    # deal with figuring out the various arc flags
204    #  required by SVG.
205    if extent > 180:  # this one is easy
206      arcFlag = 1
207    else:
208      arcFlag = 0
209
210    if extent >= 0:
211      sweepFlag = 0
212    else:
213      sweepFlag = 1
214
215    # convert angles to radians (grn)
216    theta1 = pi * theta1 / 180.
217    extent = pi * extent / 180.
218
219    # center of the arc
220    cx = (x1 + x2) / 2.
221    cy = (y1 + y2) / 2.
222    # its radius
223    rx = abs(x2 - x1) / 2.
224    ry = abs(y2 - y1) / 2.
225
226    # final angle
227    theta2 = theta1 + extent
228
229    # SVG takes arcs as paths running from one point to another.
230    #  figure out what those start and end points are now.
231    #  the -thetas are required because of a difference in the handedness
232    #  of angles in Piddle and SVG
233    startx = cx + rx * cos(-theta1)
234    starty = cy + ry * sin(-theta1)
235    endx = cx + rx * cos(-theta2)
236    endy = cy + ry * sin(-theta2)
237
238    arcStr = '%.2f %.2f A%.2f %.2f 0 %d %d %.2f %.2f' % (startx, starty, rx, ry, arcFlag, sweepFlag,
239                                                         endx, endy)
240    return arcStr
241
242  # public functions
243  def clear(self):
244    self._initOutput()
245
246  def flush(self):
247    # self.save('svg')
248    pass  # to fit new definition of flush() -cwl
249
250  def save(self, file=None, format=None):
251    """Hand hand this either a file= <filename> or
252    file = <an open file object>.  By default, I've made the fomrat extension be
253    .svg.  By default it saves the file to "self.name" + '.svg' """
254
255    if file == None:
256      file = self.name
257
258    if isinstance(file, str):
259      isFileName = 1
260    else:
261      isFileName = 0
262
263    if isFileName:
264      if format == None:
265        if '.' not in file:
266          file = file + '.svg'
267      else:
268        file = file + '.' + type
269
270    fileobj = getFileObject(file, openFlags="w+")
271    fileobj.write(self._txt + '</svg:svg>')
272    if isFileName:
273      fileobj.close()  # do not close if handed a file handle instead of a file name
274
275  def text(self):
276    return self._txt + '</svg:svg>'
277
278  #------------- drawing methods --------------
279  def drawLine(self, x1, y1, x2, y2, color=None, width=None, dash=None, **kwargs):
280    "Draw a straight line between x1,y1 and x2,y2."
281    # set color...
282    if color:
283      if color == transparent:
284        return
285    elif self.defaultLineColor == transparent:
286      return
287    else:
288      color = self.defaultLineColor
289
290    svgColor = _ColorToSVG(color)
291
292    if width:
293      w = width
294    else:
295      w = self.defaultLineWidth
296
297    styleStr = 'stroke="%s" stroke-width="%d"' % (svgColor, w)
298    if dash is not None:
299      styleStr += ' stroke-dasharray="'
300      styleStr += ' '.join([str(x) for x in dash])
301      styleStr += '"'
302    outStr = '<svg:line x1="%.2f" y1="%.2f" x2="%.2f" y2="%.2f" %s>' % (x1, y1, x2, y2, styleStr)
303    if 'bodyText' in kwargs:
304      outStr += kwargs['bodyText']
305    outStr += '</svg:line>\n'
306    self._txt = self._txt + outStr
307
308  def drawPolygon(self, pointlist, edgeColor=None, edgeWidth=None, fillColor=transparent, closed=0,
309                  dash=None, **kwargs):
310    """drawPolygon(pointlist) -- draws a polygon
311    pointlist: a list of (x,y) tuples defining vertices
312    """
313
314    # get the points into SVG format
315    pointStr = _PointListToSVG(pointlist, dupFirst=closed)
316
317    # set color for fill...
318    filling = 0
319    if fillColor:
320      if fillColor != transparent:
321        filling = 1
322
323    # do the fill
324    if filling:
325      fillStr = 'fill="%s"' % _ColorToSVG(fillColor)
326    else:
327      fillStr = 'fill="none"'
328
329    # set color for edge...
330    if not edgeColor:
331      edgeColor = self.defaultLineColor
332    # set edge width...
333    if edgeWidth == None:
334      edgeWidth = self.defaultLineWidth
335
336    # SVG markers
337    edgeStr = 'stroke="%s" stroke-width="%d"' % (_ColorToSVG(edgeColor), int(edgeWidth))
338    if dash is not None:
339      edgeStr += ' stroke-dasharray="'
340      edgeStr += ' '.join([str(x) for x in dash])
341      edgeStr += '"'
342
343    # draw it
344    outStr = '<svg:polygon %s %s points="%s">' % (fillStr, edgeStr, pointStr)
345    if 'bodyText' in kwargs:
346      outStr += kwargs['bodyText']
347    outStr += '</svg:polygon>\n'
348    self._txt = self._txt + outStr
349
350  def drawEllipse(self, x1, y1, x2, y2, edgeColor=None, edgeWidth=None, fillColor=transparent,
351                  dash=None, **kwargs):
352
353    # get the points into SVG format
354    cx = (x1 + x2) / 2.
355    cy = (y1 + y2) / 2.
356    rx = abs(x2 - x1) / 2.
357    ry = abs(y2 - y1) / 2.
358    ellipseStr = 'cx="%.2f" cy="%.2f" rx="%.2f" ry="%.2f"' % (cx, cy, rx, ry)
359
360    # set color for fill...
361    filling = 0
362    if fillColor:
363      if fillColor != transparent:
364        filling = 1
365
366    # do the fill
367    if filling:
368      fillStr = 'fill="%s"' % _ColorToSVG(fillColor)
369    else:
370      fillStr = 'fill="none"'
371
372    # set color for edge...
373    if not edgeColor:
374      edgeColor = self.defaultLineColor
375    # set edge width...
376    if edgeWidth == None:
377      edgeWidth = self.defaultLineWidth
378
379    edgeStr = 'stroke="%s" stroke-width="%d"' % (_ColorToSVG(edgeColor), int(edgeWidth))
380    if dash is not None:
381      edgeStr += ' stroke-dasharray="'
382      edgeStr += ' '.join([str(x) for x in dash])
383      edgeStr += '"'
384
385    # draw it
386    mods = [fillStr, edgeStr, ellipseStr]
387    if 'extraAttribs' in kwargs:
388      mods.append(kwargs['extraAttribs'])
389    outStr = '<svg:ellipse %s>' % (' '.join(mods))
390    if 'bodyText' in kwargs:
391      outStr += kwargs['bodyText']
392    outStr += '</svg:ellipse>\n'
393    self._txt = self._txt + outStr
394
395  def drawArc(self, x1, y1, x2, y2, theta1=0, extent=360, edgeColor=None, edgeWidth=None,
396              fillColor=None, dash=None, **kwargs):
397
398    # set color for fill...
399    filling = 0
400    if not fillColor:
401      fillColor = self.defaultFillColor
402
403    if fillColor != transparent:
404      filling = 1
405
406    # do the fill
407    if filling:
408      fillStr = 'fill="%s"' % _ColorToSVG(fillColor)
409    else:
410      fillStr = 'fill="none"'
411    arcStr = self._FormArcStr(x1, y1, x2, y2, theta1, extent)
412
413    if not filling:
414      pathStr = 'M' + arcStr
415    else:
416      # this is a bit trickier.  Piddle requires filled arcs to stroke the
417      #  arc bit and fill into the middle (like a piece of pie) without
418      #  stroking the lines to the middle.  So we need *two* paths here.
419      strokePathStr = 'M' + arcStr
420      cx = (x1 + x2) / 2.
421      cy = (y1 + y2) / 2.
422      fillPathStr = 'M%.2f %.2f L%sZ' % (cx, cy, arcStr)
423
424    # set color for edge...
425    if not edgeColor:
426      edgeColor = self.defaultLineColor
427    # set edge width...
428    if edgeWidth == None:
429      edgeWidth = self.defaultLineWidth
430
431    # SVG markers
432    edgeStr = 'stroke="%s" stroke-width"%d"' % (_ColorToSVG(edgeColor), int(edgeWidth))
433    if dash is not None:
434      edgeStr += ' stroke-dasharray="'
435      edgeStr += ' '.join([str(x) for x in dash])
436      edgeStr += '"'
437
438    # draw it
439    if not filling:
440      outStr = '<svg:path %s %s d="%s">' % (fillStr, edgeStr, pathStr)
441      if 'bodyText' in kwargs:
442        outStr += kwargs['bodyText']
443      outStr += '</svg:path>\n'
444    else:
445      outStr = '<svg:path %s d="%s">' % (fillStr, fillPathStr)
446      outStr += '</svg:path>\n'
447      outStr = outStr + '<svg:path fill="none" %s d="%s">' % (edgeStr, strokePathStr)
448      if 'bodyText' in kwargs:
449        outStr += kwargs['bodyText']
450      outStr += '</svg:path>\n'
451    self._txt = self._txt + outStr
452
453  def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, edgeColor=None, edgeWidth=None,
454                fillColor=transparent, closed=0, dash=None, **kwargs):
455
456    # get the points into SVG format
457    curveStr = 'M%.2f %.2f C%.2f %.2f %.2f %.2f %.2f %.2f' % (x1, y1, x2, y2, x3, y3, x4, y4)
458    if closed:
459      curveStr = curveStr + 'Z'
460
461    # set color for fill...
462    filling = 0
463    if fillColor:
464      if fillColor != transparent:
465        filling = 1
466
467    # do the fill
468    if filling:
469      fillStr = 'fill="%s"' % _ColorToSVG(fillColor)
470    else:
471      fillStr = 'fill="none"'
472
473    # set color for edge...
474    if not edgeColor:
475      edgeColor = self.defaultLineColor
476
477    # set edge width...
478    if edgeWidth == None:
479      edgeWidth = self.defaultLineWidth
480
481    # SVG markers
482    edgeStr = 'stroke="%s" stroke-width="%d"' % (_ColorToSVG(edgeColor), int(edgeWidth))
483    if dash is not None:
484      edgeStr += ' stroke-dasharray="'
485      edgeStr += ' '.join([str(x) for x in dash])
486      edgeStr += '"'
487
488    # draw it
489    outStr = '<svg:path %s %s d="%s">' % (fillStr, edgeStr, curveStr)
490    if 'bodyText' in kwargs:
491      outStr += kwargs['bodyText']
492    outStr += '</svg:path>\n'
493    self._txt = self._txt + outStr
494
495  def drawString(self, s, x, y, font=None, color=None, angle=0, **kwargs):
496    # set color...
497    if color:
498      if color == transparent:
499        return
500    elif self.defaultLineColor == transparent:
501      return
502    else:
503      color = self.defaultLineColor
504    if font is None:
505      font = self.defaultFont
506    if font:
507      fontStr = self._FormFontStr(font)
508    else:
509      fontStr = ''
510
511    svgColor = _ColorToSVG(color)
512
513    outStr = ''
514    if angle != 0:
515      # note: this is the correct order of the transforms according to my reading of
516      #  the SVG spec and the behavior of Adobe's SVG plugin.  If you want it to work
517      #  in IBM's SVGView, you'll have to use the second (commented out) form.
518      # Ah, the joys of using mature technologies. ;-)
519      outStr += '<svg:g transform="translate(%.2f,%.2f) rotate(%.2f)">\n' % (x, y, 360 - angle)
520      #outStr += '<svg:g transform="rotate(%.2f) translate(%.2f,%.2f)">\n'%(360-angle,x,y)
521      xLoc = 0
522      yLoc = 0
523    else:
524      xLoc = x
525      yLoc = y
526      outStr += '<svg:g>'
527    lines = s.split('\n')
528    lineHeight = self.fontHeight(font)
529    yP = yLoc
530    for line in lines:
531      outStr += self._drawStringOneLine(line, xLoc, yP, fontStr, svgColor, **kwargs)
532      yP = yP + lineHeight
533
534    if 'bodyText' in kwargs:
535      outStr += kwargs['bodyText']
536    outStr += '</svg:g>'
537
538    self._txt = self._txt + outStr
539
540  def _drawStringOneLine(self, line, x, y, fontStr, svgColor, **kwargs):
541    styleStr = '%s fill="%s"' % (fontStr, svgColor)
542    return '  <svg:text %s x="%.2f" y="%.2f">%s</svg:text>\n' % (styleStr, x, y, line)
543
544  def drawFigure(self, partList, edgeColor=None, edgeWidth=None, fillColor=None, closed=0,
545                 dash=None, **kwargs):
546    """drawFigure(partList) -- draws a complex figure
547    partlist: a set of lines, curves, and arcs defined by a tuple whose
548    first element is one of figureLine, figureArc, figureCurve
549    and whose remaining 4, 6, or 8 elements are parameters."""
550
551    filling = 0
552    if fillColor:
553      if fillColor != transparent:
554        filling = 1
555
556    # do the fill
557    if filling:
558      fillStr = 'fill="%s"' % _ColorToSVG(fillColor)
559    else:
560      fillStr = 'fill="none"'
561
562    # set color for edge...
563    if not edgeColor:
564      edgeColor = self.defaultLineColor
565    # set edge width...
566    if edgeWidth == None:
567      edgeWidth = self.defaultLineWidth
568
569    # SVG markers
570    edgeStr = 'stroke="%s" stroke-width="%d"' % (_ColorToSVG(edgeColor), int(edgeWidth))
571    if dash is not None:
572      edgeStr += ' stroke-dasharray="'
573      edgeStr += ' '.join([str(x) for x in dash])
574      edgeStr += '"'
575
576    pathStr = ''
577    for item in partList:
578      op = item[0]
579      args = list(item[1:])
580
581      if pathStr == '':
582        pathStr = pathStr + 'M'
583      else:
584        pathStr = pathStr + 'L'
585      if op == figureLine:
586        pathStr = pathStr + '%.2f %.2f L%.2f %.2f' % (tuple(args))
587      elif op == figureCurve:
588        pathStr = pathStr + '%.2f %.2f C%.2f %.2f %.2f %.2f %.2f %.2f' % (tuple(args))
589      elif op == figureArc:
590        x1, y1, x2, y2, theta1, extent = tuple(args)
591        pathStr = pathStr + self._FormArcStr(x1, y1, x2, y2, theta1, extent)
592
593      else:
594        raise TypeError("unknown figure operator: " + op)
595
596    if closed == 1:
597      pathStr = pathStr + 'Z'
598    outStr = '<svg:path %s %s d="%s">' % (edgeStr, fillStr, pathStr)
599    if 'bodyText' in kwargs:
600      outStr += kwargs['bodyText']
601    outStr += '</svg:path>\n'
602    self._txt = self._txt + outStr
603
604  def drawImage(self, image, x1, y1, x2=None, y2=None, **kwargs):
605    """
606      to the best of my knowledge, the only real way to get an image
607      into SVG is to read it from a file.  So we'll save out to a PNG
608      file, then set a link to that in the SVG.
609    """
610    imageFileName = '%s-%d.%s' % (self.name, self._nImages, self._imageFormat.lower())
611    self._nImages = self._nImages + 1
612    image.save(imageFileName, format=self._imageFormat)
613
614    im_width, im_height = image.size
615    if x2 is not None:
616      im_width = abs(x2 - x1)
617    if y2 is not None:
618      im_height = abs(y2 - y1)
619    outStr = '<svg:image x="%.2f" y="%.2f" width="%.2f" height="%.2f" xlink:href="%s">'%\
620             (x1,y1,im_width,im_height,imageFileName)
621    if 'bodyText' in kwargs:
622      outStr += kwargs['bodyText']
623    outStr += '</svg:image>\n'
624    self._txt = self._txt + outStr
625
626  def stringWidth(self, s, font=None):
627    "Return the logical width of the string if it were drawn \
628    in the current font (defaults to self.font)."
629
630    if not font:
631      font = self.defaultFont
632    fontname = self._findExternalFontName(font)
633    return pdfmetrics.stringwidth(s, fontname) * font.size * 0.001
634
635  def fontAscent(self, font=None):
636    if not font:
637      font = self.defaultFont
638    #return -font.size
639    fontname = self._findExternalFontName(font)
640    return pdfmetrics.ascent_descent[fontname][0] * 0.001 * font.size
641
642  def fontDescent(self, font=None):
643    if not font:
644      font = self.defaultFont
645    fontname = self._findExternalFontName(font)
646    return -pdfmetrics.ascent_descent[fontname][1] * 0.001 * font.size
647
648
649def test():
650  #... for testing...
651  canvas = SVGCanvas(name="test")
652
653  canvas.defaultLineColor = Color(0.7, 0.7, 1.0)  # light blue
654  canvas.drawLines(map(lambda i: (i * 10, 0, i * 10, 300), range(30)))
655  canvas.drawLines(map(lambda i: (0, i * 10, 300, i * 10), range(30)))
656  canvas.defaultLineColor = black
657
658  canvas.drawLine(10, 200, 20, 190, color=red)
659
660  canvas.drawEllipse(130, 30, 200, 100, fillColor=yellow, edgeWidth=4)
661
662  canvas.drawArc(130, 30, 200, 100, 45, 50, fillColor=blue, edgeColor=navy, edgeWidth=4)
663
664  canvas.defaultLineWidth = 4
665  canvas.drawRoundRect(30, 30, 100, 100, fillColor=blue, edgeColor=maroon)
666  canvas.drawCurve(20, 20, 100, 50, 50, 100, 160, 160)
667
668  canvas.drawString("This is a test!", 30, 130, Font(face="times", size=16, bold=1), color=green,
669                    angle=-45)
670
671  canvas.drawString("This is a test!", 30, 130, color=red, angle=-45)
672
673  polypoints = [(160, 120), (130, 190), (210, 145), (110, 145), (190, 190)]
674  canvas.drawPolygon(polypoints, fillColor=lime, edgeColor=red, edgeWidth=3, closed=1)
675
676  canvas.drawRect(200, 200, 260, 260, edgeColor=yellow, edgeWidth=5)
677  canvas.drawLine(200, 260, 260, 260, color=green, width=5)
678  canvas.drawLine(260, 200, 260, 260, color=red, width=5)
679
680  canvas.flush()
681  canvas.save('test.svg')
682
683
684def dashtest():
685  #... for testing...
686  canvas = SVGCanvas(name="dashtest.svg")
687
688  canvas.defaultLineColor = Color(0.7, 0.7, 1.0)  # light blue
689  canvas.drawLines(map(lambda i: (i * 10, 0, i * 10, 300), range(30)), dash=(3, 3))
690  canvas.drawLines(map(lambda i: (0, i * 10, 300, i * 10), range(30)), dash=(3, 3))
691  canvas.defaultLineColor = black
692
693  canvas.drawLine(10, 200, 20, 190, color=red, dash=(3, 3))
694
695  canvas.drawEllipse(130, 30, 200, 100, fillColor=yellow, edgeWidth=4, dash=(3, 3))
696
697  canvas.drawArc(130, 30, 200, 100, 45, 50, fillColor=blue, edgeColor=navy, edgeWidth=4, dash=(3,
698                                                                                               3))
699
700  canvas.defaultLineWidth = 4
701  canvas.drawRoundRect(30, 30, 100, 100, fillColor=blue, edgeColor=maroon, dash=(3, 3))
702  canvas.drawCurve(20, 20, 100, 50, 50, 100, 160, 160, dash=(3, 3))
703
704  canvas.drawString("This is a test!", 30, 130, Font(face="times", size=16, bold=1), color=green,
705                    angle=-45)
706
707  canvas.drawString("This is a test!", 30, 130, color=red, angle=-45)
708
709  polypoints = [(160, 120), (130, 190), (210, 145), (110, 145), (190, 190)]
710  canvas.drawPolygon(polypoints, fillColor=lime, edgeColor=red, edgeWidth=3, closed=1, dash=(3, 3))
711
712  canvas.drawRect(200, 200, 260, 260, edgeColor=yellow, edgeWidth=5, dash=(3, 3))
713  canvas.drawLine(200, 260, 260, 260, color=green, width=5, dash=(3, 3))
714  canvas.drawLine(260, 200, 260, 260, color=red, width=5, dash=(3, 3))
715
716  canvas.flush()
717  canvas.save()
718
719
720def testit(canvas, s, x, y, font=None):
721  canvas.defaultLineColor = black
722  canvas.drawString(s, x, y, font=font)
723  canvas.defaultLineColor = blue
724  w = canvas.stringWidth(s, font=font)
725  canvas.drawLine(x, y, x + w, y)
726  canvas.drawLine(x, y - canvas.fontAscent(font=font), x + w, y - canvas.fontAscent(font=font))
727  canvas.drawLine(x, y + canvas.fontDescent(font=font), x + w, y + canvas.fontDescent(font=font))
728
729
730def test2():
731
732  canvas = SVGCanvas(name="Foogar")
733  testit(canvas, "Foogar", 20, 30)
734
735  testit(canvas, "Foogar", 20, 90, font=Font(size=24))
736  global dammit
737
738  testit(canvas, "Foogar", 20, 150, font=Font(face='courier', size=24))
739
740  testit(canvas, "Foogar", 20, 240, font=Font(face='courier'))
741  canvas.flush()
742  canvas.save()
743
744
745if __name__ == '__main__':
746  test()
747  dashtest()
748  test2()
749