main is ready to ship as 0.26.0, but I cannot publish it: I have GitHub maintain on this repo, which does not carry crates.io ownership. The crate's owners are @RazrFalcon, @alerque, @LaurenzV and @khaledhosny.
@alerque — could you add me as a crates.io owner (or whichever existing owner is easiest to reach)? I'll follow up with my crates.io handle. That is the only thing standing between the current main and a release.
Also flagging this for visibility rather than landing a release unannounced: the backlog was large and some of it is behavioural.
What is on main
14 PRs merged, from 8 contributors, some open for over a year. Tests went 153 -> 196; the suite now runs in both debug and release.
Six of these are denial-of-service fixes against malformed input, with measured impact:
- A 255-byte
CFF table hung for over 30 seconds. STACK_LIMIT bounded subroutine nesting depth but not total invocations, so work grew as fanout^depth.
- 8 bytes of
GSUB caused a self-referential extension lookup to recurse unboundedly — a stack-overflow abort in debug, an infinite loop in release.
- The same depth-vs-total-work mistake appeared independently in
glyf/gvar composite outlining and in the COLRv1 paint graph.
The pattern is worth naming, because it recurred three times: a depth limit does not bound total work. With fan-out b and depth d, a small font forces b^d visits while never exceeding the cap. Each of these now bounds visits, not just depth.
One correctness bug is worth calling out separately: a well-formed 65535-glyph font was rejected outright, glyf was dropped, and every outline returned None (#226).
Breaking changes
Four, all listed under ### Changed in the changelog. The two that produce no compile error, and so need the most visibility:
Face::set_variation now returns None for an axis the face does not expose. It previously returned Some(()) unconditionally, contradicting its own documentation.
gvar-alloc now implies alloc rather than std, so default-features = false, features = ["gvar-alloc"] no longer satisfies the float guard and must select std or no-std-float explicitly.
Infrastructure
CI was structurally unable to see some failures. cargo test --release was red on main and had been for months — a #[should_panic] had enshrined a debug_assert!, which is compiled out in release. CI ran debug only. Likewise the c-api crate's Rust tests had never run in CI at all, because the repo root is not a workspace and a root cargo test never reaches it. Both now run, along with the alloc-without-std feature permutation, which no other leg covered.
MSRV is 1.88 and all four crates are on edition 2024. CONTRIBUTING.md did not exist and now does.
mainis ready to ship as 0.26.0, but I cannot publish it: I have GitHubmaintainon this repo, which does not carry crates.io ownership. The crate's owners are @RazrFalcon, @alerque, @LaurenzV and @khaledhosny.@alerque — could you add me as a crates.io owner (or whichever existing owner is easiest to reach)? I'll follow up with my crates.io handle. That is the only thing standing between the current
mainand a release.Also flagging this for visibility rather than landing a release unannounced: the backlog was large and some of it is behavioural.
What is on
main14 PRs merged, from 8 contributors, some open for over a year. Tests went 153 -> 196; the suite now runs in both debug and release.
Six of these are denial-of-service fixes against malformed input, with measured impact:
CFFtable hung for over 30 seconds.STACK_LIMITbounded subroutine nesting depth but not total invocations, so work grew as fanout^depth.GSUBcaused a self-referential extension lookup to recurse unboundedly — a stack-overflow abort in debug, an infinite loop in release.glyf/gvarcomposite outlining and in the COLRv1 paint graph.The pattern is worth naming, because it recurred three times: a depth limit does not bound total work. With fan-out
band depthd, a small font forcesb^dvisits while never exceeding the cap. Each of these now bounds visits, not just depth.One correctness bug is worth calling out separately: a well-formed 65535-glyph font was rejected outright,
glyfwas dropped, and every outline returnedNone(#226).Breaking changes
Four, all listed under
### Changedin the changelog. The two that produce no compile error, and so need the most visibility:Face::set_variationnow returnsNonefor an axis the face does not expose. It previously returnedSome(())unconditionally, contradicting its own documentation.gvar-allocnow impliesallocrather thanstd, sodefault-features = false, features = ["gvar-alloc"]no longer satisfies the float guard and must selectstdorno-std-floatexplicitly.Infrastructure
CI was structurally unable to see some failures.
cargo test --releasewas red onmainand had been for months — a#[should_panic]had enshrined adebug_assert!, which is compiled out in release. CI ran debug only. Likewise thec-apicrate's Rust tests had never run in CI at all, because the repo root is not a workspace and a rootcargo testnever reaches it. Both now run, along with thealloc-without-stdfeature permutation, which no other leg covered.MSRV is 1.88 and all four crates are on edition 2024.
CONTRIBUTING.mddid not exist and now does.