From 54b157bb8cbc6a76bfd4158ae43fa416da1935a8 Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 17 Sep 2024 16:00:14 +0100 Subject: [PATCH] DirectWriteTypeface: Use GDI fallback behaviour for missing fonts Previously, when attempting to create a font with a name different to that of any font on the system, the returned typeface could be nullptr. This could lead to crashes when attempting to use the typeface. Now, if we fail to find a matching font using DirectWrite, we fall back to the older LOGFONT and DC approach, which will generally locate a usable typeface, though not necessarily an exact match. The new behaviour more closely matches the behaviour of JUCE 7, which would attempt to construct a DirectWrite typeface, but would fall back to creating an HFONT on failure. --- .../juce_DirectWriteTypeface_windows.cpp | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp b/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp index ea95665adc..f947abfcf0 100644 --- a/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp +++ b/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp @@ -181,6 +181,19 @@ public: return {}; } + ComSmartPtr findFontForFace (IDWriteFontFace* face) + { + for (const auto& collection : collections) + { + ComSmartPtr result; + + if (SUCCEEDED (collection->GetFontFromFontFace (face, result.resetAndGetPointerAddress()))) + return result; + } + + return {}; + } + void addCollection (ComSmartPtr collection) { const std::scoped_lock lock { mutex }; @@ -552,7 +565,7 @@ public: const auto family = factories->getFonts().getFamilyByName (name.toWideCharPointer()); if (family == nullptr) - return {}; + return getLastResortTypeface (f); const auto weight = f.isBold() ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL; const auto italic = f.isItalic() ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; @@ -831,6 +844,52 @@ private: collection); } + // This attempts to replicate the behaviour of the non-directwrite typeface lookup in JUCE 7 and older + static Typeface::Ptr getLastResortTypeface (const Font& font) + { + auto* dc = CreateCompatibleDC (nullptr); + const ScopeGuard deleteDC { [&] { DeleteDC (dc); } }; + + SetMapperFlags (dc, 0); + SetMapMode (dc, MM_TEXT); + + const auto style = font.getTypefaceStyle(); + + LOGFONTW lf{}; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfOutPrecision = OUT_OUTLINE_PRECIS; + lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + lf.lfQuality = PROOF_QUALITY; + lf.lfItalic = (BYTE) (style.contains ("Italic") ? TRUE : FALSE); + lf.lfWeight = style.contains ("Bold") ? FW_BOLD : FW_NORMAL; + lf.lfHeight = -256; + font.getTypefaceName().copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName)); + + auto* hfont = CreateFontIndirectW (&lf); + const ScopeGuard deleteFont { [&] { DeleteObject (hfont); } }; + + auto* prevFont = hfont != nullptr ? SelectObject (dc, hfont) : nullptr; + const ScopeGuard reinstateFont { [&] { if (prevFont != nullptr) SelectObject (dc, prevFont); } }; + + SharedResourcePointer factories; + + ComSmartPtr interop; + if (FAILED (factories->getDWriteFactory()->GetGdiInterop (interop.resetAndGetPointerAddress())) || interop == nullptr) + return {}; + + ComSmartPtr dwFontFace; + if (FAILED (interop->CreateFontFaceFromHdc (dc, dwFontFace.resetAndGetPointerAddress())) || dwFontFace == nullptr) + return {}; + + const auto dwFont = factories->getFonts().findFontForFace (dwFontFace); + + if (dwFont == nullptr) + return {}; + + return fromFont (dwFont, nullptr, nullptr, MetricsMechanism::gdiWithDwriteFallback); + } + SharedResourcePointer factories; ComSmartPtr collection; ComSmartPtr dwFont;