Align LaTeX with surrounding text

So far, LaTeX images were just rendered and included as pictures.
However, the bounding box of the image might not necessarily coincide
with the actual baseline of the equation. By using preview.sty (which is
hopefully not that big of an additional dependency), we can however
access the "depth" -- the offset from the baseline -- of the generated
image and correct for that.
This commit is contained in:
Tony Zorman 2025-05-24 15:29:56 +02:00
parent 16c5eaf00a
commit f081001201
4 changed files with 35 additions and 3 deletions

View file

@ -230,6 +230,7 @@ KolbyML <https://github.com/KolbyML>
Adnane Taghi <dev@soleuniverse.me>
Spiritual Father <https://github.com/spiritualfather>
Emmanuel Ferdman <https://github.com/emmanuel-ferdman>
Tony Zorman <mail@tony-zorman.com>
********************

View file

@ -5,6 +5,7 @@ from __future__ import annotations
import html
import os
import re
from dataclasses import dataclass
import anki
@ -25,6 +26,7 @@ pngCommands = [
"200",
"-T",
"tight",
"--depth",
"tmp.dvi",
"-o",
"tmp.png",
@ -84,6 +86,25 @@ def render_latex(
return html
def _add_depth_to_html(
html: str,
latex: ExtractedLatex,
col: anki.collection.Collection,
) -> str:
"""Add depth information, as provided by preview.sty, to the HTML."""
try:
with open(
os.path.join(col.media.dir(), f"{latex.filename}.depth"), encoding="utf8"
) as depth:
html = html.replace(
f' src="{latex.filename}"',
f' style="vertical-align: -{depth.read()}" src="{latex.filename}"',
)
except Exception: # Depth information is non-critical.
pass
return html
def render_latex_returning_errors(
html: str,
model: NotetypeDict,
@ -105,7 +126,8 @@ def render_latex_returning_errors(
for latex in out.latex:
# don't need to render?
if col.media.have(latex.filename):
if col.media.have(latex.filename) and col.media.have(f"{latex.filename}.depth"):
html = _add_depth_to_html(html, latex, col)
continue
if not render_latex:
errors.append(col.tr.preferences_latex_generation_disabled())
@ -114,6 +136,7 @@ def render_latex_returning_errors(
err = _save_latex_image(col, latex, header, footer, svg)
if err is not None:
errors.append(err)
html = _add_depth_to_html(html, latex, col)
return html, errors
@ -137,7 +160,7 @@ def _save_latex_image(
ext = "png"
# write into a temp file
log = open(namedtmp("latex_log.txt"), "w", encoding="utf8")
log = open(namedtmp("latex_log.txt"), "w+", encoding="utf8")
texpath = namedtmp("tmp.tex")
texfile = open(texpath, "w", encoding="utf8")
texfile.write(latex)
@ -155,6 +178,11 @@ def _save_latex_image(
data = file.read()
col.media.write_data(extracted.filename, data)
os.unlink(png_or_svg)
# add depth data
log.seek(0)
match = re.search(r"depth=(.*)", log.read())
if match:
col.media.write_data(f"{extracted.filename}.depth", match.group(1).encode())
return None
finally:
os.chdir(oldcwd)

View file

@ -2,6 +2,9 @@
\special{papersize=3in,5in}
\usepackage[utf8]{inputenc}
\usepackage{amssymb,amsmath}
\usepackage[active,tightpage]{preview}
\pagestyle{empty}
\setlength{\parindent}{0in}
\setlength{\abovedisplayskip}{0pt}
\begin{document}
\begin{preview}

View file

@ -66,7 +66,7 @@ define_newtype!(NotetypeId, i64);
pub(crate) const DEFAULT_CSS: &str = include_str!("styling.css");
pub(crate) const DEFAULT_CLOZE_CSS: &str = include_str!("cloze_styling.css");
pub(crate) const DEFAULT_LATEX_HEADER: &str = include_str!("header.tex");
pub(crate) const DEFAULT_LATEX_FOOTER: &str = r"\end{document}";
pub(crate) const DEFAULT_LATEX_FOOTER: &str = r"\end{preview}\end{document}";
/// New entries must be handled in render.rs/add_special_fields().
static SPECIAL_FIELDS: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
HashSet::from_iter(vec![