fix: lift TrueType quadratics to cubics on binary import#120
Merged
Conversation
The binary reader's pen emitted each quadratic as a single off-curve plus an on-curve, which the UFO writer then classified as a cubic curve segment with one control point — corrupting TTF imports on save. Each quad is now lifted to its exact cubic equivalent, keeping the IR cubic-only (the wire and renderer layers have no quadratic support). Also drop the duplicate start point skrifa emits when a contour's closing segment is a curve; closed contours already imply a return to the first point.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the TTF-import geometry-corruption path from the 2026-07 review: the binary reader emitted quadratic segments as generic off/on-curve runs, which the UFO writer then classified as spec-invalid one-control-point cubics.
quad_tonow lifts each quadratic to its exact cubic equivalent (c1 = q0 + ⅔(q1−q0), c2 = q2 + ⅔(q1−q2)) — mathematically lossless. Policy documented at the site: binary imports produce cubic-only IR (wire/renderer layers don't support quadratics).finish()emits an explicit on-curve at the start coordinates beforeclose()when a contour's last segment ends at its start point — the old pen kept it, so closed contours imported with a duplicated first/last point (confirmed on MutatorSans's O). Now deduped.Tests: structural validator over every contour of MutatorSans .ttf and .otf (every on-curve preceded by exactly 0 or 2 off-curves, no duplicated closing points); geometric fidelity sampled at 1e-9 against skrifa's own quad elements; end-to-end TTF → UFO → norad reload asserting every curve is spec-valid. Regression-verified: tests fail against the old reader with the fix stashed.
cargo test --workspace: 306 passed, 0 failed. clippy zero warnings.🤖 Generated with Claude Code