1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120

content / browser / font_access / font_enumeration_data_source_mac.mm [blame]

// 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_mac.h"

#import <CoreFoundation/CoreFoundation.h>
#import <CoreText/CoreText.h>

#include <string>

#include "base/apple/bridging.h"
#include "base/apple/scoped_cftyperef.h"
#include "base/notreached.h"
#include "base/strings/sys_string_conversions.h"
#include "third_party/blink/public/common/font_access/font_enumeration_table.pb.h"

namespace content {

namespace {

base::apple::ScopedCFTypeRef<CFStringRef> GetLocalizedString(
    CTFontDescriptorRef fd,
    CFStringRef attribute) {
  return base::apple::ScopedCFTypeRef<CFStringRef>(
      base::apple::CFCast<CFStringRef>(CTFontDescriptorCopyLocalizedAttribute(
          fd, attribute, /*language=*/nullptr)));
}

base::apple::ScopedCFTypeRef<CFStringRef> GetString(CTFontDescriptorRef fd,
                                                    CFStringRef attribute) {
  return base::apple::ScopedCFTypeRef<CFStringRef>(
      base::apple::CFCast<CFStringRef>(
          CTFontDescriptorCopyAttribute(fd, attribute)));
}

}  // namespace

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

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

bool FontEnumerationDataSourceMac::IsValidFontMac(
    const CTFontDescriptorRef& fd) {
  base::apple::ScopedCFTypeRef<CFStringRef> cf_postscript_name =
      GetString(fd, kCTFontNameAttribute);
  base::apple::ScopedCFTypeRef<CFStringRef> cf_full_name =
      GetLocalizedString(fd, kCTFontDisplayNameAttribute);
  base::apple::ScopedCFTypeRef<CFStringRef> cf_family =
      GetString(fd, kCTFontFamilyNameAttribute);
  base::apple::ScopedCFTypeRef<CFStringRef> cf_style =
      GetString(fd, kCTFontStyleNameAttribute);

  if (!cf_postscript_name || !cf_full_name || !cf_family || !cf_style) {
    // Check for invalid attribute returns as MacOS may allow
    // OS-level installation of fonts for some of these.
    return false;
  }
  this->cf_postscript_name_ = cf_postscript_name;
  this->cf_full_name_ = cf_full_name;
  this->cf_family_ = cf_family;
  this->cf_style_ = cf_style;
  return true;
}

blink::FontEnumerationTable FontEnumerationDataSourceMac::GetFonts(
    const std::string& locale) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  blink::FontEnumerationTable font_enumeration_table;

  @autoreleasepool {
    NSDictionary* options = @{
      base::apple::CFToNSPtrCast(kCTFontCollectionRemoveDuplicatesOption) : @YES
    };

    base::apple::ScopedCFTypeRef<CTFontCollectionRef> collection(
        CTFontCollectionCreateFromAvailableFonts(
            base::apple::NSToCFPtrCast(options)));

    base::apple::ScopedCFTypeRef<CFArrayRef> font_descs(
        CTFontCollectionCreateMatchingFontDescriptors(collection.get()));

    // Used to filter duplicates.
    std::set<std::string> fonts_seen;

    for (CFIndex i = 0; i < CFArrayGetCount(font_descs.get()); ++i) {
      CTFontDescriptorRef fd = base::apple::CFCast<CTFontDescriptorRef>(
          CFArrayGetValueAtIndex(font_descs.get(), i));
      if (!IsValidFontMac(fd)) {
        // Skip invalid fonts.
        continue;
      }

      std::string postscript_name =
          base::SysCFStringRefToUTF8(cf_postscript_name_.get());

      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(std::move(postscript_name));
      data->set_full_name(base::SysCFStringRefToUTF8(cf_full_name_.get()));
      data->set_family(base::SysCFStringRefToUTF8(cf_family_.get()));
      data->set_style(base::SysCFStringRefToUTF8(cf_style_.get()));
    }

    return font_enumeration_table;
  }
}

}  // namespace content