#include "ui/gfx/linux/fontconfig_util.h"
#include <fontconfig/fontconfig.h>
#include "base/check_op.h"
#include "base/environment.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "ui/gfx/font_render_params.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "base/check_deref.h"
#include "base/containers/flat_set.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#endif
namespace features {
BASE_FEATURE(kFontConfigFontationsIndexing, base::FEATURE_ENABLED_BY_DEFAULT);
}
namespace gfx {
namespace {
#if BUILDFLAG(IS_CHROMEOS)
constexpr base::FilePath::CharType kGoogleSansVariablePath[] =
FILE_PATH_LITERAL("/usr/share/fonts/google-sans/variable");
constexpr base::FilePath::CharType kGoogleSansStaticPath[] =
FILE_PATH_LITERAL("/usr/share/fonts/google-sans/static");
constexpr base::FilePath::CharType kImageloaderMountBase[] =
FILE_PATH_LITERAL("/run/imageloader/");
#endif
class COMPONENT_EXPORT(GFX) GlobalFontConfig {
public:
GlobalFontConfig() {
if (base::FeatureList::IsEnabled(features::kFontConfigFontationsIndexing)) {
std::unique_ptr<base::Environment> environment =
base::Environment::Create();
environment->SetVar("FC_FONTATIONS", "1");
}
FcInit();
fc_config_ = FcConfigGetCurrent();
FcConfigReference(fc_config_);
#if BUILDFLAG(IS_CHROMEOS)
if (base::PathExists(base::FilePath(kGoogleSansVariablePath))) {
const FcChar8* kVariableFontPath =
reinterpret_cast<const FcChar8*>(kGoogleSansVariablePath);
CHECK(FcConfigAppFontAddDir(fc_config_, kVariableFontPath));
} else {
const FcChar8* kStaticFontPath =
reinterpret_cast<const FcChar8*>(kGoogleSansStaticPath);
CHECK(FcConfigAppFontAddDir(fc_config_, kStaticFontPath));
}
#endif
FcBool result = FcConfigSetRescanInterval(fc_config_, 0);
DCHECK_EQ(result, FcTrue);
}
GlobalFontConfig(const GlobalFontConfig&) = delete;
GlobalFontConfig& operator=(const GlobalFontConfig&) = delete;
~GlobalFontConfig() { FcConfigDestroy(fc_config_.ExtractAsDangling()); }
FcConfig* Get() const {
DCHECK_EQ(fc_config_, FcConfigGetCurrent());
return fc_config_;
}
#if BUILDFLAG(IS_CHROMEOS)
bool AddAppFontDir(const base::FilePath& dir) {
if (dir.ReferencesParent()) {
return false;
}
if (!base::FilePath(kImageloaderMountBase).IsParent(dir)) {
return false;
}
if (app_font_dirs_added_.contains(dir)) {
return false;
}
app_font_dirs_added_.insert(dir);
const FcChar8* dir_fcstring =
reinterpret_cast<const FcChar8*>(dir.value().c_str());
return FcConfigAppFontAddDir(fc_config_, dir_fcstring);
}
#endif
void OverrideForTesting(FcConfig* config) {
FcConfigSetCurrent(config);
fc_config_ = config;
}
static GlobalFontConfig* GetInstance() {
static base::NoDestructor<GlobalFontConfig> fontconfig;
return fontconfig.get();
}
private:
raw_ptr<FcConfig> fc_config_ = nullptr;
#if BUILDFLAG(IS_CHROMEOS)
base::flat_set<base::FilePath> app_font_dirs_added_;
#endif
};
FontRenderParams::Hinting ConvertFontconfigHintStyle(int hint_style) {
switch (hint_style) {
case FC_HINT_SLIGHT:
return FontRenderParams::HINTING_SLIGHT;
case FC_HINT_MEDIUM:
return FontRenderParams::HINTING_MEDIUM;
case FC_HINT_FULL:
return FontRenderParams::HINTING_FULL;
default:
return FontRenderParams::HINTING_NONE;
}
}
FontRenderParams::SubpixelRendering ConvertFontconfigRgba(int rgba) {
switch (rgba) {
case FC_RGBA_RGB:
return FontRenderParams::SUBPIXEL_RENDERING_RGB;
case FC_RGBA_BGR:
return FontRenderParams::SUBPIXEL_RENDERING_BGR;
case FC_RGBA_VRGB:
return FontRenderParams::SUBPIXEL_RENDERING_VRGB;
case FC_RGBA_VBGR:
return FontRenderParams::SUBPIXEL_RENDERING_VBGR;
default:
return FontRenderParams::SUBPIXEL_RENDERING_NONE;
}
}
std::string GetFontConfigPropertyAsString(FcPattern* pattern,
const char* property) {
FcChar8* text = nullptr;
if (FcPatternGetString(pattern, property, 0, &text) != FcResultMatch ||
text == nullptr) {
return std::string();
}
return std::string(reinterpret_cast<const char*>(text));
}
int GetFontConfigPropertyAsInt(FcPattern* pattern,
const char* property,
int default_value) {
int value = -1;
if (FcPatternGetInteger(pattern, property, 0, &value) != FcResultMatch) {
return default_value;
}
return value;
}
bool GetFontConfigPropertyAsBool(FcPattern* pattern, const char* property) {
FcBool value = FcFalse;
if (FcPatternGetBool(pattern, property, 0, &value) != FcResultMatch) {
return false;
}
return value != FcFalse;
}
}
void InitializeGlobalFontConfigAsync() {
if (base::ThreadPoolInstance::Get()) {
base::ThreadPool::PostTask(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce([]() { GlobalFontConfig::GetInstance(); }));
} else {
GlobalFontConfig::GetInstance();
}
}
FcConfig* GetGlobalFontConfig() {
return GlobalFontConfig::GetInstance()->Get();
}
void OverrideGlobalFontConfigForTesting(FcConfig* config) {
return GlobalFontConfig::GetInstance()->OverrideForTesting(config);
}
std::string GetFontName(FcPattern* pattern) {
return GetFontConfigPropertyAsString(pattern, FC_FAMILY);
}
std::string GetFilename(FcPattern* pattern) {
return GetFontConfigPropertyAsString(pattern, FC_FILE);
}
base::FilePath GetFontPath(FcPattern* pattern) {
std::string filename = GetFilename(pattern);
const char* sysroot =
reinterpret_cast<const char*>(FcConfigGetSysRoot(nullptr));
if (!sysroot) {
return base::FilePath(filename);
}
if (!filename.empty() && base::FilePath::IsSeparator(filename[0])) {
filename = filename.substr(1);
}
if (filename.empty()) {
return base::FilePath();
}
return base::FilePath(sysroot).Append(filename);
}
int GetFontTtcIndex(FcPattern* pattern) {
return GetFontConfigPropertyAsInt(pattern, FC_INDEX, 0);
}
bool IsFontBold(FcPattern* pattern) {
int weight = GetFontConfigPropertyAsInt(pattern, FC_WEIGHT, FC_WEIGHT_NORMAL);
return weight >= FC_WEIGHT_BOLD;
}
bool IsFontItalic(FcPattern* pattern) {
int slant = GetFontConfigPropertyAsInt(pattern, FC_SLANT, FC_SLANT_ROMAN);
return slant != FC_SLANT_ROMAN;
}
bool IsFontScalable(FcPattern* pattern) {
return GetFontConfigPropertyAsBool(pattern, FC_SCALABLE);
}
std::string GetFontFormat(FcPattern* pattern) {
return GetFontConfigPropertyAsString(pattern, FC_FONTFORMAT);
}
void GetFontRenderParamsFromFcPattern(FcPattern* pattern,
FontRenderParams* param_out) {
FcBool fc_antialias = 0;
if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &fc_antialias) ==
FcResultMatch) {
param_out->antialiasing = fc_antialias;
}
FcBool fc_autohint = 0;
if (FcPatternGetBool(pattern, FC_AUTOHINT, 0, &fc_autohint) ==
FcResultMatch) {
param_out->autohinter = fc_autohint;
}
FcBool fc_bitmap = 0;
if (FcPatternGetBool(pattern, FC_EMBEDDED_BITMAP, 0, &fc_bitmap) ==
FcResultMatch) {
param_out->use_bitmaps = fc_bitmap;
}
FcBool fc_hinting = 0;
if (FcPatternGetBool(pattern, FC_HINTING, 0, &fc_hinting) == FcResultMatch) {
int fc_hint_style = FC_HINT_NONE;
if (fc_hinting) {
FcPatternGetInteger(pattern, FC_HINT_STYLE, 0, &fc_hint_style);
}
param_out->hinting = ConvertFontconfigHintStyle(fc_hint_style);
}
int fc_rgba = FC_RGBA_NONE;
if (FcPatternGetInteger(pattern, FC_RGBA, 0, &fc_rgba) == FcResultMatch) {
param_out->subpixel_rendering = ConvertFontconfigRgba(fc_rgba);
}
}
#if BUILDFLAG(IS_CHROMEOS)
bool AddAppFontDir(const base::FilePath& dir) {
return CHECK_DEREF(GlobalFontConfig::GetInstance()).AddAppFontDir(dir);
}
#endif
}