http://bugs.winehq.org/show_bug.cgi?id=60172 Bug ID: 60172 Summary: WPF font fallback does not work: the families named by GlobalUserInterface.CompositeFont do not resolve under Wine Product: Wine Version: 11.15 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: gdi32 Assignee: wine-bugs@list.winehq.org Reporter: wehrwolfmann@gmail.com Target Milestone: --- Distribution: --- Created attachment 81815 --> http://bugs.winehq.org/attachment.cgi?id=81815 family resolution and glyph outlines for Latin and Cyrillic WPF (.NET Framework, PresentationCore) does not use DirectWrite layout or the dwrite fallback API -- it carries its own per-character fallback table, the GlobalUserInterface.CompositeFont file installed with the framework. That table is written entirely in terms of Windows system font families. Under Wine essentially none of those families exist, so the table has nowhere to go and WPF emits .notdef for the character instead. Measured on this prefix (Wine 11.15, WINEARCH=win64, reported Windows 10 build 19045, native .NET Framework 4.8, host CachyOS/Arch with the usual Noto, DejaVu and Liberation font packages installed), against the RS5 section of GlobalUserInterface.CompositeFont, which is the section .NET selects when Wine reports Windows 10: families named as fallback targets 44 resolvable through Wine 1 (Tahoma) not resolvable 43 (Segoe UI, Segoe UI Symbol, Ebrima, Nirmala UI, Leelawadee UI, Malgun Gothic, Gulim, Gadugi, Yu Gothic UI, Meiryo UI, Microsoft YaHei UI, Microsoft JhengHei UI, SimSun, MS Gothic, MingLiU, Calibri, Cambria Math, Myanmar Text, MV Boli, Mongolian Baiti, Simplified Arabic, ... ) The single map that matters most is the first one: <FontFamilyMap Unicode = "0000-052F, 0590-05FF, ..." Target = "Segoe UI, Segoe UI Symbol, Ebrima" /> U+0000-U+052F is Latin, Latin Extended, IPA, Greek and Cyrillic; the same map also carries Hebrew. All three targets are missing, so under Wine a WPF application has no fallback at all for those scripts. A plain WPF application does not notice, because its default typeface comes from SystemParametersInfo(SPI_GETNONCLIENTMETRICS) and Wine answers "Tahoma", which covers Latin, Greek and Cyrillic by itself. An application that ships its own branded font does notice, and branded fonts are usually Latin-only. WHAT THIS LOOKS LIKE IN A REAL APPLICATION Razer Central 7.23.0.1220 (the launcher bundled with Razer Axon) draws its tray icon context menu with WPF, in its own font "RazerF5" (SIL OFL, derived from Titillium, embedded in the executable and also shipped as a separate download). RazerF5 has 229 glyphs: Latin and punctuation, no Cyrillic. With the application's language set to Russian, every Latin menu entry renders and every Cyrillic menu entry is a row of empty boxes. On Windows the same build renders the Russian entries, because the fallback lands on Segoe UI. MINIMAL REPRODUCER wpf_fonts.cs (attached). It is a console program; build it with the csc.exe that comes with the framework already installed in the prefix, no SDK needed: wine "C:\windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe" /nologo \ /nowarn:0618 /target:exe /out:C:\wpf_fonts.exe \ "/r:C:\windows\Microsoft.NET\Framework64\v4.0.30319\WPF\PresentationCore.dll" \ "/r:C:\windows\Microsoft.NET\Framework64\v4.0.30319\WPF\PresentationFramework.dll" \ "/r:C:\windows\Microsoft.NET\Framework64\v4.0.30319\WPF\WindowsBase.dll" \ "/r:C:\windows\Microsoft.NET\assembly\GAC_MSIL\System.Xaml\v4.0_4.0.0.0__b77a5c561934e089\System.Xaml.dll" \ C:\wpf_fonts.cs For every family name given on the command line it prints whether the family resolves, whether the resolved face covers Latin and Cyrillic, and the bounding box of the outlines FormattedText.BuildGeometry() actually produced for the Latin string "Settings" and the Cyrillic string "Настройки". An empty bounding box means WPF produced no glyph outline at all -- the tofu case. It also writes wpf_fonts.png so the failure can be seen rather than read. Run it with any font that covers Latin but not Cyrillic; RazerF5 is the one used here (razerf5-light.otf, freely downloadable, SIL OFL): family "RazerF5" resolved=True latin=True cyrillic=False latin width= 55.26 height= 18.66 outline=53.7x14.8 cyrillic width= 33.84 height= 18.66 outline=EMPTY (nothing drawn) family "Tahoma" resolved=True latin=True cyrillic=True latin width= 56.50 height= 19.31 outline=55.9x15.0 cyrillic width= 77.58 height= 19.31 outline=75.6x15.0 family "Segoe UI" resolved=False Tahoma proves the text path itself is fine. RazerF5 shows that once the requested face lacks the character, nothing takes over. THE GDI CONTRAST gdi_cyrillic.c (attached) does the same two strings through plain GDI: CreateFontIndirectA, GetGlyphIndicesW(GGI_MARK_NONEXISTING_GLYPHS), TextOutW into a DIB, counting painted pixels. face "Segoe UI" latin missing_glyphs=0/8 painted_pixels=522 (GetTextFace Liberation Sans) cyrillic missing_glyphs=0/9 painted_pixels=637 (GetTextFace Liberation Sans) face "RazerF5" latin missing_glyphs=0/8 painted_pixels=489 (GetTextFace RazerF5) cyrillic missing_glyphs=9/9 painted_pixels=653 (GetTextFace RazerF5) So Wine already degrades gracefully when a Win32 application asks for a Windows font by name -- "Segoe UI" quietly becomes Liberation Sans and the Cyrillic renders. That graceful degradation happens at font *selection* time and is invisible to any consumer that first asks whether the family exists. WPF asks exactly that question, gets "no", and its whole fallback table dies with it. (GDI's own per-glyph behaviour for RazerF5 -- 9 unmapped glyphs, boxes painted -- matches Windows; GDI TextOutW does not font-link either. The difference is only in the family lookup.) A HARSHER MANIFESTATION If the *requested* family also fails to resolve, WPF does not fall back to a default -- the process dies silently inside text formatting. With the reproducer above: wine wpf_fonts.exe "Segoe UI" family "Segoe UI" resolved=False <process gone, no exception, no message> This looks like the same root cause as bug 50021 (PS Remote Play, assertion in MS.Internal.Shaping.TypefaceMap.MapUnresolvedCharacters, "installing corefonts changes the behaviour"): MapUnresolvedCharacters has nothing left to map to. Related but distinct: bug 59925 is also WPF fallback failing, but there the requested families do resolve and a single character (U+002D) is orphaned into a run of its own. Here the fallback targets do not exist as families at all, so the table has nothing to reach in the first place. SUGGESTED FIX Wine already has the mechanism, it just has no default entries for these names: HKCU\Software\Wine\Fonts\Replacements, honoured by both win32u/font.c (load_gdi_font_replacements) and dwrite/font.c. It enumerates an existing family under an additional name, and it steps aside automatically if a real font of that name is ever installed. REG_MULTI_SZ values are already supported and the first available entry wins, so one value works across distributions: [HKEY_CURRENT_USER\Software\Wine\Fonts\Replacements] "Segoe UI"=hex(7):... Noto Sans, DejaVu Sans, Liberation Sans, Arial, Tahoma "Segoe UI Symbol"=hex(7):... DejaVu Sans, Noto Sans, Liberation Sans, Arial, Tahoma "Ebrima"=hex(7):... Noto Sans, DejaVu Sans, Liberation Sans, Arial, Tahoma Adding just those three restores WPF fallback for Latin Extended, Greek, Cyrillic and Hebrew. Verified on this prefix with the reproducer, with GlobalUserInterface.CompositeFont left untouched: before family "RazerF5" cyrillic outline=EMPTY (nothing drawn) after family "RazerF5" cyrillic outline=80.8x16.0 (Noto Sans metrics) Tahoma as the last entry is a safe floor: Wine ships it, and it covers Latin, Greek and Cyrillic. The same treatment would extend naturally to the CJK and Indic target names for hosts that have suitable fonts. An independent cross-check that the composite font really is the mechanism: appending an existing family to every Target attribute in the prefix's *.CompositeFont files fixes it too, with no registry change at all. The registry route is preferable because it needs no edits to files that belong to the framework. VERSIONS Reproduced on wine-11.15 (Arch/CachyOS package), WINEARCH=win64, native .NET Framework 4.8, reported Windows version 10 (build 19045). Not tested on Wine-Mono; Wine-Mono's WPF carries its own copy of the same composite font files, so the same reasoning should apply. ATTACHMENTS wpf_fonts.cs WPF reproducer described above gdi_cyrillic.c the GDI counterpart used for the contrast wpf_fonts_before.png offscreen render, stock prefix: Cyrillic missing wpf_fonts_after.png the same render once "Segoe UI" resolves -- 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.