blob: b6ee0a8ff952c9ebc614c2b53b39c0bc018a9f69 [file] [log] [blame]
/*
* Copyright (c) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
* OWNER OR 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 "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
// clang-format off
#include <hb.h>
#include <hb-cplusplus.hh>
#include <hb-ot.h>
// clang-format on
#include <memory>
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face_from_typeface.h"
#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h"
#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_data.h"
#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
#include "third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h"
#include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
#include "third_party/blink/renderer/platform/resolution_units.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkPoint.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkTypeface.h"
namespace blink {
std::unique_ptr<HarfBuzzFace> HarfBuzzFace::Create(
FontPlatformData* platform_data) {
auto harfbuzz_font_data =
FontGlobalContext::GetHarfBuzzFontCache().GetOrCreateFontData(
platform_data);
return base::WrapUnique(new HarfBuzzFace(platform_data, harfbuzz_font_data));
}
HarfBuzzFace::HarfBuzzFace(FontPlatformData* platform_data,
scoped_refptr<HarfBuzzFontData> harfbuzz_font_data)
: platform_data_(platform_data),
unique_id_(platform_data->UniqueID()),
harfbuzz_font_data_(harfbuzz_font_data),
unscaled_font_(harfbuzz_font_data->unscaled_font_.get()) {}
HarfBuzzFace::~HarfBuzzFace() = default;
static hb_bool_t HarfBuzzGetGlyph(hb_font_t* hb_font,
void* font_data,
hb_codepoint_t unicode,
hb_codepoint_t variation_selector,
hb_codepoint_t* glyph,
void* user_data) {
HarfBuzzFontData* hb_font_data =
reinterpret_cast<HarfBuzzFontData*>(font_data);
CHECK(hb_font_data);
if (hb_font_data->range_set_ && !hb_font_data->range_set_->Contains(unicode))
return false;
return hb_font_get_glyph(hb_font_get_parent(hb_font), unicode,
variation_selector, glyph);
}
static hb_bool_t HarfBuzzGetNominalGlyph(hb_font_t* hb_font,
void* font_data,
hb_codepoint_t unicode,
hb_codepoint_t* glyph,
void* user_data) {
return HarfBuzzGetGlyph(hb_font, font_data, unicode, 0, glyph, user_data);
}
static hb_position_t HarfBuzzGetGlyphHorizontalAdvance(hb_font_t* hb_font,
void* font_data,
hb_codepoint_t glyph,
void* user_data) {
HarfBuzzFontData* hb_font_data =
reinterpret_cast<HarfBuzzFontData*>(font_data);
hb_position_t advance = 0;
SkFontGetGlyphWidthForHarfBuzz(hb_font_data->font_, glyph, &advance);
return advance;
}
static void HarfBuzzGetGlyphHorizontalAdvances(
hb_font_t* font,
void* font_data,
unsigned count,
const hb_codepoint_t* first_glyph,
unsigned int glyph_stride,
hb_position_t* first_advance,
unsigned int advance_stride,
void* user_data) {
HarfBuzzFontData* hb_font_data =
reinterpret_cast<HarfBuzzFontData*>(font_data);
SkFontGetGlyphWidthForHarfBuzz(hb_font_data->font_, count, first_glyph,
glyph_stride, first_advance, advance_stride);
}
static hb_bool_t HarfBuzzGetGlyphVerticalOrigin(hb_font_t* hb_font,
void* font_data,
hb_codepoint_t glyph,
hb_position_t* x,
hb_position_t* y,
void* user_data) {
HarfBuzzFontData* hb_font_data =
reinterpret_cast<HarfBuzzFontData*>(font_data);
scoped_refptr<OpenTypeVerticalData> vertical_data =
hb_font_data->VerticalData();
if (!vertical_data)
return false;
float result[] = {0, 0};
Glyph the_glyph = glyph;
vertical_data->GetVerticalTranslationsForGlyphs(hb_font_data->font_,
&the_glyph, 1, result);
*x = SkiaScalarToHarfBuzzPosition(-result[0]);
*y = SkiaScalarToHarfBuzzPosition(-result[1]);
return true;
}
static hb_position_t HarfBuzzGetGlyphVerticalAdvance(hb_font_t* hb_font,
void* font_data,
hb_codepoint_t glyph,
void* user_data) {
HarfBuzzFontData* hb_font_data =
reinterpret_cast<HarfBuzzFontData*>(font_data);
scoped_refptr<OpenTypeVerticalData> vertical_data =
hb_font_data->VerticalData();
if (!vertical_data) {
return SkiaScalarToHarfBuzzPosition(hb_font_data->height_fallback_);
}
Glyph the_glyph = glyph;
float advance_height = -vertical_data->AdvanceHeight(the_glyph);
return SkiaScalarToHarfBuzzPosition(SkFloatToScalar(advance_height));
}
static hb_bool_t HarfBuzzGetGlyphExtents(hb_font_t* hb_font,
void* font_data,
hb_codepoint_t glyph,
hb_glyph_extents_t* extents,
void* user_data) {
HarfBuzzFontData* hb_font_data =
reinterpret_cast<HarfBuzzFontData*>(font_data);
SkFontGetGlyphExtentsForHarfBuzz(hb_font_data->font_, glyph, extents);
return true;
}
static inline bool TableHasSpace(hb_face_t* face,
hb_set_t* glyphs,
hb_tag_t tag,
hb_codepoint_t space) {
unsigned count = hb_ot_layout_table_get_lookup_count(face, tag);
for (unsigned i = 0; i < count; i++) {
hb_ot_layout_lookup_collect_glyphs(face, tag, i, glyphs, glyphs, glyphs,
nullptr);
if (hb_set_has(glyphs, space))
return true;
}
return false;
}
static bool GetSpaceGlyph(hb_font_t* font, hb_codepoint_t& space) {
return hb_font_get_nominal_glyph(font, kSpaceCharacter, &space);
}
bool HarfBuzzFace::HasSpaceInLigaturesOrKerning(TypesettingFeatures features) {
const hb_codepoint_t kInvalidCodepoint = static_cast<hb_codepoint_t>(-1);
hb_codepoint_t space = kInvalidCodepoint;
hb::unique_ptr<hb_set_t> glyphs(hb_set_create());
// Check whether computing is needed and compute for gpos/gsub.
if (features & kKerning &&
harfbuzz_font_data_->space_in_gpos_ ==
HarfBuzzFontData::SpaceGlyphInOpenTypeTables::kUnknown) {
if (space == kInvalidCodepoint && !GetSpaceGlyph(unscaled_font_, space))
return false;
// Compute for gpos.
hb_face_t* face = hb_font_get_face(unscaled_font_);
DCHECK(face);
harfbuzz_font_data_->space_in_gpos_ =
hb_ot_layout_has_positioning(face) &&
TableHasSpace(face, glyphs.get(), HB_OT_TAG_GPOS, space)
? HarfBuzzFontData::SpaceGlyphInOpenTypeTables::kPresent
: HarfBuzzFontData::SpaceGlyphInOpenTypeTables::kNotPresent;
}
hb_set_clear(glyphs.get());
if (features & kLigatures &&
harfbuzz_font_data_->space_in_gsub_ ==
HarfBuzzFontData::SpaceGlyphInOpenTypeTables::kUnknown) {
if (space == kInvalidCodepoint && !GetSpaceGlyph(unscaled_font_, space))
return false;
// Compute for gpos.
hb_face_t* face = hb_font_get_face(unscaled_font_);
DCHECK(face);
harfbuzz_font_data_->space_in_gsub_ =
hb_ot_layout_has_substitution(face) &&
TableHasSpace(face, glyphs.get(), HB_OT_TAG_GSUB, space)
? HarfBuzzFontData::SpaceGlyphInOpenTypeTables::kPresent
: HarfBuzzFontData::SpaceGlyphInOpenTypeTables::kNotPresent;
}
return (features & kKerning &&
harfbuzz_font_data_->space_in_gpos_ ==
HarfBuzzFontData::SpaceGlyphInOpenTypeTables::kPresent) ||
(features & kLigatures &&
harfbuzz_font_data_->space_in_gsub_ ==
HarfBuzzFontData::SpaceGlyphInOpenTypeTables::kPresent);
}
unsigned HarfBuzzFace::UnitsPerEmFromHeadTable() {
hb_face_t* face = hb_font_get_face(unscaled_font_);
return hb_face_get_upem(face);
}
Glyph HarfBuzzFace::HbGlyphForCharacter(UChar32 character) {
hb_codepoint_t glyph = 0;
HarfBuzzGetNominalGlyph(unscaled_font_, harfbuzz_font_data_.get(), character,
&glyph, nullptr);
return glyph;
}
bool HarfBuzzFace::ShouldSubpixelPosition() {
return harfbuzz_font_data_->font_.isSubpixel();
}
// `HarfBuzzSkiaFontFuncs` is shared hb_font_funcs_t`s among threads for
// calculating horizontal advances functions.
class HarfBuzzSkiaFontFuncs final {
public:
static HarfBuzzSkiaFontFuncs& Get() {
DEFINE_THREAD_SAFE_STATIC_LOCAL(HarfBuzzSkiaFontFuncs, shared_hb_funcs, ());
return shared_hb_funcs;
}
#if BUILDFLAG(IS_MAC)
HarfBuzzSkiaFontFuncs()
: hb_font_funcs_skia_advances_(
CreateFontFunctions(kSkiaHorizontalAdvances)),
hb_font_funcs_harfbuzz_advances_(
CreateFontFunctions(kHarfBuzzHorizontalAdvances)) {}
~HarfBuzzSkiaFontFuncs() {
hb_font_funcs_destroy(hb_font_funcs_skia_advances_);
hb_font_funcs_destroy(hb_font_funcs_harfbuzz_advances_);
}
hb_font_funcs_t* GetFunctions(SkTypeface* typeface) {
bool has_trak = false;
bool has_sbix = false;
const int num_tags = typeface->countTables();
SkFontTableTag tags[num_tags];
const int returned_tags = typeface->getTableTags(tags);
DCHECK_EQ(num_tags, returned_tags);
for (auto& tag : tags) {
if (tag == SkSetFourByteTag('t', 'r', 'a', 'k'))
has_trak = true;
if (tag == SkSetFourByteTag('s', 'b', 'i', 'x'))
has_sbix = true;
}
return has_trak && !has_sbix ? hb_font_funcs_harfbuzz_advances_
: hb_font_funcs_skia_advances_;
}
#else
HarfBuzzSkiaFontFuncs()
: hb_font_funcs_skia_advances_(
CreateFontFunctions(kSkiaHorizontalAdvances)) {}
~HarfBuzzSkiaFontFuncs() {
hb_font_funcs_destroy(hb_font_funcs_skia_advances_);
}
hb_font_funcs_t* GetFunctions(SkTypeface*) {
return hb_font_funcs_skia_advances_;
}
#endif
HarfBuzzSkiaFontFuncs(const HarfBuzzSkiaFontFuncs&) = delete;
HarfBuzzSkiaFontFuncs(HarfBuzzSkiaFontFuncs&&) = delete;
HarfBuzzSkiaFontFuncs& operator=(const HarfBuzzSkiaFontFuncs&) = delete;
HarfBuzzSkiaFontFuncs& operator=(HarfBuzzSkiaFontFuncs&&) = delete;
private:
enum HorizontalAdvanceSource {
kSkiaHorizontalAdvances,
#if BUILDFLAG(IS_MAC)
kHarfBuzzHorizontalAdvances,
#endif
};
static hb_font_funcs_t* CreateFontFunctions(
HorizontalAdvanceSource advance_source) {
hb_font_funcs_t* funcs = hb_font_funcs_create();
if (advance_source == kSkiaHorizontalAdvances) {
hb_font_funcs_set_glyph_h_advance_func(
funcs, HarfBuzzGetGlyphHorizontalAdvance, nullptr, nullptr);
hb_font_funcs_set_glyph_h_advances_func(
funcs, HarfBuzzGetGlyphHorizontalAdvances, nullptr, nullptr);
}
hb_font_funcs_set_variation_glyph_func(funcs, HarfBuzzGetGlyph, nullptr,
nullptr);
hb_font_funcs_set_nominal_glyph_func(funcs, HarfBuzzGetNominalGlyph,
nullptr, nullptr);
// TODO(crbug.com/899718): Replace vertical metrics callbacks with
// HarfBuzz VORG/VMTX internal implementation by deregistering those.
hb_font_funcs_set_glyph_v_advance_func(
funcs, HarfBuzzGetGlyphVerticalAdvance, nullptr, nullptr);
hb_font_funcs_set_glyph_v_origin_func(funcs, HarfBuzzGetGlyphVerticalOrigin,
nullptr, nullptr);
hb_font_funcs_set_glyph_extents_func(funcs, HarfBuzzGetGlyphExtents,
nullptr, nullptr);
hb_font_funcs_make_immutable(funcs);
return funcs;
}
hb_font_funcs_t* const hb_font_funcs_skia_advances_;
#if BUILDFLAG(IS_MAC)
hb_font_funcs_t* const hb_font_funcs_harfbuzz_advances_;
#endif
};
static hb_blob_t* HarfBuzzSkiaGetTable(hb_face_t* face,
hb_tag_t tag,
void* user_data) {
SkTypeface* typeface = reinterpret_cast<SkTypeface*>(user_data);
const wtf_size_t table_size =
base::checked_cast<wtf_size_t>(typeface->getTableSize(tag));
if (!table_size) {
return nullptr;
}
char* buffer = reinterpret_cast<char*>(WTF::Partitions::FastMalloc(
table_size, WTF_HEAP_PROFILER_TYPE_NAME(HarfBuzzFontData)));
if (!buffer)
return nullptr;
size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer);
if (table_size != actual_size) {
WTF::Partitions::FastFree(buffer);
return nullptr;
}
return hb_blob_create(const_cast<char*>(buffer), table_size,
HB_MEMORY_MODE_WRITABLE, buffer,
WTF::Partitions::FastFree);
}
// TODO(yosin): We should move |CreateFace()| to "harfbuzz_font_cache.cc".
static hb::unique_ptr<hb_face_t> CreateFace(FontPlatformData* platform_data) {
hb::unique_ptr<hb_face_t> face;
sk_sp<SkTypeface> typeface = sk_ref_sp(platform_data->Typeface());
CHECK(typeface);
#if !BUILDFLAG(IS_MAC)
face = HbFaceFromSkTypeface(typeface);
#endif
// Fallback to table copies if there is no in-memory access.
if (!face) {
face = hb::unique_ptr<hb_face_t>(hb_face_create_for_tables(
HarfBuzzSkiaGetTable, typeface.get(), nullptr));
}
DCHECK(face);
return face;
}
// TODO(yosin): We should move |CreateHarfBuzzFontData()| to
// "harfbuzz_font_cache.cc".
static scoped_refptr<HarfBuzzFontData> CreateHarfBuzzFontData(
hb_face_t* face,
SkTypeface* typeface) {
hb::unique_ptr<hb_font_t> ot_font(hb_font_create(face));
hb_ot_font_set_funcs(ot_font.get());
int axis_count = typeface->getVariationDesignPosition(nullptr, 0);
if (axis_count > 0) {
Vector<SkFontArguments::VariationPosition::Coordinate> axis_values;
axis_values.resize(axis_count);
if (typeface->getVariationDesignPosition(axis_values.data(),
axis_values.size()) > 0) {
hb_font_set_variations(
ot_font.get(), reinterpret_cast<hb_variation_t*>(axis_values.data()),
axis_values.size());
}
}
// Creating a sub font means that non-available functions
// are found from the parent.
hb_font_t* const unscaled_font = hb_font_create_sub_font(ot_font.get());
scoped_refptr<HarfBuzzFontData> harfbuzz_font_data =
HarfBuzzFontData::Create(unscaled_font);
hb_font_set_funcs(unscaled_font,
HarfBuzzSkiaFontFuncs::Get().GetFunctions(typeface),
harfbuzz_font_data.get(), nullptr);
return harfbuzz_font_data;
}
scoped_refptr<HarfBuzzFontData> HarfBuzzFontCache::GetOrCreateFontData(
FontPlatformData* platform_data) {
const auto& result = font_map_.insert(platform_data->UniqueID(), nullptr);
if (result.is_new_entry) {
hb::unique_ptr<hb_face_t> face = CreateFace(platform_data);
result.stored_value->value =
CreateHarfBuzzFontData(face.get(), platform_data->Typeface());
}
return result.stored_value->value;
}
static_assert(
std::is_same<decltype(SkFontArguments::VariationPosition::Coordinate::axis),
decltype(hb_variation_t::tag)>::value &&
std::is_same<
decltype(SkFontArguments::VariationPosition::Coordinate::value),
decltype(hb_variation_t::value)>::value &&
sizeof(SkFontArguments::VariationPosition::Coordinate) ==
sizeof(hb_variation_t),
"Skia and HarfBuzz Variation parameter types must match in structure and "
"size.");
hb_font_t* HarfBuzzFace::GetScaledFont(scoped_refptr<UnicodeRangeSet> range_set,
VerticalLayoutCallbacks vertical_layout,
float specified_size) const {
harfbuzz_font_data_->range_set_ = std::move(range_set);
harfbuzz_font_data_->UpdateFallbackMetricsAndScale(*platform_data_,
vertical_layout);
int scale = SkiaScalarToHarfBuzzPosition(platform_data_->size());
hb_font_set_scale(unscaled_font_, scale, scale);
// See contended discussion in https://github.com/harfbuzz/harfbuzz/pull/1484
// Setting ptem here is critical for HarfBuzz to know where to lookup spacing
// offset in the AAT trak table, the unit pt in ptem here means "CoreText"
// points. After discussion on the pull request and with Apple developers, the
// meaning of HarfBuzz' hb_font_set_ptem API was changed to expect the
// equivalent of CSS pixels here.
hb_font_set_ptem(unscaled_font_, specified_size > 0 ? specified_size
: platform_data_->size());
return unscaled_font_;
}
hb_font_t* HarfBuzzFace::GetScaledFont() const {
return GetScaledFont(nullptr, HarfBuzzFace::kNoVerticalLayout,
platform_data_->size());
}
void HarfBuzzFace::Init() {
DCHECK(IsMainThread());
HarfBuzzSkiaFontFuncs::Get();
}
} // namespace blink