diff --git a/README.md b/README.md
index 2513944..f9dd407 100644
--- a/README.md
+++ b/README.md
@@ -130,6 +130,11 @@ currently recognized:
- `query`: Specify default query for `apy list`, `apy review` and `apy tag`.
- `review_show_cards`: Whether to show list of cards by default during note
review
+- `latexTranslateMode`: Specify which characters should be used as latex delimiters.
+ The following values are recognized:
+ - `off`: Default behaviour (block: `\[`, `\]` and inline: `\(`,`\)`)
+ - `mathjax`: common markdown syntax (block: `$$`, `$$` and inline: `$`,`$`)
+ - `latex`: latex behaviour (block: `[$$]` `[/$$]` and inline: `[$]`,`[$/]`)
An example configuration:
@@ -151,7 +156,8 @@ An example configuration:
["pdfcrop", "tmp.pdf", "tmp.pdf"],
["pdf2svg", "tmp.pdf", "tmp.svg"]
],
- "review_show_cards": true
+ "review_show_cards": true,
+ "latexTranslateMode": "off"
}
```
diff --git a/src/apyanki/anki.py b/src/apyanki/anki.py
index fe6d4f3..591bb1b 100644
--- a/src/apyanki/anki.py
+++ b/src/apyanki/anki.py
@@ -664,6 +664,7 @@ def add_notes_single(
tags: str = "",
model_name_in: str | None = None,
deck: str | None = None,
+ latexTranslateMode: str | None = None,
) -> Note:
"""Add new note to collection from args"""
model_name: str
@@ -677,5 +678,7 @@ def add_notes_single(
field_names: list[str] = [field["name"] for field in model["flds"]]
fields = dict(zip(field_names, field_values))
- new_note = NoteData(model_name, tags, fields, markdown, deck)
+ new_note = NoteData(
+ model_name, tags, fields, markdown, deck, latexMode=latexTranslateMode
+ )
return new_note.add_to_collection(self)
diff --git a/src/apyanki/config.py b/src/apyanki/config.py
index dedcd94..973a05d 100644
--- a/src/apyanki/config.py
+++ b/src/apyanki/config.py
@@ -53,6 +53,7 @@ def get_base_path() -> str | None:
"profile_name": None,
"query": "tag:marked OR -flag:0",
"review_show_cards": False,
+ "latexTranslateMode": "off",
}
# Ensure that cfg has required keys
diff --git a/src/apyanki/fields.py b/src/apyanki/fields.py
index 1964cb3..f37885e 100644
--- a/src/apyanki/fields.py
+++ b/src/apyanki/fields.py
@@ -117,10 +117,12 @@ def convert_field_to_text(field: str, check_consistency: bool = True) -> str:
return text.strip()
-def convert_text_to_field(text: str, use_markdown: bool) -> str:
+def convert_text_to_field(
+ text: str, use_markdown: bool, latexMode: str | None = None
+) -> str:
"""Convert text to Anki field html."""
if use_markdown:
- return _convert_markdown_to_field(text)
+ return _convert_markdown_to_field(text, latexMode=latexMode)
# Convert newlines to
tags
text = text.replace("\n", "
")
@@ -209,7 +211,7 @@ def _convert_field_to_markdown(field: str, check_consistency: bool = False) -> s
return text
-def _convert_markdown_to_field(text: str) -> str:
+def _convert_markdown_to_field(text: str, latexMode: str | None = None) -> str:
"""Convert Markdown to field HTML"""
# Don't convert if md text is really plain
if re.match(r"[a-zA-Z0-9æøåÆØÅ ,.?+-]*$", text):
@@ -230,11 +232,49 @@ def _convert_markdown_to_field(text: str) -> str:
# Fix whitespaces in input
text = text.replace("\xc2\xa0", " ").replace("\xa0", " ")
- # For convenience: Fix mathjax escaping
- text = text.replace(r"\[", r"\\[")
- text = text.replace(r"\]", r"\\]")
- text = text.replace(r"\(", r"\\(")
- text = text.replace(r"\)", r"\\)")
+ # get the correct LatexTranslateMode
+ if not latexMode:
+ latexMode = cfg["latexTranslateMode"]
+
+ # default behaviour
+ if latexMode == "off":
+ text = text.replace(r"\[", r"\\[")
+ text = text.replace(r"\]", r"\\]")
+ text = text.replace(r"\(", r"\\(")
+ text = text.replace(r"\)", r"\\)")
+ elif latexMode == "mathjax":
+ # blocks
+ subs: list[str] = text.split("$$")
+ text = ""
+ open: bool = False
+
+ for sub in subs[:-1]:
+ if open:
+ text += sub + r"\\]"
+ else:
+ text += sub + r"\\["
+ open = not open
+ text += subs[-1]
+
+ # inline
+ subs = text.split("$")
+ text = ""
+ open = False
+
+ for sub in subs[:-1]:
+ if open:
+ text += sub + r"\\)"
+ else:
+ text += sub + r"\\("
+ open = not open
+
+ text += subs[-1]
+
+ elif latexMode == "latex":
+ text = text.replace(r"[$$]", r"\\[")
+ text = text.replace(r"[/$$]", r"\\]")
+ text = text.replace(r"[$]", r"\\(")
+ text = text.replace(r"[$/]", r"\\)")
html = markdown.markdown(
text,
diff --git a/src/apyanki/note.py b/src/apyanki/note.py
index a444d59..bb26a45 100644
--- a/src/apyanki/note.py
+++ b/src/apyanki/note.py
@@ -574,6 +574,7 @@ class NoteData:
deck: str | None = None
nid: str | None = None
cid: str | None = None
+ latexMode: str | None = None
def add_to_collection(self, anki: Anki) -> Note:
"""Add note to collection
@@ -600,7 +601,9 @@ def add_to_collection(self, anki: Anki) -> Note:
note_type["did"] = anki.deck_name_to_id[self.deck]
new_note.fields = [
- convert_text_to_field(f, use_markdown=self.markdown)
+ convert_text_to_field(
+ f, use_markdown=self.markdown, latexMode=self.latexMode
+ )
for f in self.fields.values()
]
@@ -736,6 +739,7 @@ def markdown_file_to_notes(filename: str) -> list[NoteData]:
deck=x["deck"],
nid=x["nid"],
cid=x["cid"],
+ latexMode=x["latexTranslateMode"],
)
for x in _parse_markdown_file(filename)
]
@@ -759,6 +763,7 @@ def _parse_markdown_file(filename: str) -> list[dict[str, Any]]:
"deck": None,
"nid": None,
"cid": None,
+ "latexTranslateMode": None,
}
with open(filename, "r", encoding="utf8") as f:
for line in f:
@@ -779,6 +784,12 @@ def _parse_markdown_file(filename: str) -> list[dict[str, Any]]:
defaults["nid"] = v
elif k == "cid":
defaults["cid"] = v
+ elif k in ("latextranslatemode", "latexmode") and v in (
+ "off",
+ "mathjax",
+ "latex",
+ ):
+ defaults["latexTranslateMode"] = v
else:
defaults[k] = v
diff --git a/tests/test_latex.py b/tests/test_latex.py
new file mode 100644
index 0000000..0d286d9
--- /dev/null
+++ b/tests/test_latex.py
@@ -0,0 +1,40 @@
+from common import AnkiEmpty
+
+
+def test_latexTranslateMode_off_simple():
+ """Simple text replacement test for off option"""
+ with AnkiEmpty() as a:
+ note = a.add_notes_single(
+ ["This is \\[block\\] math.", "This is \\(inline\\) math."],
+ markdown=True,
+ latexTranslateMode="off",
+ )
+ assert "data-original-markdown" in note.n.fields[0]
+ assert "\\[block\\]" in note.n.fields[0]
+ assert "\\(inline\\)" in note.n.fields[1]
+
+
+def test_latexTranslateMode_mathjax_simple():
+ """Simple text replacement test for mathjax option"""
+ with AnkiEmpty() as a:
+ note = a.add_notes_single(
+ ["This is $$block$$ math.", "This is $inline$ math."],
+ markdown=True,
+ latexTranslateMode="mathjax",
+ )
+ assert "data-original-markdown" in note.n.fields[0]
+ assert "\\[block\\]" in note.n.fields[0]
+ assert "\\(inline\\)" in note.n.fields[1]
+
+
+def test_latexTranslateMode_latex_simple():
+ """Simple text replacement test for latex option"""
+ with AnkiEmpty() as a:
+ note = a.add_notes_single(
+ ["This is [$$]block[/$$] math.", "This is [$]inline[$/] math."],
+ markdown=True,
+ latexTranslateMode="latex",
+ )
+ assert "data-original-markdown" in note.n.fields[0]
+ assert "\\[block\\]" in note.n.fields[0]
+ assert "\\(inline\\)" in note.n.fields[1]