From 3b3fef4ebc38f07a012bd47b10d4bbf03233c1b9 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Tue, 23 Dec 2008 18:00:12 +0900 Subject: [PATCH] refactor latex into media support --- anki/deck.py | 3 ++ anki/latex.py | 108 ++++++++++++++++++++++++++++++-------------------- anki/media.py | 22 ++++++---- 3 files changed, 83 insertions(+), 50 deletions(-) diff --git a/anki/deck.py b/anki/deck.py index ebe1d4309..fd59a3889 100644 --- a/anki/deck.py +++ b/anki/deck.py @@ -1940,6 +1940,8 @@ create index if not exists ix_fields_value on fields (value)""") # media deck.s.statement(""" create unique index if not exists ix_media_filename on media (filename)""") + deck.s.statement(""" +create index if not exists ix_media_originalPath on media (originalPath)""") # deletion tracking deck.s.statement(""" create index if not exists ix_cardsDeleted_cardId on cardsDeleted (cardId)""") @@ -2182,6 +2184,7 @@ where interval < 1""") if deck.version < 18: deck.version = 18 deck.s.commit() + DeckStorage._addIndices(deck) deck.s.statement("analyze") return deck _upgradeDeck = staticmethod(_upgradeDeck) diff --git a/anki/latex.py b/anki/latex.py index 521fae620..3ff07e3a3 100644 --- a/anki/latex.py +++ b/anki/latex.py @@ -8,14 +8,10 @@ Latex support """ __docformat__ = 'restructuredtext' -import re, tempfile, os, sys, subprocess +import re, tempfile, os, sys, subprocess, stat, time +from anki.utils import genID, checksum +from anki.media import copyToMedia from htmlentitydefs import entitydefs -try: - import hashlib - md5 = hashlib.md5 -except ImportError: - import md5 - md5 = md5.new latexPreamble = ("\\documentclass[12pt]{article}\n" "\\special{papersize=3in,5in}" @@ -74,13 +70,14 @@ def call(*args, **kwargs): break return ret -def generatedFile(latexCode): - return "%s.png" % md5(latexCode).hexdigest() +def latexImgFile(deck, latexCode): + key = checksum(latexCode) + return deck.s.scalar("select filename from media where originalPath = :k", + k=key) -def generatedPath(deck, latexCode): +def latexImgPath(deck, file): "Return the path to the cache file in system encoding format." - path = os.path.join(deck.mediaDir(create=True), - generatedFile(latexCode)) + path = os.path.join(deck.mediaDir(create=True), file) return path.encode(sys.getfilesystemencoding()) def mungeLatex(latex): @@ -92,38 +89,65 @@ def mungeLatex(latex): latex = latex.encode("utf-8") return latex +def deleteAllLatexImages(deck): + for f in deck.s.column0( + "select filename from media where description = 'latex'"): + path = latexImgPath(deck, f) + try: + os.unlink(path) + except (OSError, IOError): + pass + deck.s.statement("delete from media where description = 'latex'") + deck.flushMod() + +def cacheAllLatexImages(deck): + fields = deck.s.column0("select value from fields") + for field in fields: + renderLatex(deck, field) + +def buildImg(deck, latex): + log = open(os.path.join(tmpdir, "latex_log.txt"), "w+") + texpath = os.path.join(tmpdir, "tmp.tex") + texfile = file(texpath, "w") + texfile.write(latexPreamble + "\n") + texfile.write(latex + "\n") + texfile.write(latexPostamble + "\n") + texfile.close() + texpath = texpath.encode(sys.getfilesystemencoding()) + oldcwd = os.getcwd() + if sys.platform == "win32": + si = subprocess.STARTUPINFO() + si.dwFlags |= subprocess.STARTF_USESHOWWINDOW + else: + si = None + try: + os.chdir(tmpdir) + errmsg = _( + "Error executing 'latex' or 'dvipng'.\n" + "A log file is available here:\n%s") % tmpdir + if call(["latex", "-interaction=nonstopmode", + texpath], stdout=log, stderr=log, startupinfo=si): + return (False, errmsg) + if call(latexDviPngCmd + ["tmp.dvi", "-o", "tmp.png"], + stdout=log, stderr=log, startupinfo=si): + return (False, errmsg) + # add to media + path = copyToMedia(deck, "tmp.png", latex=checksum(latex)) + return (True, path) + finally: + os.chdir(oldcwd) + def imageForLatex(deck, latex): "Return an image that represents 'latex', building if necessary." - imagePath = generatedPath(deck, latex) - if not os.path.exists(imagePath): - log = open(os.path.join(tmpdir, "latex_log.txt"), "w+") - texpath = os.path.join(tmpdir, "tmp.tex") - texfile = file(texpath, "w") - texfile.write(latexPreamble + "\n") - texfile.write(latex + "\n") - texfile.write(latexPostamble + "\n") - texfile.close() - texpath = texpath.encode(sys.getfilesystemencoding()) - oldcwd = os.getcwd() - if sys.platform == "win32": - si = subprocess.STARTUPINFO() - si.dwFlags |= subprocess.STARTF_USESHOWWINDOW - else: - si = None - try: - os.chdir(tmpdir) - errmsg = _( - "Error executing 'latex' or 'dvipng'.\n" - "A log file is available here:\n%s") % tmpdir - if call(["latex", "-interaction=nonstopmode", - texpath], stdout=log, stderr=log, startupinfo=si): - return (False, errmsg) - if call(latexDviPngCmd + ["tmp.dvi", "-o", imagePath], - stdout=log, stderr=log, startupinfo=si): - return (False, errmsg) - finally: - os.chdir(oldcwd) - return (True, imagePath) + imageFile = latexImgFile(deck, latex) + if imageFile: + path = latexImgPath(deck, imageFile) + ok = True + if not imageFile or not os.path.exists(path): + (ok, imageFile) = buildImg(deck, latex) + if not ok: + return (False, imageFile) + return (True, imageFile) def imgLink(deck, latex): "Parse LATEX and return a HTML image representing the output." diff --git a/anki/media.py b/anki/media.py index b1202401d..ec36c822b 100644 --- a/anki/media.py +++ b/anki/media.py @@ -51,9 +51,15 @@ def mediaFilename(path): ext = os.path.splitext(path)[1].lower() return "%s%s" % (new, ext) -def copyToMedia(deck, path): +def copyToMedia(deck, path, latex=None): """Copy PATH to MEDIADIR, and return new filename. Update media table. If file already exists, don't copy.""" + if latex: + origPath = latex + description = "latex" + else: + origPath = path + description = os.path.splitext(os.path.basename(path))[0] newBase = mediaFilename(path) new = os.path.join(deck.mediaDir(create=True), newBase) # copy if not existing @@ -80,9 +86,9 @@ values (:id, :filename, :size, :created, :originalPath, filename=newBase, size=newSize, created=time.time(), - originalPath=path, - description=os.path.splitext( - os.path.basename(path))[0]) + originalPath=origPath, + description=description) + deck.flushMod() return newBase def _modifyFields(deck, fieldsToUpdate, modifiedFacts, dirty): @@ -118,8 +124,6 @@ def rebuildMediaDir(deck, deleteRefs=False, dirty=True): oldPath = os.path.join(deck.mediaDir(), oldBase) if oldBase.startswith("."): continue - if oldBase.startswith("latex-"): - continue if os.path.isdir(oldPath): continue newBase = copyToMedia(deck, oldPath) @@ -164,12 +168,14 @@ def rebuildMediaDir(deck, deleteRefs=False, dirty=True): deck.deleteTags(unmodifiedFacts.keys(), _("Media Missing")) # build cache of db records mediaIds = dict(deck.s.all("select filename, id from media")) + # assume latex files exist + for f in deck.s.column0( + "select filename from media where description = 'latex'"): + usedFiles[f] = 1 # look through the media dir for any unused files, and delete for f in os.listdir(unicode(deck.mediaDir())): if f.startswith("."): continue - if f.startswith("latex-"): - continue path = os.path.join(deck.mediaDir(), f) if os.path.isdir(path): shutil.rmtree(path)