1"""SAX filter for MathML-to-SVG conversion."""
2
3import sys
4from xml import sax
5from mathnode import MathNode
6from mathconfig import MathConfig
7from nodelocator import NodeLocator
8
9# MathML namespace
10MathNS = u"http://www.w3.org/1998/Math/MathML"
11
12class MathHandler (sax.ContentHandler):
13    """SAX ContentHandler for converting MathML formulae to SVG.
14
15    Instances of this class read MathML through SAX callbacks, and write
16    SVG to the destination (specified as another SAX ContentHandler).
17    Uses namespace-aware SAX calls for both input and output."""
18
19    def __init__(self, saxoutput, config):
20        self.config = MathConfig(config)
21        self.output = saxoutput
22        self.skip = 0
23        self.currentNode = None
24        self.locator = None
25
26
27    def setDocumentLocator(self, locator):
28        self.locator = locator
29
30
31    def startDocument(self):
32        self.output.startDocument()
33
34
35    def endDocument(self):
36        self.output.endDocument()
37
38
39    def startElementNS(self, elementName, qName, attributes):
40        if self.skip > 0: self.skip += 1; return
41
42        locator = NodeLocator(self.locator)
43        (namespace, localName) = elementName
44        if namespace and namespace != MathNS:
45            if self.config.verbose:
46                locator.message("Skipped element '%s' from an unknown namespace '%s'" % (localName, namespace), "INFO")
47            self.skip = 1; return
48
49        properties = {}
50        for (attName, value) in attributes.items():
51            (attNamespace, attLocalName) = attName
52            if attNamespace and attNamespace != MathNS:
53                if self.config.verbose:
54                    locator.message("Ignored attribute '%s' from an unknown namespace '%s'" % (attLocalName, attNamespace), "INFO")
55                continue
56            properties[attLocalName] = value
57
58        self.currentNode = MathNode (localName, properties, locator, self.config, self.currentNode)
59
60
61    def endElementNS(self, elementName, qName):
62        if self.skip > 0:
63            self.skip -= 1
64            if self.skip > 0: return
65
66        (namespace, localname) = elementName
67        if namespace and namespace != MathNS:
68            raise sax.SAXParseException("SAX parser error: namespace on opening and closing elements don't match", None, self.locator)
69        if self.currentNode is None:
70            raise sax.SAXParseException("SAX parser error: unmatched closing tag", None, self.locator)
71
72        # Normalize text
73        self.currentNode.text = u' '.join(self.currentNode.text.split())
74
75        # If we're back to the top of the tree, measure and draw everything
76        if self.currentNode.parent is None:
77            self.currentNode.makeImage(self.output)
78
79        self.currentNode = self.currentNode.parent
80
81
82    def characters(self, content):
83        if self.skip > 0: return
84        if self.currentNode:
85            self.currentNode.text += content
86
87