A small python script that uses plastex to convert a LaTeX document consisting of structured notes into a csv file that can be imported into Anki.
It's designed for the same use case as the older package LaTeX-Note-Importer-for-Anki, by the same author, but aims to make better use of some of Anki's inbuilt features:
- cloze deletions
- dynamic page rendering based on screen size and user preferences (night mode)
Notes created and shared using LaTeX-Note-Importer are essentially static images. Notes created and shared using the script and workflow described here are html based, with maths mixed in and rendered using Anki's built-in MathJax engine.
-
LaTeX
(Strictly speaking, this is optional, but why would you be here if you didn't have LaTeX installed?)
-
Python
You need
python 3and several packages, including recent versions ofpip,setuptoolsandwheels. You can see which versions you have withpython3 -m pip show wheel pip setuptoolsand update if necessary with
python -m pip install --upgrade pip setuptools wheelThe installation process for
latex2latexdescribed in the next step works for me with the following versions of the above packages:pip 25.0.1 setuptools 45.2.0 wheel 0.34.2 -
LaTeX2Anki
Clone this repository into some local folder and run
pip install .in that folder (the folder containing
pyproject.toml). This should automatically install the python packagesplastex(version ≥ 3.1) andbeautifulsoup4. (In case you want to do local development on this python script, usepip install --editable .instead, so you do not need to reinstall after each edit. If the installation oflatex2ankifails, you might still be able to use the script by calling it directly, see Step 2 of Workflow below.) -
Anki
In Anki, you will once need to import the deck
example/example.apkgso that the note typeMathClozebecomes available in Anki. (Hopefully, this will also install the necessary fonts for MathCloze in Anki -- I need to test this. Exportingexample.apkgwith the optionexport mediadid lead to a larger file than exporting without this option, so hopefully this difference is caused precisely by the fonts.)You can immediately delete the deck again after importing it.
I describe the workflow here using the file example.tex.
-
Copy or symlink the following 4 files into the directory in which your tex file
example.texlives:MathCloze.styMathCloze.iniMathCloze.cssMathCloze.js
You only need to do this once for each directory in which you want to keep your texed notes.
Details: These files are specifically designed for the Anki note template
MathCloze. If you use a different template, you will need to adapt this file. Thestyandinifiles are essential for the conversion process; thecssandjsfiles are just there to prettify the html preview. -
optional: Compile
example.textoexample.pdfwith LaTeX.Check that the pdf file looks as expected.
Details: The layout of the pdf file is controlled by (the
\if\plastex\else-branches of) the LaTeX packageMathCloze.sty(see previous step). -
Run
latex2anki example.tex.You need to call this command in the folder in which
example.texlives. This script does two things. In a first step,plastexis called to convert the tex file toexample/example.html, which you can view in your browser. In a second step, the script converts the html file toexample/example.csv.Aside: If the installation of
latex2ankidescribed in Step 2 of Prerequisites fails, you might still be able to runlatex2ankiby callingpython3 latex2anki/cli.py example.tex
Details: The main code of
latex2ankiis contained inlatex2anki/cli.py.The first conversion step (
tex > html) is delegated toplastex. The details of this conversion are controlled by three files: by (the\if\plastex-branches of)MathCloze.styandMathCloze.ini(see initial step) and a temporary template file created on the fly by the script itself (see constantJINJA_TEMPLATE_FOR_PLASTEXdefined at the top oflatex2anki/cli.py). Note thatplastexautomatically expands all user-defined macros, so that the html file only contains standard LaTeX commands. The appearance ofexample.htmlin the browser is controlled by the filesMathCloze.jsandMathCloze.css. It will stil look very different from the final cards in Anki, but at least you can check whether your equations render at all.For details of the second conversion step (
html > csv), see the code incli.py. -
Import
example/example.csvinto Anki.In the dialog window, choose:
- update existing notes
- note type:
MathCloze
For a more elaborate example of what a tex file with notes might look like, see LinACards2025.tex.
Do not use $…$ or $$…$$ or \begin{equation} … \end{equation} etc.
It seems that at some stage of the conversion process, \(\text{\(x\)}\) gets converted into \(\text{$x$}\), which then displays incorrectly. It looks like a plastex bug to me, but I have not investigated details.
While clozes within maths work in principal, they do tend to break things.
One of the things that definitely does not work for clozes within maths is colour-highlighting.
By default, Anki highlights the revealed cloze deletions in blue. This works for maths contained within clozes, e.g. the answer is \cloze{1}\(b^2 + c^2\)\clend. But it does not work for clozes within maths, e.g. the answer is \(a^2 = \cloze{1} b^2 + c^2\clend\). You could write the answer is \(a^2 = \)\cloze{1}\(b^2 + c^2\)\clend instead, and then everything will look as expected again.
Note that this is a limitation of Anki/MathJax, not a limitation of the conversion process.
LaTeX's \newcommand works, and \renewcommand mostly works, except for inbuilt commands.
For inbuilt commands, plastex ignores \renewcommand, see plastex:issue#90.
Workaround: use \def to overwrite inbuilt commands, e.g.
\def\vec #1{\mathbf{#1}}to redefine \vec. Don't put a space between …#1 and {…!
The alignedat environment appears to break plastex.
The aligned environment works.
For exmple, empty lines produced with
\\
\\ in an aligned environment are lost when processing with plastex. Using \\~ instead of \\ provides a simple workaround.
It appears that plastex does not render \slash.
It appears that plastex does not stop parsing at \end{document}, and thus easily gets confused if the file continues past this point. I have not investigated details.
You won't get any errors if you don't, but the cards in Anki will look much nicer on large screens if you do.
That is, write your paragraph as follows:
\begin{center}
\par This is some nice text.
\end{center}Otherwise, each word will appear on a new line in Anki. (The center environment is converted to a <div class="centered"> environment by plastex, which is styled with the css attributes display:flex and flex-direction:column in the MathCloze card template. These css attributes are often what we want, e.g. when the center environment contains a list, but for pure it results in each word being placed on a new line. If we add \par in the tex file, the converted text gets wrapped in a
environment, and the problem disappears.
For some reason, MathJax displays \(\text{\ }\) as a visible backslash.
See MathJax 3 documentation for a list. (The list of MathJax 4 looks similar. See Anki:issue4277 for updates on the inclusion of MathJax 4 into Anki.)
The structure of note environment and field command is supposed to be reminiscent of the structure of itemize environment and item argument. So it should feel sufficiently “LaTeXy”. On the other hand, simply “itemizing” all field entries is very close to the internal logic of Anki, and it certainly accurately reflects the structure of the csv file that is used as an intermediary to import notes form LaTeX into Anki.
In the conversion to html, plastex converts the note environment to div class="note">...</div>, while the \field, \cloze{n}, \hint and \clend commands are simply translated to the strings ((FIELDSEPARATOR)), ((CLOZEn)), ((HINT)) and ((CLEND)). In the html preview, MathCloze.js is used to post-process these strings into adequate html markup. Similarly, latex2anki post-processes these strings into css/Anki-syntax.
It's tempting to implement clozes as a command with one mandatory argument (the cloze text) and one optional argument (the hint), e.g.
\cloze[a hint]{the hidden text}That would make it possible (or at least much easier) to produce a nice html preview of the notes via plastex.
However, using three different commands offers maximal flexibilty. Note that for notes that are generated live within Anki via MathJax, the only question that should matter is whether both the note with the cloze and the note with the hint are compilable. For example:
\begin{tabular}{cc}
a {{c1:: & b \\
c }} & d
\end{tabular}and
\(a + {{c1:: b\) and \(c + }} d\)are both completely legitimate in Anki, but it would not be possible to generated these examples with a syntax of the form \cloze[a hint]{the hidden text}
There are two possible approaches to rendering an html-tex mix in Anki.
-
Include LaTeX directly within the html code, so that it is renedered with Anki's built-in MathJax engine.
Pros & Cons:
- ❌ Loading probably slower.
- ✅ Can use clozes inside maths (but cloze-specific colour highlighting not supported).
- ✅ Importing is easy.
-
Convert LaTeX to
svgs, and include references to the svg files in the html code.Pros & Cons:
- ✅ Loading probably faster.
- ❌ Importing into Anki requires additional step: need to manually copy the
svgfiles to Anki's media folder (e.g. to…/snap/anki-desktop/common/Benutzer 1/collection.mediaif Anki is installed as a snap package). - ❌ Cannot included clozes within diagrams.
In both options, the cards can be viewed offline, and both options support nightmode colouring via css (colour of images can be inverted via css).
plastex mostly caters for option 1, so this is the path we follow here. One exception is tikz-cd diagrams, which plastex converts to svgs. My current inclination is to simply avoid such diagrams, so that we don't end up having to deal with the worst of both worlds 1 & 2. If I really need a card with a diagram, perhaps amscd would work instead.
It should be possible to set up plastex to use option 2 for all maths, i.e. to turn all maths into images. But this is difficult to set up. See plastex:issue163.