2.1 --- a/libxml2dom/svg.py Sun Mar 25 01:59:26 2007 +0000
2.2 +++ b/libxml2dom/svg.py Sun Mar 25 22:55:35 2007 +0000
2.3 @@ -25,8 +25,11 @@
2.4 import libxml2dom
2.5 from libxml2dom.events import *
2.6 from libxml2dom.macrolib import *
2.7 +from libxml2dom.macrolib import \
2.8 + createDocument as Node_createDocument
2.9 import xml.dom
2.10 import math
2.11 +import re
2.12
2.13 SVG_NAMESPACE = "http://www.w3.org/2000/svg"
2.14
2.15 @@ -77,6 +80,14 @@
2.16 else:
2.17 return libxml2dom.Implementation.get_node(self, _node, context_node)
2.18
2.19 + # Convenience functions.
2.20 +
2.21 + def createSVGDocument(self):
2.22 +
2.23 + "Create a new SVG document."
2.24 +
2.25 + return SVGDocument(Node_createDocument(SVG_NAMESPACE, "svg", None), self)
2.26 +
2.27 # Interfaces and helper classes.
2.28
2.29 class AsyncStatusCallback:
2.30 @@ -93,6 +104,43 @@
2.31 def __init__(self, success, contentType, content):
2.32 self.success, self.contentType, self.content = success, contentType, content
2.33
2.34 +class ElementTraversal:
2.35 +
2.36 + "An interface for element traversal."
2.37 +
2.38 + def _firstElementChild(self):
2.39 + l = self.xpath("*")
2.40 + if l:
2.41 + return l[0]
2.42 + else:
2.43 + return None
2.44 +
2.45 + def _lastElementChild(self):
2.46 + l = self.xpath("*")
2.47 + if l:
2.48 + return l[-1]
2.49 + else:
2.50 + return None
2.51 +
2.52 + def _nextElementSibling(self):
2.53 + l = self.xpath("following-sibling::*")
2.54 + if l:
2.55 + return l[0]
2.56 + else:
2.57 + return None
2.58 +
2.59 + def _previousElementSibling(self):
2.60 + l = self.xpath("preceding-sibling::*")
2.61 + if l:
2.62 + return l[0]
2.63 + else:
2.64 + return None
2.65 +
2.66 + firstElementChild = property(_firstElementChild)
2.67 + lastElementChild = property(_lastElementChild)
2.68 + nextElementSibling = property(_nextElementSibling)
2.69 + previousElementSibling = property(_previousElementSibling)
2.70 +
2.71 class SVGGlobal: # Global, EventListenerInitializer2
2.72
2.73 "An SVG global."
2.74 @@ -155,9 +203,134 @@
2.75 See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__SVGMatrix
2.76 """
2.77
2.78 - def __init__(self, a, b, c, d, e, f):
2.79 + translate_regexp = re.compile("translate\((.*)\)$")
2.80 + scale_regexp = re.compile("scale\((.*)\)$")
2.81 + rotate_regexp = re.compile("rotate\((.*)\)$")
2.82 + skewX_regexp = re.compile("skewX\((.*)\)$")
2.83 + skewY_regexp = re.compile("skewY\((.*)\)$")
2.84 + matrix_regexp = re.compile("matrix\((.*)\)$")
2.85 +
2.86 + def __init__(self, a=0, b=0, c=0, d=0, e=0, f=0):
2.87 self.matrix = a, b, c, d, e, f
2.88
2.89 + def __eq__(self, other):
2.90 + return self.matrix == other.matrix
2.91 +
2.92 + def __ne__(self, other):
2.93 + return not (self == other)
2.94 +
2.95 + def _get_params(self, param_string):
2.96 + return map(float, map(lambda s: s.strip(), param_string.split(",")))
2.97 +
2.98 + def fromNode(self, node, name):
2.99 +
2.100 + """
2.101 + Initialise this object from the trait on the 'node' having the given
2.102 + 'name'.
2.103 + """
2.104 +
2.105 + value = node.getAttribute(name)
2.106 + if value is None:
2.107 + raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR)
2.108 +
2.109 + value = value.strip()
2.110 +
2.111 + # Translation.
2.112 +
2.113 + m = self.translate_regexp.match(value)
2.114 + if m:
2.115 + a, b, c, d = 1, 0, 0, 1
2.116 + e, f = self._get_params(m.group(1))
2.117 + self.matrix = a, b, c, d, e, f
2.118 + return
2.119 +
2.120 + # Scaling.
2.121 +
2.122 + m = self.scale_regexp.match(value)
2.123 + if m:
2.124 + b, c, e, f = 0, 0, 0, 0
2.125 + a, d = self._get_params(m.group(1))
2.126 + self.matrix = a, b, c, d, e, f
2.127 + return
2.128 +
2.129 + # Rotation.
2.130 +
2.131 + m = self.rotate_regexp.match(value)
2.132 + if m:
2.133 + e, f = 0, 0
2.134 + angle = float(m.group(1).strip())
2.135 + a = d = math.cos(math.radians(angle))
2.136 + b = math.sin(math.radians(angle))
2.137 + c = -b
2.138 + self.matrix = a, b, c, d, e, f
2.139 + return
2.140 +
2.141 + # Skew.
2.142 +
2.143 + m = self.skewX_regexp.match(value)
2.144 + if m:
2.145 + a, b, d, e, f = 1, 0, 1, 0, 0
2.146 + angle = float(m.group(1).strip())
2.147 + c = math.tan(math.radians(angle))
2.148 + self.matrix = a, b, c, d, e, f
2.149 + return
2.150 +
2.151 + m = self.skewY_regexp.match(value)
2.152 + if m:
2.153 + a, c, d, e, f = 1, 0, 1, 0, 0
2.154 + angle = float(m.group(1).strip())
2.155 + b = math.tan(math.radians(angle))
2.156 + self.matrix = a, b, c, d, e, f
2.157 + return
2.158 +
2.159 + # Generic.
2.160 +
2.161 + m = self.matrix_regexp.match(value)
2.162 + if m:
2.163 + self.matrix = self._get_params(m.group(1))
2.164 + return
2.165 +
2.166 + # Otherwise, complain.
2.167 +
2.168 + raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR)
2.169 +
2.170 + def toNode(self, node, name):
2.171 +
2.172 + """
2.173 + Set the trait on the given 'node' using the given 'name' according to
2.174 + this object's attributes.
2.175 + """
2.176 +
2.177 + a, b, c, d, e, f = self.matrix
2.178 +
2.179 + # Translation.
2.180 +
2.181 + if (a, b, c, d) == (1, 0, 0, 1):
2.182 + node.setAttribute(name, "translate(%f, %f)" % (e, f))
2.183 +
2.184 + # Scaling.
2.185 +
2.186 + elif (b, c, e, f) == (0, 0, 0, 0):
2.187 + node.setAttribute(name, "scale(%f, %f)" % (a, d))
2.188 +
2.189 + # Rotation.
2.190 +
2.191 + elif a == d and b == -c and (e, f) == (0, 0) and math.degrees(math.acos(a)) == math.degrees(math.asin(b)):
2.192 + node.setAttribute(name, "rotate(%f)" % math.degrees(math.acos(a)))
2.193 +
2.194 + # Skew.
2.195 +
2.196 + elif (a, b, d, e, f) == (1, 0, 1, 0, 0) and c != 0:
2.197 + node.setAttribute(name, "skewX(%f)" % math.degrees(math.atan(c)))
2.198 +
2.199 + elif (a, c, d, e, f) == (1, 0, 1, 0, 0) and b != 0:
2.200 + node.setAttribute(name, "skewX(%f)" % math.degrees(math.atan(b)))
2.201 +
2.202 + # Generic matrix.
2.203 +
2.204 + else:
2.205 + node.setAttribute(name, "matrix(%f, %f, %f, %f, %f, %f)" % (a, b, c, d, e, f))
2.206 +
2.207 def getComponent(self, index):
2.208
2.209 """
2.210 @@ -238,7 +411,15 @@
2.211 [ 0 0 1 ]
2.212 """
2.213
2.214 - return self.mMultiply(SVGMatrix(math.cos(angle), math.sin(angle), -math.sin(angle), math.cos(angle), 0, 0))
2.215 + return self.mMultiply(
2.216 + SVGMatrix(
2.217 + math.cos(math.radians(angle)),
2.218 + math.sin(math.radians(angle)),
2.219 + -math.sin(math.radians(angle)),
2.220 + math.cos(math.radians(angle)),
2.221 + 0, 0
2.222 + )
2.223 + )
2.224
2.225 class SVGPath:
2.226
2.227 @@ -253,10 +434,74 @@
2.228 CURVE_TO = 67
2.229 QUAD_TO = 81
2.230 CLOSE = 90
2.231 + _CLOSE = 122 # More baggage (name not standard).
2.232 +
2.233 + nparams = {
2.234 + MOVE_TO : 2,
2.235 + LINE_TO : 2,
2.236 + CURVE_TO : 6,
2.237 + QUAD_TO : 4,
2.238 + CLOSE : 0,
2.239 + _CLOSE : 0
2.240 + }
2.241
2.242 def __init__(self):
2.243 self.segments = []
2.244
2.245 + def __eq__(self, other):
2.246 + return self.segments == other.segments
2.247 +
2.248 + def __ne__(self, other):
2.249 + return not (self == other)
2.250 +
2.251 + def fromNode(self, node, name):
2.252 +
2.253 + """
2.254 + Initialise this object from the trait on the 'node' having the given
2.255 + 'name'.
2.256 + """
2.257 +
2.258 + value = node.getAttribute(name)
2.259 + if value is None:
2.260 + raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR)
2.261 +
2.262 + # Try and unpack the attribute value.
2.263 +
2.264 + data = value.split()
2.265 + self.segments = []
2.266 + try:
2.267 + i = 0
2.268 + while i < len(data):
2.269 + cmd = ord(data[i])
2.270 + if cmd == self._CLOSE:
2.271 + cmd = self.CLOSE
2.272 + i += 1
2.273 + n = self.nparams[cmd]
2.274 + params = map(float, data[i:i+n])
2.275 + self.segments.append((cmd, params))
2.276 + i += n
2.277 + except (IndexError, ValueError):
2.278 + raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR)
2.279 +
2.280 + def toNode(self, node, name):
2.281 +
2.282 + """
2.283 + Set the trait on the given 'node' using the given 'name' according to
2.284 + this object's attributes.
2.285 + """
2.286 +
2.287 + try:
2.288 + l = []
2.289 + for cmd, params in self.segments:
2.290 + l.append(unichr(cmd))
2.291 + for param in params:
2.292 + l.append(str(param))
2.293 + node.setAttribute(name, " ".join(l))
2.294 + except (IndexError, ValueError):
2.295 + raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR)
2.296 +
2.297 + # Interface methods.
2.298 +
2.299 def _numberOfSegments(self):
2.300 return len(self.segments)
2.301
2.302 @@ -291,7 +536,7 @@
2.303
2.304 class SVGPoint:
2.305
2.306 - "A point."
2.307 + "A point used to provide currentTranslate."
2.308
2.309 def __init__(self, x, y):
2.310 self.x = x
2.311 @@ -301,9 +546,44 @@
2.312
2.313 "A rectangle."
2.314
2.315 - def __init__(self, x, y, width, height):
2.316 + def __init__(self, x=0, y=0, width=0, height=0):
2.317 self.x, self.y, self.width, self.height = x, y, width, height
2.318
2.319 + def __eq__(self, other):
2.320 + return (self.x, self.y, self.width, self.height) == (other.x, other.y, other.width, other.height)
2.321 +
2.322 + def __ne__(self, other):
2.323 + return not (self == other)
2.324 +
2.325 + def fromNode(self, node, name):
2.326 +
2.327 + """
2.328 + Initialise this object from the trait on the 'node' having the given
2.329 + 'name'.
2.330 + """
2.331 +
2.332 + value = node.getAttribute(name)
2.333 + if value is None:
2.334 + raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR)
2.335 + try:
2.336 + values = map(float, value.split())
2.337 + self.x, self.y, self.width, self.height = values
2.338 + except (IndexError, ValueError):
2.339 + raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR)
2.340 +
2.341 + def toNode(self, node, name):
2.342 +
2.343 + """
2.344 + Set the trait on the given 'node' using the given 'name' according to
2.345 + this object's attributes.
2.346 + """
2.347 +
2.348 + try:
2.349 + values = map(str, [self.x, self.y, self.width, self.height])
2.350 + node.setAttribute(name, " ".join(values))
2.351 + except (IndexError, ValueError):
2.352 + raise xml.dom.DOMException(xml.dom.TYPE_MISMATCH_ERR)
2.353 +
2.354 class SVGRGBColor:
2.355
2.356 "A colour."
2.357 @@ -311,9 +591,57 @@
2.358 def __init__(self, red, green, blue):
2.359 self.red, self.green, self.blue = red, green, blue
2.360
2.361 +class TraitAccess:
2.362 +
2.363 + """
2.364 + Access to traits stored on elements.
2.365 + See: http://www.w3.org/TR/SVGMobile12/svgudom.html#svg__TraitAccess
2.366 + """
2.367 +
2.368 + def getPathTrait(self, name):
2.369 + path = SVGPath()
2.370 + path.fromNode(self, name)
2.371 + return path
2.372 +
2.373 + def setPathTrait(self, name, path):
2.374 + path.toNode(self, name)
2.375 +
2.376 + def getRectTrait(self, name):
2.377 + rect = SVGRect()
2.378 + rect.fromNode(self, name)
2.379 + return rect
2.380 +
2.381 + def setRectTrait(self, name, rect):
2.382 + rect.toNode(self, name)
2.383 +
2.384 + def getMatrixTrait(self, name):
2.385 + matrix = SVGMatrix()
2.386 + matrix.fromNode(self, name)
2.387 + return matrix
2.388 +
2.389 + def setMatrixTrait(self, name, matrix):
2.390 + matrix.toNode(self, name)
2.391 +
2.392 # Node classes.
2.393
2.394 -class SVGDocument(libxml2dom.Document, DocumentEvent, EventTarget):
2.395 +class SVGNode(libxml2dom.Node):
2.396 +
2.397 + "Convenience modifications to nodes specific to libxml2dom.svg."
2.398 +
2.399 + def xpath(self, expr, variables=None, namespaces=None):
2.400 +
2.401 + """
2.402 + Evaluate the given 'expr' using the optional 'variables' and
2.403 + 'namespaces'. If not otherwise specified, the "svg" prefix will be bound
2.404 + to SVG_NAMESPACE as defined in this module.
2.405 + """
2.406 +
2.407 + namespaces = namespaces or {}
2.408 + if not namespaces.has_key("svg"):
2.409 + namespaces["svg"] = SVG_NAMESPACE
2.410 + return libxml2dom.Node.xpath(self, expr, variables, namespaces)
2.411 +
2.412 +class SVGDocument(libxml2dom._Document, SVGNode, DocumentEvent, EventTarget):
2.413
2.414 "An SVG-specific document node."
2.415
2.416 @@ -324,10 +652,10 @@
2.417 and global (SVGGlobal) details.
2.418 """
2.419
2.420 - libxml2dom.Document.__init__(self, node, impl)
2.421 + libxml2dom._Document.__init__(self, node, impl)
2.422 self.global_ = SVGGlobal(self) # parent
2.423
2.424 -class SVGElement(libxml2dom.Node, EventTarget): # (Element), TraitAccess, ElementTraversal
2.425 +class SVGElement(SVGNode, EventTarget, TraitAccess, ElementTraversal): # NOTE: SVGNode instead of Element.
2.426
2.427 "An SVG-specific element."
2.428
2.429 @@ -401,11 +729,7 @@
2.430 return self.translate
2.431
2.432 def _viewport(self):
2.433 - attr = self.getAttribute("viewBox")
2.434 - if attr is None:
2.435 - return attr
2.436 - l = map(int, attr.split())
2.437 - return SVGRect(*l)
2.438 + return self.getRectTrait("viewBox")
2.439
2.440 def getCurrentTime(self):
2.441 return self.document_time
2.442 @@ -417,7 +741,7 @@
2.443 return SVGMatrix(a, b, c, d, e, f)
2.444
2.445 def createSVGRect(self):
2.446 - return SVGRect(0, 0, 0, 0)
2.447 + return SVGRect()
2.448
2.449 def createSVGPath(self):
2.450 return SVGPath()
2.451 @@ -439,7 +763,13 @@
2.452 currentTranslate = property(_currentTranslate)
2.453 viewport = property(_viewport)
2.454
2.455 -# Convenience functions.
2.456 +# Utility functions.
2.457 +
2.458 +createDocument = libxml2dom.createDocument
2.459 +createDocumentType = libxml2dom.createDocumentType
2.460 +
2.461 +def createSVGDocument():
2.462 + return default_impl.createSVGDocument()
2.463
2.464 def parse(stream_or_string, html=0, htmlencoding=None):
2.465 return libxml2dom.parse(stream_or_string, html, htmlencoding, default_impl)