blob: 12b268bbdb952bf1bef102d43dd470877fb68ead [file] [log] [blame]
// Copyright 2019 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/linux/fontconfig_util.h"
#include <fontconfig/fontconfig.h>
#include "base/no_destructor.h"
#include "ui/gfx/font_render_params.h"
namespace gfx {
namespace {
// A singleton class to wrap a global font-config configuration. The
// configuration reference counter is incremented to avoid the deletion of the
// structure while being used. This class is single-threaded and should only be
// used on the UI-Thread.
class GFX_EXPORT GlobalFontConfig {
public:
GlobalFontConfig() {
// Without this call, the FontConfig library gets implicitly initialized
// on the first call to FontConfig. Since it's not safe to initialize it
// concurrently from multiple threads, we explicitly initialize it here
// to prevent races when there are multiple renderer's querying the library:
// http://crbug.com/404311
// Note that future calls to FcInit() are safe no-ops per the FontConfig
// interface.
FcInit();
// Increment the reference counter to avoid the config to be deleted while
// being used (see http://crbug.com/1004254).
fc_config_ = FcConfigGetCurrent();
FcConfigReference(fc_config_);
// Set rescan interval to 0 to disable re-scan. Re-scanning in the
// background is a source of thread safety issues.
// See in http://crbug.com/1004254.
FcBool result = FcConfigSetRescanInterval(fc_config_, 0);
DCHECK_EQ(result, FcTrue);
}
~GlobalFontConfig() { FcConfigDestroy(fc_config_); }
// Retrieve the native font-config FcConfig pointer.
FcConfig* Get() const {
DCHECK_EQ(fc_config_, FcConfigGetCurrent());
return fc_config_;
}
// Override the font-config configuration.
void OverrideForTesting(FcConfig* config) {
FcConfigSetCurrent(config);
fc_config_ = config;
}
// Retrieve the global font-config configuration.
static GlobalFontConfig* GetInstance() {
static base::NoDestructor<GlobalFontConfig> fontconfig;
return fontconfig.get();
}
private:
FcConfig* fc_config_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(GlobalFontConfig);
};
// Converts Fontconfig FC_HINT_STYLE to FontRenderParams::Hinting.
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;
}
}
// Converts Fontconfig FC_RGBA to FontRenderParams::SubpixelRendering.
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;
}
}
// Extracts a string property from a font-config pattern (e.g. FcPattern).
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));
}
// Extracts an integer property from a font-config pattern (e.g. FcPattern).
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;
}
// Extracts an boolean property from a font-config pattern (e.g. FcPattern).
bool GetFontConfigPropertyAsBool(FcPattern* pattern, const char* property) {
FcBool value = FcFalse;
if (FcPatternGetBool(pattern, property, 0, &value) != FcResultMatch)
return false;
return value != FcFalse;
}
} // namespace
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);
// Obtains the system root directory in 'config' if available. All files
// (including file properties in patterns) obtained from this 'config' are
// relative to this system root directory.
const char* sysroot =
reinterpret_cast<const char*>(FcConfigGetSysRoot(nullptr));
if (!sysroot)
return base::FilePath(filename);
// Paths may be specified with a heading slash (e.g.
// /test_fonts/DejaVuSans.ttf).
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);
}
} // namespace gfx