@docfonts/fallbacks 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -86,6 +86,7 @@ Keys are normalized. Use `normalizeFamilyName` for lookups. Rows whose substitut
86
86
  - `lineBreakSafe` - true when advances preserve line breaks: `metric_safe`, `near_metric`, or monospace `cell_width_only`.
87
87
  - `faces` - reviewed face coverage for this evidence row. If any face is `true`, respect it as face-scoped coverage (a row can be Regular-only). If all faces are `false`, the row is **not** face-scoped (e.g. a category fallback whose physical font does have faces) and the face-aware helpers treat it as renderable for any face.
88
88
  - `evidenceId` - the stable id for the reviewed evidence row; look the full row up in `SUBSTITUTION_EVIDENCE`.
89
+ - `glyphExceptions` - named glyph-level divergences that qualify this fallback (e.g. one codepoint reflows), or omitted when none. A family lookup carries all of the row's; a face lookup (`getRenderableFallbackForFace`) carries only that face's, so Cambria Regular shows none while Bold Italic shows its grave-accent exception.
89
90
 
90
91
  `cell_width_only` keeps monospace advances stable, but glyph shapes can still differ. A `substitute` can still have a lower-fidelity `verdict` when one face or glyph is qualified. The verdict is the fidelity signal.
91
92
 
package/dist/fallbacks.js CHANGED
@@ -34,8 +34,17 @@ const BY_LOGICAL = new Map(SUBSTITUTION_EVIDENCE.map((row) => [
34
34
  * Build the FontFallback for a row known to carry a renderable physical family. `verdict` is passed in
35
35
  * so a face-aware caller can supply the per-face verdict (faceVerdicts[face]) instead of the worst-face
36
36
  * top-level one - e.g. Cambria regular is metric_safe even though the family rolls up to visual_only.
37
+ * `faceSlot` (a face lookup) scopes the glyph exceptions to that face; omitting it (a family lookup)
38
+ * carries all of the row's. Empty exception sets are dropped so the field is present only when it bites.
37
39
  */
38
- function buildFallback(row, physicalFamily, verdict) {
40
+ function buildFallback(row, physicalFamily, verdict, faceSlot) {
41
+ // Always hand back a FRESH array - filter() already copies; the family path must copy too, or a
42
+ // consumer mutating it would corrupt the shared evidence row for later lookups.
43
+ const glyphExceptions = faceSlot
44
+ ? row.glyphExceptions?.filter((g) => g.slot === faceSlot)
45
+ : row.glyphExceptions
46
+ ? [...row.glyphExceptions]
47
+ : undefined;
39
48
  return {
40
49
  substituteFamily: physicalFamily,
41
50
  policyAction: row.policyAction,
@@ -43,6 +52,9 @@ function buildFallback(row, physicalFamily, verdict) {
43
52
  lineBreakSafe: LINE_BREAK_SAFE_VERDICTS.has(verdict),
44
53
  faces: row.faces,
45
54
  evidenceId: row.evidenceId,
55
+ ...(glyphExceptions && glyphExceptions.length > 0
56
+ ? { glyphExceptions }
57
+ : {}),
46
58
  };
47
59
  }
48
60
  /** Decide a single row against the consumer's asset availability. Pure. */
@@ -98,7 +110,7 @@ function decideRowForFace(row, face, canRenderFamily) {
98
110
  const faceVerdict = row.faceVerdicts?.[face] ?? row.verdict;
99
111
  return {
100
112
  kind: "fallback",
101
- fallback: buildFallback(row, base.fallback.substituteFamily, faceVerdict),
113
+ fallback: buildFallback(row, base.fallback.substituteFamily, faceVerdict, face),
102
114
  };
103
115
  }
104
116
  /**
package/dist/types.d.ts CHANGED
@@ -99,6 +99,14 @@ export interface FontFallback {
99
99
  faces: FaceCoverage;
100
100
  /** stable reviewed-evidence id; look the full row up in {@link SUBSTITUTION_EVIDENCE}. */
101
101
  evidenceId: string;
102
+ /**
103
+ * Named glyph-level divergences that qualify this fallback (e.g. one codepoint reflows). Scoped to
104
+ * the lookup: a family lookup ({@link getRenderableFallback}) carries ALL of the row's exceptions; a
105
+ * face lookup ({@link getRenderableFallbackForFace}) carries only the requested face's. Omitted when
106
+ * none apply - so a renderer can surface a precise "this face reflows on U+0060" without re-deriving.
107
+ * A fresh array each call (readonly): mutating it never affects another lookup.
108
+ */
109
+ glyphExceptions?: readonly GlyphException[];
102
110
  }
103
111
  /**
104
112
  * The full, honest outcome of a fallback lookup. A discriminated union so a consumer can tell apart
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docfonts/fallbacks",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Measured open-font fallbacks for proprietary document fonts.",
5
5
  "license": "MIT",
6
6
  "type": "module",