| // Copyright 2020 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 "content/browser/font_access/font_enumeration_cache_fontconfig.h" |
| |
| #include <fontconfig/fontconfig.h> |
| |
| #include <memory> |
| |
| #include "base/feature_list.h" |
| #include "base/location.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/sequence_checker.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/task_traits.h" |
| #include "base/task/thread_pool.h" |
| #include "base/threading/scoped_blocking_call.h" |
| #include "base/threading/sequence_bound.h" |
| #include "base/time/time.h" |
| #include "base/types/pass_key.h" |
| #include "content/browser/font_access/font_enumeration_cache.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/font_access/font_enumeration_table.pb.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| std::unique_ptr<FcPattern, decltype(&FcPatternDestroy)> CreateFormatPattern( |
| const char* format) { |
| std::unique_ptr<FcPattern, decltype(&FcPatternDestroy)> pattern( |
| FcPatternCreate(), FcPatternDestroy); |
| FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue); |
| FcPatternAddString(pattern.get(), FC_FONTFORMAT, |
| reinterpret_cast<const FcChar8*>(format)); |
| return pattern; |
| } |
| |
| // Returns a font set comprising of fonts in the provided object set. |
| FcFontSet* ListFonts(FcObjectSet* object_set) { |
| FcFontSet* output = FcFontSetCreate(); |
| |
| // See https://www.freetype.org/freetype2/docs/reference/ft2-font_formats.html |
| // for the list of possible formats. |
| const char* allowed_formats[] = {"TrueType", "CFF"}; |
| for (const auto* format : allowed_formats) { |
| auto format_pattern = CreateFormatPattern(format); |
| std::unique_ptr<FcFontSet, decltype(&FcFontSetDestroy)> fontset( |
| FcFontList(nullptr, format_pattern.get(), object_set), |
| FcFontSetDestroy); |
| for (int j = 0; j < fontset->nfont; ++j) { |
| FcPattern* font = fontset->fonts[j]; |
| // Increments the refcount for the font. |
| FcPatternReference(font); |
| FcBool result = FcFontSetAdd(output, font); |
| DCHECK_EQ(result, FcTrue); |
| } |
| } |
| return output; |
| } |
| |
| // Utility function to pull out an integer value from a pattern and return it, |
| // returning a default value if the key is not present or the wrong type. |
| int GetIntegerFromFCPattern(FcPattern* pattern, |
| const char* object, |
| int index, |
| int default_value) { |
| int result; |
| if (FcPatternGetInteger(pattern, object, index, &result) == FcResultMatch) |
| return result; |
| return default_value; |
| } |
| |
| // Map FC_SLANT_* values to a boolean for italic/oblique. |
| bool FCSlantToWebItalic(int slant) { |
| return slant != FC_SLANT_ROMAN; |
| } |
| |
| // Map FC_WEIGHT_* values to a font-weight (number in [1,1000]). |
| // https://drafts.csswg.org/css-fonts-4/#font-weight-prop |
| float FCWeightToWebWeight(int weight) { |
| // A lookup table is used to provide values that exactly match CSS numeric |
| // keywords. -1 is used as a sentinel value. |
| constexpr struct { |
| int limit; |
| float weight; |
| } map[] = { |
| {20, 100.f}, // FC_WEIGHT_THIN = 0 |
| {45, 200.f}, // FC_WEIGHT_EXTRALIGHT = 40 |
| {52, 300.f}, // FC_WEIGHT_LIGHT = 50 |
| {65, 350.f}, // FC_WEIGHT_DEMILIGHT = 55 |
| {77, 375.f}, // FC_WEIGHT_BOOK = 75 |
| {90, 400.f}, // FC_WEIGHT_REGULAR = 80 |
| {140, 500.f}, // FC_WEIGHT_MEDIUM = 100 |
| {190, 600.f}, // FC_WEIGHT_DEMIBOLD = 180 |
| {202, 700.f}, // FC_WEIGHT_BOLD = 200 |
| {207, 800.f}, // FC_WEIGHT_EXTRABOLD = 205 |
| {212, 900.f}, // FC_WEIGHT_BLACK = 210 |
| {-1, 950.f} // FC_WEIGHT_EXTRABLACK = 215 |
| }; |
| |
| for (auto entry : map) { |
| if (weight < entry.limit || entry.limit < 0) |
| return entry.weight; |
| } |
| NOTREACHED(); |
| return 400; |
| } |
| |
| // Map FC_WIDTH_* values to a font-stretch value (percentage). |
| // https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch |
| float FCWidthToWebStretch(int width) { |
| // FC_WIDTH_* values are effectively rounded percentages as integers. A lookup |
| // table is used to provide values that exactly match CSS percentages. -1 is |
| // used as a sentinel value. |
| constexpr struct { |
| int limit; |
| float stretch; |
| } map[] = { |
| {56, 0.500f}, // FC_WIDTH_ULTRACONDENSED = 50 |
| {69, 0.625f}, // FC_WIDTH_EXTRACONDENSED = 63 |
| {81, 0.750f}, // FC_WIDTH_CONDENSED = 75 |
| {93, 0.875f}, // FC_WIDTH_SEMICONDENSED = 87 |
| {106, 1.000f}, // FC_WIDTH_NORMAL = 100 |
| {119, 1.125f}, // FC_WIDTH_SEMIEXPANDED = 113 |
| {137, 1.250f}, // FC_WIDTH_EXPANDED = 125 |
| {175, 1.500f}, // FC_WIDTH_EXTRAEXPANDED = 150 |
| {-1, 2.000f}, // FC_WIDTH_ULTRAEXPANDED = 200 |
| }; |
| |
| for (auto entry : map) { |
| if (width < entry.limit || entry.limit < 0) |
| return entry.stretch; |
| } |
| NOTREACHED(); |
| return 100; |
| } |
| |
| } // namespace |
| |
| // static |
| base::SequenceBound<FontEnumerationCache> |
| FontEnumerationCache::CreateForTesting( |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| absl::optional<std::string> locale_override) { |
| return base::SequenceBound<FontEnumerationCacheFontconfig>( |
| std::move(task_runner), std::move(locale_override), |
| base::PassKey<FontEnumerationCache>()); |
| } |
| |
| FontEnumerationCacheFontconfig::FontEnumerationCacheFontconfig( |
| absl::optional<std::string> locale_override, |
| base::PassKey<FontEnumerationCache>) |
| : FontEnumerationCache(std::move(locale_override)) {} |
| |
| FontEnumerationCacheFontconfig::~FontEnumerationCacheFontconfig() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| blink::FontEnumerationTable |
| FontEnumerationCacheFontconfig::ComputeFontEnumerationData( |
| const std::string& locale) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, |
| base::BlockingType::MAY_BLOCK); |
| |
| blink::FontEnumerationTable font_enumeration_table; |
| |
| std::unique_ptr<FcObjectSet, decltype(&FcObjectSetDestroy)> object_set( |
| FcObjectSetBuild(FC_POSTSCRIPT_NAME, FC_FULLNAME, FC_FAMILY, FC_STYLE, |
| nullptr), |
| FcObjectSetDestroy); |
| |
| std::unique_ptr<FcFontSet, decltype(&FcFontSetDestroy)> fontset( |
| ListFonts(object_set.get()), FcFontSetDestroy); |
| |
| // Used to filter duplicates. |
| std::set<std::string> fonts_seen; |
| |
| for (int i = 0; i < fontset->nfont; ++i) { |
| char* postscript_name = nullptr; |
| if (FcPatternGetString(fontset->fonts[i], FC_POSTSCRIPT_NAME, 0, |
| reinterpret_cast<FcChar8**>(&postscript_name)) != |
| FcResultMatch) { |
| // Skip incomplete or malformed font. |
| continue; |
| } |
| |
| char* full_name = nullptr; |
| if (FcPatternGetString(fontset->fonts[i], FC_FULLNAME, 0, |
| reinterpret_cast<FcChar8**>(&full_name)) != |
| FcResultMatch) { |
| // Skip incomplete or malformed font. |
| continue; |
| } |
| |
| char* family = nullptr; |
| if (FcPatternGetString(fontset->fonts[i], FC_FAMILY, 0, |
| reinterpret_cast<FcChar8**>(&family)) != |
| FcResultMatch) { |
| // Skip incomplete or malformed font. |
| continue; |
| } |
| |
| char* style = nullptr; |
| if (FcPatternGetString(fontset->fonts[i], FC_STYLE, 0, |
| reinterpret_cast<FcChar8**>(&style)) != |
| FcResultMatch) { |
| // Skip incomplete or malformed font. |
| continue; |
| } |
| |
| auto it_and_success = fonts_seen.emplace(postscript_name); |
| if (!it_and_success.second) { |
| // Skip duplicate. |
| continue; |
| } |
| |
| // These properties may not be present, so defaults are provided and such |
| // fonts are not skipped. These defaults should map to the default web |
| // values when passed to the FCXXXToWebYYY functions. |
| int slant = |
| GetIntegerFromFCPattern(fontset->fonts[i], FC_SLANT, 0, |
| FC_SLANT_ROMAN); // Maps to italic: false. |
| int weight = GetIntegerFromFCPattern( |
| fontset->fonts[i], FC_WEIGHT, 0, |
| FC_WEIGHT_REGULAR); // Maps to weight: 400 (regular). |
| int width = GetIntegerFromFCPattern( |
| fontset->fonts[i], FC_WIDTH, 0, |
| FC_WIDTH_NORMAL); // Maps to width: 100% (normal). |
| |
| blink::FontEnumerationTable_FontMetadata* metadata = |
| font_enumeration_table.add_fonts(); |
| metadata->set_postscript_name(postscript_name); |
| metadata->set_full_name(full_name); |
| metadata->set_family(family); |
| metadata->set_style(style); |
| metadata->set_italic(FCSlantToWebItalic(slant)); |
| metadata->set_weight(FCWeightToWebWeight(weight)); |
| metadata->set_stretch(FCWidthToWebStretch(width)); |
| } |
| |
| return font_enumeration_table; |
| } |
| |
| } // namespace content |