Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1204)

Side by Side Diff: headless/public/util/fontconfig.cc

Issue 2877813002: [headless] Enable headless embedders to specify fonts. (Closed)
Patch Set: addressed skyostil@'s comments Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "headless/public/util/fontconfig.h"
6
7 #include <dirent.h>
8 #include <fontconfig/fontconfig.h>
9 #include <freetype/freetype.h>
10 #include <ft2build.h>
11 #include <set>
12 #include <string>
13
14 #include "base/logging.h"
15
16 namespace headless {
17 namespace {
18 void GetFontFileNames(const FcFontSet* font_set,
19 std::set<std::string>* file_names) {
20 if (font_set == NULL)
21 return;
22 for (int i = 0; i < font_set->nfont; ++i) {
23 FcPattern* pattern = font_set->fonts[i];
24 FcValue font_file;
25 if (FcPatternGet(pattern, "file", 0, &font_file) == FcResultMatch) {
26 file_names->insert(reinterpret_cast<const char*>(font_file.u.s));
27 } else {
28 VLOG(1) << "Failed to find filename.";
29 FcPatternPrint(pattern);
30 }
31 }
32 }
33 FcConfig* g_config = nullptr;
34 } // namespace
35
36 void InitFonts(const char* fontconfig_path) {
37 // The following is roughly equivalent to calling FcInit(). We jump through
38 // a bunch of hoops here to avoid using fontconfig's directory scanning
39 // logic. The problem with fontconfig is that it follows symlinks when doing
40 // recursive directory scans.
41 //
42 // The approach below ignores any <dir>...</dir> entries in fonts.conf. This
43 // is deliberate. Specifying dirs is problematic because they're either
44 // absolute or relative to our process's current working directory which
45 // could be anything. Instead we assume that all font files will be in the
46 // same directory as fonts.conf. We'll scan + load them here.
47 FcConfig* config = FcConfigCreate();
48 g_config = config;
49 CHECK(config);
50
51 // FcConfigParseAndLoad is a seriously goofy function. Depending on whether
52 // name passed in begins with a slash, it will treat it either as a file name
53 // to be found in the directory where it expects to find the font
54 // configuration OR it will will treat it as a directory where it expects to
55 // find fonts.conf. The latter behavior is the one we want. Passing
56 // fontconfig_path via the environment is a quick and dirty way to get
57 // uniform behavior regardless whether it's a relative path or not.
58 setenv("FONTCONFIG_PATH", fontconfig_path, 1);
59 CHECK(FcConfigParseAndLoad(config, nullptr, FcTrue))
60 << "Failed to load font configuration. FONTCONFIG_PATH="
61 << fontconfig_path;
62
63 DIR* fc_dir = opendir(fontconfig_path);
64 CHECK(fc_dir) << "Failed to open font directory " << fontconfig_path << ": "
65 << strerror(errno);
66
67 // The fonts must be loaded in a consistent order. This makes rendered results
68 // stable across runs, otherwise replacement font picks are random
69 // and cause flakiness.
70 std::set<std::string> fonts;
71 struct dirent entry, *result;
72 while (readdir_r(fc_dir, &entry, &result) == 0 && result != NULL) {
73 fonts.insert(result->d_name);
74 }
75 for (const std::string& font : fonts) {
76 const std::string full_path = fontconfig_path + ("/" + font);
77 struct stat statbuf;
78 CHECK_EQ(0, stat(full_path.c_str(), &statbuf))
79 << "Failed to stat " << full_path << ": " << strerror(errno);
80 if (S_ISREG(statbuf.st_mode)) {
81 // FcConfigAppFontAddFile will silently ignore non-fonts.
82 FcConfigAppFontAddFile(
83 config, reinterpret_cast<const FcChar8*>(full_path.c_str()));
84 }
85 }
86 closedir(fc_dir);
87 CHECK(FcConfigSetCurrent(config));
88
89 // Retrieve font from both of fontconfig's font sets for pre-loading.
90 std::set<std::string> font_files;
91 GetFontFileNames(FcConfigGetFonts(NULL, FcSetSystem), &font_files);
92 GetFontFileNames(FcConfigGetFonts(NULL, FcSetApplication), &font_files);
93 CHECK_GT(font_files.size(), 0u)
94 << "Font configuration doesn't contain any fonts!";
95
96 // Get freetype to load every font file we know about. This will cause the
97 // font files to get cached in memory. Once that's done we shouldn't have to
98 // access the file system for fonts at all.
99 FT_Library library;
100 FT_Init_FreeType(&library);
101 for (std::set<std::string>::const_iterator iter = font_files.begin();
102 iter != font_files.end(); ++iter) {
103 FT_Face face;
104 CHECK_EQ(0, FT_New_Face(library, iter->c_str(), 0, &face))
105 << "Failed to load font face: " << *iter;
106 FT_Done_Face(face);
107 }
108 FT_Done_FreeType(library); // Cached stuff will stick around... ?
109 }
110
111 void ReleaseFonts() {
112 CHECK(g_config);
113 FcConfigDestroy(g_config);
114 FcFini();
115 }
116
117 } // namespace headless
OLDNEW
« headless/public/util/fontconfig.h ('K') | « headless/public/util/fontconfig.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698