exporting, latex

This commit is contained in:
Damien Elmes 2019-12-20 11:47:19 +10:00
parent 55795822b5
commit f170763ef1
2 changed files with 24 additions and 19 deletions

View file

@ -11,23 +11,26 @@ from anki.hooks import runHook
from anki.storage import Collection from anki.storage import Collection
from typing import Any, Dict, List, Tuple from typing import Any, Dict, List, Tuple
from io import BufferedWriter
from anki.collection import _Collection
from zipfile import ZipFile
class Exporter: class Exporter:
includeHTML: typing.Union[bool, None] = None includeHTML: typing.Union[bool, None] = None
def __init__(self, col, did=None) -> None: def __init__(self, col: _Collection, did: None = None) -> None:
self.col = col self.col = col
self.did = did self.did = did
def doExport(self, path) -> None: def doExport(self, path) -> None:
raise Exception("not implemented") raise Exception("not implemented")
def exportInto(self, path) -> None: def exportInto(self, path: str) -> None:
self._escapeCount = 0 self._escapeCount = 0
file = open(path, "wb") file = open(path, "wb")
self.doExport(file) self.doExport(file)
file.close() file.close()
def processText(self, text) -> str: def processText(self, text: str) -> str:
if self.includeHTML is False: if self.includeHTML is False:
text = self.stripHTML(text) text = self.stripHTML(text)
@ -35,7 +38,7 @@ class Exporter:
return text return text
def escapeText(self, text) -> str: def escapeText(self, text: str) -> str:
"Escape newlines, tabs, CSS and quotechar." "Escape newlines, tabs, CSS and quotechar."
# fixme: we should probably quote fields with newlines # fixme: we should probably quote fields with newlines
# instead of converting them to spaces # instead of converting them to spaces
@ -47,7 +50,7 @@ class Exporter:
text = "\"" + text.replace("\"", "\"\"") + "\"" text = "\"" + text.replace("\"", "\"\"") + "\""
return text return text
def stripHTML(self, text) -> str: def stripHTML(self, text: str) -> str:
# very basic conversion to text # very basic conversion to text
s = text s = text
s = re.sub(r"(?i)<(br ?/?|div|p)>", " ", s) s = re.sub(r"(?i)<(br ?/?|div|p)>", " ", s)
@ -101,11 +104,11 @@ class TextNoteExporter(Exporter):
includeTags = True includeTags = True
includeHTML = True includeHTML = True
def __init__(self, col) -> None: def __init__(self, col: _Collection) -> None:
Exporter.__init__(self, col) Exporter.__init__(self, col)
self.includeID = False self.includeID = False
def doExport(self, file) -> None: def doExport(self, file: BufferedWriter) -> None:
cardIds = self.cardIds() cardIds = self.cardIds()
data = [] data = []
for id, flds, tags in self.col.db.execute(""" for id, flds, tags in self.col.db.execute("""
@ -138,10 +141,10 @@ class AnkiExporter(Exporter):
includeSched: typing.Union[bool, None] = False includeSched: typing.Union[bool, None] = False
includeMedia = True includeMedia = True
def __init__(self, col) -> None: def __init__(self, col: _Collection) -> None:
Exporter.__init__(self, col) Exporter.__init__(self, col)
def exportInto(self, path) -> None: def exportInto(self, path: str) -> None:
# sched info+v2 scheduler not compatible w/ older clients # sched info+v2 scheduler not compatible w/ older clients
self._v2sched = self.col.schedVer() != 1 and self.includeSched self._v2sched = self.col.schedVer() != 1 and self.includeSched
@ -259,7 +262,7 @@ class AnkiExporter(Exporter):
# such as update the deck description # such as update the deck description
pass pass
def removeSystemTags(self, tags) -> Any: def removeSystemTags(self, tags: str) -> Any:
return self.src.tags.remFromStr("marked leech", tags) return self.src.tags.remFromStr("marked leech", tags)
def _modelHasMedia(self, model, fname) -> bool: def _modelHasMedia(self, model, fname) -> bool:
@ -280,10 +283,10 @@ class AnkiPackageExporter(AnkiExporter):
key = _("Anki Deck Package") key = _("Anki Deck Package")
ext = ".apkg" ext = ".apkg"
def __init__(self, col): def __init__(self, col: _Collection) -> None:
AnkiExporter.__init__(self, col) AnkiExporter.__init__(self, col)
def exportInto(self, path): def exportInto(self, path: str) -> None:
# open a zip file # open a zip file
z = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED, allowZip64=True) z = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED, allowZip64=True)
media = self.doExport(z, path) media = self.doExport(z, path)
@ -291,7 +294,7 @@ class AnkiPackageExporter(AnkiExporter):
z.writestr("media", json.dumps(media)) z.writestr("media", json.dumps(media))
z.close() z.close()
def doExport(self, z, path) -> Dict[str, str]: def doExport(self, z: ZipFile, path: str) -> Dict[str, str]:
# export into the anki2 file # export into the anki2 file
colfile = path.replace(".apkg", ".anki2") colfile = path.replace(".apkg", ".anki2")
AnkiExporter.exportInto(self, colfile) AnkiExporter.exportInto(self, colfile)
@ -315,7 +318,7 @@ class AnkiPackageExporter(AnkiExporter):
shutil.rmtree(path.replace(".apkg", ".media")) shutil.rmtree(path.replace(".apkg", ".media"))
return media return media
def _exportMedia(self, z, files, fdir) -> Dict[str, str]: def _exportMedia(self, z: ZipFile, files: List[str], fdir: str) -> Dict[str, str]:
media = {} media = {}
for c, file in enumerate(files): for c, file in enumerate(files):
cStr = str(c) cStr = str(c)

View file

@ -8,6 +8,8 @@ from anki.hooks import addHook
from anki.lang import _ from anki.lang import _
from typing import Any from typing import Any
from typing import Any, Dict, List, Optional, Union
pngCommands = [ pngCommands = [
["latex", "-interaction=nonstopmode", "tmp.tex"], ["latex", "-interaction=nonstopmode", "tmp.tex"],
["dvipng", "-D", "200", "-T", "tight", "tmp.dvi", "-o", "tmp.png"] ["dvipng", "-D", "200", "-T", "tight", "tmp.dvi", "-o", "tmp.png"]
@ -38,7 +40,7 @@ def stripLatex(text) -> Any:
text = text.replace(match.group(), "") text = text.replace(match.group(), "")
return text return text
def mungeQA(html, type, fields, model, data, col) -> Any: def mungeQA(html: str, type: Optional[str], fields: Optional[Dict[str, str]], model: Dict[str, Any], data: Optional[List[Union[int, str]]], col) -> Any:
"Convert TEXT with embedded latex tags to image links." "Convert TEXT with embedded latex tags to image links."
for match in regexps['standard'].finditer(html): for match in regexps['standard'].finditer(html):
html = html.replace(match.group(), _imgLink(col, match.group(1), model)) html = html.replace(match.group(), _imgLink(col, match.group(1), model))
@ -51,7 +53,7 @@ def mungeQA(html, type, fields, model, data, col) -> Any:
"\\begin{displaymath}" + match.group(1) + "\\end{displaymath}", model)) "\\begin{displaymath}" + match.group(1) + "\\end{displaymath}", model))
return html return html
def _imgLink(col, latex, model) -> Any: def _imgLink(col, latex: str, model: Dict[str, Any]) -> Any:
"Return an img link for LATEX, creating if necesssary." "Return an img link for LATEX, creating if necesssary."
txt = _latexFromHtml(col, latex) txt = _latexFromHtml(col, latex)
@ -76,13 +78,13 @@ def _imgLink(col, latex, model) -> Any:
else: else:
return link return link
def _latexFromHtml(col, latex) -> Any: def _latexFromHtml(col, latex: str) -> Any:
"Convert entities and fix newlines." "Convert entities and fix newlines."
latex = re.sub("<br( /)?>|<div>", "\n", latex) latex = re.sub("<br( /)?>|<div>", "\n", latex)
latex = stripHTML(latex) latex = stripHTML(latex)
return latex return latex
def _buildImg(col, latex, fname, model) -> Any: def _buildImg(col, latex: str, fname: str, model: Dict[str, Any]) -> Any:
# add header/footer # add header/footer
latex = (model["latexPre"] + "\n" + latex = (model["latexPre"] + "\n" +
latex + "\n" + latex + "\n" +
@ -130,7 +132,7 @@ package in the LaTeX header instead.""") % bad
os.chdir(oldcwd) os.chdir(oldcwd)
log.close() log.close()
def _errMsg(type, texpath) -> Any: def _errMsg(type: str, texpath: str) -> Any:
msg = (_("Error executing %s.") % type) + "<br>" msg = (_("Error executing %s.") % type) + "<br>"
msg += (_("Generated file: %s") % texpath) + "<br>" msg += (_("Generated file: %s") % texpath) + "<br>"
try: try: