The documentation for CreateTextFormat says nothing about font fallback selection, but if the default (NULL = system) collection is chosen, then DirectWrite clearly implements font fallback. For example, if I add two glyphs not found in the Gabriola font to the test string used by the DirectWrite SDK demo app, then DirectWrite picks the missing glyphs from the Segoe UI Symbol font. This happens with the basic DrawText
call and also with a custom renderer (that does nothing custom for font fallback) as shown below (the only modification is the test string):
The checkmark and wite star come from Segoe UI Symbol, not from Gabriola, even though only Gabriola is specified in the demo app. So, does anyone know how the fallback font(s) is/are selected by DirectWrite (CreateTextFormat)?
Update. I see there is a GetSystemFontFallback that can list the fallback fonts, but it's only available in Windows 8.1 (because it's in IDWriteFactory2). I guess they've noticed the gap in the API with respect to enumerating the fallback fonts. So I'm guessing there's no way to do this before Windows 8.1, but if anyone knows a hack/workaround...
Update2. Quoting a MSFT employee:
DirectWrite has fallback data that is not read from the registry or in any way configurable. In Windows 8.1, though, APIs have been introduced that allow an app to specify its own fallback. (It's similar to the WPF APIs for creating a composite font definition.)
Which still doesn't explain exactly what the hardcoded algorithm/replacement scheme actually is.
IDWriteTextLayout
calls IDWriteFontFallback::MapCharacters
to map each Unicode character to an ordered list of font families that are tried until that character is satisfied. Think of a loop reading each character one-by-one, mapping the code point value and language tag to a Unicode range, and stopping at the first font which supports the character in the cmap table. Pseudocode:
for each ch in text
if ch is a combining mark or other similar extending character
if the previously selected font supports the combining mark too
use the previously selected font
continue
endif
endif
find first mapping for ch within the Unicode range, which matches
the current language and base font family too (if pertinent)
if mapping found
for each font in mapping (starting with first listed)
if ch in font cmap
use current font
endif
endfor
else
use base font and undefined (.notdef) glyph
endif
endfor
There is also logic to determine fallback locales (like for zh-Hans
/Hant
or the general ja
matching the more specific ja-jp
). It is somewhat similar to (but less convoluted than 😅) the font fallback algorithm specified by CSS for browsers [http://www.w3.org/TR/css3-fonts/#font-matching-algorithm] and similar to the fallback used by WPF/XAML/Silverlight.
See the IDWriteFontFallbackBuilder::AddMapping
API (Win 8.1+), used to build a custom fallback list, for an idea of the inputs used.
See C:\Windows\Fonts\GlobalUserInterface.CompositeFont for example data (note this file is actually for WPF, not quite the same definition which DWrite uses).
<FontFamilyMap
Unicode = "3000-30FF, 31F0-31FF"
Language = "ja"
Target = "Meiryo UI, Meiryo, Microsoft YaHei UI, Microsoft YaHei, MS Gothic, MingLiu, Arial Unicode MS"
Scale = "1.0" />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With