Skip to content

Instantly share code, notes, and snippets.



Created Jul 22, 2012
What would you like to do?
Anki 2 Egyptian Hieroglyph support
# -*- coding: utf-8 -*-
# Copyright: Damien Elmes <>
# License: GNU AGPL, version 3 or later;
# Hacked to support HieroTeX by Eric Kidd.
import re, os, sys, shutil, cgi, subprocess
from anki.utils import checksum, call, namedtmp, tmpdir, isMac, stripHTML
from anki.hooks import addHook
from htmlentitydefs import entitydefs
from anki.lang import _
seshCmd = ["sesh"]
latexCmd = ["latex", "-interaction=nonstopmode"]
latexDviPngCmd = ["convert", "-density", "200", "-trim"] # For rotations.
build = True # if off, use existing media but don't create new
regexps = {
"standard": re.compile(r"\[h\](.+?)\[/h\]", re.DOTALL | re.IGNORECASE),
# add standard tex install location to osx
if isMac:
os.environ['PATH'] += ":/usr/texbin"
def stripLatex(text):
for match in regexps['standard'].finditer(text):
text = text.replace(, "")
return text
# Shared between desktop-only auto-renderer and manual renderer at end of
# file.
def renderHieroglyphs(html, model, col):
"Convert TEXT with embedded latex tags to image links."
for match in regexps['standard'].finditer(html):
html = html.replace(, _imgLink(
"\\begin{hieroglyph}" + + "\\end{hieroglyph}", model))
return html
# Hooks into desktop-only rendering process.
def mungeQA(html, type, fields, model, data, col):
"Convert TEXT with embedded latex tags to image links."
return renderHieroglyphs(html, model, col)
def _imgLink(col, latex, model):
"Return an img link for LATEX, creating if necesssary."
txt = _latexFromHtml(col, latex)
fname = "hierotex-%s.png" % checksum(txt.encode("utf8"))
link = '<img src="%s">' % fname
if os.path.exists(fname):
return link
elif not build:
return u"[latex]%s[/latex]" % latex
err = _buildImg(col, txt, fname, model)
if err:
return err
return link
def _latexFromHtml(col, latex):
"Convert entities and fix newlines."
# entitydefs defines nbsp as \xa0 instead of a standard space, so we
# replace it first
latex = latex.replace("&nbsp;", " ")
for match in re.compile("&([a-z]+);", re.IGNORECASE).finditer(latex):
if in entitydefs:
latex = latex.replace(, entitydefs[])
latex = re.sub("<br( /)?>", "\n", latex)
# replace <div> etc with spaces
latex = re.sub("<.+?>", " ", latex)
latex = stripHTML(latex)
return latex
def _buildImg(col, latex, fname, model):
# add header/footer & convert to utf8
latex = (model["latexPre"] + "\n" +
latex + "\n" +
latex = latex.encode("utf8")
# it's only really secure if run in a jail, but these are the most common
for bad in ("write18", "\\readline", "\\input", "\\include", "\\catcode",
"\\openout", "\\write", "\\loop", "\\def", "\\shipout"):
assert bad not in latex
# write into a temp file
log = file(namedtmp("latex_log.txt"), "w")
htxpath = namedtmp("tmp.htx")
htxfile = file(htxpath, "w")
htxfile = file(htxpath, "r")
texpath = namedtmp("tmp.tex")
texfile = file(texpath, "w")
mdir =
oldcwd = os.getcwd()
png = namedtmp("tmp.png")
# generate dvi
if call(seshCmd, stdin=htxfile, stdout=texfile, stderr=log):
return _errMsg("sesh")
if call(latexCmd + ["tmp.tex"], stdout=log, stderr=log):
return _errMsg("latex")
# and png
if call(latexDviPngCmd + ["tmp.dvi", "tmp.png"],
stdout=log, stderr=log):
return _errMsg("dvipng")
# add to media
shutil.copyfile(png, os.path.join(mdir, fname))
def _errMsg(type):
msg = (_("Error executing %s.") % type) + "<br>"
log = open(namedtmp("latex_log.txt", rm=False)).read()
if not log:
raise Exception()
msg += "<small><pre>" + cgi.escape(log) + "</pre></small>"
msg += _("Have you installed sesh, latex and dvipng?")
return msg
# setup q/a filter
addHook("mungeQA", mungeQA)
# For compatibility with AnkiDroid, allow this to be run manually, and
# copy the rendered text to another field.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from aqt import editor, addons, mw
# If we have two fields named "Foo" and "Foo H", render "Foo" and place the
# result in "Foo H".
def onRenderHieroglyphs(self):
for name, val in self.note.items():
renderedField = name + " H"
if renderedField in self.note.keys():
html = renderHieroglyphs(val, self.note.model(), self.note.col)
self.note[renderedField] = html
if not self.addMode:
# Allow user to re-render cards in the browser.
def onUpdate():
self.checkValid(), onUpdate, False)
# Add our custom button to the editor.
def mySetupButtons(self):
b = QPushButton(u"𓀀")
b.connect(b, SIGNAL("clicked()"), self.onRenderHieroglyphs)
b.setToolTip("Render Hieroglyphs (Ctrl+H)")
self._buttons[u"𓀀"] = b
editor.Editor.onRenderHieroglyphs = onRenderHieroglyphs
oldSetupButtons = editor.Editor.setupButtons
editor.Editor.setupButtons = mySetupButtons
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.