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