diff --git a/gfx/thebes/gfxFcPlatformFontList.cpp b/gfx/thebes/gfxFcPlatformFontList.cpp new file mode 100644 index 000000000000..266530b02c04 --- /dev/null +++ b/gfx/thebes/gfxFcPlatformFontList.cpp @@ -0,0 +1,1458 @@ +/* -*- 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 "prlog.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 + +#ifdef MOZ_WIDGET_GTK +#include +#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" + +#ifdef PR_LOGGING + +#define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ + PR_LOG_DEBUG, args) +#define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \ + gfxPlatform::GetLog(eGfxLog_fontlist), \ + PR_LOG_DEBUG) +#define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \ + gfxPlatform::GetLog(eGfxLog_cmapdata), \ + PR_LOG_DEBUG) + +#endif + +static const FcChar8* +ToFcChar8Ptr(const char* aStr) +{ + return reinterpret_cast(aStr); +} + +static const char* +ToCharPtr(const FcChar8 *aStr) +{ + return reinterpret_cast(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), mFontPattern(FcPatternCreate()), + mFTFace(aFace), mFTFaceInitialized(true), + mAspect(0.0), mFontData(aData) +{ + mWeight = aWeight; + mItalic = aItalic; + mStretch = aStretch; + mIsDataUserFont = true; + + // 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 charmap; + nsresult rv; + bool symbolFont; + + 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, symbolFont = false; // currently ignored + uint32_t cmapLen; + const uint8_t* cmapData = + reinterpret_cast(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(); + } + +#ifdef PR_LOGGING + 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); + } +#endif + + 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 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 gfxPangoFonts 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) +{ + // 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 pattern(FcPatternCreate()); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aFontStyle->size); + + PreparePattern(pattern, aFontStyle->printerFont); + nsAutoRef 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& 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)) { + 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 + gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList(); + 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); + + // add entry to local name lists + if (!psname.IsEmpty()) { + fp->AddPostscriptName(fontEntry, psname); + } + NS_ASSERTION(!fullname.IsEmpty(), "empty font fullname"); + if (!fullname.IsEmpty()) { + fp->AddFullname(fontEntry, fullname); + } + +#ifdef PR_LOGGING + 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())); + } +#endif + } + 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 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 +mozilla::TemporaryRef +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_font_options_destroy(options); + + mozilla::gfx::FontHinting hinting; + + switch (hint_style) { + case CAIRO_HINT_STYLE_NONE: + hinting = mozilla::gfx::FontHinting::NONE; + break; + case CAIRO_HINT_STYLE_SLIGHT: + hinting = mozilla::gfx::FontHinting::LIGHT; + break; + case CAIRO_HINT_STYLE_FULL: + hinting = mozilla::gfx::FontHinting::FULL; + break; + default: + hinting = mozilla::gfx::FontHinting::NORMAL; + break; + } + + // We don't want to force the use of the autohinter over the font's built in hints + return mozilla::gfx::Factory::CreateCairoGlyphRenderingOptions(hinting, false); +} +#endif + +gfxFcPlatformFontList::~gfxFcPlatformFontList() +{ +} + +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. + + 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(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, fontFamily); + } + if (!fullname.IsEmpty()) { + ToLowerCase(fullname); + mLocalNames.Put(fullname, fontFamily); + } + } +} + +nsresult +gfxFcPlatformFontList::InitFontList() +{ + // reset font lists + gfxPlatformFontList::InitFontList(); + + mLocalNames.Clear(); + mGenericMappings.Clear(); + + // iterate over available fonts + FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem); + AddFontSetFamilies(systemFonts); + +#ifdef MOZ_BUNDLED_FONTS + 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& aListOfFonts, nsIAtom *aLangGroup) +{ + aListOfFonts.Clear(); + + nsAutoRef pat(FcPatternCreate()); + if (!pat) { + return; + } + + nsAutoRef 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 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& 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 + // gFontsDialog.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) +{ + return FindGenericFamily(NS_LITERAL_STRING("serif"), nsGkAtoms::x_western); +} + +gfxFontEntry* +gfxFcPlatformFontList::LookupLocalFont(const nsAString& aFontName, + uint16_t aWeight, + int16_t aStretch, + bool aItalic) +{ + gfxFontEntry* lookup; + + // first, lookup in face name lists + lookup = FindFaceName(aFontName); + if (!lookup) { + // if not found, check in global facename ==> family list + nsAutoString keyName(aFontName); + ToLowerCase(keyName); + gfxFontFamily* fontFamily = mLocalNames.GetWeak(keyName); + + // name is not in the global list, done + if (!fontFamily) { + return nullptr; + } + + // name is in global list but family needs enumeration + fontFamily->FindStyleVariations(); + + // facename ==> font entry should now be in the list + lookup = FindFaceName(aFontName); + NS_ASSERTION(lookup, "facename to family mapping failure"); + if (!lookup) { + return nullptr; + } + } + + gfxFontConfigFontEntry* fcFontEntry = + static_cast(lookup); + + return new gfxFontConfigFontEntry(fcFontEntry->Name(), + fcFontEntry->GetPattern(), + 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, + nsIAtom* aLanguage, + bool aUseSystemFonts) +{ + nsAutoString familyName(aFamily); + ToLowerCase(familyName); + + // 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, aLanguage); + } + + // 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. + + // substitutions for serif pattern + const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel"); + nsAutoRef sentinelSubst(FcPatternCreate()); + FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName); + FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern); + FcChar8* sentinelFirstFamily = nullptr; + FcPatternGetString(sentinelSubst, FC_FAMILY, 0, &sentinelFirstFamily); + + // substitutions for font, -moz-sentinel pattern + nsAutoRef fontWithSentinel(FcPatternCreate()); + NS_ConvertUTF16toUTF8 familyToFind(familyName); + FcPatternAddString(fontWithSentinel, FC_FAMILY, ToFcChar8Ptr(familyToFind.get())); + FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName); + FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern); + + // iterate through substitutions until hitting the first serif font + 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) { + 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 font = fe->FindOrMakeFont(&style, false); + if (!font) { + return nullptr; + } + + gfxFT2FontBase* ft2Font = reinterpret_cast(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 genericPattern(FcPatternCreate()); + FcPatternAddString(genericPattern, FC_FAMILY, + ToFcChar8Ptr(generic.get())); + + // -- 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 faces(FcFontSort(nullptr, genericPattern, FcFalse, + nullptr, &result)); + + // -- 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; +} + +#ifdef MOZ_BUNDLED_FONTS +void +gfxFcPlatformFontList::ActivateBundledFonts() +{ + if (!mBundledFontsInitialized) { + mBundledFontsInitialized = true; + nsCOMPtr 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 + + diff --git a/gfx/thebes/gfxFcPlatformFontList.h b/gfx/thebes/gfxFcPlatformFontList.h new file mode 100644 index 000000000000..b17b61801f86 --- /dev/null +++ b/gfx/thebes/gfxFcPlatformFontList.h @@ -0,0 +1,255 @@ +/* -*- 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/. */ + +#ifndef GFXFCPLATFORMFONTLIST_H_ +#define GFXFCPLATFORMFONTLIST_H_ + +#include "gfxFont.h" +#include "gfxFontEntry.h" +#include "gfxFT2FontBase.h" +#include "gfxPlatformFontList.h" +#include "mozilla/mozalloc.h" + +#include +#include "ft2build.h" +#include FT_FREETYPE_H +#include FT_TRUETYPE_TABLES_H +#include +#include + +#include "gfxFontconfigUtils.h" // xxx - only for nsAutoRefTraits, etc. + +template <> +class nsAutoRefTraits : public nsPointerRefTraits +{ +public: + static void Release(FcObjectSet *ptr) { FcObjectSetDestroy(ptr); } +}; + + +// Helper classes used for clearning out user font data when cairo font +// face is destroyed. Since multiple faces may use the same data, be +// careful to assure that the data is only cleared out when all uses +// expire. The font entry object contains a refptr to FTUserFontData and +// each cairo font created from that font entry contains a +// FTUserFontDataRef with a refptr to that same FTUserFontData object. + +class FTUserFontData { +public: + NS_INLINE_DECL_REFCOUNTING(FTUserFontData) + + explicit FTUserFontData(FT_Face aFace, const uint8_t* aData) + : mFace(aFace), mFontData(aData) + { + } + + const uint8_t *FontData() const { return mFontData; } + +private: + ~FTUserFontData() + { + FT_Done_Face(mFace); + if (mFontData) { + NS_Free((void*)mFontData); + } + } + + FT_Face mFace; + const uint8_t *mFontData; +}; + +class FTUserFontDataRef { +public: + explicit FTUserFontDataRef(FTUserFontData *aUserFontData) + : mUserFontData(aUserFontData) + { + } + + static void Destroy(void* aData) { + FTUserFontDataRef* aUserFontDataRef = + static_cast(aData); + delete aUserFontDataRef; + } + +private: + nsRefPtr mUserFontData; +}; + +// The names for the font entry and font classes should really +// the common 'Fc' abbreviation but the gfxPangoFontGroup code already +// defines versions of these, so use the verbose name for now. + +class gfxFontConfigFontEntry : public gfxFontEntry { +public: + // used for system fonts with explicit patterns + explicit gfxFontConfigFontEntry(const nsAString& aFaceName, + FcPattern* aFontPattern); + + // used for data fonts where the fontentry takes ownership + // of the font data and the FT_Face + explicit gfxFontConfigFontEntry(const nsAString& aFaceName, + uint16_t aWeight, + int16_t aStretch, + bool aItalic, + const uint8_t *aData, + FT_Face aFace); + + // used for @font-face local system fonts with explicit patterns + explicit gfxFontConfigFontEntry(const nsAString& aFaceName, + FcPattern* aFontPattern, + uint16_t aWeight, + int16_t aStretch, + bool aItalic); + + FcPattern* GetPattern() { return mFontPattern; } + + bool SupportsLangGroup(nsIAtom *aLangGroup) const override; + + nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override; + bool TestCharacterMap(uint32_t aCh) override; + + hb_blob_t* GetFontTable(uint32_t aTableTag) override; + + void ForgetHBFace() override; + void ReleaseGrFace(gr_face* aFace) override; + +protected: + virtual ~gfxFontConfigFontEntry(); + + gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, + bool aNeedsBold) override; + + // helper method for creating cairo font from pattern + cairo_scaled_font_t* + CreateScaledFont(FcPattern* aRenderPattern, + const gfxFontStyle *aStyle, + bool aNeedsBold); + + // override to pull data from FTFace + virtual nsresult + CopyFontTable(uint32_t aTableTag, + FallibleTArray& aBuffer) override; + + // if HB or GR faces are gone, close down the FT_Face + void MaybeReleaseFTFace(); + + double GetAspect(); + + // pattern for a single face of a family + nsCountedRef mFontPattern; + + // user font data, when needed + nsRefPtr mUserFontData; + + // FTFace - initialized when needed + FT_Face mFTFace; + bool mFTFaceInitialized; + double mAspect; + + // data font + const uint8_t* mFontData; +}; + +class gfxFontConfigFontFamily : public gfxFontFamily { +public: + gfxFontConfigFontFamily(const nsAString& aName) : + gfxFontFamily(aName) { } + + void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) override; + + // Families are constructed initially with just references to patterns. + // When necessary, these are enumerated within FindStyleVariations. + void AddFontPattern(FcPattern* aFontPattern); + +protected: + virtual ~gfxFontConfigFontFamily() { } + + nsTArray > mFontPatterns; +}; + +class gfxFontConfigFont : public gfxFT2FontBase { +public: + gfxFontConfigFont(cairo_scaled_font_t *aScaledFont, + gfxFontEntry *aFontEntry, + const gfxFontStyle *aFontStyle, + bool aNeedsBold); + +#ifdef USE_SKIA + virtual mozilla::TemporaryRef + GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) override; +#endif + +protected: + virtual ~gfxFontConfigFont(); +}; + +class gfxFcPlatformFontList : public gfxPlatformFontList { +public: + gfxFcPlatformFontList() + : mLocalNames(64), mGenericMappings(32) + { +#ifdef MOZ_BUNDLED_FONTS + mBundledFontsInitialized = false; +#endif + } + + // initialize font lists + nsresult InitFontList() override; + + void GetFontList(nsIAtom *aLangGroup, + const nsACString& aGenericFamily, + nsTArray& aListOfFonts) override; + + + gfxFontFamily* + GetDefaultFont(const gfxFontStyle* aStyle) override; + + gfxFontEntry* + LookupLocalFont(const nsAString& aFontName, uint16_t aWeight, + int16_t aStretch, bool aItalic) override; + + gfxFontEntry* + MakePlatformFont(const nsAString& aFontName, uint16_t aWeight, + int16_t aStretch, bool aItalic, + const uint8_t* aFontData, + uint32_t aLength) override; + + gfxFontFamily* FindFamily(const nsAString& aFamily, + nsIAtom* aLanguage = nullptr, + bool aUseSystemFonts = false) override; + + bool GetStandardFamilyName(const nsAString& aFontName, + nsAString& aFamilyName) override; + + static FT_Library GetFTLibrary(); + +protected: + virtual ~gfxFcPlatformFontList(); + + // add all the font families found in a font set + void AddFontSetFamilies(FcFontSet* aFontSet); + + // figure out which family fontconfig maps a generic to + // (aGeneric assumed already lowercase) + gfxFontFamily* FindGenericFamily(const nsAString& aGeneric, + nsIAtom* aLanguage); + +#ifdef MOZ_BUNDLED_FONTS + void ActivateBundledFonts(); + nsCString mBundledFontsPath; + bool mBundledFontsInitialized; +#endif + + // to avoid enumerating all fonts, maintain a mapping of local font + // names to family + nsRefPtrHashtable mLocalNames; + + // caching generic/lang ==> font family + nsRefPtrHashtable mGenericMappings; + + static FT_Library sCairoFTLibrary; +}; + +#endif /* GFXPLATFORMFONTLIST_H_ */ diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index 3a877ed0a6a5..3126bd5d193d 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -54,6 +54,8 @@ class gfxTextContextPaint; // we use a platform-dependent value to harmonize with the platform's own APIs. #ifdef XP_WIN #define OBLIQUE_SKEW_FACTOR 0.3 +#elif defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT) +#define OBLIQUE_SKEW_FACTOR 0.2 #else #define OBLIQUE_SKEW_FACTOR 0.25 #endif diff --git a/gfx/thebes/gfxPangoFonts.cpp b/gfx/thebes/gfxPangoFonts.cpp index f98be1df03ca..ace78d193723 100644 --- a/gfx/thebes/gfxPangoFonts.cpp +++ b/gfx/thebes/gfxPangoFonts.cpp @@ -1262,6 +1262,7 @@ gfxPangoFontGroup::gfxPangoFontGroup(const FontFamilyList& aFontFamilyList, // dummy entry, will be replaced when actually needed mFonts.AppendElement(FamilyFace()); + mSkipUpdateUserFonts = true; } gfxPangoFontGroup::~gfxPangoFontGroup() diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 554b4d2c3c77..10fe98fcbf1e 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -76,6 +76,10 @@ #include "GLContextProvider.h" #include "mozilla/gfx/Logging.h" +#if defined(MOZ_WIDGET_GTK) +#include "gfxPlatformGtk.h" // xxx - for UseFcFontList +#endif + #ifdef MOZ_WIDGET_ANDROID #include "TexturePoolOGL.h" #endif @@ -536,13 +540,20 @@ gfxPlatform::Init() nsresult rv; -#if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID) // temporary, until this is implemented on others - rv = gfxPlatformFontList::Init(); - if (NS_FAILED(rv)) { - NS_RUNTIMEABORT("Could not initialize gfxPlatformFontList"); - } + bool usePlatformFontList = true; +#if defined(MOZ_WIDGET_GTK) + usePlatformFontList = gfxPlatformGtk::UseFcFontList(); +#elif defined(MOZ_WIDGET_QT) + usePlatformFontList = false; #endif + if (usePlatformFontList) { + rv = gfxPlatformFontList::Init(); + if (NS_FAILED(rv)) { + NS_RUNTIMEABORT("Could not initialize gfxPlatformFontList"); + } + } + gPlatform->mScreenReferenceSurface = gPlatform->CreateOffscreenSurface(IntSize(1, 1), gfxContentType::COLOR_ALPHA); diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp index d375546d97dc..4e694ad604f4 100644 --- a/gfx/thebes/gfxPlatformFontList.cpp +++ b/gfx/thebes/gfxPlatformFontList.cpp @@ -650,8 +650,7 @@ gfxPlatformFontList::CommonFontFallback(uint32_t aCh, uint32_t aNextCh, const char *fallbackFamily = defaultFallbacks[i]; familyName.AppendASCII(fallbackFamily); - gfxFontFamily *fallback = - gfxPlatformFontList::PlatformFontList()->FindFamily(familyName); + gfxFontFamily *fallback = FindFamilyByCanonicalName(familyName); if (!fallback) continue; diff --git a/gfx/thebes/gfxPlatformFontList.h b/gfx/thebes/gfxPlatformFontList.h index 40e0ba2305cd..7eacb332066c 100644 --- a/gfx/thebes/gfxPlatformFontList.h +++ b/gfx/thebes/gfxPlatformFontList.h @@ -110,9 +110,9 @@ public: // initialize font lists virtual nsresult InitFontList(); - void GetFontList (nsIAtom *aLangGroup, - const nsACString& aGenericFamily, - nsTArray& aListOfFonts); + virtual void GetFontList(nsIAtom *aLangGroup, + const nsACString& aGenericFamily, + nsTArray& aListOfFonts); void UpdateFontList(); @@ -211,6 +211,18 @@ protected: nsRefPtr& aFamilyEntry, void* userArg); + // Lookup family name in global family list without substitutions or + // localized family name lookup. Used for common font fallback families. + gfxFontFamily* FindFamilyByCanonicalName(const nsAString& aFamily) { + nsAutoString key; + gfxFontFamily *familyEntry; + GenerateFontListKey(aFamily, key); + if ((familyEntry = mFontFamilies.GetWeak(key))) { + return CheckFamily(familyEntry); + } + return nullptr; + } + // returns default font for a given character, null otherwise gfxFontEntry* CommonFontFallback(uint32_t aCh, uint32_t aNextCh, int32_t aRunScript, diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp index bd2a49b3d564..a09488436515 100644 --- a/gfx/thebes/gfxPlatformGtk.cpp +++ b/gfx/thebes/gfxPlatformGtk.cpp @@ -12,6 +12,7 @@ #include "nsUnicharUtils.h" #include "nsUnicodeProperties.h" #include "gfx2DGlue.h" +#include "gfxFcPlatformFontList.h" #include "gfxFontconfigUtils.h" #include "gfxPangoFonts.h" #include "gfxContext.h" @@ -59,10 +60,15 @@ static cairo_user_data_key_t cairo_gdk_drawable_key; bool gfxPlatformGtk::sUseXRender = true; #endif +bool gfxPlatformGtk::sUseFcFontList = false; + gfxPlatformGtk::gfxPlatformGtk() { - if (!sFontconfigUtils) + sUseFcFontList = mozilla::Preferences::GetBool("gfx.font_rendering.fontconfig.fontlist.enabled"); + if (!sUseFcFontList && !sFontconfigUtils) { sFontconfigUtils = gfxFontconfigUtils::GetFontconfigUtils(); + } + #ifdef MOZ_X11 sUseXRender = (GDK_IS_X11_DISPLAY(gdk_display_get_default())) ? mozilla::Preferences::GetBool("gfx.xrender.enabled") : false; @@ -76,10 +82,11 @@ gfxPlatformGtk::gfxPlatformGtk() gfxPlatformGtk::~gfxPlatformGtk() { - gfxFontconfigUtils::Shutdown(); - sFontconfigUtils = nullptr; - - gfxPangoFontGroup::Shutdown(); + if (!sUseFcFontList) { + gfxFontconfigUtils::Shutdown(); + sFontconfigUtils = nullptr; + gfxPangoFontGroup::Shutdown(); + } } void @@ -149,19 +156,84 @@ gfxPlatformGtk::GetFontList(nsIAtom *aLangGroup, const nsACString& aGenericFamily, nsTArray& aListOfFonts) { - return sFontconfigUtils->GetFontList(aLangGroup, aGenericFamily, + if (sUseFcFontList) { + gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup, + aGenericFamily, + aListOfFonts); + return NS_OK; + } + + return sFontconfigUtils->GetFontList(aLangGroup, + aGenericFamily, aListOfFonts); } nsresult gfxPlatformGtk::UpdateFontList() { + if (sUseFcFontList) { + gfxPlatformFontList::PlatformFontList()->UpdateFontList(); + return NS_OK; + } + return sFontconfigUtils->UpdateFontList(); } +// xxx - this is ubuntu centric, need to go through other distros and flesh +// out a more general list +static const char kFontDejaVuSans[] = "DejaVu Sans"; +static const char kFontDejaVuSerif[] = "DejaVu Serif"; +static const char kFontFreeSans[] = "FreeSans"; +static const char kFontFreeSerif[] = "FreeSerif"; +static const char kFontTakaoPGothic[] = "TakaoPGothic"; +static const char kFontDroidSansFallback[] = "Droid Sans Fallback"; +static const char kFontWenQuanYiMicroHei[] = "WenQuanYi Micro Hei"; +static const char kFontNanumGothic[] = "NanumGothic"; + +void +gfxPlatformGtk::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, + int32_t aRunScript, + nsTArray& aFontList) +{ + aFontList.AppendElement(kFontDejaVuSerif); + aFontList.AppendElement(kFontFreeSerif); + aFontList.AppendElement(kFontDejaVuSans); + aFontList.AppendElement(kFontFreeSans); + + // add fonts for CJK ranges + // xxx - this isn't really correct, should use the same CJK font ordering + // as the pref font code + if (aCh >= 0x3000 && + ((aCh < 0xe000) || + (aCh >= 0xf900 && aCh < 0xfff0) || + ((aCh >> 16) == 2))) { + aFontList.AppendElement(kFontTakaoPGothic); + aFontList.AppendElement(kFontDroidSansFallback); + aFontList.AppendElement(kFontWenQuanYiMicroHei); + aFontList.AppendElement(kFontNanumGothic); + } +} + +gfxPlatformFontList* +gfxPlatformGtk::CreatePlatformFontList() +{ + gfxPlatformFontList* list = new gfxFcPlatformFontList(); + if (NS_SUCCEEDED(list->InitFontList())) { + return list; + } + gfxPlatformFontList::Shutdown(); + return nullptr; +} + nsresult gfxPlatformGtk::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) { + if (sUseFcFontList) { + gfxPlatformFontList::PlatformFontList()-> + GetStandardFamilyName(aFontName, aFamilyName); + return NS_OK; + } + return sFontconfigUtils->GetStandardFamilyName(aFontName, aFamilyName); } @@ -170,6 +242,10 @@ gfxPlatformGtk::CreateFontGroup(const FontFamilyList& aFontFamilyList, const gfxFontStyle *aStyle, gfxUserFontSet *aUserFontSet) { + if (sUseFcFontList) { + return new gfxFontGroup(aFontFamilyList, aStyle, aUserFontSet); + } + return new gfxPangoFontGroup(aFontFamilyList, aStyle, aUserFontSet); } @@ -179,6 +255,11 @@ gfxPlatformGtk::LookupLocalFont(const nsAString& aFontName, int16_t aStretch, bool aItalic) { + if (sUseFcFontList) { + gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); + return pfl->LookupLocalFont(aFontName, aWeight, aStretch, aItalic); + } + return gfxPangoFontGroup::NewFontEntry(aFontName, aWeight, aStretch, aItalic); } @@ -191,6 +272,12 @@ gfxPlatformGtk::MakePlatformFont(const nsAString& aFontName, const uint8_t* aFontData, uint32_t aLength) { + if (sUseFcFontList) { + gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); + return pfl->MakePlatformFont(aFontName, aWeight, aStretch, aItalic, + aFontData, aLength); + } + // passing ownership of the font data to the new font entry return gfxPangoFontGroup::NewFontEntry(aFontName, aWeight, aStretch, aItalic, diff --git a/gfx/thebes/gfxPlatformGtk.h b/gfx/thebes/gfxPlatformGtk.h index 33655fc750e9..aa06b70586b6 100644 --- a/gfx/thebes/gfxPlatformGtk.h +++ b/gfx/thebes/gfxPlatformGtk.h @@ -41,6 +41,13 @@ public: virtual nsresult UpdateFontList() override; + virtual void + GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, + int32_t aRunScript, + nsTArray& aFontList) override; + + virtual gfxPlatformFontList* CreatePlatformFontList(); + virtual nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) override; @@ -101,6 +108,8 @@ public: #endif } + static bool UseFcFontList() { return sUseFcFontList; } + bool UseImageOffscreenSurfaces() { // We want to turn on image offscreen surfaces ONLY for GTK3 builds // since GTK2 theme rendering still requires xlib surfaces per se. @@ -129,6 +138,10 @@ private: #ifdef MOZ_X11 static bool sUseXRender; #endif + + // xxx - this will be removed once the new fontconfig platform font list + // replaces gfxPangoFontGroup + static bool sUseFcFontList; }; #endif /* GFX_PLATFORM_GTK_H */ diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index 62ffa1d58d9b..4288386a33c7 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -23,6 +23,10 @@ #include "mozilla/Likely.h" #include "gfx2DGlue.h" +#if defined(MOZ_WIDGET_GTK) +#include "gfxPlatformGtk.h" // xxx - for UseFcFontList +#endif + #include "cairo.h" using namespace mozilla; @@ -1536,6 +1540,7 @@ gfxFontGroup::gfxFontGroup(const FontFamilyList& aFontFamilyList, , mTextPerf(nullptr) , mPageLang(gfxPlatform::GetFontPrefLangFor(aStyle->language)) , mSkipDrawing(false) + , mSkipUpdateUserFonts(false) { // We don't use SetUserFontSet() here, as we want to unconditionally call // BuildFontList() rather than only do UpdateUserFonts() if it changed. @@ -1682,10 +1687,17 @@ void gfxFontGroup::EnumerateFontList(nsIAtom *aLanguage, void *aClosure) void gfxFontGroup::BuildFontList() { -// gfxPangoFontGroup behaves differently, so this method is a no-op on that platform -#if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID) - EnumerateFontList(mStyle.language); + bool enumerateFonts = true; + +#if defined(MOZ_WIDGET_GTK) + // xxx - eliminate this once gfxPangoFontGroup is no longer needed + enumerateFonts = gfxPlatformGtk::UseFcFontList(); +#elif defined(MOZ_WIDGET_QT) + enumerateFonts = false; #endif + if (enumerateFonts) { + EnumerateFontList(mStyle.language); + } } void @@ -2345,13 +2357,11 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext, NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted, "don't call InitScriptRun with aborted shaping state"); -#if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID) - // non-linux platforms build the fontlist lazily and include userfonts - // so need to confirm the load state of userfonts in the list - if (mUserFontSet && mCurrGeneration != mUserFontSet->GetGeneration()) { + // confirm the load state of userfonts in the list + if (!mSkipUpdateUserFonts && mUserFontSet && + mCurrGeneration != mUserFontSet->GetGeneration()) { UpdateUserFonts(); } -#endif gfxFont *mainFont = GetFirstValidFont(); @@ -2607,10 +2617,6 @@ gfxFontGroup::FindNonItalicFaceForChar(gfxFontFamily* aFamily, uint32_t aCh) NS_ASSERTION(mStyle.style != NS_FONT_STYLE_NORMAL, "should only be called in the italic/oblique case"); - if (!aFamily->TestCharacterMap(aCh)) { - return nullptr; - } - gfxFontStyle regularStyle = mStyle; regularStyle.style = NS_FONT_STYLE_NORMAL; bool needsBold; diff --git a/gfx/thebes/gfxTextRun.h b/gfx/thebes/gfxTextRun.h index a5cf38f4c459..ae31de94ea3f 100644 --- a/gfx/thebes/gfxTextRun.h +++ b/gfx/thebes/gfxTextRun.h @@ -1056,6 +1056,9 @@ protected: // download to complete (or fallback // timer to fire) + // xxx - gfxPangoFontGroup skips UpdateUserFonts + bool mSkipUpdateUserFonts; + /** * Textrun creation short-cuts for special cases where we don't need to * call a font shaper to generate glyphs. diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build index 6c316f9c1e29..d032931ce71a 100644 --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -111,6 +111,7 @@ elif CONFIG['MOZ_WIDGET_GTK']: ] SOURCES += [ + 'gfxFcPlatformFontList.cpp', 'gfxFontconfigUtils.cpp', 'gfxFT2FontBase.cpp', 'gfxFT2Utils.cpp', diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 3ea485ab5c94..f0a02c14cb3a 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -68,6 +68,11 @@ #include "gfxTextRun.h" #include "nsFontFaceUtils.h" +#if defined(MOZ_WIDGET_GTK) +#include "gfxPlatformGtk.h" // xxx - for UseFcFontList +#endif + + // Needed for Start/Stop of Image Animation #include "imgIContainer.h" #include "nsIImageLoadingContent.h" @@ -2170,8 +2175,10 @@ nsPresContext::UserFontSetUpdated(gfxUserFontEntry* aUpdatedFont) return; bool usePlatformFontList = true; -#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT) - usePlatformFontList = false; +#if defined(MOZ_WIDGET_GTK) + usePlatformFontList = gfxPlatformGtk::UseFcFontList(); +#elif defined(MOZ_WIDGET_QT) + usePlatformFontList = false; #endif // xxx - until the Linux platform font list is always used, use full diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index c9ef06a54500..f1f8ff04de8e 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3608,6 +3608,18 @@ pref("intl.ime.use_simple_context_on_password_field", true); pref("intl.ime.use_simple_context_on_password_field", false); #endif +# enable new platform fontlist for linux on GTK platforms +# temporary pref to allow flipping back to the existing +# gfxPangoFontGroup/gfxFontconfigUtils code for handling system fonts + +#ifdef MOZ_WIDGET_GTK +#ifdef RELEASE_BUILD +pref("gfx.font_rendering.fontconfig.fontlist.enabled", false); +#else +pref("gfx.font_rendering.fontconfig.fontlist.enabled", true); +#endif +#endif + # XP_UNIX #endif #endif