Repository navigation
Releases
v3.0.1
latex-rust 3.0.0
latex-rust 3.0.0 is published on crates.io: https://crates.io/crates/latex-rust/3.0.0
Highlights: infix commands, exact depth, lexicons, and a printer (E1–E4); MSRV 1.99; fuzz crash G8 fixed; egui/epaint 0.36.2 (RUSTSEC-2026-0192 gone).
See the 3.0.0 section of CHANGELOG.md for the full list of changes and spec changes. Merged from #16.
v2.1.1
The dvips color names now carry their license, and TealBlue matches
drivers.dtx. No API changes.
Fixed
TealBlueis the upstream dvipsnames CMYK0.86, 0, 0.34, 0.02. The
table shipped0.86, 0.09, 0.54, 0.10, which is not indrivers.dtx
and has no other documented source. A formula that namesTealBlue
therefore changes color. The other 67 tuples were already the upstream
values.
Added
data/dvipsnames.tsvidentifies itself as a modified form of
dvipsnam.deffrom the%<*dvipsnames>section ofdrivers.dtx
(Copyright 1994 David Carlisle and Sebastian Rahtz; 1995-1999 David
Carlisle; 2000-2026 The LaTeX Project; LPPL-1.3c or later). The
modifications are the tab-separated layout and the alphabetical
re-sort.NOTICErecords the same terms, including where to obtain
an unmodified copy of the graphics bundle.tests/dvipsnames_upstream.rschecks all 68 CMYK tuples against the
values transcribed fromdrivers.dtx.
Known issues
README and CHANGELOG had no Known issues section. GitHub issues #1
through #7 and #11 are already fixed in 2.0.0, 2.0.1, and 2.1.0.
ttf-parser 0.25 is flagged unmaintained (RUSTSEC-2026-0192, informational,
no patched release). It stays in 2.1.1. MathFont::face returns
ttf_parser::Face, and the crate re-exports ttf_parser so embedders
share that version (issue #7). Layout reads the OpenType MATH table,
glyph assemblies, and GSUB ssty through that face, and the SVG, PNG,
and egui backends consume its outlines. RustSec points at skrifa.
read-fonts merged MATH table support on 2026-09-08 (fontations pull
request 2105), but those types are not a stand-in for ttf_parser::Face,
and no maintained API-compatible fork is published. Replacing the parser
would be a breaking change to the public face API and a rewrite of the
metric and outline paths. That does not belong in a patch. The allowed
font stack in CONTRIBUTING.md is still ttf-parser.
Published to crates.io on 2026-10-06: https://crates.io/crates/latex-rust/2.1.1
latex-rust 2.1.0
[2.1.0] — 2026-10-02
Layout follows more of TeX and the OpenType MATH table. Dimensions of fractions, delimiters, radicals, scripts, accents, large operators, integrals, and AMSMath grids change. layout_with_em_size_pt and layout_with_numbering_and_em_size_pt take the physical root em so absolute TeX lengths stay stable when the render size changes. SVG, PNG, and egui pass their existing font size (12 pt, 12 pt, and 14 pt). layout still assumes 10 pt.
Fixed
- Delimiter-less fractions are as wide as the numerator, the denominator, and the rule. Automatic delimiters use TeX's delimiter factor and a physical 5 pt
\delimitershortfall, and sit on the math axis. - Radical variant slack goes into the rule gap and the descent.
radicalDegreeBottomRaisePercentraises the bottom of the degree. - Display large operators sit on the math axis. Upper and lower limits each obey their own baseline and gap minima.
- Script style uses OpenType
sstyalternates.SpaceAfterScriptis taken at the parent style. A row adds math italic correction to a bare variable, and does not add it again when that variable's scripts already include it. - Wide hats and tildes use the smallest MATH horizontal variant that covers the nucleus, placed with
TopAccentAttachment. - Matrices, cases, and aligned environments use their TeX styles and physical column spacing. Row rhythm uses LaTeX's 12 pt
\baselineskip.alignedadds\jotthe way amsmath's\openupdoes.casesuses\arraystretch1.2 and a\quadbetween columns. - A display integral with side scripts is centered on the math axis. Its italic correction shifts the superscript and not the subscript.
- Exact-rational decimal formatting no longer overflows while printing a remainder. egui tessellation rejects vertex indexes that do not fit in a
u32.
Thanks
baselogic (pull request 13) wrote the layout corrections and the regression tests for delimiter sizing, fraction width, radical variant slack, large-operator limits, script placement, row italic correction, AMSMath column spacing, display-integral side scripts, and both overflow checks.
latex-rust 2.0.1
A fix for the egui backend. No API changes. Upgrade with
cargo update -p latex-rust. SVG and PNG output is unchanged from 2.0.0.
Fixed
- The egui backend renders every glyph in the bundled font. Before, formulas using
H, math italict(so\partial_t u = \Delta u),π,σ,#,$,€,\dashv,\natural, bold and blackboard-bold capitals, and about 260 other glyphs failed withError::Unsupported { what: "glyph tessellation" }.B,k,τ,↦, and about 140 other glyphs were drawn with a stray triangle outside the glyph. - Cause: the ear clipper that turns glyph outlines into triangles accepted an ear whose closing diagonal ran exactly through another outline point, which happens where points line up, such as on straight stems. Its result is now checked exactly. An outline it cannot triangulate correctly is triangulated instead by exact scanline trapezoids under the non-zero rule, the rule the SVG and PNG backends fill with. Glyphs that were already drawn correctly get the same meshes as before.
The same fix is in 1.0.5.
latex-rust 1.0.5
Upgrade notice. Versions 1.0.0 through 1.0.4 abort the whole process with a stack overflow when given deeply nested input. If your program renders LaTeX from any untrusted source, upgrade to 1.0.5:
cargo update -p latex-rust1.0.5 has no API changes. It also includes the 2.0.0 rendering fixes that fit the 1.x API and the 2.0.1 egui fix, so most formulas now draw differently from 1.0.4 (see below).
Security
- Deeply nested input now returns an error instead of aborting the process (#6). The parser and the layout engine recursed once per nesting level with no limit. For example, 700 nested
\frac{1}{…}(about 7 KB of input) overflowed an 8 MiB stack in an optimised build, and far fewer levels were enough on smaller thread stacks. A stack overflow cannot be caught withcatch_unwind, so any program rendering untrusted LaTeX could be terminated.
Fixed
These fixes are backported from 2.0.0 and draw the same output as 2.0.0.
- Superscripts, subscripts, limits, and fraction parts in text style are drawn at script size in the SVG, PNG, and egui renderers. Before, they were positioned at script size but drawn at full size. 2.0.0 added a
scalefield toBoxContent::Glyphfor this; 1.0.5 leaves that type unchanged and works out the scale from the glyph box's size instead. (#1) - Unstyled Latin letters and lowercase Greek letters are set in math italic (for example
xis drawn as U+1D465), as TeX does. Digits, uppercase Greek,\mathrm,\text, and operator names stay upright. (#2) - A math-mode
-is drawn as U+2212 MINUS SIGN instead of the hyphen. (#3) - Font switches such as
\mathbfand\mathrmreach nested subformulae (fractions, script bases, radicals), so\mathbf{\frac{a}{b}}is bold. (#4) - Diacritic accents (
\hat,\dot,\bar, and the rest) are placed by the TeX rule. Before, they sat one accent base height too high. (#5) - The egui backend renders every glyph. Before, formulas using
H,π,σ, math italict(so\partial_t u),#,$,€, and a few hundred other glyphs failed withError::Unsupported { what: "glyph tessellation" }, andB,k,τ,↦, and about 140 others were drawn with a stray triangle outside the glyph. This is the same fix as in 2.0.1.
Changed behaviour
- Input that nests more than 32 levels is refused.
parse,parse_with_colors, and thelatex_to_*functions returnParseError::Malformed("input nests deeper than 32 levels").layoutreturnsError::Unsupported { what: "tree nests deeper than 32 levels" }for a tree built by hand that nests that deeply. Both are existing error variants. - The count is of parser recursion levels, and a braced argument costs two, so the limit admits 15 nested
\frac,\sqrt, orx^{…}, and 31 nested groups,\left…\rightpairs, or environments. Deeper input that 1.0.4 rendered (for example 16 to about 650 nested fractions in an optimised build) is now refused. The limit and the messages are the same as in 2.0.0. - Rendered output changes because of the fixes above. On our 87-input test corpus, 82 inputs draw differently from 1.0.4, and all 87 draw exactly as in 2.0.1 (SVG, PNG, egui, and box dimensions). Code that pinned 1.0.4 images, dimensions, or glyph characters will see new values.
Not in 1.0.5
These 2.0.0 changes add or change public API, so they are only in 2.0.0: Dim::as_ratio(), MathFont::face() and the ttf_parser re-export (#7), STIX_TWO_MATH_OTF as a static (#11), and ParseOptions, parse_with_options, and layout_with_max_depth for changing the depth limit.
Stack use
With the deepest input the limit admits, an optimised build needs at most about 512 KiB of stack, and an unoptimised build up to about 4 MiB (nested matrix environments). If you render on a thread with less stack than that, give the rendering thread more.
Advisory
This crash is tracked as RUSTSEC-2026-0311. cargo audit flags latex-rust versions before 1.0.5.
Thanks
Tom Clark (IronLAB) reported all of these issues with reproductions and proposed fixes, and wrote the fix for #4 and the depth limit (#6, #10). Both are backported here under his name.
latex-rust 2.0.0
Upgrade notice. 2.0.0 fixes a crash on deeply nested input and several rendering errors in 1.x. It has breaking changes, listed below. If you cannot take breaking changes yet, use 1.0.5, which has the crash fix and the rendering fixes #1–#5 without API changes. If you use the egui backend, use 2.0.1, which fixes glyphs that failed to render there.
This release fixes the eight defects Tom Clark reported while integrating latex-rust into IronLAB (issues #1–#7 and #11). Several of them change what the renderer draws.
Breaking changes
BoxContent::Glyphhas a new field,scale: Dim, and the variant is now#[non_exhaustive]. Match it withBoxContent::Glyph { ch, glyph_id, .. }and build it withBoxContent::glyph(ch, glyph_id, scale). (#1)STIX_TWO_MATH_OTFis now astaticinstead of aconst. It can no longer be used in aconstcontext. (#11)- Parsing and layout return
Errfor input nested deeper thanDEFAULT_MAX_NESTING_DEPTH(32 parser levels): 15 nested fractions, roots, or scripts, or 31 nested groups,\left…\rightpairs, or environments. UseParseOptions/parse_with_optionsandlayout_with_max_depthto change the limit. (#6) - Output changes: variable letters are drawn in math italic, math-mode
-is drawn as U+2212, script glyphs are drawn smaller, and diacritic accents sit lower. Code that pinned the old dimensions or images will see new values.
Fixed
- Deeply nested input returns
Errinstead of aborting the process with a stack overflow. (#6) - Superscripts, subscripts, and limits are drawn at script size in the SVG, PNG, and egui renderers. Before, they were positioned at script size but drawn at full size. (#1)
- Diacritic accents (
\hat,\dot,\bar, and the rest) are placed by the TeX rule. Before, they sat one accent base height too high. (#5) - Font switches such as
\mathrmand\mathbfreach nested subformulae (script bases, fractions, radicals). (#4) - Unstyled Latin letters and lowercase Greek letters are set in math italic, as TeX does. Digits, uppercase Greek,
\mathrm,\text, and operator names stay upright. (#2) - A math-mode
-is drawn as U+2212 MINUS SIGN instead of the hyphen. (#3) STIX_TWO_MATH_OTFis linked into a binary once rather than once per use. (#11)
Added
BoxContent::glyph(ch, glyph_id, scale). (#1)Dim::as_ratio()returns aDim's exact value as a numerator and denominator. (#7)MathFont::face()exposes the parsed OpenType face, and the crate re-exportsttf_parser. (#7)ParseOptions,parse_with_options,layout_with_max_depth, andDEFAULT_MAX_NESTING_DEPTH. (#6)
Advisory
The crash fixed by #6 is tracked as RUSTSEC-2026-0311. Every 2.x release includes the fix.
Thanks
Tom Clark (IronLAB) reported all eight issues with reproductions and proposed fixes, and contributed the pull requests for #4, #7, and #11 and the first depth limit for #6.