Skip to content

Vinculum 1.4.2

Choose a tag to compare

@clintecker clintecker released this 17 Jul 02:06
· 64 commits to main since this release

Correctness release: Linux font metrics, two parser data-loss bugs, and FreeType memory/thread safety. No API changes — a pure bug-fix patch.

If you render on Linux with anything but Latin Modern, your output changes (and is now correct).

Fixed

  • Linux: every font now uses its own MATH constants. (#1)
    MathTableParser parses a MATH table, but the Linux renderer handed it the whole .otf — whose first bytes are the sfnt tag ('OTTO'), failing the version guard. constants returned nil, and the ?? .latinModern fallback silently gave Termes, Pagella, STIX Two and Fira Math Latin Modern's metrics — so font choice had no effect on Linux layout, and nothing went red. The table is now extracted with FreeType, mirroring the Apple path's CGFont.table(for:).
    Measured: Fira Math's axis height is 0.28 and STIX Two's 0.258 — both had been rendering as 0.25. Axis height places every fraction bar and minus sign. Latin Modern is unchanged.

  • Parser: a stateful switch no longer swallows \end. (#2)
    \bf / \color{…} / \displaystyle as the last token of an environment cell scanned straight through the environment's own \end, absorbing the terminator, the env name, and any trailing content — silently lost.

  • Parser: double sub/superscript degrades visibly instead of dropping content. (#9)
    a_b_c / x^2^3 (TeX's "Double subscript" error) silently discarded the earlier atom and rendered a_c. It now degrades to a visible, source-locatable fallback. Behavior note: that input previously rendered a_c; it now shows a fallback card. That's the fix — it was deleting your content.

  • Linux: FreeTypeFont owns its font bytes. (#4)
    FT_New_Memory_Face doesn't copy — it reads through the pointer for the face's whole lifetime — but the pointer came from Data.withUnsafeBytes, which only guarantees validity inside the closure. Correctness rested on Data's undocumented representation.

  • Linux: FreeTypeFont's shared FT_Face is locked. (#3)
    Its @unchecked Sendable was a false promise: every accessor calls FT_Load_Glyph, which mutates the face's shared glyph slot. Sharing one font across threads didn't merely race — it crashed (SIGBUS). Now serialized behind an NSLock, mirroring MathFont.ctFontLock on the Apple side.

Under the hood

  • Reproducible Linux CI (#32) — the only job that builds the Silica/Cairo/FreeType backend was resolving branch:master against upstream HEAD every run. It now restores a pinned, CI-scoped lockfile. Default consumers stay Silica-free.
  • A Linux render-regression net (#11) — the backend's only assertion was "is it a valid PNG?", so a blank or garbled render passed silently. Renders are now diffed against committed ink signatures.
  • Doc-drift guard (#31, #47) — CI now fails if CONTRIBUTING.md's stated swift-tools-version diverges from Package.swift; the ARCHITECTURE checklist now lists all seven exhaustive MathNode switches (it claimed four).

Performance claims were re-verified in release after the parser changes rather than assumed — the published numbers hold (parse 22.7µs, warm hit 0.50µs, headless layout 7.9µs, ~76% of a cold render is CoreText). See docs/PERFORMANCE.md.

Install

.package(url: "https://github.com/2389-research/Vinculum.git", from: "1.4.2")

Full detail: CHANGELOG.md · Docs & gallery