diff --git a/AGENTS.md b/AGENTS.md index a2f33c8..730934a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,8 +22,8 @@ The renderer and test projects target `net8.0` and `net10.0`; both runtimes are - `DisplayList` is the backend-agnostic, flat command model. Scoped effects are push/pop pairs (`PushClip`, `PushTransform`, `PushOpacity`, `PushFilter`, ...) that the backend maps to `Save`/`SaveLayer`/`Restore`. `InsertRange` and `MoveRangeToEnd` exist to reorder already-emitted commands. - `SkiaRenderBackend` rasterizes the display list and implements `ITextMeasurer`, so layout measures with the very typefaces it paints with. - `ITextMeasurer` is the seam between layout and rasterization (line breaking, alignment, table widths). Never reintroduce a font-independent width heuristic - it silently decouples layout from what is drawn. -- `SkiaTextShaping` is the single font-resolution path for both measuring and painting (and SVG text). Text is drawn glyph by glyph with `canvas.DrawText` - no HarfBuzz shaping (no Arabic joining, ligatures or `font-feature-settings`). -- `BidiAlgorithm` (UAX #9: levels, `unicode-bidi` embeddings/overrides/isolates, mirroring) and `WebFontDecoder` (WOFF via zlib, WOFF2 via Brotli plus the `glyf`/`loca`/`hmtx` transforms, back to plain sfnt) are self-contained internal helpers. +- `SkiaTextShaping` is the single font-resolution and shaping path for measuring, painting and SVG text. `ShapeText` splits text into fallback-typeface runs and script runs, shapes each with HarfBuzz (HarfBuzzSharp; one cached `hb_font` per `SKTypeface`, positions in font units scaled by the font size), and painting draws the very same glyphs and positions as `SKTextBlob`s, so measuring and painting cannot disagree. Text reaches it in logical order: `RenderFont.IsRightToLeft` is the bidi run direction, HarfBuzz reverses and mirrors. A right-to-left script is always shaped right to left (strong RTL characters always end up at odd levels), because layout measures words before resolving levels and HarfBuzz shapes Arabic differently when forced left to right. `RenderFont.FontFeatures` carries HarfBuzz feature strings; letter spacing goes after each cluster and turns optional ligatures off. `RenderFont.Language` (from `lang`/`xml:lang`, element semantics, carried in the style map under `lang` and inherited through `RenderTextStyle.Language`) selects `locl` forms; without one the buffer language is fixed (`und`) so results do not depend on the host locale. `RenderFont.ContextBefore`/`ContextAfter` are neighbouring characters shaped elsewhere (`ApplyShapingContext` sets them where IFC text pieces meet across an inline box edge), handed to HarfBuzz as pre/post context so Arabic joins across elements; they are part of the measure cache key. Callers without a layout (SVG text, Canvas 2D `fillText`) use `MeasureParagraph`/`DrawParagraph`, which split a line into bidi runs (`BidiAlgorithm.GetVisualRuns`, shared with the paragraph path) first. +- `BidiAlgorithm` (UAX #9: levels, `unicode-bidi` embeddings/overrides/isolates; mirroring is left to shaping) and `WebFontDecoder` (WOFF via zlib, WOFF2 via Brotli plus the `glyf`/`loca`/`hmtx` transforms, back to plain sfnt) are self-contained internal helpers. - `HtmlRenderOptions` holds viewport and text defaults. - `SvgRasterizer` (`Skia/Svg/`) paints SVG by walking the DOM AngleSharp already produced - no second SVG parser. Its PNG output becomes an ordinary `RenderedImage`. - `InteractiveHtmlRendererState` / `IDomHarness` back interactive documents (`:hover`, scrolling, focus, a virtual clock for transitions/animations/caret). @@ -32,12 +32,12 @@ The renderer and test projects target `net8.0` and `net10.0`; both runtimes are `SkiaTextShaping.CreateTypeface` walks the `font-family` list in order; the first usable entry wins: -1. Generic families (`serif`, `sans-serif`, `monospace`, `cursive`, `fantasy`, `system-ui`, `ui-*`) always map to the bundled fonts in `Resources/Fonts`. This is what keeps snapshots reproducible; `@font-face` cannot take them over. +1. Generic families (`serif`, `sans-serif`, `monospace`, `cursive`, `fantasy`, `system-ui`, `ui-*`) always map to the bundled DejaVu fonts in `Resources/Fonts`. This is what keeps snapshots reproducible; `@font-face` cannot take them over. 2. `@font-face` rules (`FontFaceLoader`, carried on `DisplayList.Fonts`). `local()` resolves against installed fonts; `url()` against `data:` URIs, or the network only when the browsing context has an `IDocumentLoader`. WOFF/WOFF2 are decoded to sfnt by `WebFontDecoder` first; SVG and EOT fonts are rejected. 3. Installed families - host-dependent, never assert them in snapshots. 4. Bundled sans-serif as the last resort. -A character the resolved typeface has no glyph for falls back per character (`SplitFallbackRuns`, shared by measuring and painting): the next `font-family` entry that has it, then the bundled fonts, then `SKFontManager.MatchCharacter` (host-dependent, so never in snapshots). Text the typeface fully covers stays one run. +A character the resolved typeface has no glyph for falls back per character (`SplitFallbackRuns`, shared by measuring and painting): the next `font-family` entry that has it, then the bundled fonts (DejaVu, then Noto Sans Devanagari, which is fallback-only and never a generic family; its OFL license ships in the package), then `SKFontManager.MatchCharacter` (host-dependent, so never in snapshots). Text the typeface fully covers stays one run. Never resolve named families with `SKTypeface.FromFamilyName`: it substitutes the host default for unknown names instead of returning null, swallowing the rest of the fallback list. Availability goes through a case-insensitive index of installed families (Skia's lookup is case-sensitive on Linux only). @@ -47,7 +47,7 @@ Never resolve named families with `SKTypeface.FromFamilyName`: it substitutes th **Styling comes only from the computed style** (the render tree, cascaded with the UA stylesheet), never from tag names: no "`` is bold", "`h1` is 700", "`` is inline", "`