Skip to content

Add glyph-id rendering, skip-ink decorations, and native font matching#545

Merged
JimBobSquarePants merged 14 commits into
mainfrom
js/browser-equivalence
Jul 17, 2026
Merged

Add glyph-id rendering, skip-ink decorations, and native font matching#545
JimBobSquarePants merged 14 commits into
mainfrom
js/browser-equivalence

Conversation

@JimBobSquarePants

Copy link
Copy Markdown
Member

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

This pull request introduces several improvements and refactorings to the font handling and metrics system, focusing on enhanced variable font weight support, more flexible font collection management, and improved glyph lookup performance and clarity. The changes affect core classes such as Font, FontCollection, and FileFontMetrics, adding new APIs and internal logic to support these features.

Variable Font and Weight Support:

  • Added the ability to create variable-font instances at a requested weight using the new Font.WithWeight(FontWeight weight, out bool applied) method, allowing more precise control over font weight selection and fallback to system fonts or synthetic weights when necessary.
  • Introduced a private requestedWeight field and corresponding constructor in Font, enabling numeric weight lookup and integration with system font collections. The ResolveBaseMetrics method now respects this requested weight when resolving metrics. [1] [2] [3]

Font Metrics and Glyph Lookup Enhancements:

  • Added a public TryGetGlyphId(CodePoint codePoint, out ushort glyphId) method to Font for direct glyph ID retrieval, and optimized TryGetGlyph to use glyph ID lookup before fetching metrics, improving glyph lookup performance. [1] [2]
  • Extended FileFontMetrics to expose underlying StreamFontMetrics publicly, implement table data and stream access methods, and support glyph metrics lookup by glyph ID, enhancing flexibility for advanced font operations. [1] [2] [3]

Font Collection and Metrics Management:

  • Refactored FontCollection to use a new FontCollectionEntry type, allowing explicit family name and style overrides when adding metrics. This enables more accurate and flexible font family and style lookups, especially for custom or memory-based fonts. [1] [2] [3] [4]
  • Updated all font metrics and style lookup methods in FontCollection to use the new entry structure, improving culture-aware and style-specific matching.

File and Memory Font Metrics Improvements:

  • Refactored FileFontMetrics to internally use a FontSource abstraction, improving stream handling and table data access, and updated constructors for clarity and encapsulation. [1] [2]
  • Changed memory font loading in FontCollection to use MemoryFontMetrics instead of StreamFontMetrics, unifying font metric handling for in-memory fonts.

Other Notable Enhancements:

  • Improved the Bounds.Transform method to correctly compute the transformed bounding box by transforming all four corners, ensuring accurate bounds after arbitrary transformations.
  • Added support for MemoryFontMetrics in font name resolution logic.

These changes collectively improve the flexibility, accuracy, and extensibility of font handling in the codebase, especially for advanced variable font and custom font collection scenarios.

Introduces several major features and API additions:

- **Glyph-id rendering/measurement**: `TextRenderer.RenderTo(renderer, glyphId, GlyphOptions)`, `GlyphRun`, `GlyphOptions`, and `TextMeasurer` overloads for measuring by glyph id without text layout.
- **`FontMetrics` public API**: `TryGetGlyphMetrics(ushort glyphId, ...)`, `TryGetTableData(Tag, ...)`, and `OpenStream()` are now public/abstract members.
- **`MemoryFontMetrics`**: Stream-loaded fonts now copy data into owned memory so they remain valid after the source stream is closed.
- **`FontSource` abstraction**: Unifies file-backed and memory-backed font data access behind a single type used by `StreamFontMetrics`.
- **Text-decoration skip-ink** (`TextDecorationSkipInk.Auto`): Underlines and overlines now break around glyph descenders/ascenders, implemented via `GlyphIntersectionCollector` and `TeeGlyphRenderer`.
- **Native OS font matching**: New `DirectWriteSystemFontMatcher`, `CoreTextSystemFontMatcher`, and `FontConfigSystemFontMatcher` replace `MacSystemFontsEnumerator`. `SystemFonts.TryMatchCharacter` and `IReadOnlySystemFontCollection.TryMatchCharacter` provide OS-level font fallback.
- **`FontCollectionExtensions`/`FontCollection`**: Support explicit family-name and style overrides when adding metrics, preserving platform family groupings from native matchers.
- `TextRenderer.RenderTextTo` renamed to `RenderTo`; old name removed.
Replace the eager COLR v1 paint cache with on-demand paint decoding backed by a snapshot of the raw COLR table data. This avoids walking every color glyph paint graph during font load, reduces startup cost for fonts with many color glyphs, and keeps resolved paints cached by offset for reuse across renders. The loader now also accepts the table length so it can capture the exact COLR table bytes for later resolution.
Improves native font discovery and matching across macOS, Linux, and Windows. On macOS, CoreFoundation interop is corrected for CFNumberType ABI and descriptor trait extraction now reads weight/symbolic traits in one pass with fewer font instantiations. On Linux, Fontconfig calls are conditionally serialized for older non-thread-safe versions, family matching uses weak binding, and matched fonts are validated for actual character coverage. On Windows, non-local DirectWrite loaders are safely skipped and COM wrapper disposal guidance is clarified. The old AppContext switch path that disabled native enumeration was removed, along with its test helper and related test.
Updates the TrueType interpreter to better match FreeType v40 behavior. The class docs now describe backward-compatibility mode as the default and explain how fonts can waive it via INSTCTRL selector 3. INSTCTRL handling now allows selector 3 inside glyph programs (temporarily per glyph, since prep state is restored each run), and GETINFO now reports ClearType/grayscale support by setting bit 19 when selector bit 12 is requested.
Move skip-ink carving responsibility out of `FontGlyphMetrics` by sending full decoration lines plus merged ink intersection ranges through `IGlyphRenderer.SetDecoration(...)`. This updates all renderer implementations/callers to the new signature and simplifies `GlyphIntersectionCollector` to track per-contour band extents. The change also adds `TextDecorationOptions` and `TextRun.GetDecorationOptions(...)` so runs can override decoration thickness (for example, stroke-driven styling) instead of always using font metric thickness.
# Conflicts:
#	src/SixLabors.Fonts/Tables/Cff/CffGlyphMetrics.cs
#	src/SixLabors.Fonts/Tables/TrueType/TrueTypeGlyphMetrics.cs
Updated synthetic italic and bold rendering to better match Chromium/SkParagraph and FreeType behavior: switched to 1/4 oblique shear, adjusted bold strength to 1/31 em, unified outline transforms, fixed bounds transforms for rotated/sheared boxes, and avoided synthetic emboldening for painted color glyphs. Reworked emboldening to use a pooled renderer and FreeType-style contour dilation, added painted-glyph bounds support, expanded font synthesis coverage (including emoji and visual/browser comparison cases), and refreshed affected reference images.
Introduce a new FontWeight enum and comprehensive weight resolution system that supports both variable fonts (via wght axis) and static fonts (via synthetic bold). The implementation follows CSS weight semantics with a 600-weight boundary for synthetic bold synthesis. Updates include:

- New FontWeight enum (100-900) matching OpenType and CSS standards
- Weight property on FontDescription from OS/2 table
- TextOptions and TextRun now support FontWeight configuration
- Font.WithWeight() method for applying weights to variable fonts
- Updated bold synthesis logic to use weight-based approach instead of boolean style flag
- Support for weight overrides per text run or globally via TextOptions
Resolve numeric weights through system font families before synthetic bolding, while preserving existing variable-font behavior. Also switch OpenType axis tags to the shared `Tag` type and add browser and unit coverage for Windows system families like Segoe UI and Arial.
Tightens system font discovery and fallback across CoreText, DirectWrite, and Fontconfig so native matches resolve to managed, file-backed fonts more reliably. The change adds explicit face-name tracking, maps CoreText fallback fonts by PostScript name, includes Fontconfig application and localized family aliases, respects sysroot-resolved paths, skips non-loadable variable-instance indexes, and treats OTC files as font collections throughout the managed loader.
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 53.54730% with 1100 lines in your changes missing coverage. Please review.
✅ Project coverage is 79%. Comparing base (98aeb3f) to head (52ff71c).

Files with missing lines Patch % Lines
...ixLabors.Fonts/Native/CoreTextSystemFontMatcher.cs 0% 347 Missing ⚠️
...Labors.Fonts/Native/FontConfigSystemFontMatcher.cs 0% 266 Missing ⚠️
...abors.Fonts/Native/DirectWriteSystemFontMatcher.cs 55% 95 Missing and 38 partials ⚠️
src/SixLabors.Fonts/SystemFontCollection.cs 64% 67 Missing and 11 partials ⚠️
src/SixLabors.Fonts/Native/SystemFontMatcher.cs 17% 39 Missing and 3 partials ⚠️
src/SixLabors.Fonts/MemoryFontMetrics.cs 53% 34 Missing ⚠️
src/SixLabors.Fonts/Rendering/TeeGlyphRenderer.cs 34% 19 Missing and 10 partials ⚠️
...bors.Fonts/Rendering/GlyphIntersectionCollector.cs 75% 22 Missing and 6 partials ⚠️
src/SixLabors.Fonts/Rendering/PaintedGlyph.cs 53% 20 Missing and 2 partials ⚠️
src/SixLabors.Fonts/FontGlyphMetrics.cs 86% 11 Missing and 8 partials ⚠️
... and 21 more
Additional details and impacted files
@@           Coverage Diff           @@
##            main    #545     +/-   ##
=======================================
- Coverage     84%     79%     -5%     
=======================================
  Files        333     348     +15     
  Lines      24815   26810   +1995     
  Branches    3640    3997    +357     
=======================================
+ Hits       20917   21433    +516     
- Misses      3009    4401   +1392     
- Partials     889     976     +87     
Flag Coverage Δ
unittests 79% <53%> (-5%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JimBobSquarePants
JimBobSquarePants merged commit 30b77af into main Jul 17, 2026
12 of 13 checks passed
@JimBobSquarePants
JimBobSquarePants deleted the js/browser-equivalence branch July 17, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant