microsoft / microsoft/react-native-windows

DirectWrite text layout cannot resolve font families whose names contain spaces — 'Material Icons' renders tofu, same font renamed 'MaterialIcons' renders

Open
#16,306 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Needs: Triage :mag:
Dominant language
C++
Stars
17.3k
Forks
1.2k
Avg merge
1d 13h
Merged PRs (30d)
33

Description

Problem Description

On the new architecture, a fontFamily whose name contains a space (e.g. 'Material Icons') fails to resolve even though the font is registered and enumerable in DirectWrite — glyphs render as tofu / fall back to Segoe UI. The identical TTF re-emitted with a space-less family name ('MaterialIcons', name table IDs 1/16 rewritten, glyph outlines untouched) resolves and renders. The space in the requested family name is the only variable.

What we verified empirically (production app):

  • Fonts registered via the packaged windows.sharedFonts manifest extension land in DirectWrite's per-user system font collection; WPF/DirectWrite enumeration shows the family present under its spaced name (Material Icons), correct cmap/name records.
  • fontFamily: 'Material Icons' → tofu.
  • Same file, family renamed to MaterialIcons (only the OpenType name table changed) → renders correctly.

Root cause — diagnosed, NOT traced to a specific line (filing for visibility; confidence level stated honestly). The resolution lives in the DirectWrite text-layout path — vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager and/or the font-collection/loader it uses — i.e. how the requested family string reaches CreateTextFormat / FindFamilyName. Plain DirectWrite handles spaced family names fine (Segoe UI is the platform default), so we suspect RNW-side handling of the fontFamily string (tokenizing/trimming, or the custom collection lookup) rather than DirectWrite itself. We could not pin the failing lookup with confidence from a shallow source read, so we did not attempt a patch.

Steps To Reproduce
  1. Take a TTF whose family name contains a space — e.g. stock MaterialIcons.ttf from react-native-vector-icons (family name Material Icons).
  2. Register it so DirectWrite can see it (we use the windows.sharedFonts package-manifest extension; confirm via any DirectWrite/WPF font enumeration that the family is present).
  3. Render <Text style={{ fontFamily: 'Material Icons' }}>{''}</Text> (any mapped codepoint).
  4. Observe tofu / Segoe UI fallback.
  5. Control: rewrite the font's name table so the family is MaterialIcons (no other change), request fontFamily: 'MaterialIcons' → glyphs render.
Expected Results

Spaced family names resolve exactly as space-less ones do (and as they do on iOS/Android, and in DirectWrite generally).

CLI version

18.0.0

Environment
System:
  OS: Windows 11 10.0.26200 (ARM64 device; app builds and runs ARM64)
  CPU: (6) x64 Apple Silicon (Windows-on-ARM)
  Memory: 6.49 GB / 15.99 GB
Binaries:
  Node: 22.15.0
  Yarn: 4.5.1
  npm: 10.9.2
SDKs:
  Windows SDK versions: 10.0.19041.0, 10.0.22621.0, 10.0.26100.0
IDEs:
  Visual Studio: 18.6.11822.322 (Community 2026), 17.14.37314.3 (Community 2022)
npmPackages (yarn 4 workspaces — `cli info` reports Not Found in-workspace):
  react-native: 0.83.2
  react-native-windows: 0.83.2 (New Architecture / Fabric composition)
  Microsoft.WindowsAppSDK: 1.8
Community Modules

react-native-vector-icons 10.3.0 / @expo/vector-icons 15.1.1 are where the spaced-name fonts come from, but the bug is core: it reproduces with any registered TTF whose family name contains a space, rendered through core <Text>.

Target React Native Architecture

New Architecture (WinAppSDK) Only

Target Platform Version

10.0.22621

Visual Studio Version

Visual Studio 2026

Build Configuration

Debug

Snack, code example, screenshot, or link to a repository

Minimal probe is the two-step control above (steps 3 vs 5) — same file, same glyphs, only the family name differs.

Our production workaround: a dependency-free Node script re-emits the Windows copies of all affected fonts with space-less family names (rewriting name IDs 1/16 and recomputing table checksums), registered via windows.sharedFonts; a platform shim makes JS request the space-less names on Windows only.

Related issues: #15316 (umbrella: custom .ttf fontFamily support on new arch), #3463 (old-arch custom-font parity), #15750 (IProvideFontInfo font-loading unification). Related but distinct, filed separately: #16308 — registered stock icon TTFs render blank glyphs until table checksums/table directory are recomputed (loader validation vs. this issue's family-name lookup).

Context: found during a Windows hardening pass of a production RNW app (Facilitron FIT — RNW 0.83.2 new-arch, Windows 11 ARM64, WinAppSDK 1.8, 250% display scale). Sibling PRs from the same investigation: #16302, #16303, #16304. Happy to build a minimal repro repo or test candidate fixes in our app if useful.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager and trace how fontFamily reaches CreateTextFormat or FindFamilyName, including the font-collection or loader path. Reproduce with the registered Material Icons TTF and its space-less control, then verify that spaced family names resolve and render correctly without the workaround.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, react-native
Domain
computer-graphics, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.