gecko-dev/gfx/thebes/gfxFcPlatformFontList.cpp
John Daggett 1b7d8ac5fd Bug 1201318 - revise OSX system font handling. r=mstange
Rework system font lookup under OSX so that the style system passes around the same "meta" name used by Webkit, -apple-system. This makes it so that the hidden system fonts used aren't exposed to authors. It also allows us to handle the two optical sizes of font families used under OSX 10.11, which uses San Francisco as the new UI font, with one family for text sizes and another for larger, display sizes. The patch here:

  - moves system font lookup into gfxMacPlatformFontList
  - assigns the font family name to "-apple-system"
  - derives FindFamily to take a gfxFontStyle parameter to allow size-based lookups
  - maintains a list of hidden system font families (not exposed to authors)
  - maintains a mapping from -apple-system to the underlying hidden font families
2015-10-08 15:04:08 +09:00

1511 lines
51 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Logging.h"
#include "gfxFcPlatformFontList.h"
#include "gfxFont.h"
#include "gfxFontConstants.h"
#include "gfxFontFamilyList.h"
#include "gfxFT2Utils.h"
#include "gfxPlatform.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/TimeStamp.h"
#include "nsGkAtoms.h"
#include "nsILanguageAtomService.h"
#include "nsUnicodeProperties.h"
#include "nsUnicodeRange.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "mozilla/gfx/HelpersCairo.h"
#include <fontconfig/fcfreetype.h>
#ifdef MOZ_WIDGET_GTK
#include <gdk/gdk.h>
#endif
using namespace mozilla;
using namespace mozilla::unicode;
#ifndef FC_POSTSCRIPT_NAME
#define FC_POSTSCRIPT_NAME "postscriptname" /* String */
#endif
#define PRINTING_FC_PROPERTY "gfx.printing"
#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
LogLevel::Debug, args)
#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
gfxPlatform::GetLog(eGfxLog_fontlist), \
LogLevel::Debug)
#define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \
gfxPlatform::GetLog(eGfxLog_cmapdata), \
LogLevel::Debug)
static const FcChar8*
ToFcChar8Ptr(const char* aStr)
{
return reinterpret_cast<const FcChar8*>(aStr);
}
static const char*
ToCharPtr(const FcChar8 *aStr)
{
return reinterpret_cast<const char*>(aStr);
}
FT_Library gfxFcPlatformFontList::sCairoFTLibrary = nullptr;
static cairo_user_data_key_t sFcFontlistUserFontDataKey;
// canonical name ==> first en name or first name if no en name
// This is the required logic for fullname lookups as per CSS3 Fonts spec.
static uint32_t
FindCanonicalNameIndex(FcPattern* aFont, const char* aLangField)
{
uint32_t n = 0, en = 0;
FcChar8* lang;
while (FcPatternGetString(aFont, aLangField, n, &lang) == FcResultMatch) {
// look for 'en' or variants, en-US, en-JP etc.
uint32_t len = strlen(ToCharPtr(lang));
bool enPrefix = (strncmp(ToCharPtr(lang), "en", 2) == 0);
if (enPrefix && (len == 2 || (len > 2 && aLangField[2] == '-'))) {
en = n;
break;
}
n++;
}
return en;
}
static void
GetFaceNames(FcPattern* aFont, const nsAString& aFamilyName,
nsAString& aPostscriptName, nsAString& aFullname)
{
// get the Postscript name
FcChar8* psname;
if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) == FcResultMatch) {
AppendUTF8toUTF16(ToCharPtr(psname), aPostscriptName);
}
// get the canonical fullname (i.e. en name or first name)
uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG);
FcChar8* fullname;
if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) {
AppendUTF8toUTF16(ToCharPtr(fullname), aFullname);
}
// if have fullname, done
if (!aFullname.IsEmpty()) {
return;
}
// otherwise, set the fullname to family + style name [en] and use that
aFullname.Append(aFamilyName);
// figure out the en style name
en = FindCanonicalNameIndex(aFont, FC_STYLELANG);
nsAutoString style;
FcChar8* stylename = nullptr;
FcPatternGetString(aFont, FC_STYLE, en, &stylename);
if (stylename) {
AppendUTF8toUTF16(ToCharPtr(stylename), style);
}
if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) {
aFullname.Append(' ');
aFullname.Append(style);
}
}
static uint16_t
MapFcWeight(int aFcWeight)
{
if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) {
return 100;
} else if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
return 200;
} else if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) {
return 300;
} else if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
// This includes FC_WEIGHT_BOOK
return 400;
} else if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
return 500;
} else if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
return 600;
} else if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
return 700;
} else if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
return 800;
} else if (aFcWeight <= FC_WEIGHT_BLACK) {
return 900;
}
// including FC_WEIGHT_EXTRABLACK
return 901;
}
static int16_t
MapFcWidth(int aFcWidth)
{
if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
return NS_FONT_STRETCH_ULTRA_CONDENSED;
}
if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
return NS_FONT_STRETCH_EXTRA_CONDENSED;
}
if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
return NS_FONT_STRETCH_CONDENSED;
}
if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
return NS_FONT_STRETCH_SEMI_CONDENSED;
}
if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
return NS_FONT_STRETCH_NORMAL;
}
if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
return NS_FONT_STRETCH_SEMI_EXPANDED;
}
if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
return NS_FONT_STRETCH_EXPANDED;
}
if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
return NS_FONT_STRETCH_EXTRA_EXPANDED;
}
return NS_FONT_STRETCH_ULTRA_EXPANDED;
}
// mapping of moz lang groups ==> default lang
struct MozLangGroupData {
nsIAtom* const& mozLangGroup;
const char *defaultLang;
};
const MozLangGroupData MozLangGroups[] = {
{ nsGkAtoms::x_western, "en" },
{ nsGkAtoms::x_cyrillic, "ru" },
{ nsGkAtoms::x_devanagari, "hi" },
{ nsGkAtoms::x_tamil, "ta" },
{ nsGkAtoms::x_armn, "hy" },
{ nsGkAtoms::x_beng, "bn" },
{ nsGkAtoms::x_cans, "iu" },
{ nsGkAtoms::x_ethi, "am" },
{ nsGkAtoms::x_geor, "ka" },
{ nsGkAtoms::x_gujr, "gu" },
{ nsGkAtoms::x_guru, "pa" },
{ nsGkAtoms::x_khmr, "km" },
{ nsGkAtoms::x_knda, "kn" },
{ nsGkAtoms::x_mlym, "ml" },
{ nsGkAtoms::x_orya, "or" },
{ nsGkAtoms::x_sinh, "si" },
{ nsGkAtoms::x_tamil, "ta" },
{ nsGkAtoms::x_telu, "te" },
{ nsGkAtoms::x_tibt, "bo" },
{ nsGkAtoms::Unicode, 0 }
};
static void
GetSampleLangForGroup(nsIAtom* aLanguage, nsACString& aLangStr)
{
aLangStr.Truncate();
if (aLanguage) {
// set up lang string
const MozLangGroupData *mozLangGroup = nullptr;
// -- look it up in the list of moz lang groups
for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
if (aLanguage == MozLangGroups[i].mozLangGroup) {
mozLangGroup = &MozLangGroups[i];
break;
}
}
// xxx - Is this sufficient? The code in
// gfxFontconfigUtils::GetSampleLangForGroup has logic for sniffing the
// LANGUAGE environment to try and map langGroup ==> closest user language
// but I'm guessing that's not really all that useful. For now, just use
// the default lang mapping.
// -- get the BCP47 string representation of the lang group
if (mozLangGroup) {
if (mozLangGroup->defaultLang) {
aLangStr.Assign(mozLangGroup->defaultLang);
}
} else {
// Not a special mozilla language group.
// Use aLangGroup as a language code.
aLanguage->ToUTF8String(aLangStr);
}
}
}
gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
FcPattern* aFontPattern)
: gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
mFTFace(nullptr), mFTFaceInitialized(false),
mAspect(0.0), mFontData(nullptr)
{
// italic
int slant;
if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
slant = FC_SLANT_ROMAN;
}
if (slant > 0) {
mItalic = true;
}
// weight
int weight;
if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) != FcResultMatch) {
weight = FC_WEIGHT_REGULAR;
}
mWeight = MapFcWeight(weight);
// width
int width;
if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
width = FC_WIDTH_NORMAL;
}
mStretch = MapFcWidth(width);
}
gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
uint16_t aWeight,
int16_t aStretch,
bool aItalic,
const uint8_t *aData,
FT_Face aFace)
: gfxFontEntry(aFaceName),
mFTFace(aFace), mFTFaceInitialized(true),
mAspect(0.0), mFontData(aData)
{
mWeight = aWeight;
mItalic = aItalic;
mStretch = aStretch;
mIsDataUserFont = true;
// Use fontconfig to fill out the pattern from the FTFace.
// The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
// least). The dummy file passed here is removed below.
//
// When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
// is passed as the "blanks" argument, which provides that unexpectedly
// blank glyphs are elided. Here, however, we pass nullptr for
// "blanks", effectively assuming that, if the font has a blank glyph,
// then the author intends any associated character to be rendered
// blank.
mFontPattern = FcFreeTypeQueryFace(mFTFace, ToFcChar8Ptr(""), 0, nullptr);
// given that we have a FT_Face, not really sure this is possible...
if (!mFontPattern) {
mFontPattern = FcPatternCreate();
}
FcPatternDel(mFontPattern, FC_FILE);
FcPatternDel(mFontPattern, FC_INDEX);
// Make a new pattern and store the face in it so that cairo uses
// that when creating a cairo font face.
FcPatternAddFTFace(mFontPattern, FC_FT_FACE, mFTFace);
mUserFontData = new FTUserFontData(mFTFace, mFontData);
}
gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
FcPattern* aFontPattern,
uint16_t aWeight,
int16_t aStretch,
bool aItalic)
: gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
mFTFace(nullptr), mFTFaceInitialized(false),
mAspect(0.0), mFontData(nullptr)
{
mWeight = aWeight;
mItalic = aItalic;
mStretch = aStretch;
mIsLocalUserFont = true;
}
gfxFontconfigFontEntry::~gfxFontconfigFontEntry()
{
}
bool
gfxFontconfigFontEntry::SupportsLangGroup(nsIAtom *aLangGroup) const
{
if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
return true;
}
nsAutoCString fcLang;
GetSampleLangForGroup(aLangGroup, fcLang);
if (fcLang.IsEmpty()) {
return true;
}
// is lang included in the underlying pattern?
FcLangSet *langset;
if (FcPatternGetLangSet(mFontPattern, FC_LANG, 0, &langset) != FcResultMatch) {
return false;
}
if (FcLangSetHasLang(langset, (FcChar8 *)fcLang.get()) != FcLangDifferentLang) {
return true;
}
return false;
}
nsresult
gfxFontconfigFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
{
// attempt this once, if errors occur leave a blank cmap
if (mCharacterMap) {
return NS_OK;
}
nsRefPtr<gfxCharacterMap> charmap;
nsresult rv;
bool symbolFont = false; // currently ignored
if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
mUVSOffset,
symbolFont))) {
rv = NS_OK;
} else {
uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
charmap = new gfxCharacterMap();
AutoTable cmapTable(this, kCMAP);
if (cmapTable) {
bool unicodeFont = false; // currently ignored
uint32_t cmapLen;
const uint8_t* cmapData =
reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
&cmapLen));
rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
*charmap, mUVSOffset,
unicodeFont, symbolFont);
} else {
rv = NS_ERROR_NOT_AVAILABLE;
}
}
mHasCmapTable = NS_SUCCEEDED(rv);
if (mHasCmapTable) {
gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
mCharacterMap = pfl->FindCharMap(charmap);
} else {
// if error occurred, initialize to null cmap
mCharacterMap = new gfxCharacterMap();
}
LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
NS_ConvertUTF16toUTF8(mName).get(),
charmap->SizeOfIncludingThis(moz_malloc_size_of),
charmap->mHash, mCharacterMap == charmap ? " new" : ""));
if (LOG_CMAPDATA_ENABLED()) {
char prefix[256];
sprintf(prefix, "(cmapdata) name: %.220s",
NS_ConvertUTF16toUTF8(mName).get());
charmap->Dump(prefix, eGfxLog_cmapdata);
}
return rv;
}
static bool
HasChar(FcPattern *aFont, FcChar32 aCh)
{
FcCharSet *charset = nullptr;
FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
return charset && FcCharSetHasChar(charset, aCh);
}
bool
gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh)
{
// for system fonts, use the charmap in the pattern
if (!mIsDataUserFont) {
return HasChar(mFontPattern, aCh);
}
return gfxFontEntry::TestCharacterMap(aCh);
}
hb_blob_t*
gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag)
{
// for data fonts, read directly from the font data
if (mFontData) {
return GetTableFromFontData(mFontData, aTableTag);
}
return gfxFontEntry::GetFontTable(aTableTag);
}
void
gfxFontconfigFontEntry::MaybeReleaseFTFace()
{
// don't release if either HB or Gr face still exists
if (mHBFace || mGrFace) {
return;
}
// only close out FT_Face for system fonts, not for data fonts
if (!mIsDataUserFont) {
if (mFTFace) {
FT_Done_Face(mFTFace);
mFTFace = nullptr;
}
mFTFaceInitialized = false;
}
}
void
gfxFontconfigFontEntry::ForgetHBFace()
{
gfxFontEntry::ForgetHBFace();
MaybeReleaseFTFace();
}
void
gfxFontconfigFontEntry::ReleaseGrFace(gr_face* aFace)
{
gfxFontEntry::ReleaseGrFace(aFace);
MaybeReleaseFTFace();
}
double
gfxFontconfigFontEntry::GetAspect()
{
if (mAspect == 0.0) {
// default to aspect = 0.5
mAspect = 0.5;
// create a font to calculate x-height / em-height
gfxFontStyle s;
s.size = 100.0; // pick large size to avoid possible hinting artifacts
nsRefPtr<gfxFont> font = FindOrMakeFont(&s, false);
if (font) {
const gfxFont::Metrics& metrics =
font->GetMetrics(gfxFont::eHorizontal);
// The factor of 0.1 ensures that xHeight is sane so fonts don't
// become huge. Strictly ">" ensures that xHeight and emHeight are
// not both zero.
if (metrics.xHeight > 0.1 * metrics.emHeight) {
mAspect = metrics.xHeight / metrics.emHeight;
}
}
}
return mAspect;
}
static void
PrepareFontOptions(FcPattern* aPattern,
cairo_font_options_t* aFontOptions)
{
NS_ASSERTION(aFontOptions, "null font options passed to PrepareFontOptions");
// xxx - taken from the gfxFontconfigFonts code, needs to be reviewed
FcBool printing;
if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) !=
FcResultMatch) {
printing = FcFalse;
}
// Font options are set explicitly here to improve cairo's caching
// behavior and to record the relevant parts of the pattern for
// SetupCairoFont (so that the pattern can be released).
//
// Most font_options have already been set as defaults on the FcPattern
// with cairo_ft_font_options_substitute(), then user and system
// fontconfig configurations were applied. The resulting font_options
// have been recorded on the face during
// cairo_ft_font_face_create_for_pattern().
//
// None of the settings here cause this scaled_font to behave any
// differently from how it would behave if it were created from the same
// face with default font_options.
//
// We set options explicitly so that the same scaled_font will be found in
// the cairo_scaled_font_map when cairo loads glyphs from a context with
// the same font_face, font_matrix, ctm, and surface font_options.
//
// Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
// font_options on the cairo_ft_font_face, and doesn't consider default
// option values to not match any explicit values.
//
// Even after cairo_set_scaled_font is used to set font_options for the
// cairo context, when cairo looks for a scaled_font for the context, it
// will look for a font with some option values from the target surface if
// any values are left default on the context font_options. If this
// scaled_font is created with default font_options, cairo will not find
// it.
//
// The one option not recorded in the pattern is hint_metrics, which will
// affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
// We should be considering the font_options of the surface on which this
// font will be used, but currently we don't have different gfxFonts for
// different surface font_options, so we'll create a font suitable for the
// Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
if (printing) {
cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_OFF);
} else {
cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_ON);
}
// The remaining options have been recorded on the pattern and the face.
// _cairo_ft_options_merge has some logic to decide which options from the
// scaled_font or from the cairo_ft_font_face take priority in the way the
// font behaves.
//
// In the majority of cases, _cairo_ft_options_merge uses the options from
// the cairo_ft_font_face, so sometimes it is not so important which
// values are set here so long as they are not defaults, but we'll set
// them to the exact values that we expect from the font, to be consistent
// and to protect against changes in cairo.
//
// In some cases, _cairo_ft_options_merge uses some options from the
// scaled_font's font_options rather than options on the
// cairo_ft_font_face (from fontconfig).
// https://bugs.freedesktop.org/show_bug.cgi?id=11838
//
// Surface font options were set on the pattern in
// cairo_ft_font_options_substitute. If fontconfig has changed the
// hint_style then that is what the user (or distribution) wants, so we
// use the setting from the FcPattern.
//
// Fallback values here mirror treatment of defaults in cairo-ft-font.c.
FcBool hinting = FcFalse;
if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
hinting = FcTrue;
}
cairo_hint_style_t hint_style;
if (printing || !hinting) {
hint_style = CAIRO_HINT_STYLE_NONE;
} else {
int fc_hintstyle;
if (FcPatternGetInteger(aPattern, FC_HINT_STYLE,
0, &fc_hintstyle) != FcResultMatch) {
fc_hintstyle = FC_HINT_FULL;
}
switch (fc_hintstyle) {
case FC_HINT_NONE:
hint_style = CAIRO_HINT_STYLE_NONE;
break;
case FC_HINT_SLIGHT:
hint_style = CAIRO_HINT_STYLE_SLIGHT;
break;
case FC_HINT_MEDIUM:
default: // This fallback mirrors _get_pattern_ft_options in cairo.
hint_style = CAIRO_HINT_STYLE_MEDIUM;
break;
case FC_HINT_FULL:
hint_style = CAIRO_HINT_STYLE_FULL;
break;
}
}
cairo_font_options_set_hint_style(aFontOptions, hint_style);
int rgba;
if (FcPatternGetInteger(aPattern,
FC_RGBA, 0, &rgba) != FcResultMatch) {
rgba = FC_RGBA_UNKNOWN;
}
cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
switch (rgba) {
case FC_RGBA_UNKNOWN:
case FC_RGBA_NONE:
default:
// There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing
// is disabled through cairo_antialias_t.
rgba = FC_RGBA_NONE;
// subpixel_order won't be used by the font as we won't use
// CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for
// caching reasons described above. Fall through:
case FC_RGBA_RGB:
subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
break;
case FC_RGBA_BGR:
subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
break;
case FC_RGBA_VRGB:
subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
break;
case FC_RGBA_VBGR:
subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
break;
}
cairo_font_options_set_subpixel_order(aFontOptions, subpixel_order);
FcBool fc_antialias;
if (FcPatternGetBool(aPattern,
FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) {
fc_antialias = FcTrue;
}
cairo_antialias_t antialias;
if (!fc_antialias) {
antialias = CAIRO_ANTIALIAS_NONE;
} else if (rgba == FC_RGBA_NONE) {
antialias = CAIRO_ANTIALIAS_GRAY;
} else {
antialias = CAIRO_ANTIALIAS_SUBPIXEL;
}
cairo_font_options_set_antialias(aFontOptions, antialias);
}
cairo_scaled_font_t*
gfxFontconfigFontEntry::CreateScaledFont(FcPattern* aRenderPattern,
const gfxFontStyle *aStyle,
bool aNeedsBold)
{
if (aNeedsBold) {
FcPatternAddBool(aRenderPattern, FC_EMBOLDEN, FcTrue);
}
// synthetic oblique by skewing via the font matrix
bool needsOblique = !IsItalic() &&
(aStyle->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) &&
aStyle->allowSyntheticStyle;
if (needsOblique) {
// disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP);
FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
}
cairo_font_face_t *face =
cairo_ft_font_face_create_for_pattern(aRenderPattern);
if (mFontData) {
// for data fonts, add the face/data pointer to the cairo font face
// so that it gets deleted whenever cairo decides
NS_ASSERTION(mFTFace, "FT_Face is null when setting user data");
NS_ASSERTION(mUserFontData, "user font data is null when setting user data");
cairo_font_face_set_user_data(face,
&sFcFontlistUserFontDataKey,
new FTUserFontDataRef(mUserFontData),
FTUserFontDataRef::Destroy);
}
cairo_scaled_font_t *scaledFont = nullptr;
cairo_matrix_t sizeMatrix;
cairo_matrix_t identityMatrix;
double adjustedSize = aStyle->size;
if (aStyle->sizeAdjust >= 0.0) {
adjustedSize = aStyle->GetAdjustedSize(GetAspect());
}
cairo_matrix_init_scale(&sizeMatrix, adjustedSize, adjustedSize);
cairo_matrix_init_identity(&identityMatrix);
if (needsOblique) {
const double kSkewFactor = OBLIQUE_SKEW_FACTOR;
cairo_matrix_t style;
cairo_matrix_init(&style,
1, //xx
0, //yx
-1 * kSkewFactor, //xy
1, //yy
0, //x0
0); //y0
cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
}
cairo_font_options_t *fontOptions = cairo_font_options_create();
PrepareFontOptions(aRenderPattern, fontOptions);
scaledFont = cairo_scaled_font_create(face, &sizeMatrix,
&identityMatrix, fontOptions);
cairo_font_options_destroy(fontOptions);
NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
"Failed to make scaled font");
cairo_font_face_destroy(face);
return scaledFont;
}
#ifdef MOZ_WIDGET_GTK
// defintion included below
static void ApplyGdkScreenFontOptions(FcPattern *aPattern);
#endif
static void
PreparePattern(FcPattern* aPattern, bool aIsPrinterFont)
{
FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
// This gets cairo_font_options_t for the Screen. We should have
// different font options for printing (no hinting) but we are not told
// what we are measuring for.
//
// If cairo adds support for lcd_filter, gdk will not provide the default
// setting for that option. We could get the default setting by creating
// an xlib surface once, recording its font_options, and then merging the
// gdk options.
//
// Using an xlib surface would also be an option to get Screen font
// options for non-GTK X11 toolkits, but less efficient than using GDK to
// pick up dynamic changes.
if(aIsPrinterFont) {
cairo_font_options_t *options = cairo_font_options_create();
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
cairo_ft_font_options_substitute(options, aPattern);
cairo_font_options_destroy(options);
FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
} else {
#ifdef MOZ_WIDGET_GTK
ApplyGdkScreenFontOptions(aPattern);
#endif
}
FcDefaultSubstitute(aPattern);
}
gfxFont*
gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle,
bool aNeedsBold)
{
nsAutoRef<FcPattern> pattern(FcPatternCreate());
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aFontStyle->size);
PreparePattern(pattern, aFontStyle->printerFont);
nsAutoRef<FcPattern> renderPattern
(FcFontRenderPrepare(nullptr, pattern, mFontPattern));
cairo_scaled_font_t* scaledFont =
CreateScaledFont(renderPattern, aFontStyle, aNeedsBold);
gfxFont* newFont =
new gfxFontconfigFont(scaledFont, this, aFontStyle, aNeedsBold);
cairo_scaled_font_destroy(scaledFont);
return newFont;
}
nsresult
gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
FallibleTArray<uint8_t>& aBuffer)
{
NS_ASSERTION(!mIsDataUserFont,
"data fonts should be reading tables directly from memory");
if (!mFTFaceInitialized) {
mFTFaceInitialized = true;
FcChar8 *filename;
if (FcPatternGetString(mFontPattern, FC_FILE, 0, &filename) != FcResultMatch) {
return NS_ERROR_FAILURE;
}
int index;
if (FcPatternGetInteger(mFontPattern, FC_INDEX, 0, &index) != FcResultMatch) {
index = 0; // default to 0 if not found in pattern
}
if (FT_New_Face(gfxFcPlatformFontList::GetFTLibrary(),
(const char*)filename, index, &mFTFace) != 0) {
return NS_ERROR_FAILURE;
}
}
if (!mFTFace) {
return NS_ERROR_NOT_AVAILABLE;
}
FT_ULong length = 0;
if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, nullptr, &length) != 0) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!aBuffer.SetLength(length, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
aBuffer.Clear();
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void
gfxFontconfigFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
{
if (mHasStyles) {
return;
}
// add font entries for each of the faces
uint32_t numFonts = mFontPatterns.Length();
NS_ASSERTION(numFonts, "font family containing no faces!!");
for (uint32_t i = 0; i < numFonts; i++) {
FcPattern* face = mFontPatterns[i];
// figure out the psname/fullname and choose which to use as the facename
nsAutoString psname, fullname;
GetFaceNames(face, mName, psname, fullname);
const nsAutoString& faceName = !psname.IsEmpty() ? psname : fullname;
gfxFontconfigFontEntry *fontEntry =
new gfxFontconfigFontEntry(faceName, face);
AddFontEntry(fontEntry);
if (LOG_FONTLIST_ENABLED()) {
LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
" with style: %s weight: %d stretch: %d"
" psname: %s fullname: %s",
NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
NS_ConvertUTF16toUTF8(Name()).get(),
fontEntry->IsItalic() ? "italic" : "normal",
fontEntry->Weight(), fontEntry->Stretch(),
NS_ConvertUTF16toUTF8(psname).get(),
NS_ConvertUTF16toUTF8(fullname).get()));
}
}
mFaceNamesInitialized = true;
mFontPatterns.Clear();
SetHasStyles(true);
}
void
gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern)
{
NS_ASSERTION(!mHasStyles,
"font patterns must not be added to already enumerated families");
nsCountedRef<FcPattern> pattern(aFontPattern);
mFontPatterns.AppendElement(pattern);
}
gfxFontconfigFont::gfxFontconfigFont(cairo_scaled_font_t *aScaledFont,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold) :
gfxFT2FontBase(aScaledFont, aFontEntry, aFontStyle)
{
}
gfxFontconfigFont::~gfxFontconfigFont()
{
}
#ifdef USE_SKIA
already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
gfxFontconfigFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
{
cairo_scaled_font_t *scaled_font = CairoScaledFont();
cairo_font_options_t *options = cairo_font_options_create();
cairo_scaled_font_get_font_options(scaled_font, options);
cairo_hint_style_t hint_style = cairo_font_options_get_hint_style(options);
cairo_antialias_t antialias = cairo_font_options_get_antialias(options);
cairo_font_options_destroy(options);
mozilla::gfx::FontHinting hinting =
mozilla::gfx::CairoHintingToGfxHinting(hint_style);
mozilla::gfx::AntialiasMode aaMode =
mozilla::gfx::CairoAntialiasToGfxAntialias(antialias);
// We don't want to force the use of the autohinter over the font's built in hints
// The fontconfig AA mode must be passed along because it may override the hinting style.
return mozilla::gfx::Factory::CreateCairoGlyphRenderingOptions(hinting, false, aaMode);
}
#endif
gfxFcPlatformFontList::gfxFcPlatformFontList()
: mLocalNames(64)
, mGenericMappings(32)
, mFcSubstituteCache(64)
, mLastConfig(nullptr)
{
// if the rescan interval is set, start the timer
int rescanInterval = FcConfigGetRescanInterval(nullptr);
if (rescanInterval) {
mLastConfig = FcConfigGetCurrent();
mCheckFontUpdatesTimer = do_CreateInstance("@mozilla.org/timer;1");
if (mCheckFontUpdatesTimer) {
mCheckFontUpdatesTimer->
InitWithFuncCallback(CheckFontUpdates, this,
(rescanInterval + 1) * 1000,
nsITimer::TYPE_REPEATING_SLACK);
} else {
NS_WARNING("Failure to create font updates timer");
}
}
#ifdef MOZ_BUNDLED_FONTS
mBundledFontsInitialized = false;
#endif
}
gfxFcPlatformFontList::~gfxFcPlatformFontList()
{
if (mCheckFontUpdatesTimer) {
mCheckFontUpdatesTimer->Cancel();
mCheckFontUpdatesTimer = nullptr;
}
}
void
gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet)
{
// This iterates over the fonts in a font set and adds in gfxFontFamily
// objects for each family. The patterns for individual fonts are not
// copied here. When a family is actually used, the fonts in the family
// are enumerated and the patterns copied. Note that we're explicitly
// excluding non-scalable fonts such as X11 bitmap fonts, which
// Chrome Skia/Webkit code does also.
if (!aFontSet) {
NS_WARNING("AddFontSetFamilies called with a null font set.");
return;
}
FcChar8* lastFamilyName = (FcChar8*)"";
gfxFontFamily* fontFamily = nullptr;
nsAutoString familyName;
for (int f = 0; f < aFontSet->nfont; f++) {
FcPattern* font = aFontSet->fonts[f];
// not scalable? skip...
FcBool scalable;
if (FcPatternGetBool(font, FC_SCALABLE, 0, &scalable) != FcResultMatch ||
!scalable) {
continue;
}
// get canonical name
uint32_t cIndex = FindCanonicalNameIndex(font, FC_FAMILYLANG);
FcChar8* canonical = nullptr;
FcPatternGetString(font, FC_FAMILY, cIndex, &canonical);
if (!canonical) {
continue;
}
// same as the last one? no need to add a new family, skip
if (FcStrCmp(canonical, lastFamilyName) != 0) {
lastFamilyName = canonical;
// add new family if one doesn't already exist
familyName.Truncate();
AppendUTF8toUTF16(ToCharPtr(canonical), familyName);
nsAutoString keyName(familyName);
ToLowerCase(keyName);
fontFamily = mFontFamilies.GetWeak(keyName);
if (!fontFamily) {
fontFamily = new gfxFontconfigFontFamily(familyName);
mFontFamilies.Put(keyName, fontFamily);
}
// Add pointers to other localized family names. Most fonts
// only have a single name, so the first call to GetString
// will usually not match
FcChar8* otherName;
int n = (cIndex == 0 ? 1 : 0);
while (FcPatternGetString(font, FC_FAMILY, n, &otherName) == FcResultMatch) {
NS_ConvertUTF8toUTF16 otherFamilyName(ToCharPtr(otherName));
AddOtherFamilyName(fontFamily, otherFamilyName);
n++;
if (n == int(cIndex)) {
n++; // skip over canonical name
}
}
}
NS_ASSERTION(fontFamily, "font must belong to a font family");
gfxFontconfigFontFamily* fcFamily =
static_cast<gfxFontconfigFontFamily*>(fontFamily);
fcFamily->AddFontPattern(font);
// map the psname, fullname ==> font family for local font lookups
nsAutoString psname, fullname;
GetFaceNames(font, familyName, psname, fullname);
if (!psname.IsEmpty()) {
ToLowerCase(psname);
mLocalNames.Put(psname, font);
}
if (!fullname.IsEmpty()) {
ToLowerCase(fullname);
mLocalNames.Put(fullname, font);
}
}
}
nsresult
gfxFcPlatformFontList::InitFontList()
{
mLastConfig = FcConfigGetCurrent();
// reset font lists
gfxPlatformFontList::InitFontList();
mLocalNames.Clear();
mGenericMappings.Clear();
mFcSubstituteCache.Clear();
// iterate over available fonts
FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
AddFontSetFamilies(systemFonts);
#ifdef MOZ_BUNDLED_FONTS
ActivateBundledFonts();
FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
AddFontSetFamilies(appFonts);
#endif
mOtherFamilyNamesInitialized = true;
return NS_OK;
}
// For displaying the fontlist in UI, use explicit call to FcFontList. Using
// FcFontList results in the list containing the localized names as dictated
// by system defaults.
static void
GetSystemFontList(nsTArray<nsString>& aListOfFonts, nsIAtom *aLangGroup)
{
aListOfFonts.Clear();
nsAutoRef<FcPattern> pat(FcPatternCreate());
if (!pat) {
return;
}
nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
if (!os) {
return;
}
// add the lang to the pattern
nsAutoCString fcLang;
GetSampleLangForGroup(aLangGroup, fcLang);
if (!fcLang.IsEmpty()) {
FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get()));
}
// ignore size-specific fonts
FcPatternAddBool(pat, FC_SCALABLE, FcTrue);
nsAutoRef<FcFontSet> fs(FcFontList(nullptr, pat, os));
if (!fs) {
return;
}
for (int i = 0; i < fs->nfont; i++) {
char *family;
if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
(FcChar8 **) &family) != FcResultMatch)
{
continue;
}
// Remove duplicates...
nsAutoString strFamily;
AppendUTF8toUTF16(family, strFamily);
if (aListOfFonts.Contains(strFamily)) {
continue;
}
aListOfFonts.AppendElement(strFamily);
}
aListOfFonts.Sort();
}
void
gfxFcPlatformFontList::GetFontList(nsIAtom *aLangGroup,
const nsACString& aGenericFamily,
nsTArray<nsString>& aListOfFonts)
{
// Get the list of font family names using fontconfig
GetSystemFontList(aListOfFonts, aLangGroup);
// Under Linux, the generics "serif", "sans-serif" and "monospace"
// are included in the pref fontlist. These map to whatever fontconfig
// decides they should be for a given language, rather than one of the
// fonts listed in the prefs font lists (e.g. font.name.*, font.name-list.*)
bool serif = false, sansSerif = false, monospace = false;
if (aGenericFamily.IsEmpty())
serif = sansSerif = monospace = true;
else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
serif = true;
else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
sansSerif = true;
else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
monospace = true;
else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
serif = sansSerif = true;
else
NS_NOTREACHED("unexpected CSS generic font family");
// The first in the list becomes the default in
// FontBuilder.readFontSelection() if the preference-selected font is not
// available, so put system configured defaults first.
if (monospace)
aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace"));
if (sansSerif)
aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif"));
if (serif)
aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif"));
}
gfxFontFamily*
gfxFcPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
{
// Get the default font by using a fake name to retrieve the first
// scalable font that fontconfig suggests for the given language.
return FindGenericFamily(NS_LITERAL_STRING("-moz-default"),
aStyle->language);
}
gfxFontEntry*
gfxFcPlatformFontList::LookupLocalFont(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
bool aItalic)
{
nsAutoString keyName(aFontName);
ToLowerCase(keyName);
// if name is not in the global list, done
FcPattern* fontPattern = mLocalNames.Get(keyName);
if (!fontPattern) {
return nullptr;
}
return new gfxFontconfigFontEntry(aFontName,
fontPattern,
aWeight, aStretch, aItalic);
}
gfxFontEntry*
gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
bool aItalic,
const uint8_t* aFontData,
uint32_t aLength)
{
FT_Face face;
FT_Error error =
FT_New_Memory_Face(gfxFcPlatformFontList::GetFTLibrary(),
aFontData, aLength, 0, &face);
if (error != FT_Err_Ok) {
NS_Free((void*)aFontData);
return nullptr;
}
if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
FT_Done_Face(face);
NS_Free((void*)aFontData);
return nullptr;
}
return new gfxFontconfigFontEntry(aFontName, aWeight, aStretch, aItalic,
aFontData, face);
}
gfxFontFamily*
gfxFcPlatformFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle)
{
nsAutoString familyName(aFamily);
ToLowerCase(familyName);
nsIAtom* language = (aStyle ? aStyle->language.get() : nullptr);
// deprecated generic names are explicitly converted to standard generics
bool isDeprecatedGeneric = false;
if (familyName.EqualsLiteral("sans") ||
familyName.EqualsLiteral("sans serif")) {
familyName.AssignLiteral("sans-serif");
isDeprecatedGeneric = true;
} else if (familyName.EqualsLiteral("mono")) {
familyName.AssignLiteral("monospace");
isDeprecatedGeneric = true;
}
// fontconfig generics? use fontconfig to determine the family for lang
if (isDeprecatedGeneric ||
mozilla::FontFamilyName::Convert(familyName).IsGeneric()) {
return FindGenericFamily(familyName, language);
}
// fontconfig allows conditional substitutions in such a way that it's
// difficult to distinguish an explicit substitution from other suggested
// choices. To sniff out explicit substitutions, compare the substitutions
// for "font, -moz-sentinel" to "-moz-sentinel" to sniff out the
// substitutions
//
// Example:
//
// serif ==> DejaVu Serif, ...
// Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu Serif
//
// In this case fontconfig is including Tex Gyre Heros and
// Nimbus Sans L as alternatives for Helvetica.
// Because the FcConfigSubstitute call is quite expensive, we cache the
// actual font family found via this process. So check the cache first:
NS_ConvertUTF16toUTF8 familyToFind(familyName);
gfxFontFamily* cached = mFcSubstituteCache.GetWeak(familyToFind);
if (cached) {
return cached;
}
const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
FcChar8* sentinelFirstFamily = nullptr;
nsAutoRef<FcPattern> sentinelSubst(FcPatternCreate());
FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName);
FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern);
FcPatternGetString(sentinelSubst, FC_FAMILY, 0, &sentinelFirstFamily);
// substitutions for font, -moz-sentinel pattern
nsAutoRef<FcPattern> fontWithSentinel(FcPatternCreate());
FcPatternAddString(fontWithSentinel, FC_FAMILY,
ToFcChar8Ptr(familyToFind.get()));
FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
// iterate through substitutions until hitting the sentinel
FcChar8* substName = nullptr;
for (int i = 0;
FcPatternGetString(fontWithSentinel, FC_FAMILY,
i, &substName) == FcResultMatch;
i++)
{
NS_ConvertUTF8toUTF16 subst(ToCharPtr(substName));
if (sentinelFirstFamily &&
FcStrCmp(substName, sentinelFirstFamily) == 0) {
break;
}
gfxFontFamily* foundFamily = gfxPlatformFontList::FindFamily(subst);
if (foundFamily) {
// We've figured out what family the given name maps to, after any
// fontconfig subsitutions. Cache it to speed up future lookups.
mFcSubstituteCache.Put(familyToFind, foundFamily);
return foundFamily;
}
}
return nullptr;
}
bool
gfxFcPlatformFontList::GetStandardFamilyName(const nsAString& aFontName,
nsAString& aFamilyName)
{
// The fontconfig list of fonts includes generic family names in the
// font list. For these, just use the generic name.
if (aFontName.EqualsLiteral("serif") ||
aFontName.EqualsLiteral("sans-serif") ||
aFontName.EqualsLiteral("monospace")) {
aFamilyName.Assign(aFontName);
return true;
}
gfxFontFamily *family = FindFamily(aFontName);
if (family) {
family->LocalizedName(aFamilyName);
return true;
}
return false;
}
/* static */ FT_Library
gfxFcPlatformFontList::GetFTLibrary()
{
if (!sCairoFTLibrary) {
// Use cairo's FT_Library so that cairo takes care of shutdown of the
// FT_Library after it has destroyed its font_faces, and FT_Done_Face
// has been called on each FT_Face, at least until this bug is fixed:
// https://bugs.freedesktop.org/show_bug.cgi?id=18857
//
// Cairo keeps it's own FT_Library object for creating FT_Face
// instances, so use that. There's no simple API for accessing this
// so use the hacky method below of making a font and extracting
// the library pointer from that.
bool needsBold;
gfxFontStyle style;
gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
gfxFontFamily* family = pfl->GetDefaultFont(&style);
NS_ASSERTION(family, "couldn't find a default font family");
gfxFontEntry* fe = family->FindFontForStyle(style, needsBold);
if (!fe) {
return nullptr;
}
nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&style, false);
if (!font) {
return nullptr;
}
gfxFT2FontBase* ft2Font = reinterpret_cast<gfxFT2FontBase*>(font.get());
gfxFT2LockedFace face(ft2Font);
if (!face.get()) {
return nullptr;
}
sCairoFTLibrary = face.get()->glyph->library;
}
return sCairoFTLibrary;
}
gfxFontFamily*
gfxFcPlatformFontList::FindGenericFamily(const nsAString& aGeneric,
nsIAtom* aLanguage)
{
// set up name
NS_ConvertUTF16toUTF8 generic(aGeneric);
nsAutoCString fcLang;
GetSampleLangForGroup(aLanguage, fcLang);
nsAutoCString genericLang(generic);
genericLang.Append(fcLang);
// try to get the family from the cache
gfxFontFamily *genericFamily = mGenericMappings.GetWeak(genericLang);
if (genericFamily) {
return genericFamily;
}
// if not found, ask fontconfig to pick the appropriate font
nsAutoRef<FcPattern> genericPattern(FcPatternCreate());
FcPatternAddString(genericPattern, FC_FAMILY,
ToFcChar8Ptr(generic.get()));
// -- prefer scalable fonts
FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue);
// -- add the lang to the pattern
if (!fcLang.IsEmpty()) {
FcPatternAddString(genericPattern, FC_LANG,
ToFcChar8Ptr(fcLang.get()));
}
// -- perform substitutions
FcConfigSubstitute(nullptr, genericPattern, FcMatchPattern);
FcDefaultSubstitute(genericPattern);
// -- sort to get the closest matches
FcResult result;
nsAutoRef<FcFontSet> faces(FcFontSort(nullptr, genericPattern, FcFalse,
nullptr, &result));
if (!faces) {
return nullptr;
}
// -- pick the first font for which a font family exists
for (int i = 0; i < faces->nfont; i++) {
FcPattern* font = faces->fonts[i];
FcChar8* mappedGeneric = nullptr;
// not scalable? skip...
FcBool scalable;
if (FcPatternGetBool(font, FC_SCALABLE, 0, &scalable) != FcResultMatch ||
!scalable) {
continue;
}
FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
if (mappedGeneric) {
NS_ConvertUTF8toUTF16 mappedGenericName(ToCharPtr(mappedGeneric));
genericFamily = gfxPlatformFontList::FindFamily(mappedGenericName);
if (genericFamily) {
//printf("generic %s ==> %s\n", genericLang.get(), (const char*)mappedGeneric);
mGenericMappings.Put(genericLang, genericFamily);
break;
}
}
}
return genericFamily;
}
/* static */ void
gfxFcPlatformFontList::CheckFontUpdates(nsITimer *aTimer, void *aThis)
{
// check for font updates
FcInitBringUptoDate();
// update fontlist if current config changed
gfxFcPlatformFontList *pfl = static_cast<gfxFcPlatformFontList*>(aThis);
FcConfig* current = FcConfigGetCurrent();
if (current != pfl->GetLastConfig()) {
pfl->UpdateFontList();
pfl->ForceGlobalReflow();
}
}
#ifdef MOZ_BUNDLED_FONTS
void
gfxFcPlatformFontList::ActivateBundledFonts()
{
if (!mBundledFontsInitialized) {
mBundledFontsInitialized = true;
nsCOMPtr<nsIFile> localDir;
nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
if (NS_FAILED(rv)) {
return;
}
if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
return;
}
bool isDir;
if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
return;
}
if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) {
return;
}
}
if (!mBundledFontsPath.IsEmpty()) {
FcConfigAppFontAddDir(nullptr, ToFcChar8Ptr(mBundledFontsPath.get()));
}
}
#endif
#ifdef MOZ_WIDGET_GTK
/***************************************************************************
*
* This function must be last in the file because it uses the system cairo
* library. Above this point the cairo library used is the tree cairo if
* MOZ_TREE_CAIRO.
*/
#if MOZ_TREE_CAIRO
// Tree cairo symbols have different names. Disable their activation through
// preprocessor macros.
#undef cairo_ft_font_options_substitute
// The system cairo functions are not declared because the include paths cause
// the gdk headers to pick up the tree cairo.h.
extern "C" {
NS_VISIBILITY_DEFAULT void
cairo_ft_font_options_substitute (const cairo_font_options_t *options,
FcPattern *pattern);
}
#endif
static void
ApplyGdkScreenFontOptions(FcPattern *aPattern)
{
const cairo_font_options_t *options =
gdk_screen_get_font_options(gdk_screen_get_default());
cairo_ft_font_options_substitute(options, aPattern);
}
#endif // MOZ_WIDGET_GTK2