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