diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/fonts/opentype')
3 files changed, 201 insertions, 14 deletions
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc index 3365749c759..4c18c37138f 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc @@ -4,13 +4,43 @@ #include "third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h" +#include <hb-aat.h> +#include <hb.h> + namespace blink { +namespace { + +bool activationSelectorPresent( + hb_face_t* hb_face, + const hb_aat_layout_feature_type_t feature_type, + const hb_aat_layout_feature_selector_t enabled_selector_expectation) { + Vector<hb_aat_layout_feature_selector_info_t> feature_selectors; + unsigned num_feature_selectors = 0; + unsigned default_index = 0; + num_feature_selectors = hb_aat_layout_feature_type_get_selector_infos( + hb_face, feature_type, 0, nullptr, nullptr, nullptr); + feature_selectors.resize(num_feature_selectors); + if (!hb_aat_layout_feature_type_get_selector_infos( + hb_face, feature_type, 0, &num_feature_selectors, + feature_selectors.data(), &default_index)) { + return false; + } + for (hb_aat_layout_feature_selector_info_t selector_info : + feature_selectors) { + if (selector_info.enable == enabled_selector_expectation) + return true; + } + return false; +} +} // namespace + OpenTypeCapsSupport::OpenTypeCapsSupport() : harfbuzz_face_(nullptr), requested_caps_(FontDescription::kCapsNormal), font_support_(FontSupport::kFull), - caps_synthesis_(CapsSynthesis::kNone) {} + caps_synthesis_(CapsSynthesis::kNone), + font_format_(FontFormat::kUndetermined) {} OpenTypeCapsSupport::OpenTypeCapsSupport( const HarfBuzzFace* harfbuzz_face, @@ -19,7 +49,8 @@ OpenTypeCapsSupport::OpenTypeCapsSupport( : harfbuzz_face_(harfbuzz_face), requested_caps_(requested_caps), font_support_(FontSupport::kFull), - caps_synthesis_(CapsSynthesis::kNone) { + caps_synthesis_(CapsSynthesis::kNone), + font_format_(FontFormat::kUndetermined) { if (requested_caps != FontDescription::kCapsNormal) DetermineFontSupport(script); } @@ -102,24 +133,114 @@ CaseMapIntend OpenTypeCapsSupport::NeedsCaseChange( return case_map_intend; } +OpenTypeCapsSupport::FontFormat OpenTypeCapsSupport::GetFontFormat() const { + if (font_format_ == FontFormat::kUndetermined) { + hb_face_t* hb_face = hb_font_get_face( + harfbuzz_face_->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout)); + + std::unique_ptr<hb_blob_t, decltype(&hb_blob_destroy)> morx_blob( + hb_face_reference_table(hb_face, HB_TAG('m', 'o', 'r', 'x')), + hb_blob_destroy); + std::unique_ptr<hb_blob_t, decltype(&hb_blob_destroy)> mort_blob( + hb_face_reference_table(hb_face, HB_TAG('m', 'o', 'r', 't')), + hb_blob_destroy); + + // TODO(crbug.com/911149): Use hb_aat_layout_has_substitution() for + // has_morx_or_mort and hb_ot_layout_has_substitution() for has_gsub once is + // exposed in HarfBuzz. + bool has_morx_or_mort = hb_blob_get_length(morx_blob.get()) || + hb_blob_get_length(mort_blob.get()); + bool has_gsub = hb_ot_layout_has_substitution(hb_face); + font_format_ = has_morx_or_mort&& !has_gsub + ? font_format_ = FontFormat::kAat + : font_format_ = FontFormat::kOpenType; + } + return font_format_; +} + +bool OpenTypeCapsSupport::SupportsFeature(hb_script_t script, + uint32_t tag) const { + if (GetFontFormat() == FontFormat::kAat) + return SupportsAatFeature(tag); + return SupportsOpenTypeFeature(script, tag); +} + +bool OpenTypeCapsSupport::SupportsAatFeature(uint32_t tag) const { + // We only want to detect small-caps and capitals-to-small-capitals features + // for aat-fonts, any other requests are returned as not supported. + if (tag != HB_TAG('s', 'm', 'c', 'p') && tag != HB_TAG('c', '2', 's', 'c')) { + return false; + } + + hb_face_t* hb_face = hb_font_get_face( + harfbuzz_face_->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout)); + + Vector<hb_aat_layout_feature_type_t> aat_features; + unsigned feature_count = + hb_aat_layout_get_feature_types(hb_face, 0, nullptr, nullptr); + aat_features.resize(feature_count); + if (!hb_aat_layout_get_feature_types(hb_face, 0, &feature_count, + aat_features.data())) + return false; + + if (tag == HB_TAG('s', 'm', 'c', 'p')) { + // Check for presence of new style (feature id 38) or old style (letter + // case, feature id 3) small caps feature presence, then check for the + // specific required activation selectors. + if (!aat_features.Contains(HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE) && + !aat_features.Contains(HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE)) + return false; + + // Check for new style small caps, feature id 38. + if (aat_features.Contains(HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE)) { + if (activationSelectorPresent( + hb_face, HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS)) + return true; + } + + // Check for old style small caps enabling selector, feature id 3. + if (aat_features.Contains(HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE)) { + if (activationSelectorPresent(hb_face, + HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS)) + return true; + } + + // Neither old or new style small caps present. + return false; + } + + if (tag == HB_TAG('c', '2', 's', 'c')) { + if (!aat_features.Contains(HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE)) + return false; + + return activationSelectorPresent( + hb_face, HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS); + } + + return false; +} + void OpenTypeCapsSupport::DetermineFontSupport(hb_script_t script) { switch (requested_caps_) { case FontDescription::kSmallCaps: - if (!SupportsOpenTypeFeature(script, HB_TAG('s', 'm', 'c', 'p'))) { + if (!SupportsFeature(script, HB_TAG('s', 'm', 'c', 'p'))) { font_support_ = FontSupport::kNone; caps_synthesis_ = CapsSynthesis::kLowerToSmallCaps; } break; case FontDescription::kAllSmallCaps: - if (!(SupportsOpenTypeFeature(script, HB_TAG('s', 'm', 'c', 'p')) && - SupportsOpenTypeFeature(script, HB_TAG('c', '2', 's', 'c')))) { + if (!(SupportsFeature(script, HB_TAG('s', 'm', 'c', 'p')) && + SupportsFeature(script, HB_TAG('c', '2', 's', 'c')))) { font_support_ = FontSupport::kNone; caps_synthesis_ = CapsSynthesis::kBothToSmallCaps; } break; case FontDescription::kPetiteCaps: - if (!SupportsOpenTypeFeature(script, HB_TAG('p', 'c', 'a', 'p'))) { - if (SupportsOpenTypeFeature(script, HB_TAG('s', 'm', 'c', 'p'))) { + if (!SupportsFeature(script, HB_TAG('p', 'c', 'a', 'p'))) { + if (SupportsFeature(script, HB_TAG('s', 'm', 'c', 'p'))) { font_support_ = FontSupport::kFallback; } else { font_support_ = FontSupport::kNone; @@ -128,10 +249,10 @@ void OpenTypeCapsSupport::DetermineFontSupport(hb_script_t script) { } break; case FontDescription::kAllPetiteCaps: - if (!(SupportsOpenTypeFeature(script, HB_TAG('p', 'c', 'a', 'p')) && - SupportsOpenTypeFeature(script, HB_TAG('c', '2', 'p', 'c')))) { - if (SupportsOpenTypeFeature(script, HB_TAG('s', 'm', 'c', 'p')) && - SupportsOpenTypeFeature(script, HB_TAG('c', '2', 's', 'c'))) { + if (!(SupportsFeature(script, HB_TAG('p', 'c', 'a', 'p')) && + SupportsFeature(script, HB_TAG('c', '2', 'p', 'c')))) { + if (SupportsFeature(script, HB_TAG('s', 'm', 'c', 'p')) && + SupportsFeature(script, HB_TAG('c', '2', 's', 'c'))) { font_support_ = FontSupport::kFallback; } else { font_support_ = FontSupport::kNone; @@ -140,9 +261,9 @@ void OpenTypeCapsSupport::DetermineFontSupport(hb_script_t script) { } break; case FontDescription::kUnicase: - if (!SupportsOpenTypeFeature(script, HB_TAG('u', 'n', 'i', 'c'))) { + if (!SupportsFeature(script, HB_TAG('u', 'n', 'i', 'c'))) { caps_synthesis_ = CapsSynthesis::kUpperToSmallCaps; - if (SupportsOpenTypeFeature(script, HB_TAG('s', 'm', 'c', 'p'))) { + if (SupportsFeature(script, HB_TAG('s', 'm', 'c', 'p'))) { font_support_ = FontSupport::kFallback; } else { font_support_ = FontSupport::kNone; @@ -150,7 +271,7 @@ void OpenTypeCapsSupport::DetermineFontSupport(hb_script_t script) { } break; case FontDescription::kTitlingCaps: - if (!SupportsOpenTypeFeature(script, HB_TAG('t', 'i', 't', 'l'))) { + if (!SupportsFeature(script, HB_TAG('t', 'i', 't', 'l'))) { font_support_ = FontSupport::kNone; } break; diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h index 841e465c8a1..4f53191116f 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h @@ -29,7 +29,13 @@ class PLATFORM_EXPORT OpenTypeCapsSupport { CaseMapIntend NeedsCaseChange(SmallCapsIterator::SmallCapsBehavior run_case); private: + enum class FontFormat { kUndetermined, kOpenType, kAat }; + // Lazily intializes font_format_ when needed and returns the format of the + // underlying HarfBuzzFace/Font. + FontFormat GetFontFormat() const; void DetermineFontSupport(hb_script_t); + bool SupportsFeature(hb_script_t, uint32_t tag) const; + bool SupportsAatFeature(uint32_t tag) const; bool SupportsOpenTypeFeature(hb_script_t, uint32_t tag) const; const HarfBuzzFace* harfbuzz_face_; @@ -50,6 +56,7 @@ class PLATFORM_EXPORT OpenTypeCapsSupport { FontSupport font_support_; CapsSynthesis caps_synthesis_; + mutable FontFormat font_format_; }; }; // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_test.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_test.cc new file mode 100644 index 00000000000..f0097556ae1 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_test.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h" + +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/fonts/font_description.h" +#include "third_party/blink/renderer/platform/fonts/font_platform_data.h" + +#include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkTypeface.h" + +#if defined(OS_MACOSX) +#include "base/mac/mac_util.h" +#endif + +namespace blink { + +void ensureHasNativeSmallCaps(const std::string& font_family_name) { + sk_sp<SkTypeface> test_typeface = + SkTypeface::MakeFromName(font_family_name.c_str(), SkFontStyle()); + FontPlatformData font_platform_data(test_typeface, font_family_name.c_str(), + 16, false, false); + ASSERT_EQ(font_platform_data.FontFamilyName(), font_family_name.c_str()); + + OpenTypeCapsSupport caps_support(font_platform_data.GetHarfBuzzFace(), + FontDescription::FontVariantCaps::kSmallCaps, + HB_SCRIPT_LATIN); + // If caps_support.NeedsRunCaseSplitting() is true, this means that synthetic + // upper-casing / lower-casing is required and the run needs to be segmented + // by upper-case, lower-case properties. If it is false, it means that the + // font feature can be used and no synthetic case-changing is needed. + ASSERT_FALSE(caps_support.NeedsRunCaseSplitting()); +} + +// The AAT fonts for testing are only available on Mac OS X 10.13. +#if defined(OS_MACOSX) +#define MAYBE_SmallCapsForSFNSText SmallCapsForSFNSText +#else +#define MAYBE_SmallCapsForSFNSText DISABLED_SmallCapsForSFNSText +#endif +TEST(OpenTypeCapsSupportTest, MAYBE_SmallCapsForSFNSText) { +#if defined(OS_MACOSX) + if (!base::mac::IsAtLeastOS10_13()) + return; +#endif + std::vector<std::string> test_fonts = { + ".SF NS Text", // has OpenType small-caps + "Apple Chancery", // has old-style (feature id 3,"Letter Case") + // small-caps + "Baskerville"}; // has new-style (feature id 38, "Upper Case") + // small-case. + for (auto& test_font : test_fonts) + ensureHasNativeSmallCaps(test_font); +} + +} // namespace blink |