feat(fallbacks): face-aware lookups for Regular-only substitutes#43
Merged
Conversation
de77a2f to
86d1da6
Compare
A Regular-only row (Baskerville -> Bacasime, already published; Cooper Black -> Caprasimo, coming) could be blindly routed for bold/italic by the family-level API. Add face safety: - getRenderableFallbackForFace(family, face, opts) returns the substitute only for a covered face, null otherwise - bold/italic of a Regular-only row no longer route to a face the substitute lacks. - getFallbackDecisionForFace adds a face_missing decision kind (substitute exists, this face does not). A covered face carries its OWN verdict (Cambria regular is metric_safe though the family rolls up to visual_only). - Every FontFallback now carries faces, so the family-level createFallbackMap is self-describing; its doc notes a face-scoped entry is only safe in a face-aware resolver. Face-scope rule: an all-false faces means the row is NOT face-scoped (a category fallback whose physical font does have faces), so it renders for every face and never becomes face_missing. Only a measured per-face substitute (Baskerville) is gated. No data change.
86d1da6 to
837538f
Compare
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.
The package API is family-oriented -
getRenderableFallback("Baskerville Old Face")answers "which family", not "which face." But Baskerville -> Bacasime is Regular-only (and it's already in published 0.2.0), so a consumer wiring a family-level map could route bold/italic Baskerville to a substitute that has no such face. Cooper Black -> Caprasimo (measured, regular-only) would add a second such row. This lands the face-scope safety before any of that.getRenderableFallbackForFace(family, face, opts)returns the substitute only for a face it actually provides -nullfor bold/italic of a Regular-only row, instead of mis-routing.getFallbackDecisionForFaceadds aface_missingdecision kind (the substitute exists, but not this face) so a UI can route it through absence handling. A covered face carries its OWN verdict, not the family worst-face rollup -Cambriaregular readsmetric_safeeven though the family rolls up tovisual_only.FontFallbacknow carriesfaces(the substitute's RIBBI coverage), so the family-levelcreateFallbackMapis self-describing and a consumer can route per-face without a second lookup.No data change - this is package-level safety only. The Cooper Black row stays internal (it needs the reviewed-record -> export pipeline, not a hand-edit) until that path is restored.
Verified: typecheck, lint clean;
bun test20 pass (5 new face-aware tests incl. Baskerville Regular-only routing and Cambria per-face verdict); built dist loads in plain Node and exports the new helpers. Built from a clean worktree off origin/main; the local main tree's WIP is untouched.