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

Font: Add support for querying fallbacks

This commit is contained in:
reuk 2024-02-27 19:11:21 +00:00
parent a6a336acb8
commit a1b23c0248
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
2 changed files with 177 additions and 66 deletions

View file

@ -50,8 +50,6 @@ namespace FontValues
const float defaultFontHeight = 14.0f;
float minimumHorizontalScale = 0.7f;
String fallbackFont;
String fallbackFontStyle;
}
class HbScale
@ -455,12 +453,14 @@ public:
instance referencing the shared state.
*/
StringArray getFallbackFamilies() const { return fallbacks; }
String getTypefaceName() const { return typefaceName; }
String getTypefaceStyle() const { return typefaceStyle; }
float getHeight() const { return height; }
float getHorizontalScale() const { return horizontalScale; }
float getKerning() const { return kerning; }
bool getUnderline() const { return underline; }
bool getFallbackEnabled() const { return fallback; }
/* This shared state may be shared between two or more Font instances that are being
read/modified from multiple threads.
@ -469,10 +469,16 @@ public:
during the modification.
*/
void setTypeface (Typeface::Ptr x)
void setTypeface (Typeface::Ptr newTypeface)
{
jassert (getReferenceCount() == 1);
typeface = std::move (x);
typeface = newTypeface;
if (newTypeface != nullptr)
{
typefaceName = typeface->getName();
typefaceStyle = typeface->getStyle();
}
}
void setTypefaceName (String x)
@ -517,6 +523,18 @@ public:
underline = x;
}
void setFallbackFamilies (const StringArray& x)
{
jassert (getReferenceCount() == 1);
fallbacks = x;
}
void setFallback (bool x)
{
jassert (getReferenceCount() == 1);
fallback = x;
}
private:
static float legacyHeightToPoints (Typeface::Ptr p, float h)
{
@ -524,9 +542,11 @@ private:
}
Typeface::Ptr typeface;
StringArray fallbacks;
String typefaceName, typefaceStyle;
float height = 0.0f, horizontalScale = 1.0f, kerning = 0.0f, ascent = 0.0f;
bool underline = false;
bool fallback = true;
CriticalSection mutex;
};
@ -659,45 +679,39 @@ StringArray Font::getAvailableStyles() const
return findAllTypefaceStyles (getTypefacePtr()->getName());
}
void Font::setPreferredFallbackFamilies (const StringArray& fallbacks)
{
if (getPreferredFallbackFamilies() != fallbacks)
{
dupeInternalIfShared();
font->setFallbackFamilies (fallbacks);
}
}
StringArray Font::getPreferredFallbackFamilies() const
{
return font->getFallbackFamilies();
}
void Font::setFallbackEnabled (bool enabled)
{
if (getFallbackEnabled() != enabled)
{
dupeInternalIfShared();
font->setFallback (enabled);
}
}
bool Font::getFallbackEnabled() const
{
return font->getFallbackEnabled();
}
Typeface::Ptr Font::getTypefacePtr() const
{
return font->getTypefacePtr (*this);
}
Typeface* Font::getTypeface() const
{
return getTypefacePtr().get();
}
//==============================================================================
const String& Font::getFallbackFontName()
{
return FontValues::fallbackFont;
}
void Font::setFallbackFontName (const String& name)
{
FontValues::fallbackFont = name;
#if JUCE_MAC || JUCE_IOS
jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
#endif
}
const String& Font::getFallbackFontStyle()
{
return FontValues::fallbackFontStyle;
}
void Font::setFallbackFontStyle (const String& style)
{
FontValues::fallbackFontStyle = style;
#if JUCE_MAC || JUCE_IOS
jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
#endif
}
//==============================================================================
Font Font::withHeight (const float newHeight) const
{
@ -944,6 +958,71 @@ void Font::findFonts (Array<Font>& destArray)
}
}
static bool characterNotRendered (uint32_t c)
{
constexpr uint32_t points[]
{
// Control points
0x0000, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x001A, 0x001B, 0x0085,
// BIDI control points
0x061C, 0x200E, 0x200F, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, 0x2066, 0x2067, 0x2068, 0x2069
};
return std::find (std::begin (points), std::end (points), c) != std::end (points);
}
static bool isFontSuitableForCodepoint (const Font& font, juce_wchar c)
{
const auto& hbFont = font.getNativeDetails().font;
hb_codepoint_t glyph{};
return characterNotRendered ((uint32_t) c)
|| hb_font_get_nominal_glyph (hbFont.get(), (hb_codepoint_t) c, &glyph);
}
static bool isFontSuitableForText (const Font& font, const String& str)
{
for (const auto c : str)
if (! isFontSuitableForCodepoint (font, c))
return false;
return true;
}
Font Font::findSuitableFontForText (const String& text, const String& language) const
{
if (! getFallbackEnabled() || isFontSuitableForText (*this, text))
return *this;
for (const auto& fallback : getPreferredFallbackFamilies())
{
auto copy = *this;
copy.setTypefaceName (fallback);
if (isFontSuitableForText (copy, text))
return copy;
}
if (auto current = getTypefacePtr())
{
if (auto suggested = current->createSystemFallback (text, language))
{
auto copy = *this;
if (copy.getTypefacePtr() != suggested)
{
copy.dupeInternalIfShared();
copy.font->setTypeface (suggested);
}
return copy;
}
}
return *this;
}
//==============================================================================
String Font::toString() const
{

View file

@ -168,6 +168,30 @@ public:
/** Returns a list of the styles that this font can use. */
StringArray getAvailableStyles() const;
//==============================================================================
/** Sets the names of the fallback font families that should be tried, in order,
when searching for glyphs that are missing in the main typeface, specified via
setTypefaceName() or Font(const Typeface::Ptr&).
*/
void setPreferredFallbackFamilies (const StringArray& fallbacks);
/** Returns the names of the fallback font families.
*/
StringArray getPreferredFallbackFamilies() const;
/** When drawing text using this Font, specifies whether glyphs that are missing in the main
typeface should be replaced with glyphs from other fonts.
To find missing glyphs, the typefaces for the preferred fallback families will be checked
in order, followed by the system fallback fonts. The system fallback font is likely to be
different on each platform.
Fallback is enabled by default.
*/
void setFallbackEnabled (bool enabled);
/** Returns true if fallback is enabled, or false otherwise. */
bool getFallbackEnabled() const;
//==============================================================================
/** Returns a typeface font family that represents the default sans-serif font.
@ -208,7 +232,13 @@ public:
*/
static const String& getDefaultStyle();
/** Returns the default system typeface for the given font. */
/** Returns the default system typeface for the given font.
Note: This will only ever return the typeface for the font's "main" family.
Before attempting to render glyphs from this typeface, it's a good idea to check
that those glyphs are present in the typeface, and to select a different
face if necessary.
*/
static Typeface::Ptr getDefaultTypefaceForFont (const Font& font);
//==============================================================================
@ -346,7 +376,8 @@ public:
*/
static void setDefaultMinimumHorizontalScaleFactor (float newMinimumScaleFactor) noexcept;
/** Returns the font's kerning.
/** Returns the font's tracking, i.e. spacing applied between characters in
addition to the kerning defined by the font.
This is the extra space added between adjacent characters, as a proportion
of the font's height.
@ -356,7 +387,7 @@ public:
*/
float getExtraKerningFactor() const noexcept;
/** Returns a copy of this font with a new kerning factor.
/** Returns a copy of this font with a new tracking factor.
@param extraKerning a multiple of the font's height that will be added
to space between the characters. So a value of zero is
normal spacing, positive values spread the letters out,
@ -364,7 +395,7 @@ public:
*/
[[nodiscard]] Font withExtraKerningFactor (float extraKerning) const;
/** Changes the font's kerning.
/** Changes the font's tracking.
@param extraKerning a multiple of the font's height that will be added
to space between the characters. So a value of zero is
normal spacing, positive values spread the letters out,
@ -404,17 +435,13 @@ public:
void getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) const;
//==============================================================================
#ifndef DOXYGEN
/** Returns the typeface used by this font.
/** Returns the main typeface used by this font.
Note that the object returned may go out of scope if this font is deleted
or has its style changed.
Note: This will only ever return the typeface for the "main" family.
Before attempting to render glyphs from this typeface, it's a good idea to check
that those glyphs are present in the typeface, and to select a different
face if necessary.
*/
[[deprecated ("This method is unsafe, use getTypefacePtr() instead.")]]
Typeface* getTypeface() const;
#endif
/** Returns the typeface used by this font. */
Typeface::Ptr getTypefacePtr() const;
/** Creates an array of Font objects to represent all the fonts on the system.
@ -445,25 +472,30 @@ public:
static StringArray findAllTypefaceStyles (const String& family);
//==============================================================================
/** Returns the font family of the typeface to be used for rendering glyphs that aren't
found in the requested typeface.
*/
static const String& getFallbackFontName();
/** Attempts to locate a visually similar font that is capable of rendering the
provided string.
/** Sets the (platform-specific) font family of the typeface to use to find glyphs that
aren't available in whatever font you're trying to use.
*/
static void setFallbackFontName (const String& name);
If fallback is disabled on this Font by setFallbackEnabled(), then this will
always return a copy of the current Font.
/** Returns the font style of the typeface to be used for rendering glyphs that aren't
found in the requested typeface.
*/
static const String& getFallbackFontStyle();
Otherwise, the current font, then each of the fallback fonts specified by
setPreferredFallbackFamilies() will be checked, and the first Font that is
capable of rendering the string will be returned. If none of these fonts is
suitable, then the system font fallback mechanism will be used to locate a
font from the currently installed fonts. If the system also cannot find any
suitable font, then a copy of the original Font will be returned.
/** Sets the (platform-specific) font style of the typeface to use to find glyphs that
aren't available in whatever font you're trying to use.
Note that most fonts don't contain glyphs for all possible unicode codepoints,
and instead may contain e.g. just the glyphs required for a specific script. So,
if the provided text would be displayed using several scripts (multiple languages,
emoji, etc.) then there's a good chance that no single font will be able to
render the entire text. Shorter strings will generally produce better fallback
results than longer strings, with the caveat that the system may take control
characters such as combining marks and variation selectors into account when
selecting suitable fonts, so querying fallbacks character-by-character is likely
to produce poor results.
*/
static void setFallbackFontStyle (const String& style);
Font findSuitableFontForText (const String& text, const String& language = {}) const;
//==============================================================================
/** Creates a string to describe this font.