1#! /usr/bin/env python
2
3"""Command-line tool to replace MathML with SVG throughout a document.
4
5Replaces all instances of MathML throughout the document"""
6
7import getopt, sys, os.path
8from xml import sax
9# from xml.sax.saxutils import XMLGenerator
10from svgmath.tools.saxtools import XMLGenerator, ContentFilter
11from svgmath.mathhandler import MathHandler, MathNS
12
13def open_or_die(fname, fmode, role):
14    try: return open(fname, fmode)
15    except IOError, xcpt:
16        print "Cannot open %s file '%s': %s" % (role, fname, str(xcpt))
17        sys.exit(1)
18
19def usage():
20    sys.stderr.write("""
21Usage: math2svg.py [options] FILE
22Replaces MathML formulae in a document by SVG images. Argument is a file name.
23
24Options:
25    -h, --help               display this synopsis and exit
26    -s, --standalone         treat input as a standalone MathML document
27    -o FILE, --output=FILE   write results to FILE instead of stdout
28    -c FILE, --config=FILE   read configuration from FILE
29    -e ENC,  --encoding=ENC  produce output in ENC encoding
30""")
31
32class MathFilter (ContentFilter):
33    def __init__(self, out, mathout):
34        ContentFilter.__init__(self, out)
35        self.plainOutput = out
36        self.mathOutput = mathout
37        self.depth = 0
38
39    # ContentHandler methods
40    def setDocumentLocator(self, locator):
41        self.plainOutput.setDocumentLocator(locator)
42        self.mathOutput.setDocumentLocator(locator)
43
44    def startElementNS(self, elementName, qName, attrs):
45        if self.depth == 0:
46            (namespace, localName) = elementName
47            if namespace == MathNS:
48                self.output = self.mathOutput
49                self.depth = 1
50        else: self.depth += 1
51        ContentFilter.startElementNS(self, elementName, qName, attrs)
52
53    def endElementNS(self, elementName, qName):
54        ContentFilter.endElementNS(self, elementName, qName)
55        if self.depth > 0:
56            self.depth -= 1
57            if self.depth == 0:
58                self.output = self.plainOutput
59
60def main():
61    try:
62        (opts, args) = getopt.getopt(sys.argv[1:], "c:e:ho:s", ["config=", "encoding=", "help", "output=", "standalone"])
63    except getopt.GetoptError:
64        usage(); sys.exit(2)
65
66    outputfile = None
67    configfile = None
68    encoding = 'utf-8'
69    standalone = False
70
71    for o, a in opts:
72        if o in ("-h", "--help"):       usage(); sys.exit(0)
73        if o in ("-o", "--output"):     outputfile = a
74        if o in ("-c", "--config"):     configfile = a
75        if o in ("-e", "--encoding"):   encoding = a
76        if o in ("-s", "--standalone"): standalone = True
77
78    # Check input
79    if len(args) < 1:
80        sys.stderr.write ("No input file specified!\n")
81        usage(); sys.exit(1)
82    elif len(args) > 1:
83        sys.stderr.write("WARNING: extra command line arguments ignored\n")
84
85    source = open_or_die(args[0], "rb", "input")
86
87    # Determine output destination
88    if outputfile is None:
89        output = sys.stdout
90    else:
91        output = open_or_die(outputfile, "wb", "output")
92
93    # Determine config file location
94    if configfile is None:
95        configfile = os.path.join(os.path.dirname(__file__), "svgmath.xml")
96    config = open_or_die(configfile, "rb", "configuration")
97
98    # Create the converter as a content handler.
99    saxoutput = XMLGenerator(output, encoding)
100    handler = MathHandler(saxoutput, config)
101    if not standalone:
102        handler = MathFilter(saxoutput, handler)
103
104    # Parse input file
105    exitcode = 0
106    try:
107        parser = sax.make_parser()
108        parser.setFeature(sax.handler.feature_namespaces, 1)
109        parser.setContentHandler(handler)
110        parser.parse(source)
111    except sax.SAXException, xcpt:
112        print "Error parsing input file %s: %s" % (args[0], xcpt.getMessage())
113        exitcode = 1
114    source.close()
115    if outputfile is not None:
116        output.close()
117    sys.exit(exitcode)
118
119if __name__ == "__main__":
120    main()