1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Typeface: Add support for querying the default system UI typeface

This commit is contained in:
reuk 2024-04-17 17:26:13 +01:00
parent c083d3e9f1
commit c5a9e26960
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
8 changed files with 233 additions and 54 deletions

View file

@ -39,6 +39,8 @@ class Font::Native
{
public:
HbFont font{};
static Typeface::Ptr getDefaultPlatformTypefaceForFont (const Font&);
};
using GetTypefaceForFont = Typeface::Ptr (*)(const Font&);
@ -426,10 +428,11 @@ void Font::dupeInternalIfShared()
//==============================================================================
struct FontPlaceholderNames
{
String sans { "<Sans-Serif>" },
serif { "<Serif>" },
mono { "<Monospaced>" },
regular { "<Regular>" };
String sans = "<Sans-Serif>",
serif = "<Serif>",
mono = "<Monospaced>",
regular = "<Regular>",
systemUi = "system-ui";
};
static const FontPlaceholderNames& getFontPlaceholderNames()
@ -447,6 +450,7 @@ static FontNamePreloader fnp;
#endif
const String& Font::getDefaultSansSerifFontName() { return getFontPlaceholderNames().sans; }
const String& Font::getSystemUIFontName() { return getFontPlaceholderNames().systemUi; }
const String& Font::getDefaultSerifFontName() { return getFontPlaceholderNames().serif; }
const String& Font::getDefaultMonospacedFontName() { return getFontPlaceholderNames().mono; }
const String& Font::getDefaultStyle() { return getFontPlaceholderNames().regular; }
@ -869,4 +873,30 @@ Font::Native Font::getNativeDetails() const
return { font->getFontPtr (*this) };
}
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
{
const auto systemTypeface = [&]() -> Typeface::Ptr
{
if (font.getTypefaceName() != getSystemUIFontName())
return {};
const auto systemTypeface = Typeface::findSystemTypeface();
if (systemTypeface == nullptr)
return {};
if (systemTypeface->getStyle() == font.getTypefaceStyle())
return systemTypeface;
auto copy = font;
copy.setTypefaceName (systemTypeface->getName());
return getDefaultTypefaceForFont (copy);
}();
if (systemTypeface != nullptr)
return systemTypeface;
return Native::getDefaultPlatformTypefaceForFont (font);
}
} // namespace juce

View file

@ -212,6 +212,15 @@ public:
*/
static const String& getDefaultSansSerifFontName();
/** Returns a typeface font family that represents the system UI font.
Note that this method just returns a generic placeholder string that means "the default
UI font" - it's not the actual font family of this font.
@see getDefaultSansSerifFontName, setTypefaceName
*/
static const String& getSystemUIFontName();
/** Returns a typeface font family that represents the default serif font.
Note that this method just returns a generic placeholder string that means "the default

View file

@ -346,6 +346,24 @@ public:
*/
virtual Typeface::Ptr createSystemFallback (const String& text, const String& language) const = 0;
/** Returns the system's default UI font.
This will differ depending on the platform.
On Linux/fontconfig, this returns the typeface mapped to the name "system-ui",
or nullptr if no such font exists.
On Windows, this queries SystemParametersInfo with the key SPI_GETNONCLIENTMETRICS,
and returns the lfMessageFont that is returned, or nullptr if the font cannot be found.
On macOS and iOS, this returns the result of CTFontCreateUIFontForLanguage() for
the kCTFontUIFontSystem typeface.
On Android 29+, this will use AFontMatcher to return the "system-ui" font. On earlier
Android versions, this will attempt to return the Roboto font.
*/
static Typeface::Ptr findSystemTypeface();
private:
//==============================================================================
String name;

View file

@ -566,24 +566,45 @@ public:
ComSmartPtr<IDWriteFontFace> getIDWriteFontFace() const { return dwFontFace; }
private:
float getKerning (int glyph1, int glyph2) const
static Typeface::Ptr findSystemTypeface()
{
const auto face = dwFontFace.getInterface<IDWriteFontFace1>();
NONCLIENTMETRICS nonClientMetrics{};
nonClientMetrics.cbSize = sizeof (NONCLIENTMETRICS);
const UINT16 glyphs[] { (UINT16) glyph1, (UINT16) glyph2 };
INT32 advances [std::size (glyphs)]{};
if (FAILED (face->GetDesignGlyphAdvances ((UINT32) std::size (glyphs), std::data (glyphs), std::data (advances))))
if (! SystemParametersInfo (SPI_GETNONCLIENTMETRICS, sizeof (NONCLIENTMETRICS), &nonClientMetrics, sizeof (NONCLIENTMETRICS)))
return {};
DWRITE_FONT_METRICS metrics{};
face->GetMetrics (&metrics);
SharedResourcePointer<Direct2DFactories> factories;
// TODO(reuk) incorrect
return (float) advances[0] / (float) metrics.designUnitsPerEm;
ComSmartPtr<IDWriteGdiInterop> interop;
if (FAILED (factories->getDWriteFactory()->GetGdiInterop (interop.resetAndGetPointerAddress())) || interop == nullptr)
return {};
ComSmartPtr<IDWriteFont> dwFont;
if (FAILED (interop->CreateFontFromLOGFONT (&nonClientMetrics.lfMessageFont, dwFont.resetAndGetPointerAddress())) || dwFont == nullptr)
return {};
ComSmartPtr<IDWriteFontFace> dwFontFace;
if (FAILED (dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress())) || dwFontFace == nullptr)
return {};
const auto name = getLocalisedFamilyName (*dwFont);
const auto style = getLocalisedStyle (*dwFont);
const HbFace hbFace { hb_directwrite_face_create (dwFontFace) };
HbFont font { hb_font_create (hbFace.get()) };
const auto metrics = getGdiMetrics (font.get()).value_or (getDwriteMetrics (dwFontFace));
return new WindowsDirectWriteTypeface (name,
style,
dwFont,
dwFontFace,
std::move (font),
metrics,
{});
}
private:
static UINT32 numUtf16Words (const CharPointer_UTF16& str)
{
return (UINT32) (str.findTerminatingNull().getAddress() - str.getAddress());
@ -795,7 +816,7 @@ struct DefaultFontNames
String defaultSans, defaultSerif, defaultFixed, defaultFallback;
};
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
Typeface::Ptr Font::Native::getDefaultPlatformTypefaceForFont (const Font& font)
{
static DefaultFontNames defaultNames;
@ -822,6 +843,11 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (Span<const std::byte> data)
return WindowsDirectWriteTypeface::from (data);
}
Typeface::Ptr Typeface::findSystemTypeface()
{
return WindowsDirectWriteTypeface::findSystemTypeface();
}
void Typeface::scanFolderForFonts (const File&)
{
// TODO(reuk)

View file

@ -35,7 +35,7 @@
namespace juce
{
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
Typeface::Ptr Font::Native::getDefaultPlatformTypefaceForFont (const Font& font)
{
Font f (font);
f.setTypefaceName ([&]() -> String
@ -285,10 +285,58 @@ public:
c->remove ({ getName(), getStyle() });
}
static Typeface::Ptr findSystemTypeface()
{
if (__builtin_available (android 29, *))
return findSystemTypefaceWithMatcher();
return from (FontOptions{}.withName ("Roboto"));
}
private:
static __INTRODUCED_IN (29) Typeface::Ptr fromMatchedFont (AFont* matched)
{
if (matched == nullptr)
{
// Unable to find any matching fonts. This should never happen - in the worst case,
// we should at least get a font with the tofu character.
jassertfalse;
return {};
}
const File matchedFile { AFont_getFontFilePath (matched) };
const auto matchedIndex = AFont_getCollectionIndex (matched);
auto* cache = TypefaceFileCache::getInstance();
if (cache == nullptr)
return {}; // Perhaps we're shutting down
return cache->get ({ matchedFile, (int) matchedIndex }, &loadCompatibleFont);
}
static __INTRODUCED_IN (29) Typeface::Ptr findSystemTypefaceWithMatcher()
{
using AFontMatcherPtr = std::unique_ptr<AFontMatcher, FunctionPointerDestructor<AFontMatcher_destroy>>;
using AFontPtr = std::unique_ptr<AFont, FunctionPointerDestructor<AFont_close>>;
constexpr uint16_t testString[] { 't', 'e', 's', 't' };
const AFontMatcherPtr matcher { AFontMatcher_create() };
const AFontPtr matched { AFontMatcher_match (matcher.get(),
"system-ui",
testString,
std::size (testString),
nullptr) };
return fromMatchedFont (matched.get());
}
__INTRODUCED_IN (29) Typeface::Ptr matchWithAFontMatcher (const String& text, const String& language) const
{
using AFontMatcherPtr = std::unique_ptr<AFontMatcher, FunctionPointerDestructor<AFontMatcher_destroy>>;
using AFontPtr = std::unique_ptr<AFont, FunctionPointerDestructor<AFont_close>>;
const AFontMatcherPtr matcher { AFontMatcher_create() };
const auto weight = hb_style_get_value (hbFont.get(), HB_STYLE_TAG_WEIGHT);
@ -299,7 +347,6 @@ private:
const auto utf16 = text.toUTF16();
using AFontPtr = std::unique_ptr<AFont, FunctionPointerDestructor<AFont_close>>;
const AFontPtr matched { AFontMatcher_match (matcher.get(),
readFontName (hb_font_get_face (hbFont.get()),
HB_OT_NAME_ID_FONT_FAMILY,
@ -308,23 +355,7 @@ private:
(uint32_t) (utf16.findTerminatingNull().getAddress() - utf16.getAddress()),
nullptr) };
if (matched == nullptr)
{
// Unable to find any matching fonts. This should never happen - in the worst case,
// we should at least get a font with the tofu character.
jassertfalse;
return {};
}
const File matchedFile { AFont_getFontFilePath (matched.get()) };
const auto matchedIndex = AFont_getCollectionIndex (matched.get());
auto* cache = TypefaceFileCache::getInstance();
if (cache == nullptr)
return {}; // Perhaps we're shutting down
return cache->get ({ matchedFile, (int) matchedIndex }, &loadCompatibleFont);
return fromMatchedFont (matched.get());
}
static Typeface::Ptr loadCompatibleFont (const TypefaceFileAndIndex& info)
@ -592,6 +623,11 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (Span<const std::byte> data)
return AndroidTypeface::from (data);
}
Typeface::Ptr Typeface::findSystemTypeface()
{
return AndroidTypeface::findSystemTypeface();
}
void Typeface::scanFolderForFonts (const File&)
{
jassertfalse; // not currently available

View file

@ -39,10 +39,12 @@
namespace juce
{
#if JUCE_USE_FONTCONFIG
using FcConfigPtr = std::unique_ptr<FcConfig, FunctionPointerDestructor<FcConfigDestroy>>;
using FcPatternPtr = std::unique_ptr<FcPattern, FunctionPointerDestructor<FcPatternDestroy>>;
using FcCharSetPtr = std::unique_ptr<FcCharSet, FunctionPointerDestructor<FcCharSetDestroy>>;
using FcLangSetPtr = std::unique_ptr<FcLangSet, FunctionPointerDestructor<FcLangSetDestroy>>;
#endif
struct FTLibWrapper final : public ReferenceCountedObject
{
@ -61,7 +63,10 @@ struct FTLibWrapper final : public ReferenceCountedObject
FT_Done_FreeType (library);
}
#if JUCE_USE_FONTCONFIG
const FcConfigPtr fcConfig { FcInitLoadConfigAndFonts() };
#endif
FT_Library library = {};
using Ptr = ReferenceCountedObjectPtr<FTLibWrapper>;
@ -317,6 +322,8 @@ public:
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (FTTypefaceList)
FTLibWrapper::Ptr getLibrary() const { return library; }
private:
FTLibWrapper::Ptr library = new FTLibWrapper;
std::vector<std::unique_ptr<KnownTypeface>> faces;
@ -437,7 +444,6 @@ public:
if (cache == nullptr)
return {};
auto* config = ftFace->library->fcConfig.get();
FcPatternPtr pattern { FcPatternCreate() };
{
@ -468,11 +474,47 @@ public:
FcPatternAddLangSet (pattern.get(), FC_LANG, langset.get());
}
FcConfigSubstitute (config, pattern.get(), FcMatchPattern);
FcDefaultSubstitute (pattern.get());
return fromPattern (pattern.get());
#else
// Font substitution will not work unless fontconfig is enabled.
jassertfalse;
return nullptr;
#endif
}
~FreeTypeTypeface() override
{
if (doCache == DoCache::yes)
if (auto* list = FTTypefaceList::getInstance())
list->removeMemoryFace (ftFace);
}
static Typeface::Ptr findSystemTypeface()
{
#if JUCE_USE_FONTCONFIG
FcPatternPtr pattern { FcNameParse (unalignedPointerCast<const FcChar8*> ("system-ui")) };
return fromPattern (pattern.get());
#else
return nullptr;
#endif
}
private:
#if JUCE_USE_FONTCONFIG
static Typeface::Ptr fromPattern (FcPattern* pattern)
{
auto* cache = TypefaceFileCache::getInstance();
if (cache == nullptr)
return {};
const auto library = FTTypefaceList::getInstance()->getLibrary();
FcConfigSubstitute (library->fcConfig.get(), pattern, FcMatchPattern);
FcDefaultSubstitute (pattern);
FcResult result{};
const FcPatternPtr matched { FcFontMatch (config, pattern.get(), &result) };
const FcPatternPtr matched { FcFontMatch (library->fcConfig.get(), pattern, &result) };
if (result != FcResultMatch)
return {};
@ -502,21 +544,9 @@ public:
return new FreeTypeTypeface (DoCache::no, face, std::move (cachedFont), face->face->family_name, face->face->style_name);
});
#else
// Font substitution will not work unless fontconfig is enabled.
jassertfalse;
return nullptr;
#endif
}
#endif
~FreeTypeTypeface() override
{
if (doCache == DoCache::yes)
if (auto* list = FTTypefaceList::getInstance())
list->removeMemoryFace (ftFace);
}
private:
FreeTypeTypeface (DoCache cache,
FTFaceWrapper::Ptr ftFaceIn,
HbFont hbIn,
@ -551,4 +581,9 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (Span<const std::byte> data)
return FreeTypeTypeface::from (data);
}
Typeface::Ptr Typeface::findSystemTypeface()
{
return FreeTypeTypeface::findSystemTypeface();
}
} // namespace juce

View file

@ -197,7 +197,7 @@ private:
JUCE_DECLARE_NON_COPYABLE (DefaultFontInfo)
};
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
Typeface::Ptr Font::Native::getDefaultPlatformTypefaceForFont (const Font& font)
{
static const DefaultFontInfo defaultInfo;

View file

@ -667,6 +667,26 @@ public:
return ctFont.get();
}
static Typeface::Ptr findSystemTypeface()
{
CFUniquePtr<CTFontRef> defaultCtFont (CTFontCreateUIFontForLanguage (kCTFontUIFontSystem, 0.0, nullptr));
const CFUniquePtr<CFStringRef> newName { CTFontCopyFamilyName (defaultCtFont.get()) };
const CFUniquePtr<CTFontDescriptorRef> descriptor { CTFontCopyFontDescriptor (defaultCtFont.get()) };
const CFUniquePtr<CFStringRef> newStyle { (CFStringRef) CTFontDescriptorCopyAttribute (descriptor.get(),
kCTFontStyleNameAttribute) };
HbFont result { hb_coretext_font_create (defaultCtFont.get()) };
if (result == nullptr)
return {};
return new CoreTextTypeface { std::move (defaultCtFont),
std::move (result),
String::fromCFString (newName.get()),
String::fromCFString (newStyle.get()),
{} };
}
private:
CoreTextTypeface (CFUniquePtr<CTFontRef> nativeFont,
HbFont fontIn,
@ -737,6 +757,11 @@ void Typeface::scanFolderForFonts (const File& folder)
CTFontManagerRegisterFontsForURL (urlref.get(), kCTFontManagerScopeProcess, nullptr);
}
Typeface::Ptr Typeface::findSystemTypeface()
{
return CoreTextTypeface::findSystemTypeface();
}
StringArray Font::findAllTypefaceNames()
{
StringArray names;
@ -807,7 +832,7 @@ struct DefaultFontNames
#endif
};
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
Typeface::Ptr Font::Native::getDefaultPlatformTypefaceForFont (const Font& font)
{
static DefaultFontNames defaultNames;