SimpleFontDataFreeType.cpp [plain text]
#include "config.h"
#include "Font.h"
#include "CairoUniquePtr.h"
#include "CairoUtilities.h"
#include "FloatConversion.h"
#include "FloatRect.h"
#include "FontCache.h"
#include "FontDescription.h"
#include "GlyphBuffer.h"
#include "OpenTypeTypes.h"
#include "RefPtrCairo.h"
#include "SurrogatePairAwareTextIterator.h"
#include "UTF16UChar32Iterator.h"
#include <cairo-ft.h>
#include <cairo.h>
#include <fontconfig/fcfreetype.h>
#include <ft2build.h>
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_TAGS_H
#include <wtf/MathExtras.h>
namespace WebCore {
static RefPtr<cairo_scaled_font_t> scaledFontWithoutMetricsHinting(cairo_scaled_font_t* scaledFont)
{
CairoUniquePtr<cairo_font_options_t> fontOptions(cairo_font_options_create());
cairo_scaled_font_get_font_options(scaledFont, fontOptions.get());
cairo_font_options_set_hint_metrics(fontOptions.get(), CAIRO_HINT_METRICS_OFF);
cairo_matrix_t fontMatrix;
cairo_scaled_font_get_font_matrix(scaledFont, &fontMatrix);
cairo_matrix_t fontCTM;
cairo_scaled_font_get_ctm(scaledFont, &fontCTM);
return adoptRef(cairo_scaled_font_create(cairo_scaled_font_get_font_face(scaledFont), &fontMatrix, &fontCTM, fontOptions.get()));
}
static float scaledFontScaleFactor(cairo_scaled_font_t* scaledFont)
{
cairo_matrix_t fontMatrix;
cairo_scaled_font_get_font_matrix(scaledFont, &fontMatrix);
float determinant = fontMatrix.xx * fontMatrix.yy - fontMatrix.yx * fontMatrix.xy;
if (!std::isfinite(determinant))
return 1;
determinant = std::abs(determinant);
if (!determinant)
return 0;
double x = 1;
double y = 0;
cairo_matrix_transform_distance(&fontMatrix, &x, &y);
double xScale = std::hypot(x, y);
return xScale ? narrowPrecisionToFloat(determinant / xScale) : 0.;
}
static Optional<unsigned> fontUnitsPerEm(FT_Face freeTypeFace)
{
if (freeTypeFace->units_per_EM)
return freeTypeFace->units_per_EM;
if (auto* ttHeader = static_cast<TT_Header*>(FT_Get_Sfnt_Table(freeTypeFace, ft_sfnt_head)))
return ttHeader->Units_Per_EM;
return WTF::nullopt;
}
void Font::platformInit()
{
if (!m_platformData.size())
return;
ASSERT(m_platformData.scaledFont());
auto fontWithoutMetricsHinting = scaledFontWithoutMetricsHinting(m_platformData.scaledFont());
cairo_font_extents_t fontExtents;
cairo_scaled_font_extents(fontWithoutMetricsHinting.get(), &fontExtents);
float ascent = narrowPrecisionToFloat(fontExtents.ascent);
float descent = narrowPrecisionToFloat(fontExtents.descent);
float capHeight = narrowPrecisionToFloat(fontExtents.height);
float lineGap = narrowPrecisionToFloat(fontExtents.height - fontExtents.ascent - fontExtents.descent);
Optional<float> xHeight;
Optional<unsigned> unitsPerEm;
Optional<float> underlinePosition;
Optional<float> underlineThickness;
{
CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont());
if (FT_Face freeTypeFace = cairoFtFaceLocker.ftFace()) {
unitsPerEm = fontUnitsPerEm(freeTypeFace);
if (freeTypeFace->face_flags & FT_FACE_FLAG_SCALABLE) {
if (auto* OS2Table = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(freeTypeFace, ft_sfnt_os2))) {
const FT_Short kUseTypoMetricsMask = 1 << 7;
double yscale = (freeTypeFace->size->metrics.y_scale / 65536.0) / 64.0;
if (OS2Table->fsSelection & kUseTypoMetricsMask) {
ascent = narrowPrecisionToFloat(yscale * OS2Table->sTypoAscender);
descent = -narrowPrecisionToFloat(yscale * OS2Table->sTypoDescender);
lineGap = narrowPrecisionToFloat(yscale * OS2Table->sTypoLineGap);
}
xHeight = narrowPrecisionToFloat(yscale * OS2Table->sxHeight);
}
if (unitsPerEm) {
float scaleFactor = scaledFontScaleFactor(fontWithoutMetricsHinting.get());
underlinePosition = -((freeTypeFace->underline_position + freeTypeFace->underline_thickness / 2.) / static_cast<float>(unitsPerEm.value())) * scaleFactor;
underlineThickness = (freeTypeFace->underline_thickness / static_cast<float>(unitsPerEm.value())) * scaleFactor;
}
}
}
}
if (!xHeight) {
cairo_text_extents_t textExtents;
cairo_scaled_font_text_extents(m_platformData.scaledFont(), "x", &textExtents);
xHeight = narrowPrecisionToFloat((platformData().orientation() == FontOrientation::Horizontal) ? textExtents.height : textExtents.width);
}
m_fontMetrics.setAscent(ascent);
m_fontMetrics.setDescent(descent);
m_fontMetrics.setCapHeight(capHeight);
m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap));
m_fontMetrics.setLineGap(lineGap);
m_fontMetrics.setXHeight(xHeight.value());
if (unitsPerEm)
m_fontMetrics.setUnitsPerEm(unitsPerEm.value());
if (underlinePosition)
m_fontMetrics.setUnderlinePosition(underlinePosition.value());
if (underlineThickness)
m_fontMetrics.setUnderlineThickness(underlineThickness.value());
cairo_text_extents_t textExtents;
cairo_scaled_font_text_extents(m_platformData.scaledFont(), " ", &textExtents);
m_spaceWidth = narrowPrecisionToFloat((platformData().orientation() == FontOrientation::Horizontal) ? textExtents.x_advance : -textExtents.y_advance);
m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f;
FcChar8* fontConfigFamilyName;
if (FcPatternGetString(m_platformData.fcPattern(), FC_FAMILY, 0, &fontConfigFamilyName) == FcResultMatch) {
String familyName = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyName));
if (equalIgnoringASCIICase(familyName, "Ahem"))
m_allowsAntialiasing = false;
}
}
void Font::platformCharWidthInit()
{
m_avgCharWidth = 0.f;
m_maxCharWidth = 0.f;
initCharWidths();
}
RefPtr<Font> Font::platformCreateScaledFont(const FontDescription& fontDescription, float scaleFactor) const
{
ASSERT(m_platformData.scaledFont());
return Font::create(FontPlatformData(cairo_scaled_font_get_font_face(m_platformData.scaledFont()),
m_platformData.fcPattern(),
scaleFactor * fontDescription.computedSize(),
m_platformData.isFixedWidth(),
m_platformData.syntheticBold(),
m_platformData.syntheticOblique(),
fontDescription.orientation()),
origin(), Interstitial::No);
}
void Font::determinePitch()
{
m_treatAsFixedPitch = m_platformData.isFixedPitch();
}
FloatRect Font::platformBoundsForGlyph(Glyph glyph) const
{
if (!m_platformData.size())
return FloatRect();
cairo_glyph_t cglyph = { glyph, 0, 0 };
cairo_text_extents_t extents;
cairo_scaled_font_glyph_extents(m_platformData.scaledFont(), &cglyph, 1, &extents);
if (cairo_scaled_font_status(m_platformData.scaledFont()) == CAIRO_STATUS_SUCCESS)
return FloatRect(extents.x_bearing, extents.y_bearing, extents.width, extents.height);
return FloatRect();
}
float Font::platformWidthForGlyph(Glyph glyph) const
{
if (!m_platformData.size())
return 0;
if (cairo_scaled_font_status(m_platformData.scaledFont()) != CAIRO_STATUS_SUCCESS)
return m_spaceWidth;
cairo_glyph_t cairoGlyph = { glyph, 0, 0 };
cairo_text_extents_t extents;
cairo_scaled_font_glyph_extents(m_platformData.scaledFont(), &cairoGlyph, 1, &extents);
float width = platformData().orientation() == FontOrientation::Horizontal ? extents.x_advance : -extents.y_advance;
return width ? width : m_spaceWidth;
}
bool Font::variantCapsSupportsCharacterForSynthesis(FontVariantCaps fontVariantCaps, UChar32) const
{
switch (fontVariantCaps) {
case FontVariantCaps::Small:
case FontVariantCaps::Petite:
case FontVariantCaps::AllSmall:
case FontVariantCaps::AllPetite:
return false;
default:
return true;
}
}
bool Font::platformSupportsCodePoint(UChar32 character, Optional<UChar32> variation) const
{
CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont());
if (FT_Face face = cairoFtFaceLocker.ftFace())
return variation ? !!FT_Face_GetCharVariantIndex(face, character, variation.value()) : !!FcFreeTypeCharIndex(face, character);
return false;
}
}