blob: cd082ca5057ec48a993d78995b6c9b5dca6bdd59 [file] [log] [blame]
/*
* Copyright (C) 2006 Apple Inc. All rights reserved.
* Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
* Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com>
* Copyright (C) 2007 Holger Hans Peter Freyther
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#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()));
}
void Font::platformInit()
{
if (!m_platformData.size())
return;
ASSERT(m_platformData.scaledFont());
// Temporarily create a clone that doesn't have metrics hinting in order to avoid incorrect
// rounding resulting in incorrect baseline positioning since the sum of ascent and descent
// becomes larger than the line height.
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;
{
CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont());
// If the USE_TYPO_METRICS flag is set in the OS/2 table then we use typo metrics instead.
FT_Face freeTypeFace = cairoFtFaceLocker.ftFace();
if (freeTypeFace && 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;
// FT_Size_Metrics::y_scale is in 16.16 fixed point format.
// Its (fractional) value is a factor that converts vertical metrics from design units to units of 1/64 pixels.
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 (!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());
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);
if ((platformData().orientation() == FontOrientation::Vertical) && !isTextOrientationFallback()) {
CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont());
FT_Face freeTypeFace = cairoFtFaceLocker.ftFace();
m_fontMetrics.setUnitsPerEm(freeTypeFace->units_per_EM);
}
m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f;
}
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:
// Synthesis only supports the variant-caps values listed above.
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;
}
}