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

Unified Diff: headless/public/util/fontconfig.cc

Issue 2877813002: [headless] Enable headless embedders to specify fonts. (Closed)
Patch Set: rebased 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « headless/public/util/fontconfig.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: headless/public/util/fontconfig.cc
diff --git a/headless/public/util/fontconfig.cc b/headless/public/util/fontconfig.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d859c19063f45d3daa05952e8b161fb2e5a62b2c
--- /dev/null
+++ b/headless/public/util/fontconfig.cc
@@ -0,0 +1,119 @@
+// Copyright 2017 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 "headless/public/util/fontconfig.h"
+
+// Should be included before freetype.h.
+#include <ft2build.h>
+
+#include <dirent.h>
+#include <fontconfig/fontconfig.h>
+#include <freetype/freetype.h>
+#include <set>
+#include <string>
+
+#include "base/logging.h"
+
+namespace headless {
+namespace {
+void GetFontFileNames(const FcFontSet* font_set,
+ std::set<std::string>* file_names) {
+ if (font_set == NULL)
+ return;
+ for (int i = 0; i < font_set->nfont; ++i) {
+ FcPattern* pattern = font_set->fonts[i];
+ FcValue font_file;
+ if (FcPatternGet(pattern, "file", 0, &font_file) == FcResultMatch) {
+ file_names->insert(reinterpret_cast<const char*>(font_file.u.s));
+ } else {
+ VLOG(1) << "Failed to find filename.";
+ FcPatternPrint(pattern);
+ }
+ }
+}
+FcConfig* g_config = nullptr;
+} // namespace
+
+void InitFonts(const char* fontconfig_path) {
+ // The following is roughly equivalent to calling FcInit(). We jump through
+ // a bunch of hoops here to avoid using fontconfig's directory scanning
+ // logic. The problem with fontconfig is that it follows symlinks when doing
+ // recursive directory scans.
+ //
+ // The approach below ignores any <dir>...</dir> entries in fonts.conf. This
+ // is deliberate. Specifying dirs is problematic because they're either
+ // absolute or relative to our process's current working directory which
+ // could be anything. Instead we assume that all font files will be in the
+ // same directory as fonts.conf. We'll scan + load them here.
+ FcConfig* config = FcConfigCreate();
+ g_config = config;
+ CHECK(config);
+
+ // FcConfigParseAndLoad is a seriously goofy function. Depending on whether
+ // name passed in begins with a slash, it will treat it either as a file name
+ // to be found in the directory where it expects to find the font
+ // configuration OR it will will treat it as a directory where it expects to
+ // find fonts.conf. The latter behavior is the one we want. Passing
+ // fontconfig_path via the environment is a quick and dirty way to get
+ // uniform behavior regardless whether it's a relative path or not.
+ setenv("FONTCONFIG_PATH", fontconfig_path, 1);
+ CHECK(FcConfigParseAndLoad(config, nullptr, FcTrue))
+ << "Failed to load font configuration. FONTCONFIG_PATH="
+ << fontconfig_path;
+
+ DIR* fc_dir = opendir(fontconfig_path);
+ CHECK(fc_dir) << "Failed to open font directory " << fontconfig_path << ": "
+ << strerror(errno);
+
+ // The fonts must be loaded in a consistent order. This makes rendered results
+ // stable across runs, otherwise replacement font picks are random
+ // and cause flakiness.
+ std::set<std::string> fonts;
+ struct dirent entry, *result;
+ while (readdir_r(fc_dir, &entry, &result) == 0 && result != NULL) {
+ fonts.insert(result->d_name);
+ }
+ for (const std::string& font : fonts) {
+ const std::string full_path = fontconfig_path + ("/" + font);
+ struct stat statbuf;
+ CHECK_EQ(0, stat(full_path.c_str(), &statbuf))
+ << "Failed to stat " << full_path << ": " << strerror(errno);
+ if (S_ISREG(statbuf.st_mode)) {
+ // FcConfigAppFontAddFile will silently ignore non-fonts.
+ FcConfigAppFontAddFile(
+ config, reinterpret_cast<const FcChar8*>(full_path.c_str()));
+ }
+ }
+ closedir(fc_dir);
+ CHECK(FcConfigSetCurrent(config));
+
+ // Retrieve font from both of fontconfig's font sets for pre-loading.
+ std::set<std::string> font_files;
+ GetFontFileNames(FcConfigGetFonts(NULL, FcSetSystem), &font_files);
+ GetFontFileNames(FcConfigGetFonts(NULL, FcSetApplication), &font_files);
+ CHECK_GT(font_files.size(), 0u)
+ << "Font configuration doesn't contain any fonts!";
+
+ // Get freetype to load every font file we know about. This will cause the
+ // font files to get cached in memory. Once that's done we shouldn't have to
+ // access the file system for fonts at all.
+ FT_Library library;
+ FT_Init_FreeType(&library);
+ for (std::set<std::string>::const_iterator iter = font_files.begin();
+ iter != font_files.end(); ++iter) {
+ FT_Face face;
+ CHECK_EQ(0, FT_New_Face(library, iter->c_str(), 0, &face))
+ << "Failed to load font face: " << *iter;
+ FT_Done_Face(face);
+ }
+ FT_Done_FreeType(library); // Cached stuff will stick around... ?
+}
+
+void ReleaseFonts() {
+ CHECK(g_config);
+ FcConfigDestroy(g_config);
+ FcFini();
+}
+
+} // namespace headless
« no previous file with comments | « headless/public/util/fontconfig.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698