910e62b5创建于 1月15日历史提交
// Copyright 2020 The Chromium Authors
// 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_data_source_linux.h"

#include <fontconfig/fontconfig.h>

#include <memory>
#include <set>

#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/notreached.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 "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 = UNSAFE_TODO(fontset->fonts[j]);
      // Increments the refcount for the font.
      FcPatternReference(font);
      FcBool result = FcFontSetAdd(output, font);
      DCHECK_EQ(result, FcTrue);
    }
  }
  return output;
}

}  // namespace

FontEnumerationDataSourceLinux::FontEnumerationDataSourceLinux() {
  DETACH_FROM_SEQUENCE(sequence_checker_);
}

FontEnumerationDataSourceLinux::~FontEnumerationDataSourceLinux() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}

blink::FontEnumerationTable FontEnumerationDataSourceLinux::GetFonts(
    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(UNSAFE_TODO(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(UNSAFE_TODO(fontset->fonts[i]), FC_FULLNAME, 0,
                           reinterpret_cast<FcChar8**>(&full_name)) !=
        FcResultMatch) {
      // Skip incomplete or malformed font.
      continue;
    }

    char* family = nullptr;
    if (FcPatternGetString(UNSAFE_TODO(fontset->fonts[i]), FC_FAMILY, 0,
                           reinterpret_cast<FcChar8**>(&family)) !=
        FcResultMatch) {
      // Skip incomplete or malformed font.
      continue;
    }

    char* style = nullptr;
    if (FcPatternGetString(UNSAFE_TODO(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;
    }

    blink::FontEnumerationTable_FontData* data =
        font_enumeration_table.add_fonts();
    data->set_postscript_name(postscript_name);
    data->set_full_name(full_name);
    data->set_family(family);
    data->set_style(style);
  }

  return font_enumeration_table;
}

}  // namespace content