paulb@218 | 1 | #!/usr/bin/env python |
paulb@218 | 2 | |
paulb@218 | 3 | """ |
paulb@256 | 4 | SVG-specific document support. |
paulb@256 | 5 | See: http://www.w3.org/TR/SVGMobile12/python-binding.html |
paulb@256 | 6 | See: http://www.w3.org/TR/SVGMobile12/svgudom.html |
paulb@218 | 7 | |
paulb@218 | 8 | Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk> |
paulb@218 | 9 | |
paulb@218 | 10 | This library is free software; you can redistribute it and/or |
paulb@218 | 11 | modify it under the terms of the GNU Lesser General Public |
paulb@218 | 12 | License as published by the Free Software Foundation; either |
paulb@218 | 13 | version 2.1 of the License, or (at your option) any later version. |
paulb@218 | 14 | |
paulb@218 | 15 | This library is distributed in the hope that it will be useful, |
paulb@218 | 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
paulb@218 | 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
paulb@218 | 18 | Lesser General Public License for more details. |
paulb@218 | 19 | |
paulb@218 | 20 | You should have received a copy of the GNU Lesser General Public |
paulb@218 | 21 | License along with this library; if not, write to the Free Software |
paulb@218 | 22 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
paulb@218 | 23 | """ |
paulb@218 | 24 | |
paulb@218 | 25 | import libxml2dom |
paulb@256 | 26 | from libxml2dom.events import * |
paulb@218 | 27 | from libxml2dom.macrolib import * |
paulb@256 | 28 | import xml.dom |
paulb@256 | 29 | import math |
paulb@218 | 30 | |
paulb@235 | 31 | SVG_NAMESPACE = "http://www.w3.org/2000/svg" |
paulb@235 | 32 | |
paulb@256 | 33 | class _Exception(Exception): |
paulb@256 | 34 | |
paulb@256 | 35 | "A generic SVG exception." |
paulb@256 | 36 | |
paulb@256 | 37 | def __init__(self, code): |
paulb@256 | 38 | Exception.__init__(self, code) |
paulb@256 | 39 | self.code = code |
paulb@256 | 40 | |
paulb@256 | 41 | class SVGException(_Exception): |
paulb@256 | 42 | |
paulb@256 | 43 | "An SVG exception." |
paulb@256 | 44 | |
paulb@256 | 45 | SVG_WRONG_TYPE_ERR = 0 |
paulb@256 | 46 | SVG_INVALID_VALUE_ERR = 1 |
paulb@256 | 47 | SVG_MATRIX_NOT_INVERTABLE = 2 |
paulb@256 | 48 | |
paulb@256 | 49 | class GlobalException(_Exception): |
paulb@256 | 50 | |
paulb@256 | 51 | "A global exception." |
paulb@256 | 52 | |
paulb@256 | 53 | NOT_CONNECTED_ERR = 1 |
paulb@256 | 54 | ENCODING_ERR = 2 |
paulb@256 | 55 | DENIED_ERR = 3 |
paulb@256 | 56 | UNKNOWN_ERR = 4 |
paulb@256 | 57 | |
paulb@218 | 58 | class SVGImplementation(libxml2dom.Implementation): |
paulb@218 | 59 | |
paulb@218 | 60 | "Contains an SVG-specific implementation." |
paulb@218 | 61 | |
paulb@235 | 62 | # Wrapping of documents. |
paulb@235 | 63 | |
paulb@235 | 64 | def adoptDocument(self, node): |
paulb@235 | 65 | return SVGDocument(node, self) |
paulb@235 | 66 | |
paulb@235 | 67 | # Factory functions. |
paulb@235 | 68 | |
paulb@218 | 69 | def get_node(self, _node, context_node): |
paulb@218 | 70 | if Node_nodeType(_node) == context_node.ELEMENT_NODE and \ |
paulb@235 | 71 | Node_namespaceURI(_node) == SVG_NAMESPACE: |
paulb@218 | 72 | |
paulb@235 | 73 | if Node_localName(_node) == "svg": |
paulb@235 | 74 | return SVGSVGElement(_node, self, context_node.ownerDocument) |
paulb@235 | 75 | else: |
paulb@235 | 76 | return SVGElement(_node, self, context_node.ownerDocument) |
paulb@218 | 77 | else: |
paulb@218 | 78 | return libxml2dom.Implementation.get_node(self, _node, context_node) |
paulb@218 | 79 | |
paulb@256 | 80 | # Interfaces and helper classes. |
paulb@256 | 81 | |
paulb@256 | 82 | class AsyncStatusCallback: |
paulb@256 | 83 | |
paulb@256 | 84 | "An asynchronous callback interface." |
paulb@256 | 85 | |
paulb@256 | 86 | def operationComplete(self, status): |
paulb@256 | 87 | pass |
paulb@256 | 88 | |
paulb@256 | 89 | class AsyncURLStatus: |
paulb@256 | 90 | |
paulb@256 | 91 | "The status of a URL retrieval operation." |
paulb@256 | 92 | |
paulb@256 | 93 | def __init__(self, success, contentType, content): |
paulb@256 | 94 | self.success, self.contentType, self.content = success, contentType, content |
paulb@256 | 95 | |
paulb@256 | 96 | class SVGGlobal: # Global, EventListenerInitializer2 |
paulb@256 | 97 | |
paulb@256 | 98 | "An SVG global." |
paulb@256 | 99 | |
paulb@256 | 100 | def __init__(self, document): # parent |
paulb@256 | 101 | |
paulb@256 | 102 | "Initialise the global with the given 'document'." |
paulb@256 | 103 | |
paulb@256 | 104 | self.document = document |
paulb@256 | 105 | |
paulb@256 | 106 | def createConnection(self): |
paulb@256 | 107 | raise NotImplementedError, "createConnection" |
paulb@256 | 108 | |
paulb@256 | 109 | def createTimer(self, initialInterval, repeatInterval): |
paulb@256 | 110 | raise NotImplementedError, "createTimer" |
paulb@256 | 111 | |
paulb@256 | 112 | def gotoLocation(self, newIRI): |
paulb@256 | 113 | raise NotImplementedError, "gotoLocation" |
paulb@256 | 114 | |
paulb@256 | 115 | def binaryToString(self, octets, encoding): |
paulb@256 | 116 | raise NotImplementedError, "binaryToString" |
paulb@256 | 117 | |
paulb@256 | 118 | def stringToBinary(self, data, encoding): |
paulb@256 | 119 | raise NotImplementedError, "stringToBinary" |
paulb@256 | 120 | |
paulb@256 | 121 | def getURL(self, iri, callback): |
paulb@256 | 122 | |
paulb@256 | 123 | # NOTE: Not asynchronous. |
paulb@256 | 124 | # NOTE: The urlopen function may not support IRIs. |
paulb@256 | 125 | # No exceptions are supposed to be raised, which is a bit nasty. |
paulb@256 | 126 | |
paulb@256 | 127 | f = urllib.urlopen(iri) |
paulb@256 | 128 | try: |
paulb@256 | 129 | try: |
paulb@256 | 130 | content = f.read() |
paulb@256 | 131 | contentType = f.headers["Content-Type"] |
paulb@256 | 132 | callback.operationComplete(AsyncURLStatus(1, contentType, content)) |
paulb@256 | 133 | except: |
paulb@256 | 134 | callback.operationComplete(AsyncURLStatus(0, None, None)) |
paulb@256 | 135 | finally: |
paulb@256 | 136 | f.close() |
paulb@256 | 137 | |
paulb@256 | 138 | def postURL(self, iri, data, callback, type, encoding): |
paulb@256 | 139 | raise NotImplementedError, "postURL" |
paulb@256 | 140 | |
paulb@256 | 141 | def parseXML(self, data, contextDoc): |
paulb@256 | 142 | doc = parseString(data) |
paulb@256 | 143 | return contextDoc.importNode(doc.documentElement, 1) |
paulb@256 | 144 | |
paulb@256 | 145 | class SVGLocatable: |
paulb@256 | 146 | |
paulb@256 | 147 | "A locatable interface." |
paulb@256 | 148 | |
paulb@256 | 149 | pass |
paulb@256 | 150 | |
paulb@256 | 151 | class SVGMatrix: |
paulb@256 | 152 | |
paulb@256 | 153 | """ |
paulb@256 | 154 | A matrix. |
paulb@256 | 155 | See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__SVGMatrix |
paulb@256 | 156 | """ |
paulb@256 | 157 | |
paulb@256 | 158 | def __init__(self, a, b, c, d, e, f): |
paulb@256 | 159 | self.matrix = a, b, c, d, e, f |
paulb@256 | 160 | |
paulb@256 | 161 | def getComponent(self, index): |
paulb@256 | 162 | |
paulb@256 | 163 | """ |
paulb@256 | 164 | Return the component with the given 'index' (starting at zero) from the |
paulb@256 | 165 | sequence a, b, c, d, e, f where each element corresponds to the matrix |
paulb@256 | 166 | as follows: |
paulb@256 | 167 | |
paulb@256 | 168 | [ a c e ] |
paulb@256 | 169 | [ b d f ] |
paulb@256 | 170 | [ 0 0 1 ] |
paulb@256 | 171 | """ |
paulb@256 | 172 | |
paulb@256 | 173 | try: |
paulb@256 | 174 | return self.matrix[index] |
paulb@256 | 175 | except IndexError: |
paulb@256 | 176 | raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR) |
paulb@256 | 177 | |
paulb@256 | 178 | def mMultiply(self, secondMatrix): |
paulb@256 | 179 | |
paulb@256 | 180 | """ |
paulb@256 | 181 | Multiply this matrix with 'secondMatrix' and update its contents to the |
paulb@256 | 182 | result of the multiplication operation defined as follows: |
paulb@256 | 183 | |
paulb@256 | 184 | [ a c e ] [ A C E ] |
paulb@256 | 185 | [ b d f ] [ B D F ] |
paulb@256 | 186 | [ 0 0 1 ] [ 0 0 1 ] |
paulb@256 | 187 | |
paulb@256 | 188 | Return this object as a result. |
paulb@256 | 189 | """ |
paulb@256 | 190 | |
paulb@256 | 191 | a, b, c, d, e, f = self.matrix |
paulb@256 | 192 | A, B, C, D, E, F = secondMatrix.matrix |
paulb@256 | 193 | 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 |
paulb@256 | 194 | return self |
paulb@256 | 195 | |
paulb@256 | 196 | def inverse(self): |
paulb@256 | 197 | |
paulb@256 | 198 | """ |
paulb@256 | 199 | det = ad - cb |
paulb@256 | 200 | |
paulb@256 | 201 | See (for example): http://mathworld.wolfram.com/MatrixInverse.html |
paulb@256 | 202 | """ |
paulb@256 | 203 | |
paulb@256 | 204 | det = a*d - c*b |
paulb@256 | 205 | if det != 0: |
paulb@256 | 206 | m = 1/det |
paulb@256 | 207 | a, b, c, d, e, f = self.matrix |
paulb@256 | 208 | self.matrix = m * d, m * -b, m * -c, m * a, m * (c*f - e*d), m * (e*b - a*f) |
paulb@256 | 209 | return self |
paulb@256 | 210 | else: |
paulb@256 | 211 | raise SVGException(SVGException.SVG_MATRIX_NOT_INVERTABLE) |
paulb@256 | 212 | |
paulb@256 | 213 | def mTranslate(self, x, y): |
paulb@256 | 214 | |
paulb@256 | 215 | """ |
paulb@256 | 216 | [ 1 0 x ] |
paulb@256 | 217 | [ 0 1 y ] |
paulb@256 | 218 | [ 0 0 1 ] |
paulb@256 | 219 | """ |
paulb@256 | 220 | |
paulb@256 | 221 | return self.mMultiply(SVGMatrix(1, 0, 0, 1, x, y)) |
paulb@256 | 222 | |
paulb@256 | 223 | def mScale(self, scaleFactor): |
paulb@256 | 224 | |
paulb@256 | 225 | """ |
paulb@256 | 226 | [ scaleFactor 0 0 ] |
paulb@256 | 227 | [ 0 scaleFactor 0 ] |
paulb@256 | 228 | [ 0 0 1 ] |
paulb@256 | 229 | """ |
paulb@256 | 230 | |
paulb@256 | 231 | return self.mMultiply(SVGMatrix(scaleFactor, 0, 0, scaleFactor, 0, 0)) |
paulb@256 | 232 | |
paulb@256 | 233 | def mRotate(self, angle): |
paulb@256 | 234 | |
paulb@256 | 235 | """ |
paulb@256 | 236 | [ cos(angle) -sin(angle) 0 ] |
paulb@256 | 237 | [ sin(angle) cos(angle) 0 ] |
paulb@256 | 238 | [ 0 0 1 ] |
paulb@256 | 239 | """ |
paulb@256 | 240 | |
paulb@256 | 241 | return self.mMultiply(SVGMatrix(math.cos(angle), math.sin(angle), -math.sin(angle), math.cos(angle), 0, 0)) |
paulb@256 | 242 | |
paulb@256 | 243 | class SVGPath: |
paulb@256 | 244 | |
paulb@256 | 245 | """ |
paulb@256 | 246 | A path. |
paulb@256 | 247 | See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__SVGPath |
paulb@256 | 248 | See: http://www.w3.org/TR/SVGMobile12/paths.html |
paulb@256 | 249 | """ |
paulb@256 | 250 | |
paulb@256 | 251 | MOVE_TO = 77 |
paulb@256 | 252 | LINE_TO = 76 |
paulb@256 | 253 | CURVE_TO = 67 |
paulb@256 | 254 | QUAD_TO = 81 |
paulb@256 | 255 | CLOSE = 90 |
paulb@256 | 256 | |
paulb@256 | 257 | def __init__(self): |
paulb@256 | 258 | self.segments = [] |
paulb@256 | 259 | |
paulb@256 | 260 | def _numberOfSegments(self): |
paulb@256 | 261 | return len(self.segments) |
paulb@256 | 262 | |
paulb@256 | 263 | numberOfSegments = property(_numberOfSegments) |
paulb@256 | 264 | |
paulb@256 | 265 | def getSegment(self, cmdIndex): |
paulb@256 | 266 | try: |
paulb@256 | 267 | return self.segments[cmdIndex][0] |
paulb@256 | 268 | except IndexError: |
paulb@256 | 269 | raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR) |
paulb@256 | 270 | |
paulb@256 | 271 | def getSegmentParam(self, cmdIndex, paramIndex): |
paulb@256 | 272 | try: |
paulb@256 | 273 | return self.segments[cmdIndex][1][paramIndex] |
paulb@256 | 274 | except IndexError: |
paulb@256 | 275 | raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR) |
paulb@256 | 276 | |
paulb@256 | 277 | def moveTo(self, x, y): |
paulb@256 | 278 | self.segments.append((self.MOVE_TO, (x, y))) |
paulb@256 | 279 | |
paulb@256 | 280 | def lineTo(self, x, y): |
paulb@256 | 281 | self.segments.append((self.LINE_TO, (x, y))) |
paulb@256 | 282 | |
paulb@256 | 283 | def quadTo(self, x1, y1, x2, y2): |
paulb@256 | 284 | self.segments.append((self.QUAD_TO, (x1, y1, x2, y2))) |
paulb@256 | 285 | |
paulb@256 | 286 | def curveTo(self, x1, y1, x2, y2, x3, y3): |
paulb@256 | 287 | self.segments.append((self.CURVE_TO, (x1, y1, x2, y2, x3, y3))) |
paulb@256 | 288 | |
paulb@256 | 289 | def close(self): |
paulb@256 | 290 | self.segments.append((self.CLOSE,)) |
paulb@256 | 291 | |
paulb@256 | 292 | class SVGPoint: |
paulb@256 | 293 | |
paulb@256 | 294 | "A point." |
paulb@256 | 295 | |
paulb@256 | 296 | def __init__(self, x, y): |
paulb@256 | 297 | self.x = x |
paulb@256 | 298 | self.y = y |
paulb@256 | 299 | |
paulb@256 | 300 | class SVGRect: |
paulb@256 | 301 | |
paulb@256 | 302 | "A rectangle." |
paulb@256 | 303 | |
paulb@256 | 304 | def __init__(self, x, y, width, height): |
paulb@256 | 305 | self.x, self.y, self.width, self.height = x, y, width, height |
paulb@256 | 306 | |
paulb@256 | 307 | class SVGRGBColor: |
paulb@256 | 308 | |
paulb@256 | 309 | "A colour." |
paulb@256 | 310 | |
paulb@256 | 311 | def __init__(self, red, green, blue): |
paulb@256 | 312 | self.red, self.green, self.blue = red, green, blue |
paulb@256 | 313 | |
paulb@256 | 314 | # Node classes. |
paulb@256 | 315 | |
paulb@256 | 316 | class SVGDocument(libxml2dom.Document, DocumentEvent, EventTarget): |
paulb@256 | 317 | |
paulb@256 | 318 | "An SVG-specific document node." |
paulb@256 | 319 | |
paulb@256 | 320 | def __init__(self, node, impl): |
paulb@256 | 321 | |
paulb@256 | 322 | """ |
paulb@256 | 323 | Initialise the document with the given 'node', implementation 'impl', |
paulb@256 | 324 | and global (SVGGlobal) details. |
paulb@256 | 325 | """ |
paulb@256 | 326 | |
paulb@256 | 327 | libxml2dom.Document.__init__(self, node, impl) |
paulb@256 | 328 | self.global_ = SVGGlobal(self) # parent |
paulb@256 | 329 | |
paulb@256 | 330 | class SVGElement(libxml2dom.Node, EventTarget): # (Element), TraitAccess, ElementTraversal |
paulb@235 | 331 | |
paulb@235 | 332 | "An SVG-specific element." |
paulb@235 | 333 | |
paulb@235 | 334 | def _id(self): |
paulb@235 | 335 | return self.getAttribute("id") |
paulb@235 | 336 | |
paulb@235 | 337 | def _setId(self, value): |
paulb@235 | 338 | self.setAttribute("id", value) |
paulb@235 | 339 | |
paulb@235 | 340 | id = property(_id, _setId) |
paulb@235 | 341 | |
paulb@235 | 342 | class SVGLocatableElement(SVGElement, SVGLocatable): |
paulb@235 | 343 | |
paulb@235 | 344 | "A locatable element." |
paulb@235 | 345 | |
paulb@256 | 346 | pass |
paulb@256 | 347 | |
paulb@256 | 348 | class SVGTimedElement(SVGElement): # smil::ElementTimeControl |
paulb@256 | 349 | |
paulb@256 | 350 | "A timed element." |
paulb@256 | 351 | |
paulb@256 | 352 | def __init__(self, *args): |
paulb@256 | 353 | |
paulb@256 | 354 | "Initialise the element with the underlying 'args'." |
paulb@256 | 355 | |
paulb@256 | 356 | SVGElement.__init__(self, *args) |
paulb@256 | 357 | self.document_time = 0 |
paulb@256 | 358 | self.paused = 0 |
paulb@256 | 359 | |
paulb@256 | 360 | def _isPaused(self): |
paulb@256 | 361 | return self.paused |
paulb@256 | 362 | |
paulb@256 | 363 | def pauseElement(self): |
paulb@256 | 364 | self.paused = 1 |
paulb@256 | 365 | |
paulb@256 | 366 | def resumeElement(self): |
paulb@256 | 367 | self.paused = 0 |
paulb@256 | 368 | |
paulb@256 | 369 | class SVGSVGElement(SVGLocatableElement, SVGTimedElement): |
paulb@218 | 370 | |
paulb@218 | 371 | "An SVG-specific top-level element." |
paulb@218 | 372 | |
paulb@218 | 373 | NAV_AUTO = 1 |
paulb@218 | 374 | NAV_NEXT = 2 |
paulb@218 | 375 | NAV_PREV = 3 |
paulb@218 | 376 | NAV_UP = 4 |
paulb@218 | 377 | NAV_UP_RIGHT = 5 |
paulb@218 | 378 | NAV_RIGHT = 6 |
paulb@218 | 379 | NAV_DOWN_RIGHT = 7 |
paulb@218 | 380 | NAV_DOWN = 8 |
paulb@218 | 381 | NAV_DOWN_LEFT = 9 |
paulb@218 | 382 | NAV_LEFT = 10 |
paulb@218 | 383 | NAV_UP_LEFT = 11 |
paulb@218 | 384 | |
paulb@256 | 385 | def __init__(self, *args): |
paulb@256 | 386 | |
paulb@256 | 387 | "Initialise the element with the underlying 'args'." |
paulb@256 | 388 | |
paulb@256 | 389 | SVGTimedElement.__init__(self, *args) |
paulb@256 | 390 | self.scale = 1 |
paulb@256 | 391 | self.rotate = 0 |
paulb@256 | 392 | self.translate = SVGPoint(0, 0) |
paulb@256 | 393 | |
paulb@256 | 394 | def _currentScale(self): |
paulb@256 | 395 | return self.scale |
paulb@256 | 396 | |
paulb@256 | 397 | def _currentRotate(self): |
paulb@256 | 398 | return self.rotate |
paulb@256 | 399 | |
paulb@256 | 400 | def _currentTranslate(self): |
paulb@256 | 401 | return self.translate |
paulb@256 | 402 | |
paulb@256 | 403 | def _viewport(self): |
paulb@256 | 404 | attr = self.getAttribute("viewBox") |
paulb@256 | 405 | if attr is None: |
paulb@256 | 406 | return attr |
paulb@256 | 407 | l = map(int, attr.split()) |
paulb@256 | 408 | return SVGRect(*l) |
paulb@256 | 409 | |
paulb@256 | 410 | def getCurrentTime(self): |
paulb@256 | 411 | return self.document_time |
paulb@235 | 412 | |
paulb@256 | 413 | def setCurrentTime(self, setCurrentTime): |
paulb@256 | 414 | self.document_time = setCurrentTime |
paulb@256 | 415 | |
paulb@256 | 416 | def createSVGMatrixComponents(self, a, b, c, d, e, f): |
paulb@256 | 417 | return SVGMatrix(a, b, c, d, e, f) |
paulb@256 | 418 | |
paulb@256 | 419 | def createSVGRect(self): |
paulb@256 | 420 | return SVGRect(0, 0, 0, 0) |
paulb@256 | 421 | |
paulb@256 | 422 | def createSVGPath(self): |
paulb@256 | 423 | return SVGPath() |
paulb@235 | 424 | |
paulb@256 | 425 | def createSVGRGBColor(self, red, green, blue): |
paulb@256 | 426 | return SVGRGBColor(red, green, blue) |
paulb@256 | 427 | |
paulb@256 | 428 | def moveFocus(self, motionType): |
paulb@256 | 429 | raise NotImplementedError, "moveFocus" |
paulb@256 | 430 | |
paulb@256 | 431 | def setFocus(self, object): |
paulb@256 | 432 | raise NotImplementedError, "setFocus" |
paulb@256 | 433 | |
paulb@256 | 434 | def getCurrentFocusedObject(self): |
paulb@256 | 435 | raise NotImplementedError, "getCurrentFocusedObject" |
paulb@256 | 436 | |
paulb@256 | 437 | currentScale = property(_currentScale) |
paulb@256 | 438 | currentRotate = property(_currentRotate) |
paulb@256 | 439 | currentTranslate = property(_currentTranslate) |
paulb@256 | 440 | viewport = property(_viewport) |
paulb@235 | 441 | |
paulb@235 | 442 | # Convenience functions. |
paulb@235 | 443 | |
paulb@246 | 444 | def parse(stream_or_string, html=0, htmlencoding=None): |
paulb@246 | 445 | return libxml2dom.parse(stream_or_string, html, htmlencoding, default_impl) |
paulb@218 | 446 | |
paulb@246 | 447 | def parseFile(filename, html=0, htmlencoding=None): |
paulb@246 | 448 | return libxml2dom.parseFile(filename, html, htmlencoding, default_impl) |
paulb@218 | 449 | |
paulb@246 | 450 | def parseString(s, html=0, htmlencoding=None): |
paulb@246 | 451 | return libxml2dom.parseString(s, html, htmlencoding, default_impl) |
paulb@218 | 452 | |
paulb@246 | 453 | def parseURI(uri, html=0, htmlencoding=None): |
paulb@246 | 454 | return libxml2dom.parseURI(uri, html, htmlencoding, default_impl) |
paulb@218 | 455 | |
paulb@218 | 456 | # Single instance of the implementation. |
paulb@218 | 457 | |
paulb@218 | 458 | default_impl = SVGImplementation() |
paulb@218 | 459 | |
paulb@218 | 460 | # vim: tabstop=4 expandtab shiftwidth=4 |