1.1 --- a/libxml2dom/svg.py Sun Mar 25 01:57:53 2007 +0000
1.2 +++ b/libxml2dom/svg.py Sun Mar 25 01:59:19 2007 +0000
1.3 @@ -1,8 +1,9 @@
1.4 #!/usr/bin/env python
1.5
1.6 """
1.7 -SVG-specific document support. See:
1.8 -http://www.w3.org/TR/SVGMobile12/python-binding.html
1.9 +SVG-specific document support.
1.10 +See: http://www.w3.org/TR/SVGMobile12/python-binding.html
1.11 +See: http://www.w3.org/TR/SVGMobile12/svgudom.html
1.12
1.13 Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk>
1.14
1.15 @@ -22,16 +23,38 @@
1.16 """
1.17
1.18 import libxml2dom
1.19 +from libxml2dom.events import *
1.20 from libxml2dom.macrolib import *
1.21 -from libxml2dom.macrolib import \
1.22 - createDocument as Node_createDocument, \
1.23 - parseString as Node_parseString, parseURI as Node_parseURI, \
1.24 - parseFile as Node_parseFile, \
1.25 - toString as Node_toString, toStream as Node_toStream, \
1.26 - toFile as Node_toFile
1.27 +import xml.dom
1.28 +import math
1.29
1.30 SVG_NAMESPACE = "http://www.w3.org/2000/svg"
1.31
1.32 +class _Exception(Exception):
1.33 +
1.34 + "A generic SVG exception."
1.35 +
1.36 + def __init__(self, code):
1.37 + Exception.__init__(self, code)
1.38 + self.code = code
1.39 +
1.40 +class SVGException(_Exception):
1.41 +
1.42 + "An SVG exception."
1.43 +
1.44 + SVG_WRONG_TYPE_ERR = 0
1.45 + SVG_INVALID_VALUE_ERR = 1
1.46 + SVG_MATRIX_NOT_INVERTABLE = 2
1.47 +
1.48 +class GlobalException(_Exception):
1.49 +
1.50 + "A global exception."
1.51 +
1.52 + NOT_CONNECTED_ERR = 1
1.53 + ENCODING_ERR = 2
1.54 + DENIED_ERR = 3
1.55 + UNKNOWN_ERR = 4
1.56 +
1.57 class SVGImplementation(libxml2dom.Implementation):
1.58
1.59 "Contains an SVG-specific implementation."
1.60 @@ -54,7 +77,257 @@
1.61 else:
1.62 return libxml2dom.Implementation.get_node(self, _node, context_node)
1.63
1.64 -class SVGElement(libxml2dom.Node): # Element, EventTarget, TraitAccess, ElementTraversal
1.65 +# Interfaces and helper classes.
1.66 +
1.67 +class AsyncStatusCallback:
1.68 +
1.69 + "An asynchronous callback interface."
1.70 +
1.71 + def operationComplete(self, status):
1.72 + pass
1.73 +
1.74 +class AsyncURLStatus:
1.75 +
1.76 + "The status of a URL retrieval operation."
1.77 +
1.78 + def __init__(self, success, contentType, content):
1.79 + self.success, self.contentType, self.content = success, contentType, content
1.80 +
1.81 +class SVGGlobal: # Global, EventListenerInitializer2
1.82 +
1.83 + "An SVG global."
1.84 +
1.85 + def __init__(self, document): # parent
1.86 +
1.87 + "Initialise the global with the given 'document'."
1.88 +
1.89 + self.document = document
1.90 +
1.91 + def createConnection(self):
1.92 + raise NotImplementedError, "createConnection"
1.93 +
1.94 + def createTimer(self, initialInterval, repeatInterval):
1.95 + raise NotImplementedError, "createTimer"
1.96 +
1.97 + def gotoLocation(self, newIRI):
1.98 + raise NotImplementedError, "gotoLocation"
1.99 +
1.100 + def binaryToString(self, octets, encoding):
1.101 + raise NotImplementedError, "binaryToString"
1.102 +
1.103 + def stringToBinary(self, data, encoding):
1.104 + raise NotImplementedError, "stringToBinary"
1.105 +
1.106 + def getURL(self, iri, callback):
1.107 +
1.108 + # NOTE: Not asynchronous.
1.109 + # NOTE: The urlopen function may not support IRIs.
1.110 + # No exceptions are supposed to be raised, which is a bit nasty.
1.111 +
1.112 + f = urllib.urlopen(iri)
1.113 + try:
1.114 + try:
1.115 + content = f.read()
1.116 + contentType = f.headers["Content-Type"]
1.117 + callback.operationComplete(AsyncURLStatus(1, contentType, content))
1.118 + except:
1.119 + callback.operationComplete(AsyncURLStatus(0, None, None))
1.120 + finally:
1.121 + f.close()
1.122 +
1.123 + def postURL(self, iri, data, callback, type, encoding):
1.124 + raise NotImplementedError, "postURL"
1.125 +
1.126 + def parseXML(self, data, contextDoc):
1.127 + doc = parseString(data)
1.128 + return contextDoc.importNode(doc.documentElement, 1)
1.129 +
1.130 +class SVGLocatable:
1.131 +
1.132 + "A locatable interface."
1.133 +
1.134 + pass
1.135 +
1.136 +class SVGMatrix:
1.137 +
1.138 + """
1.139 + A matrix.
1.140 + See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__SVGMatrix
1.141 + """
1.142 +
1.143 + def __init__(self, a, b, c, d, e, f):
1.144 + self.matrix = a, b, c, d, e, f
1.145 +
1.146 + def getComponent(self, index):
1.147 +
1.148 + """
1.149 + Return the component with the given 'index' (starting at zero) from the
1.150 + sequence a, b, c, d, e, f where each element corresponds to the matrix
1.151 + as follows:
1.152 +
1.153 + [ a c e ]
1.154 + [ b d f ]
1.155 + [ 0 0 1 ]
1.156 + """
1.157 +
1.158 + try:
1.159 + return self.matrix[index]
1.160 + except IndexError:
1.161 + raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR)
1.162 +
1.163 + def mMultiply(self, secondMatrix):
1.164 +
1.165 + """
1.166 + Multiply this matrix with 'secondMatrix' and update its contents to the
1.167 + result of the multiplication operation defined as follows:
1.168 +
1.169 + [ a c e ] [ A C E ]
1.170 + [ b d f ] [ B D F ]
1.171 + [ 0 0 1 ] [ 0 0 1 ]
1.172 +
1.173 + Return this object as a result.
1.174 + """
1.175 +
1.176 + a, b, c, d, e, f = self.matrix
1.177 + A, B, C, D, E, F = secondMatrix.matrix
1.178 + 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
1.179 + return self
1.180 +
1.181 + def inverse(self):
1.182 +
1.183 + """
1.184 + det = ad - cb
1.185 +
1.186 + See (for example): http://mathworld.wolfram.com/MatrixInverse.html
1.187 + """
1.188 +
1.189 + det = a*d - c*b
1.190 + if det != 0:
1.191 + m = 1/det
1.192 + a, b, c, d, e, f = self.matrix
1.193 + self.matrix = m * d, m * -b, m * -c, m * a, m * (c*f - e*d), m * (e*b - a*f)
1.194 + return self
1.195 + else:
1.196 + raise SVGException(SVGException.SVG_MATRIX_NOT_INVERTABLE)
1.197 +
1.198 + def mTranslate(self, x, y):
1.199 +
1.200 + """
1.201 + [ 1 0 x ]
1.202 + [ 0 1 y ]
1.203 + [ 0 0 1 ]
1.204 + """
1.205 +
1.206 + return self.mMultiply(SVGMatrix(1, 0, 0, 1, x, y))
1.207 +
1.208 + def mScale(self, scaleFactor):
1.209 +
1.210 + """
1.211 + [ scaleFactor 0 0 ]
1.212 + [ 0 scaleFactor 0 ]
1.213 + [ 0 0 1 ]
1.214 + """
1.215 +
1.216 + return self.mMultiply(SVGMatrix(scaleFactor, 0, 0, scaleFactor, 0, 0))
1.217 +
1.218 + def mRotate(self, angle):
1.219 +
1.220 + """
1.221 + [ cos(angle) -sin(angle) 0 ]
1.222 + [ sin(angle) cos(angle) 0 ]
1.223 + [ 0 0 1 ]
1.224 + """
1.225 +
1.226 + return self.mMultiply(SVGMatrix(math.cos(angle), math.sin(angle), -math.sin(angle), math.cos(angle), 0, 0))
1.227 +
1.228 +class SVGPath:
1.229 +
1.230 + """
1.231 + A path.
1.232 + See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__SVGPath
1.233 + See: http://www.w3.org/TR/SVGMobile12/paths.html
1.234 + """
1.235 +
1.236 + MOVE_TO = 77
1.237 + LINE_TO = 76
1.238 + CURVE_TO = 67
1.239 + QUAD_TO = 81
1.240 + CLOSE = 90
1.241 +
1.242 + def __init__(self):
1.243 + self.segments = []
1.244 +
1.245 + def _numberOfSegments(self):
1.246 + return len(self.segments)
1.247 +
1.248 + numberOfSegments = property(_numberOfSegments)
1.249 +
1.250 + def getSegment(self, cmdIndex):
1.251 + try:
1.252 + return self.segments[cmdIndex][0]
1.253 + except IndexError:
1.254 + raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR)
1.255 +
1.256 + def getSegmentParam(self, cmdIndex, paramIndex):
1.257 + try:
1.258 + return self.segments[cmdIndex][1][paramIndex]
1.259 + except IndexError:
1.260 + raise xml.dom.DOMException(xml.dom.INDEX_SIZE_ERR)
1.261 +
1.262 + def moveTo(self, x, y):
1.263 + self.segments.append((self.MOVE_TO, (x, y)))
1.264 +
1.265 + def lineTo(self, x, y):
1.266 + self.segments.append((self.LINE_TO, (x, y)))
1.267 +
1.268 + def quadTo(self, x1, y1, x2, y2):
1.269 + self.segments.append((self.QUAD_TO, (x1, y1, x2, y2)))
1.270 +
1.271 + def curveTo(self, x1, y1, x2, y2, x3, y3):
1.272 + self.segments.append((self.CURVE_TO, (x1, y1, x2, y2, x3, y3)))
1.273 +
1.274 + def close(self):
1.275 + self.segments.append((self.CLOSE,))
1.276 +
1.277 +class SVGPoint:
1.278 +
1.279 + "A point."
1.280 +
1.281 + def __init__(self, x, y):
1.282 + self.x = x
1.283 + self.y = y
1.284 +
1.285 +class SVGRect:
1.286 +
1.287 + "A rectangle."
1.288 +
1.289 + def __init__(self, x, y, width, height):
1.290 + self.x, self.y, self.width, self.height = x, y, width, height
1.291 +
1.292 +class SVGRGBColor:
1.293 +
1.294 + "A colour."
1.295 +
1.296 + def __init__(self, red, green, blue):
1.297 + self.red, self.green, self.blue = red, green, blue
1.298 +
1.299 +# Node classes.
1.300 +
1.301 +class SVGDocument(libxml2dom.Document, DocumentEvent, EventTarget):
1.302 +
1.303 + "An SVG-specific document node."
1.304 +
1.305 + def __init__(self, node, impl):
1.306 +
1.307 + """
1.308 + Initialise the document with the given 'node', implementation 'impl',
1.309 + and global (SVGGlobal) details.
1.310 + """
1.311 +
1.312 + libxml2dom.Document.__init__(self, node, impl)
1.313 + self.global_ = SVGGlobal(self) # parent
1.314 +
1.315 +class SVGElement(libxml2dom.Node, EventTarget): # (Element), TraitAccess, ElementTraversal
1.316
1.317 "An SVG-specific element."
1.318
1.319 @@ -66,15 +339,34 @@
1.320
1.321 id = property(_id, _setId)
1.322
1.323 -class SVGLocatable:
1.324 -
1.325 - "A locatable interface."
1.326 -
1.327 class SVGLocatableElement(SVGElement, SVGLocatable):
1.328
1.329 "A locatable element."
1.330
1.331 -class SVGSVGElement(SVGLocatableElement): # SVGTimedElement
1.332 + pass
1.333 +
1.334 +class SVGTimedElement(SVGElement): # smil::ElementTimeControl
1.335 +
1.336 + "A timed element."
1.337 +
1.338 + def __init__(self, *args):
1.339 +
1.340 + "Initialise the element with the underlying 'args'."
1.341 +
1.342 + SVGElement.__init__(self, *args)
1.343 + self.document_time = 0
1.344 + self.paused = 0
1.345 +
1.346 + def _isPaused(self):
1.347 + return self.paused
1.348 +
1.349 + def pauseElement(self):
1.350 + self.paused = 1
1.351 +
1.352 + def resumeElement(self):
1.353 + self.paused = 0
1.354 +
1.355 +class SVGSVGElement(SVGLocatableElement, SVGTimedElement):
1.356
1.357 "An SVG-specific top-level element."
1.358
1.359 @@ -90,11 +382,62 @@
1.360 NAV_LEFT = 10
1.361 NAV_UP_LEFT = 11
1.362
1.363 -class SVGDocument(libxml2dom.Document):
1.364 + def __init__(self, *args):
1.365 +
1.366 + "Initialise the element with the underlying 'args'."
1.367 +
1.368 + SVGTimedElement.__init__(self, *args)
1.369 + self.scale = 1
1.370 + self.rotate = 0
1.371 + self.translate = SVGPoint(0, 0)
1.372 +
1.373 + def _currentScale(self):
1.374 + return self.scale
1.375 +
1.376 + def _currentRotate(self):
1.377 + return self.rotate
1.378 +
1.379 + def _currentTranslate(self):
1.380 + return self.translate
1.381 +
1.382 + def _viewport(self):
1.383 + attr = self.getAttribute("viewBox")
1.384 + if attr is None:
1.385 + return attr
1.386 + l = map(int, attr.split())
1.387 + return SVGRect(*l)
1.388 +
1.389 + def getCurrentTime(self):
1.390 + return self.document_time
1.391
1.392 - "An SVG-specific document node."
1.393 + def setCurrentTime(self, setCurrentTime):
1.394 + self.document_time = setCurrentTime
1.395 +
1.396 + def createSVGMatrixComponents(self, a, b, c, d, e, f):
1.397 + return SVGMatrix(a, b, c, d, e, f)
1.398 +
1.399 + def createSVGRect(self):
1.400 + return SVGRect(0, 0, 0, 0)
1.401 +
1.402 + def createSVGPath(self):
1.403 + return SVGPath()
1.404
1.405 - # NOTE: Define global (SVGGlobal).
1.406 + def createSVGRGBColor(self, red, green, blue):
1.407 + return SVGRGBColor(red, green, blue)
1.408 +
1.409 + def moveFocus(self, motionType):
1.410 + raise NotImplementedError, "moveFocus"
1.411 +
1.412 + def setFocus(self, object):
1.413 + raise NotImplementedError, "setFocus"
1.414 +
1.415 + def getCurrentFocusedObject(self):
1.416 + raise NotImplementedError, "getCurrentFocusedObject"
1.417 +
1.418 + currentScale = property(_currentScale)
1.419 + currentRotate = property(_currentRotate)
1.420 + currentTranslate = property(_currentTranslate)
1.421 + viewport = property(_viewport)
1.422
1.423 # Convenience functions.
1.424