#!/usr/bin/python # -*- coding: iso-8859-15 -*- """ New latex formatter using dvipng and tempfile Author: JohannesBerg This parser (and the corresponding macro) was tested with Python 2.3.4 and * Debian Linux with out-of-the-box tetex-bin and dvipng packages installed * Windows XP with cygwin and a self-compiled dvipng (you need tetex-devel, libpng14-devel and the usual make/gcc stuff to compile) * Windows NT 4.0 with miktex (comes with dvipng even in the 'small' install!) In the parser, you can add stuff to the prologue by writing %%end-prologue%% somewhere in the document, before that write stuff like \\usepackage and after it put the actual latex display code. """ # this plugin only works with cmd.exe import os class ShellNotSupported: '''due to deficiencies of the command shell Win9x has this plugin cannot work on those systems.''' # COMSPEC is only set on windows systems, so if it is # unset we assume all is fine (default value "cmd.exe") if (os.path.basename(os.environ.get("COMSPEC", "cmd.exe")).lower() != "cmd.exe"): raise ShellNotSupported # end of cmd.exe check Dependencies = [] import sha, tempfile, shutil, re from MoinMoin.action import AttachFile from MoinMoin.Page import Page latex_template = r''' \documentclass[12pt]{article} \pagestyle{empty} %(prologue)s \begin{document} %(raw)s \end{document} ''' max_pages = 10 latex = "latex" dvipng = "dvipng" # stdin must be connected to /dev/null or, on windows, NUL, # otherwise latex may block reading stuff from stdin! # stdout is now read by the parser in order to be able to # report latex errors to the wiki user. if os.name == 'nt': null = 'NUL' else: null = '/dev/null' latex_line = "%(latex)s %%s.tex < %(null)s" % { 'latex': latex, 'null': null, } dvipng_line = "%(dvipng)s -T tight -l %(max_pages)s %%s.dvi < %(null)s" % { 'dvipng': dvipng, 'max_pages': max_pages, 'null': null, } # this is formatted with hexdigest(texcode), # page number and extension are appended by # the tools latex_name_template = "latex2_%s_p" # keep this up-to-date, also with max_pages!! latex_attachment = re.compile((latex_name_template+'%s%s') % (r'[0-9a-fA-F]{40}', r'[0-9]{1,2}', r'\.png')) anchor = re.compile(r'^%%anchor:[ ]*([a-zA-Z0-9_-]+)$', re.MULTILINE | re.IGNORECASE) # the anchor re must start with a % sign to be ignored by latex as a comment! end_prologue = '%%end-prologue%%' def call_command_in_dir(cmd, targetdir): # well. the windows shell has separate current working directories # for each drive! so if it happens that the tmpdir is on another drive # we need to fix that up... cdcmd = '' if os.name == 'nt': if targetdir[1] == ':': cdcmd += targetdir[0:2] + " && " cdcmd += 'cd %s' % targetdir stdouterr = os.popen("%s && %s" % (cdcmd, cmd), 'r') output = ''.join(stdouterr.readlines()) err = stdouterr.close() if not err is None: return ' error! exitcode was %d, transscript follows:\n\n%s' % (err,output) return None class Parser: extensions = ['.tex'] def __init__ (self, raw, request, **kw): self.raw = raw if len(self.raw)>0 and self.raw[0] == '#': self.raw[0] = '%' self.request = request self.exclude = [] if not hasattr(request, "latex2_cleanup_done"): request.latex2_cleanup_done = {} def cleanup(self, pagename): attachdir = AttachFile.getAttachDir(self.request, pagename, create=1) for f in os.listdir(attachdir): if not latex_attachment.match(f) is None: os.remove("%s/%s" % (attachdir, f)) def format(self, formatter): tmp = self.raw.split(end_prologue, 1) if len(tmp) == 2: prologue,tex=tmp else: prologue = '' tex = tmp[0] self.request.write(self.get(formatter, tex, prologue)); def get(self, formatter, inputtex, prologue): if not self.request.latex2_cleanup_done.has_key(formatter.page.page_name): self.request.latex2_cleanup_done[formatter.page.page_name] = True self.cleanup(formatter.page.page_name) if len(inputtex) == 0: return '' tex = latex_template % { 'raw': inputtex, 'prologue': prologue } fn = latex_name_template % sha.new(tex).hexdigest() attachdir = AttachFile.getAttachDir(self.request, formatter.page.page_name, create=1) dst = "%s/%s%%d.png" % (attachdir, fn) if not os.access(dst % 1, os.R_OK): tmpdir = tempfile.mkdtemp() try: data = open ("%s/%s.tex" % (tmpdir, fn), "w") data.write(tex) data.close() res = call_command_in_dir(latex_line % fn, tmpdir) if not res is None: return formatter.preformatted(1)+formatter.text('latex'+res)+formatter.preformatted(0) res = call_command_in_dir(dvipng_line % fn, tmpdir) if not res is None: return formatter.preformatted(1)+formatter.text('dvipng'+res)+formatter.preformatted(0) page = 1 while os.access("%s/%s%d.png" % (tmpdir, fn, page), os.R_OK): shutil.copyfile ("%s/%s%d.png" % (tmpdir, fn, page), dst % page) page += 1 finally: for root,dirs,files in os.walk(tmpdir, topdown=False): for name in files: os.remove(os.path.join(root,name)) for name in dirs: os.rmdir(os.path.join(root,name)) os.rmdir(tmpdir) result = "" page = 1 loop = False for match in anchor.finditer(inputtex): result += formatter.anchordef(match.group(1)) for match in anchor.finditer(prologue): result += formatter.anchordef(match.group(1)) while os.access(dst % page, os.R_OK): url = AttachFile.getAttachUrl(formatter.page.page_name, fn+"%d.png" % page, self.request) if loop: result += formatter.linebreak(0)+formatter.linebreak(0) result += formatter.image(src="%s" % url, alt=inputtex, title=inputtex, align="absmiddle") page += 1 loop = True return result