1 #!/usr/bin/env python 2 3 """ 4 SVG-specific document support. 5 See: http://www.w3.org/TR/SVGMobile12/python-binding.html 6 See: http://www.w3.org/TR/SVGMobile12/svgudom.html 7 8 Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk> 9 10 This library is free software; you can redistribute it and/or 11 modify it under the terms of the GNU Lesser General Public 12 License as published by the Free Software Foundation; either 13 version 2.1 of the License, or (at your option) any later version. 14 15 This library is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 Lesser General Public License for more details. 19 20 You should have received a copy of the GNU Lesser General Public 21 License along with this library; if not, write to the Free Software 22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 23 """ 24 25 import libxml2dom 26 from libxml2dom.events import * 27 from libxml2dom.macrolib import * 28 from libxml2dom.macrolib import \ 29 createDocument as Node_createDocument 30 import xml.dom 31 import urllib 32 import math 33 import re 34 35 SVG_NAMESPACE = "http://www.w3.org/2000/svg" 36 37 class _Exception(Exception): 38 39 "A generic SVG exception." 40 41 def __init__(self, code): 42 Exception.__init__(self, code) 43 self.code = code 44 45 class SVGException(_Exception): 46 47 "An SVG exception." 48 49 SVG_WRONG_TYPE_ERR = 0 50 SVG_INVALID_VALUE_ERR = 1 51 SVG_MATRIX_NOT_INVERTABLE = 2 52 53 class GlobalException(_Exception): 54 55 "A global exception." 56 57 NOT_CONNECTED_ERR = 1 58 ENCODING_ERR = 2 59 DENIED_ERR = 3 60 UNKNOWN_ERR = 4 61 62 class SVGImplementation(libxml2dom.Implementation): 63 64 "Contains an SVG-specific implementation." 65 66 # Wrapping of documents. 67 68 def adoptDocument(self, node): 69 return SVGDocument(node, self) 70 71 # Factory functions. 72 73 def get_node(self, _node, context_node): 74 if Node_nodeType(_node) == context_node.ELEMENT_NODE and \ 75 Node_namespaceURI(_node) == SVG_NAMESPACE: 76 77 if Node_localName(_node) == "svg": 78 return SVGSVGElement(_node, self, context_node.ownerDocument) 79 else: 80 return SVGElement(_node, self, context_node.ownerDocument) 81 else: 82 return libxml2dom.Implementation.get_node(self, _node, context_node) 83 84 def get_global(self, doc): 85 return SVGGlobal(doc) 86 87 # Convenience functions. 88 89 def createSVGDocument(self): 90 91 "Create a new SVG document." 92 93 return SVGDocument(Node_createDocument(SVG_NAMESPACE, "svg", None), self) 94 95 # Interfaces and helper classes. 96 97 class AsyncStatusCallback: 98 99 "An asynchronous callback interface." 100 101 def operationComplete(self, status): 102 pass 103 104 class AsyncURLStatus: 105 106 "The status of a URL retrieval operation." 107 108 def __init__(self, success, contentType, content): 109 self.success, self.contentType, self.content = success, contentType, content 110 111 class ElementTraversal: 112 113 "An interface for element traversal." 114 115 def _firstElementChild(self): 116 l = self.xpath("*") 117 if l: 118 return l[0] 119 else: 120 return None 121 122 def _lastElementChild(self): 123 l = self.xpath("*") 124 if l: 125 return l[-1] 126 else: 127 return None 128 129 def _nextElementSibling(self): 130 l = self.xpath("following-sibling::*") 131 if l: 132 return l[0] 133 else: 134 return None 135 136 def _previousElementSibling(self): 137 l = self.xpath("preceding-sibling::*") 138 if l: 139 return l[0] 140 else: 141 return None 142 143 firstElementChild = property(_firstElementChild) 144 lastElementChild = property(_lastElementChild) 145 nextElementSibling = property(_nextElementSibling) 146 previousElementSibling = property(_previousElementSibling) 147 148 class EventListenerInitializer2: 149 150 "An event listener initialisation interface." 151 152 def initializeEventListeners(self, scriptElement): 153 pass 154 155 def createEventListener(self, handlerElement): 156 pass 157 158 class Global: 159 160 "An empty global interface." 161 162 pass 163 164 class SVGGlobal(Global, EventListenerInitializer2): 165 166 "An SVG global." 167 168 def __init__(self, document): # parent 169 170 "Initialise the global with the given 'document'." 171 172 self.document = document 173 174 # Listener management. 175 176 self.listeners = {} 177 178 def createConnection(self): 179 raise NotImplementedError, "createConnection" 180 181 def createTimer(self, initialInterval, repeatInterval): 182 raise NotImplementedError, "createTimer" 183 184 def gotoLocation(self, newIRI): 185 raise NotImplementedError, "gotoLocation" 186 187 def binaryToString(self, octets, encoding): 188 try: 189 return unicode(octets, encoding) 190 except UnicodeDecodeError, exc: 191 raise GlobalException(GlobalException.ENCODING_ERR) 192 193 def stringToBinary(self, data, encoding): 194 try: 195 return data.encode(encoding) 196 except UnicodeEncodeError, exc: 197 raise GlobalException(GlobalException.ENCODING_ERR) 198 199 def getURL(self, iri, callback): 200 201 # NOTE: Not asynchronous. 202 # NOTE: The urlopen function may not support IRIs. 203 # No exceptions are supposed to be raised, which is a bit nasty. 204 205 f = urllib.urlopen(iri) 206 try: 207 try: 208 content = f.read() 209 contentType = f.headers["Content-Type"] 210 callback.operationComplete(AsyncURLStatus(1, contentType, content)) 211 except: 212 callback.operationComplete(AsyncURLStatus(0, None, None)) 213 finally: 214 f.close() 215 216 def postURL(self, iri, data, callback, type=None, encoding=None): 217 218 # NOTE: Not asynchronous. 219 # NOTE: The urlopen function may not support IRIs. 220 # No exceptions are supposed to be raised, which is a bit nasty. 221 222 opener = urllib.URLopener() 223 opener.addheader("Content-Type", type or "text/plain") 224 if encoding: 225 opener.addheader("Content-Encoding", encoding) 226 f = opener.open(iri, data) 227 try: 228 try: 229 content = f.read() 230 contentType = f.headers["Content-Type"] 231 callback.operationComplete(AsyncURLStatus(1, contentType, content)) 232 except: 233 callback.operationComplete(AsyncURLStatus(0, None, None)) 234 finally: 235 f.close() 236 opener.close() 237 238 def parseXML(self, data, contextDoc): 239 doc = parseString(data) 240 return contextDoc.importNode(doc.documentElement, 1) 241 242 class SVGLocatable: 243 244 "A locatable interface." 245 246 pass 247 248 class SVGMatrix: 249 250 """ 251 A matrix. 252 See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__SVGMatrix 253 """ 254 255 translate_regexp = re.compile("translate\((.*)\)$") 256 scale_regexp = re.compile("scale\((.*)\)$") 257 rotate_regexp = re.compile("rotate\((.*)\)$") 258 skewX_regexp = re.compile("skewX\((.*)\)$") 259 skewY_regexp = re.compile("skewY\((.*)\)$") 260 matrix_regexp = re.compile("matrix\((.*)\)$") 261 262 def __init__(self, a=0, b=0, c=0, d=0, e=0, f=0): 263 self.matrix = a, b, c, d, e, f 264 265 def __eq__(self, other): 266 return self.matrix == other.matrix 267 268 def __ne__(self, other): 269 return not (self == other) 270 271 def _get_params(self, param_string): 272 return map(float, map(lambda s: s.strip(), param_string.split(","))) 273 274 def fromNode(self, node, name): 275 276 """ 277 Initialise this object from the trait on the 'node' having the given 278 'name'. 279 """ 280 281 value = node.getAttribute(name) 282 if value is None: 283 raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR) 284 285 value = value.strip() 286 287 # Translation. 288 289 m = self.translate_regexp.match(value) 290 if m: 291 a, b, c, d = 1, 0, 0, 1 292 e, f = self._get_params(m.group(1)) 293 self.matrix = a, b, c, d, e, f 294 return 295 296 # Scaling. 297 298 m = self.scale_regexp.match(value) 299 if m: 300 b, c, e, f = 0, 0, 0, 0 301 a, d = self._get_params(m.group(1)) 302 self.matrix = a, b, c, d, e, f 303 return 304 305 # Rotation. 306 307 m = self.rotate_regexp.match(value) 308 if m: 309 e, f = 0, 0 310 angle = float(m.group(1).strip()) 311 a = d = math.cos(math.radians(angle)) 312 b = math.sin(math.radians(angle)) 313 c = -b 314 self.matrix = a, b, c, d, e, f 315 return 316 317 # Skew. 318 319 m = self.skewX_regexp.match(value) 320 if m: 321 a, b, d, e, f = 1, 0, 1, 0, 0 322 angle = float(m.group(1).strip()) 323 c = math.tan(math.radians(angle)) 324 self.matrix = a, b, c, d, e, f 325 return 326 327 m = self.skewY_regexp.match(value) 328 if m: 329 a, c, d, e, f = 1, 0, 1, 0, 0 330 angle = float(m.group(1).strip()) 331 b = math.tan(math.radians(angle)) 332 self.matrix = a, b, c, d, e, f 333 return 334 335 # Generic. 336 337 m = self.matrix_regexp.match(value) 338 if m: 339 self.matrix = self._get_params(m.group(1)) 340 return 341 342 # Otherwise, complain. 343 344 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 345 346 def toNode(self, node, name): 347 348 """ 349 Set the trait on the given 'node' using the given 'name' according to 350 this object's attributes. 351 """ 352 353 a, b, c, d, e, f = self.matrix 354 355 # Translation. 356 357 if (a, b, c, d) == (1, 0, 0, 1): 358 node.setAttribute(name, "translate(%f, %f)" % (e, f)) 359 360 # Scaling. 361 362 elif (b, c, e, f) == (0, 0, 0, 0): 363 node.setAttribute(name, "scale(%f, %f)" % (a, d)) 364 365 # Rotation. 366 367 elif a == d and b == -c and (e, f) == (0, 0) and math.degrees(math.acos(a)) == math.degrees(math.asin(b)): 368 node.setAttribute(name, "rotate(%f)" % math.degrees(math.acos(a))) 369 370 # Skew. 371 372 elif (a, b, d, e, f) == (1, 0, 1, 0, 0) and c != 0: 373 node.setAttribute(name, "skewX(%f)" % math.degrees(math.atan(c))) 374 375 elif (a, c, d, e, f) == (1, 0, 1, 0, 0) and b != 0: 376 node.setAttribute(name, "skewX(%f)" % math.degrees(math.atan(b))) 377 378 # Generic matrix. 379 380 else: 381 node.setAttribute(name, "matrix(%f, %f, %f, %f, %f, %f)" % (a, b, c, d, e, f)) 382 383 def getComponent(self, index): 384 385 """ 386 Return the component with the given 'index' (starting at zero) from the 387 sequence a, b, c, d, e, f where each element corresponds to the matrix 388 as follows: 389 390 [ a c e ] 391 [ b d f ] 392 [ 0 0 1 ] 393 """ 394 395 try: 396 return self.matrix[index] 397 except IndexError: 398 raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR) 399 400 def mMultiply(self, secondMatrix): 401 402 """ 403 Multiply this matrix with 'secondMatrix' and update its contents to the 404 result of the multiplication operation defined as follows: 405 406 [ a c e ] [ A C E ] 407 [ b d f ] [ B D F ] 408 [ 0 0 1 ] [ 0 0 1 ] 409 410 Return this object as a result. 411 """ 412 413 a, b, c, d, e, f = self.matrix 414 A, B, C, D, E, F = secondMatrix.matrix 415 self.matrix = a*A + c*B, b*A + d*B, a*C + c*D, b*C + d*D, a*E + c*F + e, b*E + d*F + f 416 return self 417 418 def inverse(self): 419 420 """ 421 det = ad - cb 422 423 See (for example): http://mathworld.wolfram.com/MatrixInverse.html 424 """ 425 426 det = a*d - c*b 427 if det != 0: 428 m = 1/det 429 a, b, c, d, e, f = self.matrix 430 self.matrix = m * d, m * -b, m * -c, m * a, m * (c*f - e*d), m * (e*b - a*f) 431 return self 432 else: 433 raise SVGException(SVGException.SVG_MATRIX_NOT_INVERTABLE) 434 435 def mTranslate(self, x, y): 436 437 """ 438 [ 1 0 x ] 439 [ 0 1 y ] 440 [ 0 0 1 ] 441 """ 442 443 return self.mMultiply(SVGMatrix(1, 0, 0, 1, x, y)) 444 445 def mScale(self, scaleFactor): 446 447 """ 448 [ scaleFactor 0 0 ] 449 [ 0 scaleFactor 0 ] 450 [ 0 0 1 ] 451 """ 452 453 return self.mMultiply(SVGMatrix(scaleFactor, 0, 0, scaleFactor, 0, 0)) 454 455 def mRotate(self, angle): 456 457 """ 458 [ cos(angle) -sin(angle) 0 ] 459 [ sin(angle) cos(angle) 0 ] 460 [ 0 0 1 ] 461 """ 462 463 return self.mMultiply( 464 SVGMatrix( 465 math.cos(math.radians(angle)), 466 math.sin(math.radians(angle)), 467 -math.sin(math.radians(angle)), 468 math.cos(math.radians(angle)), 469 0, 0 470 ) 471 ) 472 473 class SVGPath: 474 475 """ 476 A path. 477 See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__SVGPath 478 See: http://www.w3.org/TR/SVGMobile12/paths.html 479 """ 480 481 MOVE_TO = 77 482 LINE_TO = 76 483 CURVE_TO = 67 484 QUAD_TO = 81 485 CLOSE = 90 486 _CLOSE = 122 # More baggage (name not standard). 487 488 nparams = { 489 MOVE_TO : 2, 490 LINE_TO : 2, 491 CURVE_TO : 6, 492 QUAD_TO : 4, 493 CLOSE : 0, 494 _CLOSE : 0 495 } 496 497 def __init__(self): 498 self.segments = [] 499 500 def __eq__(self, other): 501 return self.segments == other.segments 502 503 def __ne__(self, other): 504 return not (self == other) 505 506 def fromNode(self, node, name): 507 508 """ 509 Initialise this object from the trait on the 'node' having the given 510 'name'. 511 """ 512 513 value = node.getAttribute(name) 514 if value is None: 515 raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR) 516 517 # Try and unpack the attribute value. 518 519 data = value.split() 520 self.segments = [] 521 try: 522 i = 0 523 while i < len(data): 524 cmd = ord(data[i]) 525 if cmd == self._CLOSE: 526 cmd = self.CLOSE 527 i += 1 528 n = self.nparams[cmd] 529 params = map(float, data[i:i+n]) 530 self.segments.append((cmd, params)) 531 i += n 532 except (IndexError, ValueError): 533 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 534 535 def toNode(self, node, name): 536 537 """ 538 Set the trait on the given 'node' using the given 'name' according to 539 this object's attributes. 540 """ 541 542 try: 543 l = [] 544 for cmd, params in self.segments: 545 l.append(unichr(cmd)) 546 for param in params: 547 l.append(str(param)) 548 node.setAttribute(name, " ".join(l)) 549 except (IndexError, ValueError): 550 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 551 552 # Interface methods. 553 554 def _numberOfSegments(self): 555 return len(self.segments) 556 557 numberOfSegments = property(_numberOfSegments) 558 559 def getSegment(self, cmdIndex): 560 try: 561 return self.segments[cmdIndex][0] 562 except IndexError: 563 raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR) 564 565 def getSegmentParam(self, cmdIndex, paramIndex): 566 try: 567 return self.segments[cmdIndex][1][paramIndex] 568 except IndexError: 569 raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR) 570 571 def moveTo(self, x, y): 572 self.segments.append((self.MOVE_TO, (x, y))) 573 574 def lineTo(self, x, y): 575 self.segments.append((self.LINE_TO, (x, y))) 576 577 def quadTo(self, x1, y1, x2, y2): 578 self.segments.append((self.QUAD_TO, (x1, y1, x2, y2))) 579 580 def curveTo(self, x1, y1, x2, y2, x3, y3): 581 self.segments.append((self.CURVE_TO, (x1, y1, x2, y2, x3, y3))) 582 583 def close(self): 584 self.segments.append((self.CLOSE,)) 585 586 class SVGPoint: 587 588 "A point used to provide currentTranslate." 589 590 def __init__(self, x, y): 591 self.x = x 592 self.y = y 593 594 class SVGRect: 595 596 "A rectangle." 597 598 def __init__(self, x=0, y=0, width=0, height=0): 599 self.x, self.y, self.width, self.height = x, y, width, height 600 601 def __eq__(self, other): 602 return (self.x, self.y, self.width, self.height) == (other.x, other.y, other.width, other.height) 603 604 def __ne__(self, other): 605 return not (self == other) 606 607 def fromNode(self, node, name): 608 609 """ 610 Initialise this object from the trait on the 'node' having the given 611 'name'. 612 """ 613 614 value = node.getAttribute(name) 615 if value is None: 616 raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR) 617 try: 618 values = map(float, value.split()) 619 self.x, self.y, self.width, self.height = values 620 except (IndexError, ValueError): 621 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 622 623 def toNode(self, node, name): 624 625 """ 626 Set the trait on the given 'node' using the given 'name' according to 627 this object's attributes. 628 """ 629 630 try: 631 values = map(str, [self.x, self.y, self.width, self.height]) 632 node.setAttribute(name, " ".join(values)) 633 except (IndexError, ValueError): 634 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 635 636 class SVGRGBColor: 637 638 "A colour." 639 640 def __init__(self, red, green, blue): 641 self.red, self.green, self.blue = red, green, blue 642 643 class TraitAccess: 644 645 """ 646 Access to traits stored on elements. 647 See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__TraitAccess 648 """ 649 650 def getPathTrait(self, name): 651 path = SVGPath() 652 path.fromNode(self, name) 653 return path 654 655 def setPathTrait(self, name, path): 656 path.toNode(self, name) 657 658 def getRectTrait(self, name): 659 rect = SVGRect() 660 rect.fromNode(self, name) 661 return rect 662 663 def setRectTrait(self, name, rect): 664 rect.toNode(self, name) 665 666 def getMatrixTrait(self, name): 667 matrix = SVGMatrix() 668 matrix.fromNode(self, name) 669 return matrix 670 671 def setMatrixTrait(self, name, matrix): 672 matrix.toNode(self, name) 673 674 # Node classes. 675 676 class SVGNode(libxml2dom.Node): 677 678 "Convenience modifications to nodes specific to libxml2dom.svg." 679 680 def xpath(self, expr, variables=None, namespaces=None): 681 682 """ 683 Evaluate the given 'expr' using the optional 'variables' and 684 'namespaces'. If not otherwise specified, the "svg" prefix will be bound 685 to SVG_NAMESPACE as defined in this module. 686 """ 687 688 namespaces = namespaces or {} 689 if not namespaces.has_key("svg"): 690 namespaces["svg"] = SVG_NAMESPACE 691 return libxml2dom.Node.xpath(self, expr, variables, namespaces) 692 693 # NOTE: DocumentEvent is from DOM Level 3 Events. 694 # NOTE: EventSystem is a special libxml2dom.events class. 695 696 class SVGDocument(libxml2dom._Document, SVGNode, EventTarget, DocumentEvent, EventSystem): 697 698 "An SVG-specific document node." 699 700 def __init__(self, node, impl): 701 702 """ 703 Initialise the document with the given 'node', implementation 'impl', 704 and global (SVGGlobal) details. 705 """ 706 707 libxml2dom._Document.__init__(self, node, impl) 708 self.global_ = self.impl.get_global(self) # parent 709 710 class SVGElement(SVGNode, EventTarget, TraitAccess, ElementTraversal): # NOTE: SVGNode instead of Element. 711 712 "An SVG-specific element." 713 714 def __init__(self, *args, **kw): 715 SVGNode.__init__(self, *args, **kw) 716 717 def _id(self): 718 return self.getAttribute("id") 719 720 def _setId(self, value): 721 self.setAttribute("id", value) 722 723 id = property(_id, _setId) 724 725 class SVGLocatableElement(SVGElement, SVGLocatable): 726 727 "A locatable element." 728 729 pass 730 731 class SVGTimedElement(SVGElement): # smil::ElementTimeControl 732 733 "A timed element." 734 735 def __init__(self, *args): 736 737 "Initialise the element with the underlying 'args'." 738 739 SVGElement.__init__(self, *args) 740 self.document_time = 0 741 self.paused = 0 742 743 def _isPaused(self): 744 return self.paused 745 746 def pauseElement(self): 747 self.paused = 1 748 749 def resumeElement(self): 750 self.paused = 0 751 752 class SVGSVGElement(SVGLocatableElement, SVGTimedElement): 753 754 "An SVG-specific top-level element." 755 756 NAV_AUTO = 1 757 NAV_NEXT = 2 758 NAV_PREV = 3 759 NAV_UP = 4 760 NAV_UP_RIGHT = 5 761 NAV_RIGHT = 6 762 NAV_DOWN_RIGHT = 7 763 NAV_DOWN = 8 764 NAV_DOWN_LEFT = 9 765 NAV_LEFT = 10 766 NAV_UP_LEFT = 11 767 768 def __init__(self, *args): 769 770 "Initialise the element with the underlying 'args'." 771 772 SVGTimedElement.__init__(self, *args) 773 self.scale = 1 774 self.rotate = 0 775 self.translate = SVGPoint(0, 0) 776 777 def _currentScale(self): 778 return self.scale 779 780 def _currentRotate(self): 781 return self.rotate 782 783 def _currentTranslate(self): 784 return self.translate 785 786 def _viewport(self): 787 return self.getRectTrait("viewBox") 788 789 def getCurrentTime(self): 790 return self.document_time 791 792 def setCurrentTime(self, setCurrentTime): 793 self.document_time = setCurrentTime 794 795 def createSVGMatrixComponents(self, a, b, c, d, e, f): 796 return SVGMatrix(a, b, c, d, e, f) 797 798 def createSVGRect(self): 799 return SVGRect() 800 801 def createSVGPath(self): 802 return SVGPath() 803 804 def createSVGRGBColor(self, red, green, blue): 805 return SVGRGBColor(red, green, blue) 806 807 def moveFocus(self, motionType): 808 raise NotImplementedError, "moveFocus" 809 810 def setFocus(self, object): 811 raise NotImplementedError, "setFocus" 812 813 def getCurrentFocusedObject(self): 814 raise NotImplementedError, "getCurrentFocusedObject" 815 816 currentScale = property(_currentScale) 817 currentRotate = property(_currentRotate) 818 currentTranslate = property(_currentTranslate) 819 viewport = property(_viewport) 820 821 # Event handler initialisation. 822 823 def initialiseEvents(doc): 824 825 """ 826 See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__EventListenerInitializer2 827 See: http://www.w3.org/TR/xml-events/#section-listener-element 828 """ 829 830 # Initialise script element listeners. 831 832 for script in doc.xpath("//svg:script"): 833 doc.global_.initializeEventListeners(script) 834 835 # Initialise handler element listeners using XML Events. 836 837 for handler in doc.xpath("//svg:handler"): 838 listener = doc.global_.createEventListener(handler) 839 840 # Attempt to parameterise the registration using the XML Events attributes. 841 842 phase = handler.getAttributeNS(libxml2dom.events.XML_EVENTS_NAMESPACE, "phase") 843 844 # Add the listener for the appropriate type and phases. 845 846 handler.parentNode.addEventListener( 847 handler.getAttributeNS(libxml2dom.events.XML_EVENTS_NAMESPACE, "event"), 848 listener, 849 phase == "capture" 850 ) 851 852 # Utility functions. 853 854 createDocument = libxml2dom.createDocument 855 createDocumentType = libxml2dom.createDocumentType 856 857 def createSVGDocument(): 858 return default_impl.createSVGDocument() 859 860 def parse(stream_or_string, html=0, htmlencoding=None, unfinished=0, impl=None): 861 doc = libxml2dom.parse(stream_or_string, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 862 initialiseEvents(doc) 863 return doc 864 865 def parseFile(filename, html=0, htmlencoding=None, unfinished=0, impl=None): 866 doc = libxml2dom.parseFile(filename, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 867 initialiseEvents(doc) 868 return doc 869 870 def parseString(s, html=0, htmlencoding=None, unfinished=0, impl=None): 871 doc = libxml2dom.parseString(s, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 872 initialiseEvents(doc) 873 return doc 874 875 def parseURI(uri, html=0, htmlencoding=None, unfinished=0, impl=None): 876 doc = libxml2dom.parseURI(uri, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 877 initialiseEvents(doc) 878 return doc 879 880 # Single instance of the implementation. 881 882 default_impl = SVGImplementation() 883 884 # vim: tabstop=4 expandtab shiftwidth=4