File: font_fallback_linux.cc

package info (click to toggle)
chromium-browser 70.0.3538.110-1~deb9u1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 1,619,476 kB
  • sloc: cpp: 13,024,755; ansic: 1,349,823; python: 916,672; xml: 314,489; java: 280,047; asm: 276,936; perl: 75,771; objc: 66,634; sh: 45,860; cs: 28,354; php: 11,064; makefile: 10,911; yacc: 9,109; tcl: 8,403; ruby: 4,065; lex: 1,779; pascal: 1,411; lisp: 1,055; awk: 41; jsp: 39; sed: 17; sql: 3
file content (262 lines) | stat: -rw-r--r-- 8,290 bytes parent folder | download | duplicates (3)
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// Copyright 2014 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 "ui/gfx/font_fallback_linux.h"

#include <fontconfig/fontconfig.h>

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
#include "ui/gfx/font.h"

namespace gfx {

namespace {

const char kFontFormatTrueType[] = "TrueType";
const char kFontFormatCFF[] = "CFF";

typedef std::map<std::string, std::vector<Font> > FallbackCache;
base::LazyInstance<FallbackCache>::Leaky g_fallback_cache =
    LAZY_INSTANCE_INITIALIZER;

}  // namespace

std::vector<Font> GetFallbackFonts(const Font& font) {
  std::string font_family = font.GetFontName();
  std::vector<Font>* fallback_fonts =
      &g_fallback_cache.Get()[font_family];
  if (!fallback_fonts->empty())
    return *fallback_fonts;

  FcPattern* pattern = FcPatternCreate();
  FcValue family;
  family.type = FcTypeString;
  family.u.s = reinterpret_cast<const FcChar8*>(font_family.c_str());
  FcPatternAdd(pattern, FC_FAMILY, family, FcFalse);
  if (FcConfigSubstitute(NULL, pattern, FcMatchPattern) == FcTrue) {
    FcDefaultSubstitute(pattern);
    FcResult result;
    FcFontSet* fonts = FcFontSort(NULL, pattern, FcTrue, NULL, &result);
    if (fonts) {
      for (int i = 0; i < fonts->nfont; ++i) {
        char* name = NULL;
        FcPatternGetString(fonts->fonts[i], FC_FAMILY, 0,
            reinterpret_cast<FcChar8**>(&name));
        // FontConfig returns multiple fonts with the same family name and
        // different configurations. Check to prevent duplicate family names.
        if (fallback_fonts->empty() ||
            fallback_fonts->back().GetFontName() != name) {
          fallback_fonts->push_back(Font(std::string(name), 13));
        }
      }
      FcFontSetDestroy(fonts);
    }
  }
  FcPatternDestroy(pattern);

  if (fallback_fonts->empty())
    fallback_fonts->push_back(Font(font_family, 13));

  return *fallback_fonts;
}

namespace {

class CachedFont {
 public:
  // Note: We pass the charset explicitly as callers
  // should not create CachedFont entries without knowing
  // that the FcPattern contains a valid charset.
  CachedFont(FcPattern* pattern, FcCharSet* char_set)
      : supported_characters_(char_set) {
    DCHECK(pattern);
    DCHECK(char_set);
    fallback_font_.name = GetFontName(pattern);
    fallback_font_.filename = GetFontFilename(pattern);
    fallback_font_.ttc_index = GetFontTtcIndex(pattern);
    fallback_font_.is_bold = IsFontBold(pattern);
    fallback_font_.is_italic = IsFontItalic(pattern);
  }

  const FallbackFontData& fallback_font() const { return fallback_font_; }

  bool HasGlyphForCharacter(UChar32 c) const {
    return supported_characters_ && FcCharSetHasChar(supported_characters_, c);
  }

 private:
  static std::string GetFontName(FcPattern* pattern) {
    FcChar8* familyName = nullptr;
    if (FcPatternGetString(pattern, FC_FAMILY, 0, &familyName) != FcResultMatch)
      return std::string();
    return std::string(reinterpret_cast<const char*>(familyName));
  }

  static std::string GetFontFilename(FcPattern* pattern) {
    FcChar8* c_filename = nullptr;
    if (FcPatternGetString(pattern, FC_FILE, 0, &c_filename) != FcResultMatch)
      return std::string();
    return std::string(reinterpret_cast<const char*>(c_filename));
  }

  static int GetFontTtcIndex(FcPattern* pattern) {
    int ttcIndex = -1;
    if (FcPatternGetInteger(pattern, FC_INDEX, 0, &ttcIndex) != FcResultMatch ||
        ttcIndex < 0)
      return 0;
    return ttcIndex;
  }

  static bool IsFontBold(FcPattern* pattern) {
    int weight = 0;
    if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) != FcResultMatch)
      return false;
    return weight >= FC_WEIGHT_BOLD;
  }

  static bool IsFontItalic(FcPattern* pattern) {
    int slant = 0;
    if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant) != FcResultMatch)
      return false;
    return slant != FC_SLANT_ROMAN;
  }

  FallbackFontData fallback_font_;
  // supported_characters_ is owned by the parent
  // FcFontSet and should never be freed.
  FcCharSet* supported_characters_;
};

class CachedFontSet {
 public:
  // CachedFontSet takes ownership of the passed FcFontSet.
  static std::unique_ptr<CachedFontSet> CreateForLocale(
      const std::string& locale) {
    FcFontSet* font_set = CreateFcFontSetForLocale(locale);
    return base::WrapUnique(new CachedFontSet(font_set));
  }

  ~CachedFontSet() {
    fallback_list_.clear();
    FcFontSetDestroy(font_set_);
  }

  FallbackFontData GetFallbackFontForChar(UChar32 c) {
    for (const auto& cached_font : fallback_list_) {
      if (cached_font.HasGlyphForCharacter(c))
        return cached_font.fallback_font();
    }
    // The previous code just returned garbage if the user didn't
    // have the necessary fonts, this seems better than garbage.
    // Current callers happen to ignore any values with an empty family string.
    return FallbackFontData();
  }

 private:
  static FcFontSet* CreateFcFontSetForLocale(const std::string& locale) {
    FcPattern* pattern = FcPatternCreate();

    if (!locale.empty()) {
      // FcChar* is unsigned char* so we have to cast.
      FcPatternAddString(pattern, FC_LANG,
                         reinterpret_cast<const FcChar8*>(locale.c_str()));
    }

    FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);

    FcConfigSubstitute(0, pattern, FcMatchPattern);
    FcDefaultSubstitute(pattern);

    if (locale.empty())
      FcPatternDel(pattern, FC_LANG);

    // The result parameter returns if any fonts were found.
    // We already handle 0 fonts correctly, so we ignore the param.
    FcResult result;
    FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
    FcPatternDestroy(pattern);

    // The caller will take ownership of this FcFontSet.
    return font_set;
  }

  CachedFontSet(FcFontSet* font_set) : font_set_(font_set) {
    FillFallbackList();
  }

  void FillFallbackList() {
    DCHECK(fallback_list_.empty());
    if (!font_set_)
      return;

    for (int i = 0; i < font_set_->nfont; ++i) {
      FcPattern* pattern = font_set_->fonts[i];

      // Ignore any bitmap fonts users may still have installed from last
      // century.
      FcBool is_scalable;
      if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) !=
              FcResultMatch ||
          !is_scalable)
        continue;

      // Ignore any fonts FontConfig knows about, but that we don't have
      // permission to read.
      FcChar8* c_filename;
      if (FcPatternGetString(pattern, FC_FILE, 0, &c_filename) != FcResultMatch)
        continue;
      if (access(reinterpret_cast<char*>(c_filename), R_OK))
        continue;

      // Take only supported font formats on board.
      FcChar8* font_format;
      if (FcPatternGetString(pattern, FC_FONTFORMAT, 0, &font_format) !=
          FcResultMatch) {
        continue;
      }
      if (font_format &&
          strcmp(reinterpret_cast<char*>(font_format), kFontFormatTrueType) &&
          strcmp(reinterpret_cast<char*>(font_format), kFontFormatCFF)) {
        continue;
      }

      // Make sure this font can tell us what characters it has glyphs for.
      FcCharSet* char_set;
      if (FcPatternGetCharSet(pattern, FC_CHARSET, 0, &char_set) !=
          FcResultMatch)
        continue;

      fallback_list_.emplace_back(pattern, char_set);
    }
  }

  FcFontSet* font_set_;  // Owned by this object.
  // CachedFont has a FcCharset* which points into the FcFontSet.
  // If the FcFontSet is ever destroyed, the fallback list
  // must be cleared first.
  std::vector<CachedFont> fallback_list_;

  DISALLOW_COPY_AND_ASSIGN(CachedFontSet);
};

typedef std::map<std::string, std::unique_ptr<CachedFontSet>> FontSetCache;
base::LazyInstance<FontSetCache>::Leaky g_font_sets_by_locale =
    LAZY_INSTANCE_INITIALIZER;

}  // namespace

FallbackFontData GetFallbackFontForChar(UChar32 c, const std::string& locale) {
  auto& cached_font_set = g_font_sets_by_locale.Get()[locale];
  if (!cached_font_set)
    cached_font_set = CachedFontSet::CreateForLocale(locale);
  return cached_font_set->GetFallbackFontForChar(c);
}

}  // namespace gfx