# -*- coding: UTF-8 -*- # # Desc: This file is part of the eCromedos document preparation system # Date: 2006/03/09 # Author: Tobias Koch (tkoch@ecromedos.net) # License: GNU General Public License, version 2 # URL: http://www.ecromedos.net # # std includes import os, libxml2, StringIO, re # ecmds includes from error import ECMDSPluginError def getInstance(config): '''Returns a plugin instance.''' return Plugin(config) #end function class Plugin: def __init__(self, config): # init counter self.counter = 1 self.nodelist = [] # look for latex executable try: self.latex_bin = config['latex_bin'] except KeyError: msg = "Location of the 'latex' executable not specified." raise ECMDSPluginError(msg, "math") #end try if not os.path.isfile(self.latex_bin): msg = "Could not find latex executable '%s'." % (self.latex_bin,) raise ECMDSPluginError(msg, "math") #end if # look for conversion tool try: self.dvipng_bin = "" self.dvipng_bin = config['dvipng_bin'] except KeyError: msg = "Location of the 'dvipng' executable not specified." raise ECMDSPluginError(msg, "math") #end try if not os.path.isfile(self.dvipng_bin): msg = "Could not find 'dvipng' executable '%s'." % (self.dvipng_bin,) raise ECMDSPluginError(msg, "math") #end if # conversion dpi try: self.dvipng_dpi = config['dvipng_dpi'] except KeyError: self.dvipng_dpi = 100 #end try # output document self.out = StringIO.StringIO("") #end function def flush(self): '''If format was XHTML, generate GIFs from formulae.''' # generate bitmaps of formulae if self.out.tell() > 0: self.out.write("\\end{document}\n") self.__LaTeX2DVI2GIF() self.out.close() self.out = StringIO.StringIO() #end if #reset counter self.counter = 1 self.nodelist = [] #end function def process(self, node, format): '''Prepare @node for target @format.''' if format == "latex": result = self.LaTeX_ProcessMath(node) else: result = self.XHTML_ProcessMath(node) #end if return result #end function def LaTeX_ProcessMath(self, node): '''Mark node to be copied 1:1 to output document.''' # surround node content by "$"s string = node.getContent() if node.parent.name != "equation": string = "$" + string + "$" #end if newnode = libxml2.newNode("copy") newnode.setContent(string) node.replaceNode(newnode) node.freeNode() self.counter += 1 return newnode #end function def __LaTeX2DVI2GIF(self): '''Write formulae to LaTeX file, compile and extract images.''' # open tex file try: fp = file("math.tex", "wb+") except IOError: msg = "Could not open temporary file for writing." raise ECMDSPluginError(msg, "math") #end try # flush memory file try: fp.write(self.out.getvalue()) fp.close() except IOError: msg = "Error while writing temporary file." raise ECMDSPluginError(msg, "math") #end try # redirect latex messages try: logfp = file("math_plugin.log", "wb+") except Exception: logfp = sys.stdout # compile LaTeX file stdout = os.dup(1) os.dup2(logfp.fileno(), 1) args = ("-interaction", "nonstopmode", "math.tex") rval = os.spawnl(os.P_WAIT, self.latex_bin, self.latex_bin, *args) # LaTeX may need to be run twice if not rval: rval = os.spawnl(os.P_WAIT, self.latex_bin, self.latex_bin, *args) #end if os.dup2(stdout, 1) if logfp.fileno() != 1: logfp.close() # check exit code if rval != 0: msg = "Could not compile temporary TeX file." raise ECMDSPluginError(msg, "math") else: try: os.remove("math.tex") except OSError: pass try: os.remove("math.aux") except OSError: pass try: os.remove("math.log") except OSError: pass try: os.remove("math_plugin.log") except OSError: pass #end if # convert dvi file to GIF image rval = -1 args = ("-D", self.dvipng_dpi, "--depth", "-gif", "-T", "tight", "-o", "m%06d.gif", "math.dvi") # redirect dvipng messages try: logfp = file("math_plugin.log", "wb+") except Exception: logfp = sys.stdout stdout = os.dup(1) os.dup2(logfp.fileno(), 1) rval = os.spawnl(os.P_WAIT, self.dvipng_bin, self.dvipng_bin, *args) os.dup2(stdout, 1) if logfp.fileno() != 1: logfp.close() if rval != 0: msg = "Could not convert dvi file to GIF images." raise ECMDSPluginError(msg, "math") else: # align bitmaps with info from log before deleting it self.__alignImages() try: os.remove("math.dvi") except OSError: pass try: os.remove("math_plugin.log") except OSError: pass #end if #end function def __alignImages(self): '''Add style tag to each bitmap for correct baseline alignment.''' # read logfile try: try: fp = file("math_plugin.log", "rb") string = fp.read() finally: fp.close() #end try except Exception: return # look for [??? depth=???px] rexpr = re.compile("\\[[0-9]* depth=[0-9]*\\]") # add style property to node i = 0 for match in rexpr.finditer(string): align = match.group().split("=")[1].strip(" []") node = self.nodelist[i] node.newProp("style", "vertical-align: -" + align + "px;") i += 1 #end for #end function def XHTML_ProcessMath(self, node): '''Call LaTeX and ImageMagick to produce a GIF.''' if self.out.tell() == 0: # write latex preamble self.out.write("\\documentclass[12pt]{scrartcl}\n") self.out.write("\\usepackage{courier}\n") self.out.write("\\usepackage{helvet}\n") self.out.write("\\usepackage{mathpazo}\n") self.out.write("\\usepackage{amsmath}\n") self.out.write("\\usepackage[active,displaymath,textmath]{preview}\n") self.out.write("\\frenchspacing{}\n") self.out.write("\\usepackage{ucs}\n") self.out.write("\\usepackage[utf8x]{inputenc}\n") self.out.write("\\usepackage[T1]{autofe}\n") self.out.write("\\PrerenderUnicode{äöüß}\n") self.out.write("\\pagestyle{empty}\n") self.out.write("\\begin{document}\n") #end if self.out.write("$%s$\n\\clearpage{}\n" % (node.getContent(),)) # replace node copy_node = libxml2.newNode("copy") img_node = libxml2.newNode("img") img_node.newProp("src", "m%06d.gif" % (self.counter,)) img_node.newProp("alt", "") copy_node.addChild(img_node) node.replaceNode(copy_node) node.freeNode() self.nodelist.append(img_node) self.counter += 1 return copy_node #end function #end class