summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/fonts/opentype
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/fonts/opentype')
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc149
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h7
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_test.cc59
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