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 __repr__(self): 265 return "SVGMatrix(%f, %f, %f, %f, %f, %f)" % self.matrix 266 267 def __eq__(self, other): 268 return self.matrix == other.matrix 269 270 def __ne__(self, other): 271 return not (self == other) 272 273 def _get_params(self, param_string): 274 return map(float, map(lambda s: s.strip(), param_string.split(","))) 275 276 def fromNode(self, node, name): 277 278 """ 279 Initialise this object from the trait on the 'node' having the given 280 'name'. 281 """ 282 283 value = node.getAttribute(name) 284 if value is None: 285 raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR) 286 287 value = value.strip() 288 289 # Translation. 290 291 m = self.translate_regexp.match(value) 292 if m: 293 a, b, c, d = 1, 0, 0, 1 294 e, f = self._get_params(m.group(1)) 295 self.matrix = a, b, c, d, e, f 296 return 297 298 # Scaling. 299 300 m = self.scale_regexp.match(value) 301 if m: 302 b, c, e, f = 0, 0, 0, 0 303 a, d = self._get_params(m.group(1)) 304 self.matrix = a, b, c, d, e, f 305 return 306 307 # Rotation. 308 309 m = self.rotate_regexp.match(value) 310 if m: 311 e, f = 0, 0 312 angle = float(m.group(1).strip()) 313 a = d = math.cos(math.radians(angle)) 314 b = math.sin(math.radians(angle)) 315 c = -b 316 self.matrix = a, b, c, d, e, f 317 return 318 319 # Skew. 320 321 m = self.skewX_regexp.match(value) 322 if m: 323 a, b, d, e, f = 1, 0, 1, 0, 0 324 angle = float(m.group(1).strip()) 325 c = math.tan(math.radians(angle)) 326 self.matrix = a, b, c, d, e, f 327 return 328 329 m = self.skewY_regexp.match(value) 330 if m: 331 a, c, d, e, f = 1, 0, 1, 0, 0 332 angle = float(m.group(1).strip()) 333 b = math.tan(math.radians(angle)) 334 self.matrix = a, b, c, d, e, f 335 return 336 337 # Generic. 338 339 m = self.matrix_regexp.match(value) 340 if m: 341 self.matrix = self._get_params(m.group(1)) 342 return 343 344 # Otherwise, complain. 345 346 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 347 348 def toNode(self, node, name): 349 350 """ 351 Set the trait on the given 'node' using the given 'name' according to 352 this object's attributes. 353 """ 354 355 a, b, c, d, e, f = self.matrix 356 357 # Translation. 358 359 if (a, b, c, d) == (1, 0, 0, 1): 360 node.setAttribute(name, "translate(%f, %f)" % (e, f)) 361 362 # Scaling. 363 364 elif (b, c, e, f) == (0, 0, 0, 0): 365 node.setAttribute(name, "scale(%f, %f)" % (a, d)) 366 367 # Rotation. 368 369 elif a == d and b == -c and (e, f) == (0, 0) and math.degrees(math.acos(a)) == math.degrees(math.asin(b)): 370 node.setAttribute(name, "rotate(%f)" % math.degrees(math.acos(a))) 371 372 # Skew. 373 374 elif (a, b, d, e, f) == (1, 0, 1, 0, 0) and c != 0: 375 node.setAttribute(name, "skewX(%f)" % math.degrees(math.atan(c))) 376 377 elif (a, c, d, e, f) == (1, 0, 1, 0, 0) and b != 0: 378 node.setAttribute(name, "skewX(%f)" % math.degrees(math.atan(b))) 379 380 # Generic matrix. 381 382 else: 383 node.setAttribute(name, "matrix(%f, %f, %f, %f, %f, %f)" % (a, b, c, d, e, f)) 384 385 def getComponent(self, index): 386 387 """ 388 Return the component with the given 'index' (starting at zero) from the 389 sequence a, b, c, d, e, f where each element corresponds to the matrix 390 as follows: 391 392 [ a c e ] 393 [ b d f ] 394 [ 0 0 1 ] 395 """ 396 397 try: 398 return self.matrix[index] 399 except IndexError: 400 raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR) 401 402 def mMultiply(self, secondMatrix): 403 404 """ 405 Post-multiply this matrix with 'secondMatrix' and update its contents to 406 the result of the multiplication operation defined as follows: 407 408 [ A C E ] [ a c e ] 409 [ B D F ] [ b d f ] 410 [ 0 0 1 ] [ 0 0 1 ] 411 412 Return this object as a result. 413 """ 414 415 a, b, c, d, e, f = self.matrix 416 A, B, C, D, E, F = secondMatrix.matrix 417 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 418 return self 419 420 def inverse(self): 421 422 """ 423 det = ad - cb 424 425 See (for example): http://mathworld.wolfram.com/MatrixInverse.html 426 """ 427 428 det = a*d - c*b 429 if det != 0: 430 m = 1/det 431 a, b, c, d, e, f = self.matrix 432 self.matrix = m * d, m * -b, m * -c, m * a, m * (c*f - e*d), m * (e*b - a*f) 433 return self 434 else: 435 raise SVGException(SVGException.SVG_MATRIX_NOT_INVERTABLE) 436 437 def mTranslate(self, x, y): 438 439 """ 440 [ 1 0 x ] 441 [ 0 1 y ] 442 [ 0 0 1 ] 443 """ 444 445 return self.mMultiply(SVGMatrix(1, 0, 0, 1, x, y)) 446 447 def mScale(self, scaleFactor): 448 449 """ 450 [ scaleFactor 0 0 ] 451 [ 0 scaleFactor 0 ] 452 [ 0 0 1 ] 453 """ 454 455 return self.mMultiply(SVGMatrix(scaleFactor, 0, 0, scaleFactor, 0, 0)) 456 457 def mRotate(self, angle): 458 459 """ 460 [ cos(angle) -sin(angle) 0 ] 461 [ sin(angle) cos(angle) 0 ] 462 [ 0 0 1 ] 463 """ 464 465 return self.mMultiply( 466 SVGMatrix( 467 math.cos(math.radians(angle)), 468 math.sin(math.radians(angle)), 469 -math.sin(math.radians(angle)), 470 math.cos(math.radians(angle)), 471 0, 0 472 ) 473 ) 474 475 class SVGPath: 476 477 """ 478 A path. 479 See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__SVGPath 480 See: http://www.w3.org/TR/SVGMobile12/paths.html 481 """ 482 483 MOVE_TO = 77 484 LINE_TO = 76 485 CURVE_TO = 67 486 QUAD_TO = 81 487 CLOSE = 90 488 _CLOSE = 122 # More baggage (name not standard). 489 490 nparams = { 491 MOVE_TO : 2, 492 LINE_TO : 2, 493 CURVE_TO : 6, 494 QUAD_TO : 4, 495 CLOSE : 0, 496 _CLOSE : 0 497 } 498 499 def __init__(self): 500 self.segments = [] 501 502 def __eq__(self, other): 503 return self.segments == other.segments 504 505 def __ne__(self, other): 506 return not (self == other) 507 508 def fromNode(self, node, name): 509 510 """ 511 Initialise this object from the trait on the 'node' having the given 512 'name'. 513 """ 514 515 value = node.getAttribute(name) 516 if value is None: 517 raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR) 518 519 # Try and unpack the attribute value. 520 521 data = value.split() 522 self.segments = [] 523 try: 524 i = 0 525 while i < len(data): 526 cmd = ord(data[i]) 527 if cmd == self._CLOSE: 528 cmd = self.CLOSE 529 i += 1 530 n = self.nparams[cmd] 531 params = map(float, data[i:i+n]) 532 self.segments.append((cmd, params)) 533 i += n 534 except (IndexError, ValueError): 535 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 536 537 def toNode(self, node, name): 538 539 """ 540 Set the trait on the given 'node' using the given 'name' according to 541 this object's attributes. 542 """ 543 544 try: 545 l = [] 546 for cmd, params in self.segments: 547 l.append(unichr(cmd)) 548 for param in params: 549 l.append(str(param)) 550 node.setAttribute(name, " ".join(l)) 551 except (IndexError, ValueError): 552 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 553 554 # Interface methods. 555 556 def _numberOfSegments(self): 557 return len(self.segments) 558 559 numberOfSegments = property(_numberOfSegments) 560 561 def getSegment(self, cmdIndex): 562 try: 563 return self.segments[cmdIndex][0] 564 except IndexError: 565 raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR) 566 567 def getSegmentParam(self, cmdIndex, paramIndex): 568 try: 569 return self.segments[cmdIndex][1][paramIndex] 570 except IndexError: 571 raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR) 572 573 def moveTo(self, x, y): 574 self.segments.append((self.MOVE_TO, (x, y))) 575 576 def lineTo(self, x, y): 577 self.segments.append((self.LINE_TO, (x, y))) 578 579 def quadTo(self, x1, y1, x2, y2): 580 self.segments.append((self.QUAD_TO, (x1, y1, x2, y2))) 581 582 def curveTo(self, x1, y1, x2, y2, x3, y3): 583 self.segments.append((self.CURVE_TO, (x1, y1, x2, y2, x3, y3))) 584 585 def close(self): 586 self.segments.append((self.CLOSE,)) 587 588 class SVGPoint: 589 590 "A point used to provide currentTranslate." 591 592 def __init__(self, x, y): 593 self.x = x 594 self.y = y 595 596 def __repr__(self): 597 return "SVGPoint(%f, %f)" % (self.x, self.y) 598 599 class SVGRect: 600 601 "A rectangle." 602 603 def __init__(self, x=0, y=0, width=0, height=0): 604 self.x, self.y, self.width, self.height = x, y, width, height 605 606 def __eq__(self, other): 607 return (self.x, self.y, self.width, self.height) == (other.x, other.y, other.width, other.height) 608 609 def __ne__(self, other): 610 return not (self == other) 611 612 def fromNode(self, node, name): 613 614 """ 615 Initialise this object from the trait on the 'node' having the given 616 'name'. 617 """ 618 619 value = node.getAttribute(name) 620 if value is None: 621 raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR) 622 try: 623 values = map(float, value.split()) 624 self.x, self.y, self.width, self.height = values 625 except (IndexError, ValueError): 626 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 627 628 def toNode(self, node, name): 629 630 """ 631 Set the trait on the given 'node' using the given 'name' according to 632 this object's attributes. 633 """ 634 635 try: 636 values = map(str, [self.x, self.y, self.width, self.height]) 637 node.setAttribute(name, " ".join(values)) 638 except (IndexError, ValueError): 639 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 640 641 def __repr__(self): 642 return "SVGRect(%f, %f, %f, %f)" % (self.x, self.y, self.width, self.height) 643 644 class SVGRGBColor: 645 646 """ 647 A colour. 648 See: http://www.w3.org/TR/SVGMobile12/painting.html#colorSyntax 649 """ 650 651 def __init__(self, red, green, blue): 652 self.red, self.green, self.blue = red, green, blue 653 654 def __repr__(self): 655 return "SVGRGBColor(%f, %f, %f)" % (self.red, self.green, self.blue) 656 657 class TraitAccess: 658 659 """ 660 Access to traits stored on elements. 661 See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__TraitAccess 662 """ 663 664 def getPathTrait(self, name): 665 path = SVGPath() 666 path.fromNode(self, name) 667 return path 668 669 def setPathTrait(self, name, path): 670 path.toNode(self, name) 671 672 def getRectTrait(self, name): 673 rect = SVGRect() 674 rect.fromNode(self, name) 675 return rect 676 677 def setRectTrait(self, name, rect): 678 rect.toNode(self, name) 679 680 def getMatrixTrait(self, name): 681 if name != "transform": 682 raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR) 683 matrix = SVGMatrix() 684 matrix.fromNode(self, name) 685 return matrix 686 687 def setMatrixTrait(self, name, matrix): 688 if name != "transform": 689 raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR) 690 matrix.toNode(self, name) 691 692 # Node classes. 693 694 class SVGNode(libxml2dom.Node): 695 696 "Convenience modifications to nodes specific to libxml2dom.svg." 697 698 def xpath(self, expr, variables=None, namespaces=None): 699 700 """ 701 Evaluate the given 'expr' using the optional 'variables' and 702 'namespaces'. If not otherwise specified, the "svg" prefix will be bound 703 to SVG_NAMESPACE as defined in this module. 704 """ 705 706 namespaces = namespaces or {} 707 if not namespaces.has_key("svg"): 708 namespaces["svg"] = SVG_NAMESPACE 709 return libxml2dom.Node.xpath(self, expr, variables, namespaces) 710 711 # NOTE: DocumentEvent is from DOM Level 3 Events. 712 # NOTE: EventSystem is a special libxml2dom.events class. 713 714 class SVGDocument(libxml2dom._Document, SVGNode, EventTarget, DocumentEvent, EventSystem): 715 716 "An SVG-specific document node." 717 718 def __init__(self, node, impl): 719 720 """ 721 Initialise the document with the given 'node', implementation 'impl', 722 and global (SVGGlobal) details. 723 """ 724 725 libxml2dom._Document.__init__(self, node, impl) 726 self.global_ = self.impl.get_global(self) # parent 727 728 class SVGElement(SVGNode, EventTarget, TraitAccess, ElementTraversal): # NOTE: SVGNode instead of Element. 729 730 "An SVG-specific element." 731 732 def __init__(self, *args, **kw): 733 SVGNode.__init__(self, *args, **kw) 734 735 def _id(self): 736 return self.getAttribute("id") 737 738 def _setId(self, value): 739 self.setAttribute("id", value) 740 741 id = property(_id, _setId) 742 743 class SVGLocatableElement(SVGElement, SVGLocatable): 744 745 "A locatable element." 746 747 pass 748 749 class SVGTimedElement(SVGElement): # smil::ElementTimeControl 750 751 "A timed element." 752 753 def __init__(self, *args): 754 755 "Initialise the element with the underlying 'args'." 756 757 SVGElement.__init__(self, *args) 758 self.document_time = 0 759 self.paused = 0 760 761 def _isPaused(self): 762 return self.paused 763 764 def pauseElement(self): 765 self.paused = 1 766 767 def resumeElement(self): 768 self.paused = 0 769 770 class SVGSVGElement(SVGLocatableElement, SVGTimedElement): 771 772 "An SVG-specific top-level element." 773 774 NAV_AUTO = 1 775 NAV_NEXT = 2 776 NAV_PREV = 3 777 NAV_UP = 4 778 NAV_UP_RIGHT = 5 779 NAV_RIGHT = 6 780 NAV_DOWN_RIGHT = 7 781 NAV_DOWN = 8 782 NAV_DOWN_LEFT = 9 783 NAV_LEFT = 10 784 NAV_UP_LEFT = 11 785 786 def __init__(self, *args): 787 788 "Initialise the element with the underlying 'args'." 789 790 SVGTimedElement.__init__(self, *args) 791 self.scale = 1 792 self.rotate = 0 793 self.translate = SVGPoint(0, 0) 794 795 # NOTE: The scale, rotate and translate properties are not persistent, and 796 # NOTE: are specific to individual objects. 797 798 def _currentScale(self): 799 return self.scale 800 801 def _currentRotate(self): 802 return self.rotate 803 804 def _currentTranslate(self): 805 return self.translate 806 807 def _setCurrentScale(self, scale): 808 if scale == 0: 809 raise xml.dom.DOMException(xml.dom.INVALID_ACCESS_ERR) 810 self.scale = scale 811 812 def _setCurrentRotate(self, rotate): 813 self.rotate = rotate 814 815 def _viewport(self): 816 if self.hasAttribute("viewBox"): 817 return self.getRectTrait("viewBox") 818 else: 819 return SVGRect(0, 0, self._convertMeasurement(self.getAttribute("width")), 820 self._convertMeasurement(self.getAttribute("height"))) 821 822 # Utility methods. 823 824 units = ["in", "cm", "mm", "pt", "pc", "px", "%"] 825 826 def _convertMeasurement(self, value): 827 value = value.strip() 828 for unit in self.units: 829 if value.endswith(unit): 830 # NOTE: No conversion yet! 831 return float(value[:-len(unit)].strip()) 832 833 raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR) 834 835 # Standard methods. 836 837 def getCurrentTime(self): 838 return self.document_time 839 840 def setCurrentTime(self, setCurrentTime): 841 self.document_time = setCurrentTime 842 843 def createSVGMatrixComponents(self, a, b, c, d, e, f): 844 return SVGMatrix(a, b, c, d, e, f) 845 846 def createSVGRect(self): 847 return SVGRect() 848 849 def createSVGPath(self): 850 return SVGPath() 851 852 def createSVGRGBColor(self, red, green, blue): 853 return SVGRGBColor(red, green, blue) 854 855 def moveFocus(self, motionType): 856 raise NotImplementedError, "moveFocus" 857 858 def setFocus(self, object): 859 raise NotImplementedError, "setFocus" 860 861 def getCurrentFocusedObject(self): 862 raise NotImplementedError, "getCurrentFocusedObject" 863 864 currentScale = property(_currentScale, _setCurrentScale) 865 currentRotate = property(_currentRotate, _setCurrentRotate) 866 currentTranslate = property(_currentTranslate) 867 viewport = property(_viewport) 868 869 # Event handler initialisation. 870 871 def initialiseEvents(doc): 872 873 """ 874 See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__EventListenerInitializer2 875 See: http://www.w3.org/TR/xml-events/#section-listener-element 876 """ 877 878 # Initialise script element listeners. 879 880 for script in doc.xpath("//svg:script"): 881 doc.global_.initializeEventListeners(script) 882 883 # Initialise handler element listeners using XML Events. 884 885 for handler in doc.xpath("//svg:handler"): 886 listener = doc.global_.createEventListener(handler) 887 888 # Attempt to parameterise the registration using the XML Events attributes. 889 890 phase = handler.getAttributeNS(libxml2dom.events.XML_EVENTS_NAMESPACE, "phase") 891 892 # Add the listener for the appropriate type and phases. 893 894 handler.parentNode.addEventListener( 895 handler.getAttributeNS(libxml2dom.events.XML_EVENTS_NAMESPACE, "event"), 896 listener, 897 phase == "capture" 898 ) 899 900 # Utility functions. 901 902 createDocument = libxml2dom.createDocument 903 createDocumentType = libxml2dom.createDocumentType 904 905 def createSVGDocument(): 906 return default_impl.createSVGDocument() 907 908 def parse(stream_or_string, html=0, htmlencoding=None, unfinished=0, impl=None): 909 doc = libxml2dom.parse(stream_or_string, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 910 initialiseEvents(doc) 911 return doc 912 913 def parseFile(filename, html=0, htmlencoding=None, unfinished=0, impl=None): 914 doc = libxml2dom.parseFile(filename, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 915 initialiseEvents(doc) 916 return doc 917 918 def parseString(s, html=0, htmlencoding=None, unfinished=0, impl=None): 919 doc = libxml2dom.parseString(s, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 920 initialiseEvents(doc) 921 return doc 922 923 def parseURI(uri, html=0, htmlencoding=None, unfinished=0, impl=None): 924 doc = libxml2dom.parseURI(uri, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) 925 initialiseEvents(doc) 926 return doc 927 928 # Single instance of the implementation. 929 930 default_impl = SVGImplementation() 931 932 # vim: tabstop=4 expandtab shiftwidth=4