From c3522feb2202ef0e1f346da6617eafb9507e515f Mon Sep 17 00:00:00 2001 From: bnewbold Date: Wed, 21 Mar 2007 19:18:11 -0700 Subject: hurrah! equations in latex and html TODO: center images in html TODO: track equations in a database? --- .gitignore | 2 + bn_django/git_wiki/latex_directive.py | 129 ++++++++++++++++++++++++++++++++++ bn_django/git_wiki/urls.py | 2 +- bn_django/git_wiki/views.py | 61 +++++++++++++--- static/style/git_wiki.css | 8 ++- 5 files changed, 191 insertions(+), 11 deletions(-) create mode 100644 bn_django/git_wiki/latex_directive.py diff --git a/.gitignore b/.gitignore index d6e59c5..055a617 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ media code bn_django/bn_django.db bn_django/settings.py +bn_django/git_wiki/settings.py +bn_django/git_browse/settings.py *.pyc *.pyo *.swp diff --git a/bn_django/git_wiki/latex_directive.py b/bn_django/git_wiki/latex_directive.py new file mode 100644 index 0000000..823cc6a --- /dev/null +++ b/bn_django/git_wiki/latex_directive.py @@ -0,0 +1,129 @@ +""" +Implement latex directive. + +""" +import os +import shutil +import sha +import tempfile +import subprocess + +from docutils import nodes +from docutils.parsers.rst.directives import register_directive, flag +from docutils.parsers.rst.roles import register_canonical_role + +from settings import * + +dorawtexstuff = False + +def latex_math(tex,centerize=False): + """ Process `tex` and produce image nodes. """ + if not dorawtexstuff: + image_names = latex_snippet_to_png(tex) + the_nodes = [] + alt = tex + styleclasses = ("equation",) + if tex[:2] =='$$': + centerize=True + if centerize: + styleclasses += ("centered-equ",) + for pageno, name in enumerate(image_names): + the_nodes.append(nodes.image(uri=name, alt=alt, + classes=styleclasses,)) + alt = '' + return the_nodes + else: + return [nodes.raw(tex,tex,format='latex')] + +def latex_directive(name, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + """ Latex directive. """ + tex = '\n'.join(content) + return latex_math(tex) +latex_directive.content = True + + +def latex_role(role, rawtext, text, lineno, inliner, + options={}, content=[]): + """ Latex role. """ + i = rawtext.find('`') + tex = rawtext[i+1:-1] + return latex_math(tex,), [] + +def register(): + register_directive('latex', latex_directive) + register_canonical_role('latex', latex_role) + register_canonical_role('m', latex_role) + +def call_command_in_dir(app, args, targetdir): + + cwd = os.getcwd() + try: + os.chdir(targetdir) + #print args + #print ' '.join(args) + p = subprocess.Popen(app + ' ' + ' '.join(args), shell=True) + sts = os.waitpid(p.pid, 0) + + # FIXME -- should we raise an exception of status is non-zero? + + finally: + # Restore working directory + os.chdir(cwd) + +latex_template = r''' +\documentclass[12pt]{article} +\pagestyle{empty} +%(prologue)s +\begin{document} +%(raw)s +\end{document} +''' +max_pages = 10 +MAX_RUN_TIME = 5 # seconds +latex_name_template = 'latex2png_%s' +latex = "latex" +dvipng = "dvipng" +latex_args = ("--interaction=nonstopmode", "%s.tex") +dvipng_args = ("-q", "-bgTransparent", "-Ttight", "--noghostscript", "-l%s" % max_pages, "%s.dvi") + +def latex_snippet_to_png(inputtex, prologue=''): + """ Convert a latex snippet into a png. """ + import shutil + + tex = latex_template % { 'raw': inputtex, 'prologue': prologue } + namebase = latex_name_template % sha.new(tex).hexdigest() + dst = namebase + '%d.png' + + tmpdir = tempfile.mkdtemp() + try: + data = open("%s/%s.tex" % (tmpdir, namebase), "w") + data.write(tex) + data.close() + args = list(latex_args) + args[-1] = args[-1] % namebase + res = call_command_in_dir(latex, args, tmpdir) + if not res is None: + # FIXME need to return some sort of error + return [] + args = list(dvipng_args) + args[-1] = args[-1] % namebase + res = call_command_in_dir(dvipng, args, tmpdir) + if not res is None: + # FIXME need to return some sort of error + return [] + + page = 1 + pagenames = [] + while os.access("%s/%s%d.png" % (tmpdir, namebase, page), os.R_OK): + pagename = dst % page + shutil.copyfile("%s/%s%d.png" % (tmpdir, namebase, page), + EQU_FOLDER + pagename) + page += 1 + pagenames.append(EQU_PREFIX + pagename) + finally: + # FIXME do some tidy up here + pass + shutil.rmtree(tmpdir) + return pagenames + diff --git a/bn_django/git_wiki/urls.py b/bn_django/git_wiki/urls.py index 3489941..22f36a3 100644 --- a/bn_django/git_wiki/urls.py +++ b/bn_django/git_wiki/urls.py @@ -18,7 +18,7 @@ urlpatterns = patterns('bn_django.git_wiki.views', (r'^commit/(?P[0-9a-z]{40})/$', 'view_commit',), (r'^(?P[\w\-\_\/]*)/log/$', 'tree',), (r'^(?P[\w\-\_\/]*)/edit/$', 'tree',), - (r'^(?P[\w\-\_\/]*)/pdf/$', 'latexitem',), + (r'^(?P[\w\-\_\/]*)/pdf/$', 'pdfitem',), (r'^(?P[\w\-\_\/]*)/raw/$', 'rawitem',), (r'^(?P[\w\-\_\/]*)/latex/$', 'latexitem',), (r'^(?P[\w\-\_\/]*\.png/?)$', 'figure',), diff --git a/bn_django/git_wiki/views.py b/bn_django/git_wiki/views.py index 2bfc034..6eff54b 100644 --- a/bn_django/git_wiki/views.py +++ b/bn_django/git_wiki/views.py @@ -50,7 +50,10 @@ def item(request, reqslug, blob=None): i.update() try: - from docutils.core import publish_parts + from docutils.core import publish_parts,Publisher + import latex_directive + latex_directive.dorawtexstuff = False + latex_directive.register() except ImportError: if settings.DEBUG: raise HttpResponseServerError(request) @@ -72,12 +75,16 @@ def latexitem(request, reqslug, blob=None): i.update() try: - from docutils.core import publish_parts + from docutils.core import publish_parts,Publisher + import latex_directive + latex_directive.dorawtexstuff = True + latex_directive.register() except ImportError: if settings.DEBUG: raise HttpResponseServerError(request) else: - docutils_settings = getattr(settings, "GITWIKI_REST_SETTINGS", {}) + docutils_settings = getattr(settings, "GITWIKI_REST_SETTINGS", + {'format':'latex'}) parts = publish_parts(source=i.contents, writer_name="latex", settings_overrides=docutils_settings) hr = HttpResponse(mimetype="text/plain") hr['Content-Disposition'] = 'filename=%s.tex' % reqslug @@ -97,16 +104,54 @@ def pdfitem(request, reqslug, blob=None): i.update() try: - from docutils.core import publish_parts + from docutils.core import publish_parts,Publisher + import latex_directive + latex_directive.dorawtexstuff = True + latex_directive.register() + import tempfile,re,os,shutil except ImportError: if settings.DEBUG: raise HttpResponseServerError(request) else: - docutils_settings = getattr(settings, "GITWIKI_REST_SETTINGS", {}) + docutils_settings = getattr(settings, "GITWIKI_REST_SETTINGS", + {'format':'latex'}) parts = publish_parts(source=i.contents, writer_name="latex", settings_overrides=docutils_settings) - hr = HttpResponse(mimetype="text/plain") - hr['Content-Disposition'] = 'filename=%s.tex' % reqslug - hr.write(parts['whole']) + tmpdir = tempfile.mkdtemp() + pre = i.slug().split('/') + for fig in re.findall('\\includegraphics.*\{(.+)\}', parts['whole']): + if len(pre) > 1: + fig_blob = fromslug("%s/%s" % ('/'.join(pre[:-1])),fig) + else: + fig_blob = fromslug(fig) + # if might have to create subdirectories for figures + fig_pre = fig.split('/') + if len(fig_pre) > 1: + os.mkdir("%s/%s" % (tmpdir,'/'.join(fig_pre[:-1]))) + fig_file = file(str("%s/%s" % (tmpdir,fig)),'wb') + fig_blob.update() + fig_file.write(fig_blob.contents) + fig_file.close() + + hr = HttpResponse(mimetype="application/pdf") + #hr = HttpResponse() + hr['Content-Disposition'] = 'filename=%s.pdf' % reqslug + write_tex,pdf = os.popen2('rubber-pipe -qqq -d --into %s' % tmpdir) + write_tex.write(parts['whole'].replace('{\$}','$')) + write_tex.flush() + write_tex.close() + #hr.write(parts['whole']) + #pdf.flush() + for l in pdf.readlines(): + hr.write(l) + try: + pdff = open("%s/rubtmp0.pdf" % tmpdir,'r') + for l in pdff.readlines(): + hr.write(l) + pdff.close() + except: + pass + pdf.close() + shutil.rmtree(tmpdir) return hr def rawitem(request, reqslug, blob=None): diff --git a/static/style/git_wiki.css b/static/style/git_wiki.css index cfdc8bd..2fd7157 100644 --- a/static/style/git_wiki.css +++ b/static/style/git_wiki.css @@ -58,10 +58,14 @@ td.hash { overflow: hidden; text-align: right; font-family: courier; } - span.hash { font-family: courier; } - +img.equation { + border: none; } +img.centered-equ { + padding: 8px; + padding-left: 250px; + border: none; } span.head { color: green; } -- cgit v1.2.3