Skip to content

Commit

Permalink
Add a preference to toggle LaTeX generation (#3218)
Browse files Browse the repository at this point in the history
* Add a preference to toggle LaTeX generation

* Fix test

* Remove LaTeX security restrictions

* Show existing LaTeX images regardless of preference

* Lift config check out of loop (dae)

* Shift option to review settings; display warning when disabled (dae)
  • Loading branch information
abdnh authored Jun 1, 2024
1 parent d981a6e commit 06f7aa3
Show file tree
Hide file tree
Showing 9 changed files with 35 additions and 66 deletions.
2 changes: 2 additions & 0 deletions ftl/core/preferences.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ preferences-media-is-not-backed-up = Media is not backed up. Please create a per
preferences-on-next-sync-force-changes-in = On next sync, force changes in one direction
preferences-paste-clipboard-images-as-png = Paste clipboard images as PNG
preferences-paste-without-shift-key-strips-formatting = Paste without shift key strips formatting
preferences-generate-latex-images-automatically = Generate LaTeX images (security risk)
preferences-latex-generation-disabled = LaTeX image generation is disabled in the preferences.
preferences-periodically-sync-media = Periodically sync media
preferences-please-restart-anki-to-complete-language = Please restart Anki to complete language change.
preferences-preferences = Preferences
Expand Down
2 changes: 2 additions & 0 deletions proto/anki/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ message ConfigKey {
RESET_COUNTS_REVIEWER = 22;
RANDOM_ORDER_REPOSITION = 23;
SHIFT_POSITION_OF_EXISTING_CARDS = 24;
RENDER_LATEX = 25;
}
enum String {
SET_DUE_BROWSER = 0;
Expand Down Expand Up @@ -121,6 +122,7 @@ message Preferences {
bool paste_strips_formatting = 3;
string default_search_text = 4;
bool ignore_accents_in_search = 5;
bool render_latex = 6;
}
message BackupLimits {
uint32 daily = 1;
Expand Down
29 changes: 6 additions & 23 deletions pylib/anki/latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

import html
import os
import re
from dataclasses import dataclass

import anki
import anki.collection
from anki import card_rendering_pb2, hooks
from anki.config import Config
from anki.models import NotetypeDict
from anki.template import TemplateRenderContext, TemplateRenderOutput
from anki.utils import call, is_mac, namedtmp, tmpdir
Expand All @@ -36,9 +36,6 @@
["dvisvgm", "--no-fonts", "--exact", "-Z", "2", "tmp.dvi", "-o", "tmp.svg"],
]

# if off, use existing media but don't create new
build = True # pylint: disable=invalid-name

# add standard tex install location to osx
if is_mac:
os.environ["PATH"] += ":/usr/texbin:/Library/TeX/texbin"
Expand Down Expand Up @@ -104,11 +101,15 @@ def render_latex_returning_errors(
out = ExtractedLatexOutput.from_proto(proto)
errors = []
html = out.html
render_latex = col.get_config_bool(Config.Bool.RENDER_LATEX)

for latex in out.latex:
# don't need to render?
if not build or col.media.have(latex.filename):
if col.media.have(latex.filename):
continue
if not render_latex:
errors.append(col.tr.preferences_latex_generation_disabled())
return html, errors

err = _save_latex_image(col, latex, header, footer, svg)
if err is not None:
Expand All @@ -126,24 +127,6 @@ def _save_latex_image(
) -> str | None:
# add header/footer
latex = f"{header}\n{extracted.latex_body}\n{footer}"
# it's only really secure if run in a jail, but these are the most common
tmplatex = latex.replace("\\includegraphics", "")
for bad in (
"\\write18",
"\\readline",
"\\input",
"\\include",
"\\catcode",
"\\openout",
"\\write",
"\\loop",
"\\def",
"\\shipout",
):
# don't mind if the sequence is only part of a command
bad_re = f"\\{bad}[^a-zA-Z]"
if re.search(bad_re, tmplatex):
return col.tr.media_for_security_reasons_is_not(val=bad)

# commands to use
if svg:
Expand Down
44 changes: 3 additions & 41 deletions pylib/tests/test_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import os
import shutil

from anki.config import Config
from anki.lang import without_unicode_isolation
from tests.shared import getEmptyCol


def test_latex():
col = getEmptyCol()
col.set_config_bool(Config.Bool.RENDER_LATEX, True)
# change latex cmd to simulate broken build
import anki.latex

Expand Down Expand Up @@ -51,49 +53,9 @@ def test_latex():
assert ".png" in oldcard.question()
# if we turn off building, then previous cards should work, but cards with
# missing media will show a broken image
anki.latex.build = False
col.set_config_bool(Config.Bool.RENDER_LATEX, False)
note = col.newNote()
note["Front"] = "[latex]foo[/latex]"
col.addNote(note)
assert len(os.listdir(col.media.dir())) == 2
assert ".png" in oldcard.question()
# turn it on again so other test don't suffer
anki.latex.build = True

# bad commands
(result, msg) = _test_includes_bad_command("\\write18")
assert result, msg
(result, msg) = _test_includes_bad_command("\\readline")
assert result, msg
(result, msg) = _test_includes_bad_command("\\input")
assert result, msg
(result, msg) = _test_includes_bad_command("\\include")
assert result, msg
(result, msg) = _test_includes_bad_command("\\catcode")
assert result, msg
(result, msg) = _test_includes_bad_command("\\openout")
assert result, msg
(result, msg) = _test_includes_bad_command("\\write")
assert result, msg
(result, msg) = _test_includes_bad_command("\\loop")
assert result, msg
(result, msg) = _test_includes_bad_command("\\def")
assert result, msg
(result, msg) = _test_includes_bad_command("\\shipout")
assert result, msg

# inserting commands beginning with a bad name should not raise an error
(result, msg) = _test_includes_bad_command("\\defeq")
assert not result, msg
# normal commands should not either
(result, msg) = _test_includes_bad_command("\\emph")
assert not result, msg


def _test_includes_bad_command(bad):
col = getEmptyCol()
note = col.newNote()
note["Front"] = f"[latex]{bad}[/latex]"
col.addNote(note)
q = without_unicode_isolation(note.cards()[0].question())
return (f"'{bad}' is not allowed on cards" in q, f"Card content: {q}")
18 changes: 16 additions & 2 deletions qt/aqt/forms/preferences.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>606</width>
<width>636</width>
<height>638</height>
</rect>
</property>
Expand Down Expand Up @@ -347,7 +347,7 @@
<property name="title">
<string>preferences_review</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QCheckBox" name="showPlayButtons">
<property name="sizePolicy">
Expand Down Expand Up @@ -413,6 +413,19 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="render_latex">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>preferences_generate_latex_images_automatically</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
Expand Down Expand Up @@ -1098,6 +1111,7 @@
<tabstop>showProgress</tabstop>
<tabstop>showEstimates</tabstop>
<tabstop>spacebar_rates_card</tabstop>
<tabstop>render_latex</tabstop>
<tabstop>pastePNG</tabstop>
<tabstop>paste_strips_formatting</tabstop>
<tabstop>useCurrent</tabstop>
Expand Down
2 changes: 2 additions & 0 deletions qt/aqt/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ def setup_collection(self) -> None:
form.paste_strips_formatting.setChecked(editing.paste_strips_formatting)
form.ignore_accents_in_search.setChecked(editing.ignore_accents_in_search)
form.pastePNG.setChecked(editing.paste_images_as_png)
form.render_latex.setChecked(editing.render_latex)
form.default_search_text.setText(editing.default_search_text)

form.backup_explanation.setText(
Expand Down Expand Up @@ -154,6 +155,7 @@ def update_collection(self, on_done: Callable[[], None]) -> None:
editing.adding_defaults_to_current_deck = not form.useCurrent.currentIndex()
editing.paste_images_as_png = self.form.pastePNG.isChecked()
editing.paste_strips_formatting = self.form.paste_strips_formatting.isChecked()
editing.render_latex = self.form.render_latex.isChecked()
editing.default_search_text = self.form.default_search_text.text()
editing.ignore_accents_in_search = (
self.form.ignore_accents_in_search.isChecked()
Expand Down
1 change: 1 addition & 0 deletions rslib/src/backend/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ impl From<BoolKeyProto> for BoolKey {
BoolKeyProto::ResetCountsReviewer => BoolKey::ResetCountsReviewer,
BoolKeyProto::RandomOrderReposition => BoolKey::RandomOrderReposition,
BoolKeyProto::ShiftPositionOfExistingCards => BoolKey::ShiftPositionOfExistingCards,
BoolKeyProto::RenderLatex => BoolKey::RenderLatex,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions rslib/src/config/bool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub enum BoolKey {
NewCardsIgnoreReviewLimit,
PasteImagesAsPng,
PasteStripsFormatting,
RenderLatex,
PreviewBothSides,
RestorePositionBrowser,
RestorePositionReviewer,
Expand Down
2 changes: 2 additions & 0 deletions rslib/src/preferences.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ impl Collection {
paste_strips_formatting: self.get_config_bool(BoolKey::PasteStripsFormatting),
default_search_text: self.get_config_string(StringKey::DefaultSearchText),
ignore_accents_in_search: self.get_config_bool(BoolKey::IgnoreAccentsInSearch),
render_latex: self.get_config_bool(BoolKey::RenderLatex),
})
}

Expand All @@ -141,6 +142,7 @@ impl Collection {
self.set_config_bool_inner(BoolKey::PasteStripsFormatting, s.paste_strips_formatting)?;
self.set_config_string_inner(StringKey::DefaultSearchText, &s.default_search_text)?;
self.set_config_bool_inner(BoolKey::IgnoreAccentsInSearch, s.ignore_accents_in_search)?;
self.set_config_bool_inner(BoolKey::RenderLatex, s.render_latex)?;
Ok(())
}
}

0 comments on commit 06f7aa3

Please sign in to comment.