Created
July 22, 2012 16:47
-
-
Save emk/3160252 to your computer and use it in GitHub Desktop.
Anki 2 Egyptian Hieroglyph support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
# Copyright: Damien Elmes <anki@ichi2.net> | |
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html | |
# 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(match.group(), "") | |
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(match.group(), _imgLink( | |
col, | |
"\\begin{hieroglyph}" + match.group(1) + "\\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 | |
else: | |
err = _buildImg(col, txt, fname, model) | |
if err: | |
return err | |
else: | |
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(" ", " ") | |
for match in re.compile("&([a-z]+);", re.IGNORECASE).finditer(latex): | |
if match.group(1) in entitydefs: | |
latex = latex.replace(match.group(), entitydefs[match.group(1)]) | |
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" + | |
model["latexPost"]) | |
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.write(latex) | |
htxfile.close() | |
htxfile = file(htxpath, "r") | |
texpath = namedtmp("tmp.tex") | |
texfile = file(texpath, "w") | |
mdir = col.media.dir() | |
oldcwd = os.getcwd() | |
png = namedtmp("tmp.png") | |
try: | |
# generate dvi | |
os.chdir(tmpdir()) | |
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)) | |
return | |
finally: | |
os.chdir(oldcwd) | |
def _errMsg(type): | |
msg = (_("Error executing %s.") % type) + "<br>" | |
try: | |
log = open(namedtmp("latex_log.txt", rm=False)).read() | |
if not log: | |
raise Exception() | |
msg += "<small><pre>" + cgi.escape(log) + "</pre></small>" | |
except: | |
msg += _("Have you installed sesh, latex and dvipng?") | |
pass | |
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. | |
self.note.flush() | |
def onUpdate(): | |
self.loadNote() | |
self.checkValid() | |
self.mw.progress.timer(100, onUpdate, False) | |
# Add our custom button to the editor. | |
def mySetupButtons(self): | |
oldSetupButtons(self) | |
b = QPushButton(u"𓀀") | |
b.connect(b, SIGNAL("clicked()"), self.onRenderHieroglyphs) | |
b.setFixedHeight(20) | |
b.setFixedWidth(20) | |
b.setStyle(self.plastiqueStyle) | |
b.setFocusPolicy(Qt.NoFocus) | |
b.setShortcut(QKeySequence("Ctrl+H")) | |
b.setToolTip("Render Hieroglyphs (Ctrl+H)") | |
self.iconsBox.addWidget(b) | |
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