Skip to content

0.7.5-rc2

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 24 Jul 06:45
· 236 commits to main since this release
f8f561d

Installation

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-rc2-1_amd64.deb, or the portable latexml-oxide-0.7.5-rc2-x86_64-unknown-linux-gnu.tar.gz
  • Linux (aarch64 / arm64) — latexml-oxide_0.7.5-rc2-1_arm64.deb, or the portable latexml-oxide-0.7.5-rc2-aarch64-unknown-linux-gnu.tar.gz
  • macOS (Apple Silicon) — latexml-oxide-0.7.5-rc2-aarch64-apple-darwin.tar.gz
  • macOS (Intel) — latexml-oxide-0.7.5-rc2-x86_64-apple-darwin.tar.gz
  • Windows (x86-64) — latexml-oxide-0.7.5-rc2-x86_64-pc-windows-msvc.zip (unzip; latexml_oxide.exe is self-contained, no installer)

Changelog

[0.7.5] (Rhai binding-API parity + default-CSS sync; ar5iv corpus fixes; file-resolution robustness)

  • Runtime (Rhai) binding API brought to feature-parity with the compile-time
    macros.
    A binding can now fetch an already-installed definition and push
    before/after digest and construct hooks onto it (LookupDefinition), pass the
    same flexary, unordered option bags to DefPrimitive/DefMath/DefConstructor
    (beforeDigest/afterDigest/afterDigestBody), register new definitions from
    inside a running body, shell out to external commands, read the engine version,
    and use the full diagnostics surface (Info/Fatal/Note/progress).
    List-valued state lookups no longer panic or leak their internal representation.
  • A .rhai binding can now parse and manipulate XML/(X)HTML.
    document.insertXML(markup) splices a parsed subtree into the document at the
    current point — the runtime half of BookML's \bmlRawHTML<ltx:rawhtml>
    where previously only escaped text could be inserted. ParseXML(markup) exposes
    the parser on its own, returning nodes a script can inspect, walk and edit
    before inserting them; a chunk may be a fragment of several siblings. Parsed
    nodes are safe to hold because each owns its document. Malformed markup is
    rejected outright rather than quietly salvaged — libxml's recovery mode
    silently deletes author content (<b>a</b> <i>b</i> became <b>a</b>,
    a&nbsp;b became ab). Inserted markup also keeps its own namespace:
    namespaces resolve by URI through the registered prefix map
    (RegisterNamespace) instead of being assumed to be LaTeXML's, and wildcard
    schema entries such as xhtml:* are honoured, so attributes like class
    survive onto the final page. A malformed chunk degrades only its own binding:
    the rest of the document still converts, and the reported error names the
    likely cause and quotes the offending snippet.
  • .rhai bindings now reach the same document XML surface the compile-time
    bindings do
    — XPath query (findnodes/findnode), element insertion
    (insertElement), and structural editing (addClass, generateID,
    removeNode, replaceNode, renameNode, wrapNodes, unwrapNodes,
    appendClone, openElementAt/closeElementAt), each under its Perl name, so
    the runtime and compile-time binding layers do not drift apart. Reading a
    namespaced attribute such as xml:id back by its qualified name now works.
  • A failing .rhai binding no longer costs the whole document. An error
    raised anywhere in a script body — a throw, a typo'd method name, an
    operation-limit breach — used to abort the entire conversion; a single
    throwing macro produced an empty document. Each binding kind now degrades to a
    neutral result (an empty expansion, a skipped constructor, a false
    conditional, …), reports a clean Error:, and the rest of the document
    converts. A binding that fails repeatedly still ends the run at the usual
    error ceiling, and a script that fails to compile still simply does not
    install.
  • Default HTML styling re-synced to vanilla LaTeXML.css — restores justified
    text, \underline/\overline, and verbatim no-wrap that had drifted from
    upstream; the .htm destination extension now infers HTML5 like .html.
  • A stale or empty stylesheet already in the output directory is now overwritten
    instead of being left in place — previously a LaTeXML.css/ltx-article.css
    left by an earlier run could be truncated to empty, leaving the page unstyled.
  • Split output (--splitat) now styles every page. Each auto-generated split
    child (e.g. Ch1.html) again loads the default stylesheets and carries the
    document date, matching Perl — a shared post-processing XPath defect had been
    dropping resources, processing instructions, and other relative-path lookups
    (also repairing split navigation and cross-reference/glossary resolution).
  • Split-page navigation links reach Perl parity. Each page now emits the full
    <link rel=…> head set — prev back to the parent for a first child, the
    relation-typed chapter/section/subsection sibling and ancestor links, and
    full-breadcrumb title= attributes on all of them (previously the relation-typed
    links and prev-for-first-child were missing and the titles were empty).
  • Generator identifier now spells out the full product name ("LaTeXML oxide")
    and stamps the version into the XSLT output to match Perl.
  • A standalone document class's options are no longer mis-loaded as packages.
  • A package loaded in a subfile's preamble keeps its definitions. A
    standalone or \import-ed child used to end an otherwise complete conversion
    with \ifpgf@external@grabshipout undefined: the group around the child's
    preamble discarded what the package defined, while the document hooks reading it
    survived. The child's own definitions stay scoped to the child.
  • Named scopes are tracked correctly again. A scope that had been deactivated
    still read as active, so it could never be re-activated — a counter- or
    label-derived reference-number scope reused after a counter reset kept the
    stale one — and a second deactivation popped the same bindings twice.
  • \includefrom/\subincludefrom no longer drop their file in silence — the
    included content used to vanish with no error or warning at all.
  • The cortex worker builds without the runtime-bindings feature — 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 about it: a failed
    Kpaths::new() was discarded, so every lookup returned None while embedded
    bindings and dumps kept the conversion working. The only symptom was
    Can't find TeX file X — indistinguishable from a file that is genuinely
    absent. The trigger was an unresolvable kpsewhich: missing from the
    process's PATH (as opposed to the user's interactive shell), a stale
    KPSEWHICH, not executable, or a kpsewhich.exe beside a Linux binary under
    WSL. Fixed in kpathsea 0.3.4
    (a linked Kpaths::new() degrades its program-name anchor instead of failing)
    plus a subprocess fallback here.
  • Every conversion log now records the file-resolution backend
    in-process, subprocess kpsewhich, or unavailable. A dead or degraded
    resolver used to be invisible and had to be inferred; it is now one line in an
    ordinary log, with no special flags. A host TeX installation remains optional,
    so an absent one warns rather than errors.
  • ar5iv corpus fixes (from the 2026-07 issue sprint): xcolor now honors a
    dvipsnames global class option; \sidecaptionvpos bound as a layout no-op;
    \newtcblisting bodies close as verbatim; agujournal2019 end-matter
    (rotating + acronyms/notation); blkarray support, recovering papers that
    previously hit OOM/timeout; scrartcl \titlehead frontmatter capture; and
    frontmatter bindings for fairmeta, selfevolagent and openmoss classes.
  • A deferred package-load miss no longer poisons a later raw load.
  • Perl .ltxml bindings are never read as TeX. When raw-loading a style
    package that also ships a Perl LaTeXML binding (e.g. sTeX's stex.sty.ltxml),
    file resolution used to hand back the .ltxml — Perl source — and the loader
    tokenized it as TeX, emitting spurious errors. latexml-oxide can't run Perl
    bindings; binding availability is decided by its own dispatcher, so a missing
    binding now falls through to the raw .sty, matching pdflatex. The
    standalone binding also regained standalone.sty's real xkeyval +
    currfile → filehook dependencies, so the package-file hooks
    (\AtEndOfPackageFile, …) and \define@key exist — enough for raw sTeX 3.x
    to load cleanly.
  • \usepackage{X} now finds an X.sty.rhai runtime binding on $TEXINPUTS.
    Previously only \input{file} resolved via TEXINPUTS while a Rhai package
    binding placed in a texmf tree needed an explicit --path; the discovery now
    consults kpsewhich too (covers .sty.rhai and .cls.rhai). A .rhai beside
    your document keeps overriding a compiled binding; one that merely sits in a
    texmf tree only fills a gap, so a stray copy there can no longer displace an
    engine binding.
  • cprotect is now supported\cprotect\section{\verb|…\tensor…|} and
    \cprotect\footnote{\begin{verbatim}…\end{verbatim}} produce the verbatim
    title/footnote instead of \cprotect is not defined. The package works around
    a TeX-only limitation (a braced argument is tokenized before \verb can change
    catcodes) by writing the argument to a scratch file and \inputting it back;
    the binding does the same re-read through a mouth, with no file written.
    \cMakeRobust and \cprotEnv come with it. Perl LaTeXML has no binding for
    this package.
  • The .rhai binding interface now has a reference. Every function a
    runtime binding can call is listed, grouped by the handle it is called on,
    each with a one-line explanation and a link to the Rust item implementing it.
    It renders into cargo doc (and docs.rs) beside the Rust API. The list is
    generated from the live engine rather than hand-maintained, so it cannot
    drift from what is registered, and a test fails if a newly registered call
    is left undocumented.

What's Changed

  • chore(deps): Update syn requirement from 2 to 3 by @dependabot[bot] in #313
  • fix #309: a standalone child's \documentclass options are not packages by @dginev in #310
  • test: strict 0-error gate for hand-written conversion tests by @dginev in #322
  • fix #314: LookupTokens on a VecDequeStored value no longer panics by @dginev in #324
  • ar5iv mini-sprint: alignment + runaway fixes, parity triage by @dginev in #323
  • fix #315: LookupString reverts option lists comma-separated (no Debug leak) by @dginev in #325
  • Revert #325: rethink #315 reversion representation by @dginev in #327
  • fix #315: structural list access (LookupString scalar-only, add LookupValue array) by @dginev in #328
  • refs #312: re-sync bundled default CSS to vanilla; pin .htm→html5 inference by @dginev in #326
  • fix #319: complete the Rhai diagnostics surface (Info/Fatal/Note*/Progress*) by @dginev in #329
  • fix #317: RequireResource accepts an option map (type/media/content) by @dginev in #330
  • fix #316: allow registration (DefPrimitive/DefMacro/…) from inside a body by @dginev in #331
  • fix #318: external commands from Rhai bindings (std::process::Command mirror) by @dginev in #332
  • harden: cortex_worker builds drop runtime-bindings (rhai-free untrusted worker) by @dginev in #334
  • fix #321: LookupDefinition + before/after hook pushes for Rhai bindings by @dginev in #333
  • fix #320: expose the latexml-oxide version to Rhai via LaTeXMLVersion() by @dginev in #335
  • XSLT LATEXML_VERSION generator-stamp: restore Perl-parity + BookML utils.xsl by @dginev in #337
  • Rhai Def* option-bag parity: digest hooks + afterDigestBody by @dginev in #336
  • Generator identifier: spell out the full "LaTeXML oxide" by @dginev in #338
  • fix #312: overwrite stale destination stylesheets instead of truncating them by @dginev in #339
  • fix #333 (r3623947537): hook-push splices at same-level scope, not global by @dginev in #340
  • fix #341: split HTML pages carry the default CSS links (and PIs/date) by @dginev in #342
  • crossref: split-page navigation-link parity with Perl by @dginev in #343
  • feat: raw-load Perl-bound style packages (sTeX) instead of misreading the .ltxml by @dginev in #344
  • fix #345: find a .sty.rhai runtime binding on $TEXINPUTS by @dginev in #346
  • Add TL2026 to the release dump window by @dginev in #349
  • fix #311: hoist a package load past the enclosing group by @dginev in #348
  • feat #350: expose an XML parser to .rhai bindings by @dginev in #351
  • feat #347: bind cprotect.sty (verbatim in macro arguments) by @dginev in #353
  • docs: generate the .rhai interface reference into cargo doc by @dginev in #352
  • release: fix the RC release-body CHANGELOG slice; refresh verification numbers by @dginev in #355

Full Changelog: 0.7.5-rc1...0.7.5-rc2