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:
parent
c083d3e9f1
commit
c5a9e26960
8 changed files with 233 additions and 54 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue