feat(fonts): face-scoped font substitution so single-face clones are safe (SD-3372)#3634
Open
caio-pizzol wants to merge 1 commit into
Conversation
…safe (SD-3372) The resolver mapped logical->physical by family only, so a single-face substitute (a customer fonts.map to a Regular-only font, or a bundled single-face clone) routed bold/italic runs to it and the browser faux-synthesized the missing face, drifting advances and breaking the line-break guarantee that is the point of a metric clone. Resolution is now face-aware: a substitute applies only when it provides the run's weight/style (consulted from the registry's bundled + fonts.add() faces), else the logical family passes through, reported fallback_face_absent. One stored FontPlan per render drives load (requiredFaces), diagnostics (usedFaces), measure/paint resolution, and cache identity (effectiveSignature), replacing resolver.signature at every rendered-layout cache site since face availability can change resolution without changing the family map. resolver.signature now only detects map changes. Additive public change: FontResolutionRecord.face (optional) + FontResolutionReason 'fallback_face_absent'. Stacks on #3626 + #3631; no font files shipped here.
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.
Makes font resolution face-aware so a single-face substitute is never faux-styled onto a weight/style it lacks. Today the resolver maps logical->physical by family only, so a customer
fonts.mapto a Regular-only font (or a bundled single-face clone) would route bold/italic runs to it and the browser would synthesize a faux face, drifting advances and breaking the line-break guarantee that is the point of a metric clone. Resolver safety only; no new fonts ship here.fonts.add()faces); otherwise the logical family passes through, reportedfallback_face_absentand never faux-styled.FontPlanper render is the single source for load (requiredFaces), diagnostics (usedFaces), measure/paint resolution, and cache identity (effectiveSignature). The effective signature replacesresolver.signatureat every rendered-layout cache site, because face availability can change a face's resolution for an unchanged family map;resolver.signaturenow only detects map changes (inDocumentFontController).FontResolutionRecord.face(optional) +FontResolutionReasongainsfallback_face_absent. The report now has per-face rows; declared-but-unused fonts stay as family-level rows (faceundefined).Stacked on #3631; do not open to main.
Review: confirm no
resolver.signatureremains in a measure/resolve/reuse cache path (it should only key map-change detection).Verified:
pnpm check:typesclean;pnpm check:public:superdocgreen (13 stages, no export-snapshot growth). Unit + behavior/layout/visual run in CI.