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()