mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Font: Allow switching between legacy non-portable metrics, and new portable metrics
This commit is contained in:
parent
4f2c287f9b
commit
c2fce879c5
12 changed files with 291 additions and 464 deletions
|
|
@ -9,14 +9,24 @@ new TypefaceMetricsKind argument. The getAscent(), getDescent(), and
|
|||
getHeightToPointsFactor() members have been replaced by getMetrics(), which
|
||||
returns the same metrics information all at once.
|
||||
|
||||
Font instances now store a metrics kind internally. Calls to Font::getAscent()
|
||||
and other functions that query font metrics will always use the Font's stored
|
||||
metrics kind. Calls to Font::operator== will take the metrics kinds into
|
||||
account, so two fonts that differ only in their stored metrics kind will
|
||||
be considered non-equal.
|
||||
|
||||
**Possible Issues**
|
||||
|
||||
Code that calls any of the affected functions will fail to compile.
|
||||
Code that calls any of the affected Typeface functions will fail to compile.
|
||||
Code that compares Font instances may behave differently if the compared font
|
||||
instances use mismatched metrics kinds.
|
||||
|
||||
**Workaround**
|
||||
|
||||
Specify the kind of metrics you require when calling Typeface member functions.
|
||||
Call getMetrics() instead of the old individual getters for metrics.
|
||||
Call getMetrics() instead of the old individual getters for metrics. Review
|
||||
calls to Font::operator==, especially where comparing against a
|
||||
default-constructed Font.
|
||||
|
||||
**Rationale**
|
||||
|
||||
|
|
@ -27,8 +37,9 @@ layout changes in existing projects), and portable metrics (more suitable for
|
|||
new or cross-platform projects).
|
||||
Most users will fetch metrics from Font objects rather than from the Typeface.
|
||||
Font will continue to return non-portable metrics when constructed using the
|
||||
existing (deprecated) constructors. Portable metrics can be enabled by
|
||||
switching to the new Font constructor that takes a FontOptions argument.
|
||||
old (deprecated) constructors. Portable metrics can be enabled by switching to
|
||||
the new Font constructor that takes a FontOptions argument. See the
|
||||
documentation for TypefaceMetricsKind for more details.
|
||||
|
||||
|
||||
## Change
|
||||
|
|
|
|||
|
|
@ -533,7 +533,7 @@ void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const Affin
|
|||
{
|
||||
Path p;
|
||||
Font& font = stateStack.getLast()->font;
|
||||
font.getTypefacePtr()->getOutlineForGlyph (glyphNumber, p);
|
||||
font.getTypefacePtr()->getOutlineForGlyph (font.getMetricsKind(), glyphNumber, p);
|
||||
fillPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()).followedBy (transform));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -216,8 +216,20 @@ public:
|
|||
|
||||
HbFont getFontPtr (const Font& f)
|
||||
{
|
||||
const ScopedLock lock (mutex);
|
||||
|
||||
if (auto ptr = getTypefacePtr (f))
|
||||
return ptr->getNativeDetails().getFontAtSizeAndScale (f.getHeight(), f.getHorizontalScale());
|
||||
return ptr->getNativeDetails().getFontAtSizeAndScale (f.getMetricsKind(), f.getHeight(), f.getHorizontalScale());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
TypefaceMetrics getMetrics (const Font& f)
|
||||
{
|
||||
const ScopedLock lock (mutex);
|
||||
|
||||
if (auto ptr = getTypefacePtr (f))
|
||||
return ptr->getMetrics (f.getMetricsKind());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -228,16 +240,6 @@ public:
|
|||
typeface = nullptr;
|
||||
}
|
||||
|
||||
float getAscent (const Font& f)
|
||||
{
|
||||
const ScopedLock lock (mutex);
|
||||
|
||||
if (approximatelyEqual (ascent, 0.0f))
|
||||
ascent = getTypefacePtr (f)->getAscent();
|
||||
|
||||
return getHeight() * ascent;
|
||||
}
|
||||
|
||||
/* We do not need to lock in these functions, as it's guaranteed
|
||||
that these data members can only change if there is a single Font
|
||||
instance referencing the shared state.
|
||||
|
|
@ -256,6 +258,7 @@ public:
|
|||
float getKerning() const { return options.getKerningFactor(); }
|
||||
bool getUnderline() const { return options.getUnderline(); }
|
||||
bool getFallbackEnabled() const { return options.getFallbackEnabled(); }
|
||||
TypefaceMetricsKind getMetricsKind() const { return options.getMetricsKind(); }
|
||||
|
||||
/* This shared state may be shared between two or more Font instances that are being
|
||||
read/modified from multiple threads.
|
||||
|
|
@ -269,7 +272,7 @@ public:
|
|||
jassert (getReferenceCount() == 1);
|
||||
typeface = newTypeface;
|
||||
|
||||
if (typeface != nullptr)
|
||||
if (newTypeface != nullptr)
|
||||
{
|
||||
options = options.withTypeface (typeface)
|
||||
.withName (typeface->getName())
|
||||
|
|
@ -307,12 +310,6 @@ public:
|
|||
options = options.withKerningFactor (x);
|
||||
}
|
||||
|
||||
void setAscent (float x)
|
||||
{
|
||||
jassert (getReferenceCount() == 1);
|
||||
ascent = x;
|
||||
}
|
||||
|
||||
void setUnderline (bool x)
|
||||
{
|
||||
jassert (getReferenceCount() == 1);
|
||||
|
|
@ -348,7 +345,6 @@ private:
|
|||
}
|
||||
|
||||
Typeface::Ptr typeface;
|
||||
float ascent{};
|
||||
FontOptions options;
|
||||
CriticalSection mutex;
|
||||
};
|
||||
|
|
@ -359,22 +355,28 @@ Font::Font (FontOptions opt)
|
|||
{
|
||||
}
|
||||
|
||||
Font::Font() : font (new SharedFontInternal (FontOptions{})) {}
|
||||
Font::Font (const Typeface::Ptr& typeface) : font (new SharedFontInternal (FontOptions { typeface })) {}
|
||||
template <typename... Args>
|
||||
auto legacyArgs (Args&&... args)
|
||||
{
|
||||
return FontOptions { std::forward<Args> (args)... }.withMetricsKind (TypefaceMetricsKind::legacy);
|
||||
}
|
||||
|
||||
Font::Font() : font (new SharedFontInternal (legacyArgs())) {}
|
||||
Font::Font (const Typeface::Ptr& typeface) : font (new SharedFontInternal (legacyArgs (typeface))) {}
|
||||
Font::Font (const Font& other) noexcept : font (other.font) {}
|
||||
|
||||
Font::Font (float fontHeight, int styleFlags)
|
||||
: font (new SharedFontInternal (FontOptions { fontHeight, styleFlags }))
|
||||
: font (new SharedFontInternal (legacyArgs (fontHeight, styleFlags)))
|
||||
{
|
||||
}
|
||||
|
||||
Font::Font (const String& typefaceName, float fontHeight, int styleFlags)
|
||||
: font (new SharedFontInternal (FontOptions { typefaceName, fontHeight, styleFlags }))
|
||||
: font (new SharedFontInternal (legacyArgs (typefaceName, fontHeight, styleFlags)))
|
||||
{
|
||||
}
|
||||
|
||||
Font::Font (const String& typefaceName, const String& typefaceStyle, float fontHeight)
|
||||
: font (new SharedFontInternal (FontOptions { typefaceName, typefaceStyle, fontHeight }))
|
||||
: font (new SharedFontInternal (legacyArgs (typefaceName, typefaceStyle, fontHeight)))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -461,7 +463,6 @@ void Font::setTypefaceName (const String& faceName)
|
|||
dupeInternalIfShared();
|
||||
font->setTypefaceName (faceName);
|
||||
font->setTypeface (nullptr);
|
||||
font->setAscent (0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -472,7 +473,6 @@ void Font::setTypefaceStyle (const String& typefaceStyle)
|
|||
dupeInternalIfShared();
|
||||
font->setTypefaceStyle (typefaceStyle);
|
||||
font->setTypeface (nullptr);
|
||||
font->setAscent (0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -531,7 +531,7 @@ Font Font::withHeight (const float newHeight) const
|
|||
|
||||
float Font::getHeightToPointsFactor() const
|
||||
{
|
||||
return getTypefacePtr()->getHeightToPointsFactor();
|
||||
return getTypefacePtr()->getMetrics (getMetricsKind()).heightToPoints;
|
||||
}
|
||||
|
||||
Font Font::withPointHeight (float heightInPoints) const
|
||||
|
|
@ -591,7 +591,6 @@ void Font::setStyleFlags (const int newFlags)
|
|||
font->setTypeface (nullptr);
|
||||
font->setTypefaceStyle (FontStyleHelpers::getStyleName (newFlags));
|
||||
font->setUnderline ((newFlags & underlined) != 0);
|
||||
font->setAscent (0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -682,6 +681,8 @@ bool Font::isBold() const noexcept { return FontStyleHelpers::isBold
|
|||
bool Font::isItalic() const noexcept { return FontStyleHelpers::isItalic (font->getTypefaceStyle()); }
|
||||
bool Font::isUnderlined() const noexcept { return font->getUnderline(); }
|
||||
|
||||
TypefaceMetricsKind Font::getMetricsKind() const noexcept { return font->getMetricsKind(); }
|
||||
|
||||
void Font::setBold (const bool shouldBeBold)
|
||||
{
|
||||
auto flags = getStyleFlags();
|
||||
|
|
@ -705,7 +706,7 @@ void Font::setUnderline (const bool shouldBeUnderlined)
|
|||
|
||||
float Font::getAscent() const
|
||||
{
|
||||
return font->getAscent (*this);
|
||||
return font->getMetrics (*this).ascent * getHeight();
|
||||
}
|
||||
|
||||
float Font::getHeight() const noexcept { return font->getHeight(); }
|
||||
|
|
@ -722,13 +723,13 @@ int Font::getStringWidth (const String& text) const
|
|||
|
||||
float Font::getStringWidthFloat (const String& text) const
|
||||
{
|
||||
const auto w = getTypefacePtr()->getStringWidth (text, getHeight(), getHorizontalScale());
|
||||
const auto w = getTypefacePtr()->getStringWidth (font->getMetricsKind(), text, getHeight(), getHorizontalScale());
|
||||
return w + (font->getHeight() * font->getHorizontalScale() * font->getKerning() * (float) text.length());
|
||||
}
|
||||
|
||||
void Font::getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) const
|
||||
{
|
||||
getTypefacePtr()->getGlyphPositions (text, glyphs, xOffsets, getHeight(), getHorizontalScale());
|
||||
getTypefacePtr()->getGlyphPositions (font->getMetricsKind(), text, glyphs, xOffsets, getHeight(), getHorizontalScale());
|
||||
|
||||
if (auto num = xOffsets.size())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -349,6 +349,9 @@ public:
|
|||
/** Returns true if the font is underlined. */
|
||||
bool isUnderlined() const noexcept;
|
||||
|
||||
/** Returns the kind of metrics used by this Font. */
|
||||
TypefaceMetricsKind getMetricsKind() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the font's horizontal scale.
|
||||
A value of 1.0 is the normal scale, less than this will be narrower, greater
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ auto FontOptions::tie() const
|
|||
style,
|
||||
typeface.get(),
|
||||
fallbacks,
|
||||
metricsKind,
|
||||
height,
|
||||
tracking,
|
||||
horizontalScale,
|
||||
|
|
|
|||
|
|
@ -121,6 +121,9 @@ public:
|
|||
/** Returns a copy of these options with underline enabled or disabled, defaults to disabled. */
|
||||
[[nodiscard]] FontOptions withUnderline (bool x = true) const { return withMember (*this, &FontOptions::underlined, x); }
|
||||
|
||||
/** Returns a copy of these options the specified metrics kind. */
|
||||
[[nodiscard]] FontOptions withMetricsKind (TypefaceMetricsKind x) const { return withMember (*this, &FontOptions::metricsKind, x); }
|
||||
|
||||
/** @see withName() */
|
||||
[[nodiscard]] auto getName() const { return name; }
|
||||
/** @see withStyle() */
|
||||
|
|
@ -139,6 +142,8 @@ public:
|
|||
[[nodiscard]] auto getFallbackEnabled() const { return fallbackEnabled; }
|
||||
/** @see withUnderline() */
|
||||
[[nodiscard]] auto getUnderline() const { return underlined; }
|
||||
/** @see withMetricsKind() */
|
||||
[[nodiscard]] auto getMetricsKind() const { return metricsKind; }
|
||||
|
||||
/** Equality operator. */
|
||||
[[nodiscard]] bool operator== (const FontOptions& other) const;
|
||||
|
|
@ -159,6 +164,7 @@ private:
|
|||
String name, style;
|
||||
Typeface::Ptr typeface;
|
||||
std::vector<String> fallbacks;
|
||||
TypefaceMetricsKind metricsKind { TypefaceMetricsKind::portable };
|
||||
float height{};
|
||||
float tracking{};
|
||||
float horizontalScale = 1.0f;
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ void PositionedGlyph::createPath (Path& path) const
|
|||
if (auto t = font.getTypefacePtr())
|
||||
{
|
||||
Path p;
|
||||
t->getOutlineForGlyph (glyph, p);
|
||||
t->getOutlineForGlyph (font.getMetricsKind(), glyph, p);
|
||||
|
||||
path.addPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight())
|
||||
.translated (x, y));
|
||||
|
|
@ -96,7 +96,7 @@ bool PositionedGlyph::hitTest (float px, float py) const
|
|||
if (auto t = font.getTypefacePtr())
|
||||
{
|
||||
Path p;
|
||||
t->getOutlineForGlyph (glyph, p);
|
||||
t->getOutlineForGlyph (font.getMetricsKind(), glyph, p);
|
||||
|
||||
AffineTransform::translation (-x, -y)
|
||||
.scaled (1.0f / (font.getHeight() * font.getHorizontalScale()), 1.0f / font.getHeight())
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ static void overrideCTFontAdvances (hb_font_t* hb, CTFontRef fontRef)
|
|||
}
|
||||
#endif
|
||||
|
||||
struct TypefaceLegacyMetrics
|
||||
struct TypefaceAscentDescent
|
||||
{
|
||||
float ascent{}; // in em units
|
||||
float descent{}; // in em units
|
||||
|
|
@ -163,29 +163,46 @@ struct TypefaceLegacyMetrics
|
|||
|
||||
float getPointsToHeightFactor() const { return ascent + descent; }
|
||||
float getHeightToPointsFactor() const { return 1.0f / getPointsToHeightFactor(); }
|
||||
|
||||
TypefaceMetrics getTypefaceMetrics() const
|
||||
{
|
||||
return { getScaledAscent(), getHeightToPointsFactor() };
|
||||
}
|
||||
};
|
||||
|
||||
using HbFont = std::unique_ptr<hb_font_t, FunctionPointerDestructor<hb_font_destroy>>;
|
||||
using HbFace = std::unique_ptr<hb_face_t, FunctionPointerDestructor<hb_face_destroy>>;
|
||||
using HbBuffer = std::unique_ptr<hb_buffer_t, FunctionPointerDestructor<hb_buffer_destroy>>;
|
||||
using HbBlob = std::unique_ptr<hb_blob_t, FunctionPointerDestructor<hb_blob_destroy>>;
|
||||
|
||||
class Typeface::Native
|
||||
{
|
||||
public:
|
||||
explicit Native (hb_font_t* fontRef)
|
||||
: Native (fontRef, findLegacyMetrics (fontRef)) {}
|
||||
|
||||
Native (hb_font_t* fontRef, TypefaceLegacyMetrics metrics)
|
||||
: font (fontRef), legacyMetrics (metrics) {}
|
||||
Native (hb_font_t* fontRef, TypefaceAscentDescent nonPortableMetricsIn)
|
||||
: font (fontRef), nonPortable (nonPortableMetricsIn)
|
||||
{
|
||||
}
|
||||
|
||||
auto* getFont() const { return font; }
|
||||
|
||||
auto getLegacyMetrics() const { return legacyMetrics; }
|
||||
TypefaceAscentDescent getMetrics (TypefaceMetricsKind kind) const
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case TypefaceMetricsKind::legacy:
|
||||
return nonPortable;
|
||||
|
||||
HbFont getFontAtSizeAndScale (float height, float horizontalScale) const
|
||||
case TypefaceMetricsKind::portable:
|
||||
return portable;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
HbFont getFontAtSizeAndScale (TypefaceMetricsKind kind, float height, float horizontalScale) const
|
||||
{
|
||||
HbFont subFont { hb_font_create_sub_font (font) };
|
||||
const auto points = height * getLegacyMetrics().getHeightToPointsFactor();
|
||||
const auto points = height * getMetrics (kind).getHeightToPointsFactor();
|
||||
|
||||
hb_font_set_ptem (subFont.get(), points);
|
||||
hb_font_set_scale (subFont.get(), HbScale::juceToHb (points * horizontalScale), HbScale::juceToHb (points));
|
||||
|
|
@ -198,28 +215,27 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
static TypefaceLegacyMetrics findLegacyMetrics (hb_font_t* f)
|
||||
static TypefaceAscentDescent findPortableMetrics (hb_font_t* f, TypefaceAscentDescent fallback)
|
||||
{
|
||||
hb_font_extents_t extents{};
|
||||
|
||||
if (! hb_font_get_h_extents (f, &extents))
|
||||
{
|
||||
// jassertfalse;
|
||||
return { 0.5f, 0.5f };
|
||||
}
|
||||
return fallback;
|
||||
|
||||
const auto ascent = std::abs ((float) extents.ascender);
|
||||
const auto descent = std::abs ((float) extents.descender);
|
||||
const auto upem = (float) hb_face_get_upem (hb_font_get_face (f));
|
||||
|
||||
TypefaceLegacyMetrics result;
|
||||
TypefaceAscentDescent result;
|
||||
result.ascent = ascent / upem;
|
||||
result.descent = descent / upem;
|
||||
return result;
|
||||
}
|
||||
|
||||
hb_font_t* font = nullptr;
|
||||
TypefaceLegacyMetrics legacyMetrics;
|
||||
|
||||
TypefaceAscentDescent nonPortable;
|
||||
TypefaceAscentDescent portable = findPortableMetrics (font, nonPortable);
|
||||
};
|
||||
|
||||
struct FontStyleHelpers
|
||||
|
|
@ -292,7 +308,7 @@ struct FontStyleHelpers
|
|||
private:
|
||||
static String findName (const String& placeholder)
|
||||
{
|
||||
const Font f (FontOptions (placeholder, 15.0f, Font::plain));
|
||||
const Font f (FontOptions { placeholder, 15.0f, Font::plain });
|
||||
return Font::getDefaultTypefaceForFont (f)->getName();
|
||||
}
|
||||
|
||||
|
|
@ -315,24 +331,21 @@ struct FontStyleHelpers
|
|||
|
||||
static HbFace getFaceForBlob (Span<const char> bytes, unsigned int index)
|
||||
{
|
||||
auto* blob = hb_blob_create_or_fail (bytes.data(),
|
||||
HbBlob blob { hb_blob_create_or_fail (bytes.data(),
|
||||
(unsigned int) bytes.size(),
|
||||
HB_MEMORY_MODE_DUPLICATE,
|
||||
nullptr,
|
||||
nullptr);
|
||||
const ScopeGuard scope { [&] { hb_blob_destroy (blob); } };
|
||||
nullptr) };
|
||||
|
||||
const auto count = hb_face_count (blob);
|
||||
const auto count = hb_face_count (blob.get());
|
||||
|
||||
if (index < count)
|
||||
return HbFace { hb_face_create (blob.get(), index) };
|
||||
|
||||
if (count < 1)
|
||||
{
|
||||
// Attempted to create a font from invalid data. Perhaps the font format was unrecognised.
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
return HbFace { hb_face_create (blob, index) };
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -388,11 +401,11 @@ static HbDrawFuncs getPathDrawFuncs()
|
|||
return result;
|
||||
}
|
||||
|
||||
void Typeface::getOutlineForGlyph (int glyphNumber, Path& path)
|
||||
void Typeface::getOutlineForGlyph (TypefaceMetricsKind kind, int glyphNumber, Path& path) const
|
||||
{
|
||||
const auto native = getNativeDetails();
|
||||
auto* font = native.getFont();
|
||||
const auto metrics = getNativeDetails().getLegacyMetrics();
|
||||
const auto metrics = native.getMetrics (kind);
|
||||
const auto scale = metrics.getHeightToPointsFactor() / (float) hb_face_get_upem (hb_font_get_face (font));
|
||||
|
||||
// getTypefaceGlyph returns glyphs in em space, getOutlineForGlyph returns glyphs in "special JUCE units" space
|
||||
|
|
@ -405,10 +418,10 @@ void Typeface::applyVerticalHintingTransform (float, Path&)
|
|||
jassertfalse;
|
||||
}
|
||||
|
||||
EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float)
|
||||
EdgeTable* Typeface::getEdgeTableForGlyph (TypefaceMetricsKind kind, int glyphNumber, const AffineTransform& transform, float)
|
||||
{
|
||||
Path path;
|
||||
getOutlineForGlyph (glyphNumber, path);
|
||||
getOutlineForGlyph (kind, glyphNumber, path);
|
||||
path.applyTransform (transform);
|
||||
|
||||
return new EdgeTable (path.getBounds().getSmallestIntegerContainer().expanded (1, 0), std::move (path), {});
|
||||
|
|
@ -422,323 +435,6 @@ static Colour makeColour (hb_color_t c)
|
|||
hb_color_get_blue (c));
|
||||
}
|
||||
|
||||
class HbPaintGroup
|
||||
{
|
||||
public:
|
||||
void pushClipGlyph (const AffineTransform& t, hb_codepoint_t glyph, hb_font_t* font)
|
||||
{
|
||||
auto path = getGlyphPathInGlyphUnits (glyph, font);
|
||||
path.applyTransform (t);
|
||||
pushClip (std::move (path));
|
||||
}
|
||||
|
||||
void pushClipRect (const AffineTransform& t, Rectangle<float> rect)
|
||||
{
|
||||
Path path;
|
||||
path.addRectangle (rect);
|
||||
path.applyTransform (t);
|
||||
pushClip (std::move (path));
|
||||
}
|
||||
|
||||
void popClip()
|
||||
{
|
||||
clip.pop_back();
|
||||
}
|
||||
|
||||
void fill (hb_bool_t foreground, hb_color_t c)
|
||||
{
|
||||
addLayerChecked (foreground, c);
|
||||
}
|
||||
|
||||
void linearGradient (hb_color_line_t&, Point<float>, Point<float>, Point<float>)
|
||||
{
|
||||
// Support for COLRv1 glyphs is not fully implemented.
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
void radialGradient (hb_color_line_t&, Point<float>, float, Point<float>, float)
|
||||
{
|
||||
// Support for COLRv1 glyphs is not fully implemented.
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
void sweepGradient (hb_color_line_t&, Point<float>, float, float)
|
||||
{
|
||||
// Support for COLRv1 glyphs is not fully implemented.
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
bool image (const AffineTransform& t, hb_blob_t* image, unsigned int width, unsigned int height, hb_tag_t format, float, hb_glyph_extents_t* extents)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case HB_PAINT_IMAGE_FORMAT_BGRA:
|
||||
// Raw bitmap-based glyphs are not currently supported.
|
||||
// If you hit this assertion, please let the JUCE team know which font you're
|
||||
// attempting to use.
|
||||
// Depending on demand, support for this feature may be added in the future.
|
||||
jassertfalse;
|
||||
return false;
|
||||
|
||||
case HB_PAINT_IMAGE_FORMAT_PNG:
|
||||
{
|
||||
unsigned int imageDataSize{};
|
||||
const char* imageData = hb_blob_get_data (image, &imageDataSize);
|
||||
const auto juceImage = PNGImageFormat::loadFrom (imageData, imageDataSize);
|
||||
|
||||
if (juceImage.isNull())
|
||||
return false;
|
||||
|
||||
const auto transform = AffineTransform::scale ((float) extents->width / (float) width,
|
||||
(float) extents->height / (float) height)
|
||||
.translated ((float) extents->x_bearing,
|
||||
(float) extents->y_bearing)
|
||||
.followedBy (t);
|
||||
ImageLayer imageLayer { juceImage, transform };
|
||||
layers.push_back ({ std::move (imageLayer) });
|
||||
return true;
|
||||
}
|
||||
|
||||
case HB_PAINT_IMAGE_FORMAT_SVG:
|
||||
// SVG-based glyphs are not currently supported.
|
||||
// If you hit this assertion, please let the JUCE team know which font you're
|
||||
// attempting to use.
|
||||
// Depending on demand, support for this feature may be added in the future.
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<GlyphLayer> getLayers() &&
|
||||
{
|
||||
return std::move (layers);
|
||||
}
|
||||
|
||||
void appendLayers (Span<GlyphLayer> l)
|
||||
{
|
||||
for (auto& layer : l)
|
||||
layers.emplace_back (std::move (layer));
|
||||
}
|
||||
|
||||
private:
|
||||
GlyphLayer makeLayer (hb_bool_t foreground, hb_color_t c) const
|
||||
{
|
||||
return { ColourLayer { clip.back(), foreground ? std::optional<Colour>() : makeColour (c) } };
|
||||
}
|
||||
|
||||
void pushClip (Path path)
|
||||
{
|
||||
pushClip ({ path.getBounds().getSmallestIntegerContainer().expanded (1, 0), path, {} });
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void addLayerChecked (Args&&... args)
|
||||
{
|
||||
if (clip.empty())
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
layers.push_back (makeLayer (std::forward<Args> (args)...));
|
||||
}
|
||||
|
||||
void pushClip (const EdgeTable& et)
|
||||
{
|
||||
if (! clip.empty())
|
||||
{
|
||||
clip.push_back (clip.back());
|
||||
clip.back().clipToEdgeTable (et);
|
||||
}
|
||||
else
|
||||
{
|
||||
clip.push_back (et);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<EdgeTable> clip;
|
||||
std::vector<GlyphLayer> layers;
|
||||
};
|
||||
|
||||
class HbPaintContext
|
||||
{
|
||||
public:
|
||||
explicit HbPaintContext (const AffineTransform& transformIn)
|
||||
: baseTransform (transformIn)
|
||||
{
|
||||
}
|
||||
|
||||
void addTransform (const AffineTransform& transform)
|
||||
{
|
||||
transforms.push_back (transforms.empty() ? transform : transform.followedBy (transforms.back()));
|
||||
}
|
||||
|
||||
void popTransform()
|
||||
{
|
||||
transforms.pop_back();
|
||||
}
|
||||
|
||||
void pushClipGlyph (hb_codepoint_t glyph, hb_font_t* font)
|
||||
{
|
||||
groups.back().pushClipGlyph (getTransform(), glyph, font);
|
||||
}
|
||||
|
||||
void pushClipRect (Rectangle<float> rect)
|
||||
{
|
||||
groups.back().pushClipRect (getTransform(), rect);
|
||||
}
|
||||
|
||||
void popClip()
|
||||
{
|
||||
groups.back().popClip();
|
||||
}
|
||||
|
||||
void fill (hb_bool_t foreground, hb_color_t c)
|
||||
{
|
||||
groups.back().fill (foreground, c);
|
||||
}
|
||||
|
||||
void linearGradient (hb_color_line_t& line, Point<float> p0, Point<float> p1, Point<float> p2)
|
||||
{
|
||||
groups.back().linearGradient (line, p0, p1, p2);
|
||||
}
|
||||
|
||||
void radialGradient (hb_color_line_t& line, Point<float> p0, float r0, Point<float> p1, float r1)
|
||||
{
|
||||
groups.back().radialGradient (line, p0, r0, p1, r1);
|
||||
}
|
||||
|
||||
void sweepGradient (hb_color_line_t& line, Point<float> p, float begin, float end)
|
||||
{
|
||||
groups.back().sweepGradient (line, p, begin, end);
|
||||
}
|
||||
|
||||
bool image (hb_blob_t* image, unsigned int width, unsigned int height, hb_tag_t format, float slant, hb_glyph_extents_t* extents)
|
||||
{
|
||||
return groups.back().image (getTransform(), image, width, height, format, slant, extents);
|
||||
}
|
||||
|
||||
void pushGroup()
|
||||
{
|
||||
groups.emplace_back();
|
||||
}
|
||||
|
||||
void popGroup ([[maybe_unused]] hb_paint_composite_mode_t mode)
|
||||
{
|
||||
// There is currently extremely limited support for colour glyph blend modes
|
||||
jassert (mode == HB_PAINT_COMPOSITE_MODE_SRC_OVER);
|
||||
|
||||
auto newLayers = std::move (groups.back()).getLayers();
|
||||
groups.pop_back();
|
||||
groups.back().appendLayers (newLayers);
|
||||
}
|
||||
|
||||
std::vector<GlyphLayer> getLayers() &&
|
||||
{
|
||||
return std::move (groups.back()).getLayers();
|
||||
}
|
||||
|
||||
private:
|
||||
AffineTransform getTransform() const
|
||||
{
|
||||
const auto glyphSpaceTransform = transforms.empty() ? AffineTransform{} : transforms.back();
|
||||
return glyphSpaceTransform.followedBy (baseTransform);
|
||||
}
|
||||
|
||||
AffineTransform baseTransform;
|
||||
std::vector<AffineTransform> transforms;
|
||||
std::vector<HbPaintGroup> groups = std::vector<HbPaintGroup> (1);
|
||||
};
|
||||
|
||||
using HbPaintFuncs = std::unique_ptr<hb_paint_funcs_t, FunctionPointerDestructor<hb_paint_funcs_destroy>>;
|
||||
|
||||
static HbPaintFuncs getPathPaintFuncs()
|
||||
{
|
||||
HbPaintFuncs funcs { hb_paint_funcs_create() };
|
||||
|
||||
hb_paint_funcs_set_push_transform_func (funcs.get(), [] (auto*, auto* data, auto xx, auto yx, auto xy, auto yy, auto dx, auto dy, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.addTransform ({ xx, xy, dx, yx, yy, dy });
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_pop_transform_func (funcs.get(), [] (auto*, void* data, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.popTransform();
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_push_clip_glyph_func (funcs.get(), [] (auto*, void* data, auto glyph, auto* font, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.pushClipGlyph (glyph, font);
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_push_clip_rectangle_func (funcs.get(), [] (auto*, void* data, auto xmin, auto ymin, auto xmax, auto ymax, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.pushClipRect (Rectangle<float>::leftTopRightBottom (xmin, ymin, xmax, ymax));
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_pop_clip_func (funcs.get(), [] (auto*, void* data, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.popClip();
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_color_func (funcs.get(), [] (auto*, void* data, auto foreground, auto colour, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.fill (foreground, colour);
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_image_func (funcs.get(), [] (auto*, void* data, auto* image, auto w, auto h, auto format, auto slant, auto* extents, auto*) -> hb_bool_t
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
return context.image (image, w, h, format, slant, extents);
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_linear_gradient_func (funcs.get(), [] (auto*, auto* data, auto* colourLine, auto x0, auto y0, auto x1, auto y1, auto x2, auto y2, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.linearGradient (*colourLine, { x0, y0 }, { x1, y1 }, { x2, y2 });
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_radial_gradient_func (funcs.get(), [] (auto*, auto* data, auto* colourLine, auto x0, auto y0, auto r0, auto x1, auto y1, auto r1, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.radialGradient (*colourLine, { x0, y0 }, r0, { x1, y1 }, r1);
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_sweep_gradient_func (funcs.get(), [] (auto*, auto* data, auto* colourLine, auto x0, auto y0, auto begin, auto end, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.sweepGradient (*colourLine, { x0, y0 }, begin, end);
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_push_group_func (funcs.get(), [] (auto*, auto* data, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.pushGroup();
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_pop_group_func (funcs.get(), [] (auto*, auto* data, auto mode, auto*)
|
||||
{
|
||||
auto& context = *static_cast<HbPaintContext*> (data);
|
||||
context.popGroup (mode);
|
||||
}, nullptr, nullptr);
|
||||
|
||||
hb_paint_funcs_set_custom_palette_color_func (funcs.get(), [] (auto*, auto*, auto, auto*, auto*) -> hb_bool_t
|
||||
{
|
||||
return false;
|
||||
}, nullptr, nullptr);
|
||||
|
||||
return funcs;
|
||||
}
|
||||
|
||||
static std::vector<GlyphLayer> getCOLRv0Layers (const Typeface& typeface, int glyphNumber, const AffineTransform& transform)
|
||||
{
|
||||
auto* font = typeface.getNativeDetails().getFont();
|
||||
|
|
@ -778,33 +474,60 @@ static std::vector<GlyphLayer> getCOLRv0Layers (const Typeface& typeface, int gl
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<GlyphLayer> Typeface::getLayersForGlyph (int glyphNumber, const AffineTransform& transform, float) const
|
||||
static std::vector<GlyphLayer> getBitmapLayer (const Typeface& typeface, int glyphNumber, const AffineTransform& t)
|
||||
{
|
||||
if ((typeface.getColourGlyphFormats() & Typeface::colourGlyphFormatBitmap) == 0)
|
||||
return {};
|
||||
|
||||
auto* font = typeface.getNativeDetails().getFont();
|
||||
|
||||
const HbBlob blob { hb_ot_color_glyph_reference_png (font, (hb_codepoint_t) glyphNumber) };
|
||||
|
||||
unsigned int imageDataSize{};
|
||||
const char* imageData = hb_blob_get_data (blob.get(), &imageDataSize);
|
||||
const auto juceImage = PNGImageFormat::loadFrom (imageData, imageDataSize);
|
||||
|
||||
if (juceImage.isNull())
|
||||
return {};
|
||||
|
||||
hb_glyph_extents_t extents{};
|
||||
hb_font_get_glyph_extents (font, (hb_codepoint_t) glyphNumber, &extents);
|
||||
|
||||
const auto wDenom = std::max (1, juceImage.getWidth());
|
||||
const auto hDenom = std::max (1, juceImage.getHeight());
|
||||
|
||||
const auto transform = AffineTransform::scale ((float) extents.width / (float) wDenom,
|
||||
(float) extents.height / (float) hDenom)
|
||||
.translated ((float) extents.x_bearing,
|
||||
(float) extents.y_bearing)
|
||||
.followedBy (t);
|
||||
return { GlyphLayer { ImageLayer { juceImage, transform } } };
|
||||
}
|
||||
|
||||
std::vector<GlyphLayer> Typeface::getLayersForGlyph (TypefaceMetricsKind kind, int glyphNumber, const AffineTransform& transform, float) const
|
||||
{
|
||||
auto* font = getNativeDetails().getFont();
|
||||
const auto metrics = getNativeDetails().getLegacyMetrics();
|
||||
const auto metrics = getNativeDetails().getMetrics (kind);
|
||||
const auto scale = metrics.getHeightToPointsFactor() / (float) hb_face_get_upem (hb_font_get_face (font));
|
||||
const auto combinedTransform = AffineTransform::scale (scale, -scale).followedBy (transform);
|
||||
|
||||
// Before calling through to the 'paint' API, which JUCE can't easily support due to complex
|
||||
if (auto bitmapLayer = getBitmapLayer (*this, glyphNumber, combinedTransform); ! bitmapLayer.empty())
|
||||
return bitmapLayer;
|
||||
|
||||
// Instead of calling through to the 'paint' API, which JUCE can't easily support due to complex
|
||||
// gradients and blend modes, attempt to load COLRv0 layers for the glyph, which we'll be able
|
||||
// to render more successfully.
|
||||
auto basicLayers = getCOLRv0Layers (*this, glyphNumber, combinedTransform);
|
||||
if (auto layers = getCOLRv0Layers (*this, glyphNumber, combinedTransform); ! layers.empty())
|
||||
return layers;
|
||||
|
||||
if (! basicLayers.empty())
|
||||
return basicLayers;
|
||||
// No bitmap or COLRv0 for this glyph, so just get a simple monochromatic outline
|
||||
auto path = getGlyphPathInGlyphUnits ((hb_codepoint_t) glyphNumber, font);
|
||||
|
||||
constexpr auto palette = 0;
|
||||
if (path.isEmpty())
|
||||
return {};
|
||||
|
||||
static const auto funcs = getPathPaintFuncs();
|
||||
|
||||
HbPaintContext context { combinedTransform };
|
||||
hb_font_paint_glyph (font,
|
||||
(hb_codepoint_t) glyphNumber,
|
||||
funcs.get(),
|
||||
&context,
|
||||
palette,
|
||||
{});
|
||||
return std::move (context).getLayers();
|
||||
path.applyTransform (combinedTransform);
|
||||
return { GlyphLayer { ColourLayer { EdgeTable { path.getBounds().getSmallestIntegerContainer().expanded (1, 0), path, {} }, {} } } };
|
||||
}
|
||||
|
||||
int Typeface::getColourGlyphFormats() const
|
||||
|
|
@ -816,9 +539,10 @@ int Typeface::getColourGlyphFormats() const
|
|||
| (hb_ot_color_has_paint (face) ? colourGlyphFormatCOLRv1 : 0);
|
||||
}
|
||||
|
||||
float Typeface::getAscent() const { return getNativeDetails().getLegacyMetrics().getScaledAscent(); }
|
||||
float Typeface::getDescent() const { return getNativeDetails().getLegacyMetrics().getScaledDescent(); }
|
||||
float Typeface::getHeightToPointsFactor() const { return getNativeDetails().getLegacyMetrics().getHeightToPointsFactor(); }
|
||||
TypefaceMetrics Typeface::getMetrics (TypefaceMetricsKind kind) const
|
||||
{
|
||||
return getNativeDetails().getMetrics (kind).getTypefaceMetrics();
|
||||
}
|
||||
|
||||
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* fontFileData, size_t fontFileDataSize)
|
||||
{
|
||||
|
|
@ -847,7 +571,8 @@ static constexpr auto hbTag (const char (&arr)[5])
|
|||
}
|
||||
|
||||
template <typename Consumer>
|
||||
static void doSimpleShape (const Typeface& typeface,
|
||||
static float doSimpleShapeWithNoBreaks (const Typeface& typeface,
|
||||
TypefaceMetricsKind kind,
|
||||
const String& text,
|
||||
float height,
|
||||
float horizontalScale,
|
||||
|
|
@ -859,7 +584,7 @@ static void doSimpleShape (const Typeface& typeface,
|
|||
hb_buffer_guess_segment_properties (buffer.get());
|
||||
|
||||
const auto& native = typeface.getNativeDetails();
|
||||
const auto sized = native.getFontAtSizeAndScale (height, horizontalScale);
|
||||
const auto sized = native.getFontAtSizeAndScale (kind, height, horizontalScale);
|
||||
auto* font = sized.get();
|
||||
|
||||
// Disable ligatures, because TextEditor requires a 1:1 codepoint/glyph mapping for caret
|
||||
|
|
@ -887,32 +612,66 @@ static void doSimpleShape (const Typeface& typeface,
|
|||
{
|
||||
const auto& info = infos[i];
|
||||
const auto& position = positions[i];
|
||||
consumer (std::make_optional (info.codepoint), HbScale::hbToJuce (cursor.x + position.x_offset));
|
||||
consumer (info.codepoint, HbScale::hbToJuce (cursor.x + position.x_offset));
|
||||
cursor += Point { position.x_advance, position.y_advance };
|
||||
}
|
||||
|
||||
consumer (std::optional<hb_codepoint_t>{}, HbScale::hbToJuce (cursor.x));
|
||||
return HbScale::hbToJuce (cursor.x);
|
||||
}
|
||||
|
||||
float Typeface::getStringWidth (const String& text, float height, float horizontalScale)
|
||||
template <typename Consumer>
|
||||
static float doSimpleShape (const Typeface& typeface,
|
||||
TypefaceMetricsKind kind,
|
||||
const String& originalText,
|
||||
float height,
|
||||
float horizontalScale,
|
||||
Consumer&& consumer)
|
||||
{
|
||||
float x{};
|
||||
doSimpleShape (*this, text, height, horizontalScale, [&] (auto, auto xOffset)
|
||||
const juce_wchar zeroWidthSpace = 0x200b;
|
||||
const auto text = originalText.replaceCharacter ('\n', zeroWidthSpace);
|
||||
|
||||
float lastX{};
|
||||
|
||||
for (auto iter = text.begin(), end = text.end(); iter != end;)
|
||||
{
|
||||
x = xOffset;
|
||||
const auto next = [&]
|
||||
{
|
||||
for (auto i = iter; i != end; ++i)
|
||||
if (*i == zeroWidthSpace)
|
||||
return i + 1;
|
||||
|
||||
return end;
|
||||
}();
|
||||
|
||||
lastX += doSimpleShapeWithNoBreaks (typeface, kind, String (iter, next), height, horizontalScale, [&] (auto codepoint, auto x)
|
||||
{
|
||||
consumer (codepoint, lastX + x);
|
||||
});
|
||||
return x;
|
||||
iter = next;
|
||||
}
|
||||
|
||||
return lastX;
|
||||
}
|
||||
|
||||
void Typeface::getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets, float height, float horizontalScale)
|
||||
float Typeface::getStringWidth (TypefaceMetricsKind kind, const String& text, float height, float horizontalScale)
|
||||
{
|
||||
doSimpleShape (*this, text, height, horizontalScale, [&] (auto codepoint, auto xOffset)
|
||||
{
|
||||
if (codepoint.has_value())
|
||||
glyphs.add ((int) *codepoint);
|
||||
return doSimpleShape (*this, kind, text, height, horizontalScale, [&] (auto, auto) {});
|
||||
}
|
||||
|
||||
void Typeface::getGlyphPositions (TypefaceMetricsKind kind,
|
||||
const String& text,
|
||||
Array<int>& glyphs,
|
||||
Array<float>& xOffsets,
|
||||
float height,
|
||||
float horizontalScale)
|
||||
{
|
||||
const auto width = doSimpleShape (*this, kind, text, height, horizontalScale, [&] (auto codepoint, auto xOffset)
|
||||
{
|
||||
glyphs.add ((int) codepoint);
|
||||
xOffsets.add (xOffset);
|
||||
});
|
||||
|
||||
xOffsets.add (width);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -957,7 +716,7 @@ public:
|
|||
|
||||
beginTest ("Typefaces loaded from memory are found when creating font instances by name");
|
||||
{
|
||||
Font font (FontOptions (ptr->getName(), ptr->getStyle(), 12.0f));
|
||||
Font font (FontOptions { ptr->getName(), ptr->getStyle(), 12.0f });
|
||||
|
||||
expect (font.getTypefacePtr() != nullptr);
|
||||
expect (font.getTypefacePtr()->getName() == ptr->getName());
|
||||
|
|
|
|||
|
|
@ -59,6 +59,50 @@ struct GlyphLayer
|
|||
std::variant<ColourLayer, ImageLayer> layer;
|
||||
};
|
||||
|
||||
/** Identifiers for different styles of typeface metrics.
|
||||
|
||||
In new projects, it's recommended to use the 'portable' metrics kind, so that fonts display
|
||||
at a similar size on all platforms.
|
||||
Portable metrics are enabled by default when constructing a Font using FontOptions. The old,
|
||||
deprecated Font constructors will all request the legacy metrics kind instead.
|
||||
JUCE components that display text will query LookAndFeel::getDefaultMetricsKind() to find the
|
||||
kind of metrics that should be used. This function can be overridden to switch the metrics kind
|
||||
for the entire LookAndFeel.
|
||||
|
||||
The 'legacy' metrics kind uses the platform font facilities to retrieve font metrics.
|
||||
Each platform has its own idiosyncratic behaviour when computing metrics - depending on the
|
||||
typeface data, it's possible that the 'portable' metrics will differ from the 'legacy' metrics
|
||||
on every platform. The biggest differences between legacy and portable metrics are likely to be
|
||||
seen on Windows, so it may be helpful to check this platform first.
|
||||
*/
|
||||
enum class TypefaceMetricsKind
|
||||
{
|
||||
legacy, ///< Old-style metrics that may differ for the same font file when running on different platforms.
|
||||
///< This was the default behaviour prior to JUCE 8.
|
||||
portable ///< Where possible, will return the same font metrics on all platforms.
|
||||
///< It's a good idea to use this for new JUCE projects, to keep text layout and
|
||||
///< sizing consistent on all platforms.
|
||||
};
|
||||
|
||||
/** Font metrics using JUCE conventions.
|
||||
*/
|
||||
struct TypefaceMetrics
|
||||
{
|
||||
/** The proportion of the typeface's height that it above the baseline, as a value between 0 and 1.
|
||||
Note that 'height' here refers to the result of adding the absolute ascent and descent values.
|
||||
That is, the sum of the ascent and descent will equal 1.
|
||||
The sum of the ascent and descent will normally differ from the em size of the font.
|
||||
That is, for a font size of 14pt, there will be 14 points per em, but the sum of the ascent
|
||||
and descent in points is unlikely to be equal to 14.
|
||||
*/
|
||||
float ascent{};
|
||||
|
||||
/** The factor by which a JUCE font height should be multiplied in order to convert to a font
|
||||
size in points.
|
||||
*/
|
||||
float heightToPoints{};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A typeface represents a size-independent font.
|
||||
|
|
@ -121,24 +165,8 @@ public:
|
|||
/** Destructor. */
|
||||
~Typeface() override;
|
||||
|
||||
/** Returns the ascent of the font, as a proportion of its height.
|
||||
The height is considered to always be normalised as 1.0, so this will be a
|
||||
value less that 1.0, indicating the proportion of the font that lies above
|
||||
its baseline.
|
||||
*/
|
||||
float getAscent() const;
|
||||
|
||||
/** Returns the descent of the font, as a proportion of its height.
|
||||
The height is considered to always be normalised as 1.0, so this will be a
|
||||
value less that 1.0, indicating the proportion of the font that lies below
|
||||
its baseline.
|
||||
*/
|
||||
float getDescent() const;
|
||||
|
||||
/** Returns the value by which you should multiply a JUCE font-height value to
|
||||
convert it to the equivalent point-size.
|
||||
*/
|
||||
float getHeightToPointsFactor() const;
|
||||
/** Returns information about the horizontal metrics of this font. */
|
||||
[[nodiscard]] TypefaceMetrics getMetrics (TypefaceMetricsKind) const;
|
||||
|
||||
/** @deprecated
|
||||
This function has several shortcomings:
|
||||
|
|
@ -154,7 +182,10 @@ public:
|
|||
Measures the width of a line of text.
|
||||
You should never need to call this!
|
||||
*/
|
||||
float getStringWidth (const String& text, float normalisedHeight = 1.0f, float horizontalScale = 1.0f);
|
||||
float getStringWidth (TypefaceMetricsKind,
|
||||
const String& text,
|
||||
float normalisedHeight = 1.0f,
|
||||
float horizontalScale = 1.0f);
|
||||
|
||||
/** @deprecated
|
||||
This function has several shortcomings:
|
||||
|
|
@ -171,7 +202,8 @@ public:
|
|||
Converts a line of text into its glyph numbers and their positions.
|
||||
You should never need to call this!
|
||||
*/
|
||||
void getGlyphPositions (const String& text,
|
||||
void getGlyphPositions (TypefaceMetricsKind,
|
||||
const String& text,
|
||||
Array<int>& glyphs,
|
||||
Array<float>& xOffsets,
|
||||
float normalisedHeight = 1.0f,
|
||||
|
|
@ -180,7 +212,7 @@ public:
|
|||
/** Returns the outline for a glyph.
|
||||
The path returned will be normalised to a font height of 1.0.
|
||||
*/
|
||||
void getOutlineForGlyph (int glyphNumber, Path& path);
|
||||
void getOutlineForGlyph (TypefaceMetricsKind, int glyphNumber, Path& path) const;
|
||||
|
||||
/** @deprecated
|
||||
|
||||
|
|
@ -196,7 +228,10 @@ public:
|
|||
preferred in new code.
|
||||
*/
|
||||
[[deprecated ("Prefer getLayersForGlyph")]]
|
||||
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float normalisedHeight);
|
||||
EdgeTable* getEdgeTableForGlyph (TypefaceMetricsKind,
|
||||
int glyphNumber,
|
||||
const AffineTransform& transform,
|
||||
float normalisedHeight);
|
||||
|
||||
/** Returns the layers that should be painted in order to display this glyph.
|
||||
|
||||
|
|
@ -210,7 +245,10 @@ public:
|
|||
|
||||
The height is specified in JUCE font-height units.
|
||||
*/
|
||||
std::vector<GlyphLayer> getLayersForGlyph (int glyphNumber, const AffineTransform&, float normalisedHeight) const;
|
||||
std::vector<GlyphLayer> getLayersForGlyph (TypefaceMetricsKind,
|
||||
int glyphNumber,
|
||||
const AffineTransform&,
|
||||
float normalisedHeight) const;
|
||||
|
||||
/** Kinds of colour glyph format that may be implemented by a particular typeface.
|
||||
Most typefaces are monochromatic, and do not support any colour formats.
|
||||
|
|
|
|||
|
|
@ -696,7 +696,7 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra
|
|||
{
|
||||
Path p;
|
||||
auto& f = state->font;
|
||||
f.getTypefacePtr()->getOutlineForGlyph (glyphNumber, p);
|
||||
f.getTypefacePtr()->getOutlineForGlyph (f.getMetricsKind(), glyphNumber, p);
|
||||
const auto scale = f.getHeight();
|
||||
|
||||
fillPath (p, AffineTransform::scale (scale * f.getHorizontalScale(), scale).followedBy (transform));
|
||||
|
|
|
|||
|
|
@ -616,7 +616,7 @@ public:
|
|||
|
||||
Native getNativeDetails() const override
|
||||
{
|
||||
return Native { hb.get() };
|
||||
return Native { hb.get(), nonPortableMetrics };
|
||||
}
|
||||
|
||||
Typeface::Ptr createSystemFallback (const String& c, const String& language) const override
|
||||
|
|
@ -698,6 +698,13 @@ private:
|
|||
CFUniquePtr<CTFontRef> ctFont;
|
||||
HbFont hb;
|
||||
MemoryBlock storage;
|
||||
TypefaceAscentDescent nonPortableMetrics = [&]
|
||||
{
|
||||
const CFUniquePtr<CGFontRef> cgFont { CTFontCopyGraphicsFont (ctFont.get(), nullptr) };
|
||||
const auto upem = (float) CGFontGetUnitsPerEm (cgFont.get());
|
||||
return TypefaceAscentDescent { (float) std::abs (CGFontGetAscent (cgFont.get()) / upem),
|
||||
(float) std::abs (CGFontGetDescent (cgFont.get()) / upem) };
|
||||
}();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreTextTypeface)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -189,7 +189,8 @@ public:
|
|||
{
|
||||
auto fontHeight = key.font.getHeight();
|
||||
auto typeface = key.font.getTypefacePtr();
|
||||
return typeface->getLayersForGlyph (key.glyph,
|
||||
return typeface->getLayersForGlyph (key.font.getMetricsKind(),
|
||||
key.glyph,
|
||||
AffineTransform::scale (fontHeight * key.font.getHorizontalScale(),
|
||||
fontHeight),
|
||||
fontHeight);
|
||||
|
|
@ -2629,7 +2630,7 @@ public:
|
|||
const auto fontTransform = AffineTransform::scale (fontHeight * stack->font.getHorizontalScale(),
|
||||
fontHeight).followedBy (t);
|
||||
const auto fullTransform = stack->transform.getTransformWith (fontTransform);
|
||||
return std::tuple (stack->font.getTypefacePtr()->getLayersForGlyph (i, fullTransform, fontHeight), Point<float>{});
|
||||
return std::tuple (stack->font.getTypefacePtr()->getLayersForGlyph (stack->font.getMetricsKind(), i, fullTransform, fontHeight), Point<float>{});
|
||||
}();
|
||||
|
||||
const auto initialFill = stack->fillType;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue