import { type FaceKey, type FontResolutionReason, type FontResolver } from './resolver.js'; import type { FontRegistry } from './registry.js'; import type { GlyphException, SubstituteVerdict, SubstitutePolicyAction } from './substitution-evidence.js'; import { type FontLoadStatus } from './types.js'; /** * docfonts fidelity evidence for a row where SuperDoc rendered the recommended substitute. Local types * only (no `@docfonts/fallbacks` in the emitted `.d.ts`). `verdict` describes THIS row: the worst-face * top-level verdict on family rows, the per-face verdict on face rows - so a consumer never reads * `reason: 'bundled_substitute'` as a clean clone (Cambria -> Caladea is `visual_only` overall but * `metric_safe` at Regular). */ export interface ResolvedFontEvidence { /** docfonts evidence id, e.g. "cambria". */ evidenceId: string; /** renderer-neutral action behind the substitution. */ policyAction: SubstitutePolicyAction; /** fidelity verdict for this row (per-face on face rows; worst-face top-level on family rows). */ verdict: SubstituteVerdict; /** advances preserve line breaks: metric_safe, near_metric, or monospace cell_width_only. */ lineBreakSafe: boolean; /** named glyph-level divergences qualifying this row's face(s); omitted when none apply. */ glyphExceptions?: readonly GlyphException[]; } /** * One row of the font report: what the document asked for, what SuperDoc actually * renders, why, whether it was ready before measurement, and what export preserves. * * This is the observable answer to "what font did SuperDoc use, and is that faithful?" * e.g. requested Calibri -> rendered Carlito (bundled_substitute), loaded before measure, * export still Calibri. */ export interface FontResolutionRecord { /** The family the document requested (e.g. "Calibri"). */ logicalFamily: string; /** The physical family actually measured and painted (e.g. "Carlito"). */ physicalFamily: string; /** Why the physical family differs from the logical one. */ reason: FontResolutionReason; /** Load state of the physical face at report time (`loaded` = ready before measurement). */ loadStatus: FontLoadStatus; /** The family export writes back - always the logical name, so intent is preserved. */ exportFamily: string; /** * True when SuperDoc did NOT faithfully render the requested font with a metric-compatible face. * Two ways this happens: * - a non-metric substitute rendered but is not faithful, so it is missing EVEN WHEN `loaded`: * `reason: 'category_fallback'` (wrong weight / reflows, e.g. Calibri Light -> Carlito), and on * face-level rows `reason: 'fallback_face_absent'` (the substitute lacks this weight/style). * - the physical face settled to a state other than `loaded`: a font with no known substitute * (`reason: 'as_requested'`, e.g. Aptos), or a substitute whose asset failed * (`reason: 'bundled_substitute'`, `loadStatus: 'failed'`, e.g. a 404ing `assetBaseUrl`). * Transient states (`unloaded` / `loading`) are NOT missing, so an early `getReport()` pull before * the gate settles does not over-report. `reason` and `loadStatus` distinguish the cause. */ missing: boolean; /** * The specific face (weight/style) this row describes, present only on FACE-level rows * ({@link buildFaceReport}); undefined on family-level rows ({@link buildFontReport}). A * face-level row with `reason: 'fallback_face_absent'` means a substitute exists for the family * but not for THIS face, so this face renders unsubstituted (non-metric) while other faces of the * same family may substitute. Optional + additive, so existing consumers are unaffected. */ face?: { weight: '400' | '700'; style: 'normal' | 'italic'; }; /** * docfonts fidelity evidence, present ONLY when SuperDoc rendered the recommended substitute * (`reason` `bundled_substitute` or `category_fallback`). Lets a consumer distinguish a clean clone * (Calibri -> Carlito, `metric_safe`) from a qualified one (Cambria -> Caladea, `visual_only`) * instead of treating every `bundled_substitute` alike. Optional + additive. See {@link ResolvedFontEvidence}. */ evidence?: ResolvedFontEvidence; } /** * Build the per-font resolution report for a document's logical fonts. Pure given the * resolver map and the registry's current load state, so it reflects exactly what was * resolved and loaded for the last measurement pass. * * This is the diagnostics seam: the public `superdoc.fonts.getReport()` (T7) and the * upgraded `onFontsResolved` payload are thin wrappers over this - they must not compute * resolution independently, or the report could disagree with what actually painted. */ export declare function buildFontReport(logicalFamilies: Iterable, registry: FontRegistry, resolver?: FontResolver): FontResolutionRecord[]; /** One used (logical family + face) the document actually renders, for the face-level report. */ export interface UsedFace extends FaceKey { logicalFamily: string; } /** * Face-level resolution report: one row per (logical family, face) the document RENDERS, resolved * FACE-aware so a substitute that lacks a face is reported `fallback_face_absent` unless docfonts * explicitly names a synthetic source face. The caller passes the used faces (super-editor builds them * from the load planner, which carries logical family + weight/style); `registry.hasFace` is the * face-availability oracle. Unlike {@link buildFontReport} (one row per declared family), this * explains per-face fidelity, e.g. "Baskerville Regular -> Bacasime (custom_mapping); Baskerville Bold * -> fallback_face_absent". Deduped by logical family + weight + style. */ export declare function buildFaceReport(usedFaces: Iterable, registry: FontRegistry, resolver?: FontResolver): FontResolutionRecord[];