0.7.5-rc5
Pre-releaseInstallation
latexml-oxide ships as a single self-contained executable. It also calls a few external tools when they are on your PATH:
- a TeX distribution (TeX Live, MacTeX, or MiKTeX) — for the packages, classes, and fonts your documents load.
- graphics tools (ImageMagick, Ghostscript, MuPDF, Poppler, dvipng, dvisvgm) — to convert figures.
Per-platform install instructions are in the README Installation guide.
Download the asset for your platform (each has a matching .sha256 to verify file integrity):
- Linux (x86-64) —
latexml-oxide_0.7.5-rc5-1_amd64.deb, or the portablelatexml-oxide-0.7.5-rc5-x86_64-unknown-linux-gnu.tar.gz - Linux (aarch64 / arm64) —
latexml-oxide_0.7.5-rc5-1_arm64.deb, or the portablelatexml-oxide-0.7.5-rc5-aarch64-unknown-linux-gnu.tar.gz - macOS (Apple Silicon) —
latexml-oxide-0.7.5-rc5-aarch64-apple-darwin.tar.gz - macOS (Intel) —
latexml-oxide-0.7.5-rc5-x86_64-apple-darwin.tar.gz - Windows (x86-64) —
latexml-oxide-0.7.5-rc5-x86_64-pc-windows-msvc.zip(unzip;latexml_oxide.exeis self-contained, no installer)
Changelog
[0.7.5] (Rhai binding API; bibliography content recovery; wider math coverage; large-document memory; default-CSS sync; ar5iv corpus fixes)
-
A conversion always writes
<jobname>.latexml.log— Perllatexmlc
parity: with--logunset the log lands in the working directory
(latexml.logfor literal input), replacing any stale copy, and still
ends with the canonicalStatus:conversion:Nverdict line. rc5's first
artifacts wrote no log at all unless--logwas passed. -
A memory Fatal now says what to do — the cooperative-fuse message
names the 75% fuse and the--max-memoryceiling it derives from, and
advises raising it: hitting the fuse is a known need of large documents
(peak scales with macro expansion and math density, not source bytes),
not an anomaly. The run then ends withInfo:memory:peak— the
kernel-tracked peak RSS, the honest lower bound on what THIS document
needs — in stderr and just above the log's status tail. Clean runs stay
quiet. -
Large-document logs got humane — disk staging is by design, so the
alarming "spilled" wording is gone ("459,579 segment(s) staged to disk");
the[N]math-progress markers count conversion-wide instead of
restarting at[1]in every streamed fragment; andScan: DBStatus:
backs off exponentially (logs when the object count passes a power of
two), ~20 lines where a book-scale split wrote 115k+. -
Page rendering runs in parallel worker processes — after the
document-wide scan, N workers (LATEXML_RENDER_JOBS, self-clamped to the
machine's actual memory headroom) render page ranges concurrently,
byte-identical to the serial path. Measured on the 131 MB witness:
post-processing 37:31 → 12:17 (3.05×) at 8 workers on a fresh parent;
the full single-invocation.tex → .htmlrun 1:24 → 1:00:34 with the
self-sized fleet on a 31 GB laptop, exit 0, all 115,519 pages. The
ObjectDB behind it gained a SQLite store (Perl--dbfileheritage,
WAL-concurrent,sqlite3-CLI-inspectable). -
A book-scale document converts
.tex → .htmlin ONE invocation on a
31 GB laptop — the 131 MB witness end-to-end in 1:17 at a 22.95 GiB peak:
115,519 pages, exit 0, zero errors. The render loop is now memory-flat
(~3 KB/page, was ~150 KB/page):Node::get_namespacesin the libxml
binding leaked itsxmlGetNsListarray on every call since the crate's
beginning — fixed upstream in libxml 0.3.21, which this build requires. -
The run's LAST line is the combined core+post verdict — a core Fatal
stays the final word even after thousands of per-page post lines
(Conversion failed: …, exit 1),--logfiles and archivestatus
members end with the canonicalStatus:conversion:N(the max of the core
and post phases), so downstream frameworks derive severity from one line. -
The final tally counts every printed diagnostic, losslessly — the
same 131 MB run used to log 12,105Warning:lines and report
"2 warnings": raw log-crate emissions (chief among them the math
parser's) bypassed the counters entirely. All diagnostics now flow
through ONE Perl-shaped vehicle (count + emit + caps + taxonomy), raw
log-crate diagnostics are lint-banned in the workspace,Fatal:lines
are never suppressed at any verbosity, and what a reader greps from the
log is what the verdict reports. -
--max-memoryunset now follows actual machine headroom — 90% of
AVAILABLE RAM at startup (cgroup-capped, 64 GiB max) instead of half of
total, so an idle machine converts large documents by default while a busy
one still self-limits; hand-tuning the ceiling is no longer needed. -
Book-scale split documents render on commodity RAM — post-processing no
longer parses the whole document as one DOM before splitting: past 1 GiB of
core XML, a streaming front-end spills each page as the file streams by and
scans them one at a time, byte-identical to the whole-DOM split. First
witness across the line: a 131 MB book's 2.68 GB core XML → 115,519 pages /
11 GB of HTML in 37½ minutes at 17.4 GB peak on a 31 GB laptop — previously
an out-of-memory kill with zero pages written. -
Multi-gigabyte post-processing inputs parse trustworthily — libxml2's
hard limits (absentXML_PARSE_HUGE) silently corrupted any parse past
~1.4 GB (hundreds of thousands of phantomID already definederrors for
ids that occur exactly once) and killed it outright at ~1.7 GB. All
post-processing parses now lift those limits. -
A 2 GiB-plus core→post handoff no longer dies on libxml2's i32 buffer
ceiling — the single-invocation.tex → .htmlflow spills the handoff to
disk beside the destination and streams it. -
Split pages inherit
xml:langfrom their ancestors, as Perl does — the
copy had been silently skipped (namespaced-attribute read). -
The runtime (Rhai) binding API reaches feature-parity with the compile-time
macros — definition lookup and digest/construct hooks, the same flexary
option bags, definitions registered from a running body, external commands,
and the full diagnostics surface. -
A
.rhaibinding can parse and manipulate XML/(X)HTML —
document.insertXMLsplices a parsed subtree,ParseXMLexposes the parser
on its own, namespaces resolve by URI, and malformed markup is rejected
outright rather than silently salvaged. -
.rhaibindings reach the same document XML surface the compile-time
bindings do — XPath query, element insertion, and structural editing, each
under its Perl name. -
A failing
.rhaibinding no longer costs the whole document — each
binding kind degrades to a neutral result and reports a cleanError:. -
Default HTML styling re-synced to vanilla
LaTeXML.css— justified text,
\underline/\overlineand verbatim no-wrap are back;.htminfers HTML5. -
Adjacent display equations are vertically separated — the bundled CSS
now carries TeX's display skips (1em, collapsing with paragraph margins),
so back-to-back\[…\]displays no longer render touching (issue 473). -
Verbatim renders true to the source — fancyvrb
Verbatimlines each
keep their own row, indentation and spacing (the binding's per-line
ltx_verbatimclass had been dropped in porting), and the bundled CSS
replaces vanilla'snowrap— which collapsed a plain{verbatim}block
to a single line — with trueprewhitespace (issue 431). -
A stale or empty stylesheet in the output directory is overwritten instead
of leaving the page unstyled. -
Split output (
--splitat) styles every page and carries the document date
— a shared post-processing XPath defect had been dropping relative-path
lookups (also repairing split navigation and cross-reference/glossary
resolution). -
Split-page navigation links reach Perl parity — the full
<link rel=…>
head set with relation types and full-breadcrumb titles. -
The generator identifier spells out the product name and version, matching
Perl. -
A
standalonedocument class's options are no longer mis-loaded as packages. -
A package loaded in a subfile's preamble keeps its definitions — the group
around the child's preamble used to discard them. -
Named scopes are tracked correctly again — a deactivated scope could never
be re-activated, and a second deactivation popped the same bindings twice. -
\includefrom/\subincludefromno longer drop their file in silence. -
The cortex worker builds without the
runtime-bindingsfeature — a
Rhai-free conversion binary for the fleet. -
File resolution can no longer die silently — a conversion could run with no
kpathsea backend at all and say nothing, reporting onlyCan't find TeX file X
(fixed in kpathsea 0.3.4
plus a subprocess fallback here). -
Every conversion log records the file-resolution backend —
in-process,
subprocess kpsewhich, orunavailable. -
Dingbat and symbol fonts selected by family render their glyphs instead
of the OT1 slot's text character —bbding's\XSolidBrushwas silently
coming out as%and\Checkmarkas!, inverting whole comparison-table
columns at zero reported errors. An unrecognized font family, series or
shape is also now announced once per document rather than once per font
switch. -
ar5iv corpus fixes (2026-07 issue sprint) —
xcolordvipsnamesas a
global class option,\sidecaptionvpos, verbatim\newtcblistingbodies,
agujournal2019end-matter,blkarray(recovering papers that hit
OOM/timeout),scrartcl\titlehead, and frontmatter bindings for
fairmeta,selfevolagentandopenmoss. -
silenceand the bundled arXiv preprint styles no longer leave their
commands undefined —\WarningFilterand friends stopped spilling their
filter text into the page, and\keywordsrenders again. The silence
binding also restores diagnostics the real package's\ErrorsOffwas
swallowing. -
ICASSP/Interspeech papers keep their Index Terms and author blocks — the
spconfstyle'skeywordsenvironment and\twoauthorsnow become real
keyword and creator frontmatter instead of an undefined-environment error
(the largest such cluster in the arXiv sandbox corpora). -
\usepackage{xparse}no longer destroys the\ccedilla accent — any
document loadingxparseorexpl3renderedFran\c coisas "Fran0cois",
silently and with no error reported. -
A deferred package-load miss no longer poisons a later raw load.
-
Perl
.ltxmlbindings are never read as TeX — file resolution used to hand
back the.ltxml(Perl source) and tokenize it, so raw-loading a package that
ships one (e.g. sTeX) emitted spurious errors; a missing binding now falls
through to the raw.sty, matching pdflatex. -
\usepackage{X}finds anX.sty.rhairuntime binding on$TEXINPUTS— a
.rhaibeside your document still overrides a compiled binding; one in a
texmf tree only fills a gap. -
cprotectis now supported —\cprotect\section{\verb|…|}and
\cprotect\footnote{\begin{verbatim}…\end{verbatim}}produce the verbatim
title/footnote, with\cMakeRobustand\cprotEnv. Perl LaTeXML has no
binding for this package. -
The
.rhaibinding interface has a reference, generated from the live
engine intocargo docso it cannot drift from what is registered. -
Bibliographies stop losing reference content —
.bibfield markup
(\url,\href,\emph) survived only as dead literal text, and eleven
field kinds (howpublished,institution,address,edition,series,
type, …) were emitted by no branch at all. Recovering that content means
the fields are now interpreted, so they must also be given what a
.bst-generated.bblprovides — the\providecommand{\url}…block, and
a percent that stays literal (BibTeX has no comments, and%is routine in
an encoded URL). Measured over a 30,079-document arXiv sample: 62 more
documents convert cleanly, 44 fewer carry errors. -
A reference's
& % # _are the characters the author typed — "Taylor &
Francis" in a publisher used to report an error and print "Taylor Francis",
a gene idAT1G01010_v2came out as a subscript error, and a percent in an
encoded URL commented out the rest of the entry. A.bibfield's content is
data, so all four are kept, while\emph{…},$x_1+x_2$and accents in the
same field keep working. -
A reference exported through HTML no longer shows
&for&— a
doubly escaped ampersand (\&) in a.bibtitle, journal or booktitle
is decoded back to the one character the author wrote. -
amsrefs
\bibvalues digest as live TeX —\MR{…}came out as literal
characters andpagesrendered empty. -
MathReview / ZentralBlatt links are synthesized —
mrnumber/zblno
produced no link at all — and an entry carrying bothdateandyearno
longer emits a duplicate date. -
An accent in a MathSciNet / Zentralblatt reviewer name survives —
MRREVIEWER = {Fran\c cois Digne}came back as\ccois, an undefined
macro, because flattening a token list drops the space that terminates a
control word. That flattening had already cost author names and two column
types, so the tokenizing entry points now take a dedicated TeX-string type
and it no longer compiles anywhere in the tree. -
A biblatex
.bblcarrying two\datalistblocks no longer hangs the
conversion on a self-referential\let. -
A biber
\missing{key}is a named warning, not an undefined-command error. -
A font-encoding text symbol inside a
\citekey or package option no
longer hangs the conversion — an accented character reached the encoding
dispatch while the font encoding was the stay-ASCII one, and the fallback
could not terminate under pure expansion. -
The author-year citation label uses the short author form — a
collaboration paper's label ran to 5104 characters and displaced the entry. -
More math parses — fences split by TeX's null delimiter, bare operators
used as operands (f(\cdot),(+)), and mixed relation/term comma lists. -
$50,000$reads as one number rather thanlist(50, 000)— US default;
the European$1.234,56$reading is unchanged. -
Math in a section title survives into the table of contents and into any
\refto that section. -
latexmlmathno longer empties a single-structure formula
(\frac{1}{2},\sqrt{2}). -
siunitx complex numbers are faithful to Perl — the port had flattened away
the imaginary-unit enrichment and mantissa brackets — plus the v3 command
surface and the full\sisetupdefault set. -
\lstinputlistingconverts the requested snippet —lastline=Nused to
unbalance the listing and swallow the rest of the document, and CRLF sources
bled comment styling down the file. -
Very large documents use far less memory — peak RSS 9.05 → 5.99 GB on a
232K-line book, wall time unchanged. -
A document far larger than RAM converts — the core stage builds and
releases the document in fragments, spilling completed parts to disk, so
peak memory follows fragment size rather than document size. A 131 MB
5-million-line book that could not be converted at all now completes within
a 48 GB budget (28.1 GB peak, 2.66 GB of XML). Output is byte-identical to
the normal path. -
Post-processing no longer holds the whole site in memory — pages are
scanned once, then rendered and written one at a time, so peak memory
follows a single page instead of the page count. A 40,201-page document
that grew to 80 GB and wrote nothing now completes flat at 16 GB, and the
stage is 2.5x faster (27:04 -> 10:48) with byte-identical output. -
A query that cannot be answered is an error, not an empty result —
whole-document XPath silently returned "no matches" when libxml2 refused
it, so on a large document nothing was cross-referenced, no MathML was
generated, and a 0-byte page was written with a success exit code. Those
queries are now answered by traversal, and a genuine failure says so. -
Math parsing no longer frees nodes it is still using — discarded
subtrees are released at a formula boundary rather than at the discard
site. The use-after-free behind this crashed 22 papers in a
30,000-document corpus run, and its formulas were real content, not
garbage. Releasing per formula also uses less memory than before the
fix: a 19.8 MB book peaks 7% lower streamed and 9% lower on the plain
path. -
Very large documents convert 2.1x faster — the 131 MB / 5-million-line
book drops from 70 to 33 minutes, byte-identical output. A memory-pressure
trigger had been fragmenting the work into 459,000 tiny segments (and the
intermediate spill text was half indentation, written only to be deleted);
the work now flows in ~6,000 sensible pieces, serialized flat. -
Conversion logs shrink ~100x on such runs (3.1 million lines → 26,000):
every message carried a spurious blank line, and per-segment progress now
reports at milestones instead of three lines per segment. -
Streamed conversions report where the time went — per-phase wall time
(digest, build, math parse, …) lands in telemetry and two summary lines;
previously the streamed path reported no phase timing at all. -
A document whose XML exceeds 2 GiB post-processes instead of failing —
libxml2's in-memory parser takes its length as a 32-bit int, so the 131 MB
book's 2.68 GB core XML died at the core→post handoff (Document too large for i32) and echoed raw XML into the.htm. Oversized handoffs now spill
to a temp file beside the destination and parse through the streaming file
reader. -
A
\Descriptionon a table is expected, not a defect — acmart asks for
one on every float and a table has no image, so attaching the description
to the table is reported as information rather than a warning that demoted
otherwise-clean papers. -
A memory limit set by a container is honoured — the ceiling was derived
from the host's RAM, so a memory-limited container chose a budget it could
never reach and was killed by the kernel instead of stopping gracefully. -
Post-processing stops gracefully rather than being killed — it had no
cooperative memory check at all, so an oversized run died at the hard
ceiling with nothing written; pages that finish are now kept. -
--max-memoryis the budget, and everything follows from it — the
graceful-Fatal fuse, the point where spilling begins, and the fragment size
are all derived, so no two memory settings can contradict each other. The
default is now half of physical RAM (2 GiB floor, 64 GiB cap) rather than
90 %, which on a 16 GB laptop had let one conversion reach 10.8 GiB before
complaining.--max-memory=0lifts the ceiling but still spills.
--streamingforces fragmentation,--streaming=falseforces the plain
path, and large multi-file documents are recognised by their whole source
tree instead of the main file's size alone. -
--max-memoryis the single memory knob,0disables limiting entirely,
and no environment variable can countermand it. The stomach's box-list
ceilings now ride it too — they were fixed constants, so--max-memory=0
still Fatal'd on a 3.2 GB budget no flag could raise, and a very large
document could not be converted at all. -
A Fatal is reported as a Fatal — the stomach's runaway guards raise
outside theFatal!macro, so their diagnostic never reached the status
tally: a run could printFatal:and still sign off asConversion complete: No obvious problemswith a success exit code. Recovery of the
already-digested content is unchanged (and still runs through
post-processing, so a partial document is written) — only the verdict is
corrected. -
Environments report their true source extent — the locator spans through
the matching\endinstead of collapsing to the\begin.
What's Changed
- fix(bibliography): one runaway .bib field no longer empties the whole bibliography by @dginev in #395
- feat(bibliography): convert a raw .bib through the engine, not a string parser by @dginev in #396
- fix #386: build XML through libxml, not string concatenation by @dginev in #397
- fix(bibliography): revert author names with UnTeX — space-form accents were becoming undefined macros by @dginev in #399
- fix(bibliography): an abstract no longer takes the whole bibliography with it by @dginev in #400
- docs(release): an RC is published but stays a prerelease by @dginev in #402
- docs(bibliography): record the two-regime design tension by @dginev in #407
- fix(bibliography): a blank line in a .bib field no longer starts a new bibliography item by @dginev in #406
- fix(contrib): silence.sty and the bundled arXiv styles no longer leave commands undefined by @dginev in #404
- docs(bibliography): the data-vs-TeX seam is chosen by blast radius by @dginev in #408
- fix(bibliography): a .bib field's specials are data — the two-treatment design by @dginev in #403
- fix(bibliography): a reviewer's accented name is a name, not an undefined macro by @dginev in #411
- docs(sync-status): rationalize .bst support as R9-BST by @dginev in #412
- docs(sync-status): retract the .bst-vendoring premise behind R9-BST by @dginev in #414
- feat(core): a TeXString type, so a flattened Tokens cannot reach the tokenizer by @dginev in #413
- A .bib field's ^ is data; mathscinet.sty gets a real binding by @dginev in #415
- A .bib @Preamble already executes; guard it, and keep the \cprime stub by @dginev in #417
- fix(bibliography): digest the cited entries, not the whole .bib library by @dginev in #416
- fix(bibliography): an unmatched $ in a .bib field is currency, not math by @dginev in #418
- fix(bibliography): \cprime belongs to mathscinet.sty, not to every document by @dginev in #419
- docs: reconcile the bibliography-campaign docs with what actually landed by @dginev in #420
- fix(post): a graphics test asserting on subprocesses must bypass the cache by @dginev in #422
- fix(hyperref): \href in a Semiverbatim argument no longer expands forever by @dginev in #423
- An \ext@arrow mkern amount may be braced; the rest of the unclosed-math cluster is Perl's too by @dginev in #424
- fix(expl3): loading xparse/expl3 silently destroyed the \c cedilla accent by @dginev in #425
- A LaTeX kernel command before \documentclass no longer derails the document by @dginev in #426
- spconf papers keep their Index Terms and author blocks by @dginev in #427
- fix(engine): \meaning of a \chardef token reports the character, not "Register" by @dginev in #428
- docs: make docs/README.md the authoritative doc index; trim CLAUDE.md resident context by @dginev in #429
- feat(acmart): \Description becomes the figure's ARIA text alternative by @dginev in #430
- perf(memory): share the font handle instead of deep-cloning it per box by @dginev in #432
- fix(memory): guard the Build phase cooperatively, and keep what it built by @dginev in #434
- feat(memory): derive the default ceiling from the machine, portably by @dginev in #435
- docs: design for fragmented core-stage conversion (streaming XML) by @dginev in #433
- docs: refresh the test baseline, and flag the coming fatal-rate discontinuity by @dginev in #437
- docs(acmart): correct \Description comments left stale by the ARIA redesign by @dginev in #436
- chore(audit): re-verify clang-sys at 1.9.1 and move the pin by @dginev in #439
- perf(stomach): share the constructor definition instead of cloning it per whatsit by @dginev in #438
- perf(core): one-borrow token read, duty-cycled cycle guard, pinned hot keys by @dginev in #441
- fix(acmart): \Description describes the image, not the float by @dginev in #440
- fix(preload): strip the bracket and .cls/.sty off the preload PI, as Perl does by @dginev in #442
- docs: prune obvious/misleading standing instructions by @dginev in #443
- fix(captions): give \fnum@ an empty group so an arg-taking hook cannot eat the caption's brace by @dginev in #446
- fix(bibliography): AY is the abbreviated label, and four more MakeBibliography gaps by @dginev in #445
- feat: streaming XML core — bounded-memory conversion for book-scale documents by @dginev in #448
- Bibliography absence: 16 fixes, 291 known articles recovered and re-verified by @dginev in #444
- fix(mathml): close the F17 pMML gaps, target MathML Core, chain post-scripts to any depth by @dginev in #447
- feat: --max-memory is the budget, and everything follows from it by @dginev in #449
- fix(fonts): a font selected by family must decode through that family's fontmap by @dginev in #450
- fix(fonts): \char decodes with FontDecode semantics, in math as in text by @dginev in #452
- fix(fonts): \DeclareMathAlphabet maps NFSS codes to abstract font properties by @dginev in #453
- fix(fonts): sized delimiters scale to NOMINAL_FONT_SIZE, not a baked-in 10 by @dginev in #454
- fix(fonts): AMSb's six negated relations were decoding to the empty string by @dginev in #455
- fix(fonts): a multi-character fontmap slot survives \DeclareTextSymbol by @dginev in #456
- docs: font-chain audit status, two pinned bugs, and the rule that prevents them by @dginev in #457
- fix(post): bounded-memory page rendering, no query fails in silence, and a 2.5x faster post stage by @dginev in #451
- ci: run tests with nextest, sharded 4 ways — the suite was serialized by @dginev in #458
- fix(math): stop freeing mid-parse — a use-after-free behind 22 corpus fatals by @dginev in #459
- fix(memory): the default ceiling respects a cgroup limit by @dginev in #461
- fix(post): cooperative memory checks at the phase and page seams by @dginev in #462
- perf(math): drain discards per formula — below the pre-fix memory baseline on both paths by @dginev in #460
- release: 0.7.5-rc4 by @dginev in #463
- fix(windows): MEMORYSTATUSEX — this arm had never compiled by @dginev in #464
- ci(windows): compile the Windows arms when they change, and weekly by @dginev in #465
- fix(windows): --max-memory was never enforced on Windows (+ 3 Windows-only lint/test gaps) by @dginev in #466
- perf(streaming): 131 MB witness 70:29 → 32:56 — yield floor, flat spill, shared labels, phase telemetry by @dginev in #467
- release: recut 0.7.5-rc4 with the large-document performance work by @dginev in #468
- fix(streaming): shared conversion tail — ASF stats and source-map reach streamed runs by @dginev in #469
- docs(perf): two closed investigations from the witness campaign by @dginev in #470
- fix(post): 2 GiB-plus core→post handoff spills to disk instead of dying on libxml2's i32 limit by @dginev in #471
- release: recut 0.7.5-rc4 with the 2 GiB post-handoff fix by @dginev in #472
- feat(post): two-pass streaming split — book-scale documents render on commodity RAM by @dginev in #477
- release: recut 0.7.5-rc4 with the streaming post-split by @dginev in #478
- fix #473: adjacent display equations no longer render touching by @dginev in #479
- fix(status): the combined verdict is the LAST word of every multi-phase run by @dginev in #480
- feat(memory): default --max-memory follows actual machine headroom by @dginev in #482
- fix #431: fancyvrb Verbatim renders true to the source by @dginev in #481
- docs(audit): record libxml 0.3.21 re-verification; fix declared-license phrase by @dginev in #483
- fix(diag): every printed diagnostic counts — lossless tally across all phases by @dginev in #484
- release: recut 0.7.5-rc4 with the single-invocation witness milestone by @dginev in #485
- docs(perf): witness phase shares; tally residual closed by @dginev in #486
- feat(post): ObjectDB persists to SQLite — Perl --dbfile parity by @dginev in #487
- fix(diag): robust cap latch in emit_error; post-cap divergence documented by @dginev in #488
- docs(sync): rc4-recut 2605+2606 rerun triage round by @dginev in #489
- feat(post): process-parallel page rendering — N workers over the spilled pages by @dginev in #490
- fix(math): stale deferred-discard handles no longer straddle pooled-worker threads by @dginev in #491
- fix(post): the render fleet sizes itself to actual headroom by @dginev in #492
- release: cut 0.7.5-rc5 — parallel rendering, complete diagnostics, headroom defaults by @dginev in #493
- fix(cli): a conversion always writes .latexml.log — Perl latexmlc parity by @dginev in #495
- feat(diag): memory Fatals report what the document needs; disk staging loses the scary wording by @dginev in #496
- release: recut 0.7.5-rc5 with the UAT log/diagnostics round by @dginev in #497
Full Changelog: 0.7.5-rc3...0.7.5-rc5