http://bugs.winehq.org/show_bug.cgi?id=59925 Bug ID: 59925 Summary: WPF (.NET 8) renders U+002D (HYPHEN-MINUS) as .notdef ("tofu") for some fonts under Wine — public DirectWrite is correct; defect is in WPF's font-fallback path Product: Wine Version: 11.10 Hardware: x86-64 OS: MacOS Status: UNCONFIRMED Severity: normal Priority: P2 Component: dwrite Assignee: wine-bugs@list.winehq.org Reporter: robowens@me.com Under Wine, WPF (.NET 8) renders U+002D (HYPHEN-MINUS) as .notdef ("tofu") for some fonts (Segoe UI, Eurostile Next LT Pro) while rendering it correctly for others (Futura PT). Letters, digits and spaces always render. WPF itemizes the line and splits it at every hyphen, orphans the hyphen into its own run, and its font-fallback for that run fails to produce a glyph — the box is laid down without the hyphen ever being shaped. The public DirectWrite APIs are NOT broken for U+002D (see Negative controls below): IDWriteTextLayout, IDWriteFontFallback::MapCharacters, IDWriteFont::HasCharacter(0x2d) and IDWriteFontFace::GetGlyphIndices all resolve U+002D correctly for these exact fonts. The defect is in the font-fallback path WPF exercises under Wine (which the trace shows also drives gdi32 font-linking), not the public dwrite layout/shaping surface. Component may therefore belong to gdi32 rather than dwrite — triage's call. VERSIONS REPRODUCED Reproduced on BOTH vanilla wine-devel 11.10 and wine-staging 11.10 (Gcenx macOS builds, osx64 — same upstream release, differing only by the staging patchset). The +dwrite,+font trace signature is byte-identical on the two: line split at every hyphen, the hyphen orphaned into a run that never reaches GetGlyphs (zero L"-" runs), HasCharacter(0x2d) probed during fallback. So this is upstream Wine, not a staging artifact. PLATFORM macOS 26 / Darwin 25.5, x86-64 under Rosetta 2. .NET 8 WPF (Microsoft.WindowsDesktop.App 8.0.28), software rendering (HKCU\Software\Microsoft\Avalon.Graphics\DisableHWAcceleration=1). STEPS TO REPRODUCE 1. Build the attached WPF reproducer (Program.cs + repro.csproj) on any OS with a .NET SDK (EnableWindowsTargeting lets non-Windows hosts build net8.0-windows): dotnet publish -c Release -o out 2. Run under Wine: WINEPREFIX=/path/to/prefix \ DOTNET_EnableWriteXorExecute=0 LIBGL_ALWAYS_SOFTWARE=1 \ WINEDLLOVERRIDES="mshtml=d" \ wine out/HyphenRepro.exe (WPF needs software rendering under Wine — set HKCU\Software\Microsoft\Avalon.Graphics\DisableHWAcceleration=1 or the window paints black.) 3. The window draws "A-B-C NL-C4-1" in five font/style combinations. ACTUAL RESULT Each U+002D renders as the .notdef box for some fonts; letters/digits/spaces render fine. Font-dependent, not universal, not italic-specific (see attachment repro-output.png): [1] Segoe UI, normal -> hyphen tofu [2] Eurostile Next LT Pro, normal -> hyphen tofu [3] Eurostile Next LT Pro, italic (oblique-sim) -> hyphen tofu [4] Futura PT Book, italic -> renders correctly [5] Eurostile Next LT Pro (private collection) -> hyphen tofu That Segoe UI normal drops the hyphen while Futura renders it in the same window points at the font-fallback path, not the codepoint. EXPECTED RESULT The hyphen renders for every line (as on native Windows / native .NET). ON-SCREEN vs OFFSCREEN (RenderTargetBitmap) — backend isolation Run with --render (offscreen RenderTargetBitmap; no window, no winemac.drv surface) the reproducer drops ONLY the Segoe line; the Eurostile lines render their hyphen. Same machine, same Wine, same fonts, same text (see attachment repro-offscreen-render.png): Line / font On-screen window Offscreen RTB [1] Segoe normal tofu tofu [2] Eurostile normal tofu renders [3] Eurostile italic tofu renders [4] Futura italic renders renders [5] Eurostile (file) tofu renders This split is identical on vanilla wine-devel 11.10 and wine-staging 11.10. Two consequences: - The core defect is NOT in Wine's graphics/presentation backend. The Segoe U+002D tofu reproduces in the offscreen RTB path (WPF software MIL rasterizer to a bitmap, never touches winemac.drv) — so the hyphen-dropping lives in the platform-independent WPF text/font-fallback layer. - The extra Eurostile tofu is specific to the on-screen presentation path (GDI GetGlyphOutline route the trace shows), separate from the itemization defect that drops the hyphen for Segoe everywhere. ANALYSIS (WINEDEBUG=+dwrite,+font) - dwritetextanalyzer_GetGlyphs is called per run; WPF's itemization splits the line at every U+002D. For "[1] Segoe normal A-B-C NL-C4-1" the runs reaching GetGlyphs are L"...A", L"B", L"C NL", L"C4", L"1" — spaces stay inside a run, only hyphens break it. The split is identical on all five lines, including Futura (renders fine), so the split itself is not the bug. - The hyphen is orphaned into its own run that never reaches GetGlyphs — zero L"-" runs in the entire trace. The hyphen is never shaped; the box is laid down by WPF's fallback, not by dwrite glyph mapping. - dwritefont_HasCharacter(font, 0x2d, ...) is probed during fallback. The trace logs the probe inputs but not the boolean result, so it was tested directly (Negative controls): HasCharacter(0x2d) returns TRUE for these fonts. The failure is downstream of the coverage check, inside WPF's fallback bookkeeping under Wine. - The fonts contain the glyph: U+002D -> 'hyphen' is in every cmap subtable (verified with fontTools for Eurostile OTTO/CFF, Futura, Roboto) and at runtime by GetGlyphIndices (Eurostile->15, Futura->14, Tahoma->16). NEGATIVE CONTROLS (pure DirectWrite — why these localize the bug) Attachments repro_dwrite.cpp and repro_fallback.cpp drive the public DirectWrite APIs directly (no .NET/WPF/window) over the same "A-B-C NL-C4-1" and fonts (incl. the brand .otf loaded as a private/custom collection, as WPF loads them). All pass — none reproduces the bug: IDWriteTextLayout (system family) -> one unsplit run, shaped — OK IDWriteTextLayout (Eurostile/Futura from .otf) -> real glyph shaped — OK IDWriteFontFallback::MapCharacters -> maps '-' to Tahoma, has=yes — OK IDWriteFont::HasCharacter(0x2d) -> true — OK IDWriteFontFace::GetGlyphIndices(0x2d) -> real glyph id (15/14/16) — OK The public DirectWrite surface is correct for U+002D on these fonts. Combined with the WPF reproduction, the defect is in WPF/PresentationCore's font-fallback path under Wine (and the gdi32 font-linking it drives), not in dwrite's public layout/shaping APIs. CONFIRMED IN HOST APP (Q-SYS Designer 10.4.0) The same defect renders every hyphenated schematic component label (NL-C4-1, CX-Q..., Display-Amp) as boxes. A +dwrite,+font trace of the running Designer: - IDWriteFontFallback::MapCharacters calls = 0 — the Designer uses WPF/PresentationCore's OWN fallback, not the public dwrite fallback API. - ~45x dwritefont_HasCharacter(..., 0x2d, ...) iterating its candidate font list, plus ~29k GDI GetGlyphOutline calls; the orphaned hyphen never reaches GetGlyphs. - Brand fonts load from C:\windows\fonts (registered or not) — not a missing-/ private-font issue. Font-side workarounds do not fix the host app (tested against the live prefix): Wine's Replacements key is ignored by dwrite; a font file-swap has no effect; and registering the brand fonts fixes a plain WPF TextBlock but NOT the Designer (its schematic always routes the hyphen through the broken WPF-internal fallback regardless of registration). So this needs a Wine-side fix, not a font/config workaround. NOTES FOR TRIAGE - Public DirectWrite APIs verified correct for U+002D (Negative controls) — not a basic dwrite shaping/layout bug. Reproduction is WPF-only; defect is in the font-fallback path WPF drives under Wine (dwrite-internal and/or gdi32 font-linking). Component may need to move from dwrite to gdi32. - Upstream confirmed: reproduces on vanilla wine-devel 11.10, not just staging (identical trace signature). Not a staging-patch artifact. - Latest version tested = 11.10, the newest macOS binary available at filing (Gcenx wine-devel/staging packages); upstream 11.11 has no macOS package yet. The only font/glyph-adjacent change in wine-11.10..wine-11.11 (225 commits) is "win32u: Fixed incorrect glyph outline size" — likely unrelated to the WPF font-fallback itemization (the hyphen never reaches GetGlyphs), but a retest candidate. A retest on 11.11+ will follow once a macOS build ships. - Linux not yet tested: reporter is on Apple Silicon, where x86-64 Wine will not boot under Docker/qemu — Wine's anon_mmap_fixed page-alignment assertion fails against the host's 16 KB pages (qemu: uncaught target signal 6), a hard incompatibility, not a config issue. The reproducer is OS-portable (cross-builds via EnableWindowsTargeting, no macOS dependencies) and is expected to reproduce on Linux/X11. The offscreen RenderTargetBitmap reproduction already isolates the core case away from winemac.drv, which is the main thing a Linux run would show. - Per-font divergence has three tiers: Segoe tofus everywhere (on-screen and offscreen RTB); Eurostile tofus on-screen only; Futura never. All five lines itemize identically, so the divergence is in WPF's per-font fallback bookkeeping (and, for Eurostile, the on-screen glyph-rendering path), not the run split. ATTACHMENTS (added after filing) - Program.cs, repro.csproj — canonical WPF reproducer (no-arg = window; --render <out.png> = offscreen RTB). - repro-output.png — on-screen output (wine-staging 11.10, macOS). - repro-offscreen-render.png — offscreen --render output (vanilla wine-devel 11.10). - repro_dwrite.cpp, repro_fallback.cpp — pure-DirectWrite negative controls. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.