Font lists built with EnumFontFamiliesEx give GDI family names (the fonts' Win32 family name, name ID 1). For families with weights other than regular and bold, or with other stretches, these names include the weight or stretch: "Fira Code Light", "Fira Code Medium", "Bahnschrift SemiBold SemiConden" (GDI truncates names to 31 characters). Applications store these names in their styles, so Scintilla receives them as font names.
1. DirectWrite only knows typographic family names ("Fira Code"). CreateTextFormat with "Fira Code Light" doesn't find the family and draws with a fallback font: the text changes font entirely when switching to a DirectWrite technology.
2. Under GDI, bold of such a family is synthesised (emboldened) instead of using the family's real heavier font. For monospaced fonts this widens every glyph by a pixel, so columns no longer line up with regular text.
The attached patch (against 5.6.7, win32 only):
- SurfaceD2D: a family name DirectWrite doesn't find is matched to its DirectWrite family, weight, stretch and style through the fonts' Win32 family names (including the 31-character truncation), else through IDWriteGdiInterop::CreateFontFromLOGFONT when that doesn't simulate. The requested weight is applied relative to the matched weight, as GDI does. Matches are cached per name. HFont() of such a font gives GDI its GDI name back.
- SurfaceGDI: bold (and other weights) of a GDI weight family uses the family's real font closest to the requested weight when there is one (GdiLogFont), else GDI's synthesis as before.
- ScintillaWin: the IME composition font uses the same mapping.
Names DirectWrite already knows ("Fira Code", "Consolas") take the unchanged path.
Test: a Scintilla window drawing "iiiii" and "WWWWW" in each family name, regular and bold, GDI and DirectWrite, with width and total ink (darkness) measured, Wine 9, 96 DPI:
font bold tech 5.6.7 5.6.7 + patch
Fira Code Light no DirectWrite nothing drawn(*) ink 884748 (GDI: 884589)
Fira Code Light yes DirectWrite nothing drawn(*) ink 1564539 (GDI: 1565508)
Fira Code Medium no DirectWrite nothing drawn(*) ink 1377513 (GDI: 1377529)
Fira Code Light yes GDI width 70 (faked) width 65, ink 1565508 (a real heavier font)
Fira Code Medium yes GDI width 70 (faked) width 65
Fira Code any both unchanged unchanged
(*) Wine draws nothing for an unknown family; Windows draws a fallback font.
Builds with g++ --std=c++17 -Wpedantic -Wall -Wextra without warnings; scripts/HeaderCheck.py is clean; Scintilla.dll links with MinGW-w64. The same code has been used in Notepad++ builds, tested with static and variable fonts and their named instances, and on Windows ("Bahnschrift Light" with DirectWrite draws its Light weight).
This patch was prepared with the help of an AI assistant (Claude), then reviewed and tested.
Windows check done: "Bahnschrift Light" with DirectWrite draws its Light weight (in the description).
GDI requires typeface name instead of font family name, DirectWrite requires font family name instead of typeface name.
See [bugs:#2080], [bugs:#2356] and [feature-requests:#1452].
Related
Bugs:
#2080Bugs:
#2356Feature Requests:
#1452Hi @zufuliu,
Thanks for referencing those here.
Having read them, I understand the conclusion: mapping GDI typeface names is a naming policy that belongs to the application, which can use IDWriteGdiInterop and pass the DirectWrite family with SCI_STYLESETWEIGHT and, since 5.5.2, SCI_STYLESETSTRETCH. I'll do it in the application, so please consider the patch withdrawn unless you find this worth keeping.
One thing that may still help others: ScintillaDoc.html doesn't seem to mention the difference. Would a short note under SCI_STYLESETFONT be welcome? For example: "With SC_TECHNOLOGY_DEFAULT (GDI), fontName is a typeface name as listed by EnumFontFamiliesEx or returned by ChooseFont, such as "Fira Code Light". The DirectWrite technologies take a family name, such as "Fira Code", and select the font within the family with the weight, stretch and italic attributes. IDWriteGdiInterop::CreateFontFromLOGFONT can map a typeface name to these."
The same section has a small typo: the SCI_STYLESETSTRETCH paragraph says "The weight is a number between 1 and 9".
While a clarifying note may be helpful, mentioning
EnumFontFamiliesExanchors the explanation in legacy GDI terms and concepts.In general, DirectWrite should be treated as the default and GDI as a legacy API with some differences. Eventually GDI will be depreacted.