2015-05-13 08:11:25 +03:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
2015-05-19 21:15:34 +03:00
|
|
|
#include "mozilla/Logging.h"
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
#include "gfxFcPlatformFontList.h"
|
|
|
|
#include "gfxFont.h"
|
|
|
|
#include "gfxFontConstants.h"
|
|
|
|
#include "gfxFontFamilyList.h"
|
|
|
|
#include "gfxFT2Utils.h"
|
|
|
|
#include "gfxPlatform.h"
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
2017-11-02 23:29:33 +03:00
|
|
|
#include "mozilla/dom/ContentChild.h"
|
|
|
|
#include "mozilla/dom/ContentParent.h"
|
2015-11-16 02:48:40 +03:00
|
|
|
#include "mozilla/Preferences.h"
|
2016-08-17 01:41:12 +03:00
|
|
|
#include "mozilla/Sprintf.h"
|
2015-05-13 08:11:25 +03:00
|
|
|
#include "mozilla/TimeStamp.h"
|
|
|
|
#include "nsGkAtoms.h"
|
|
|
|
#include "nsUnicodeProperties.h"
|
|
|
|
#include "nsUnicodeRange.h"
|
2015-05-13 08:11:26 +03:00
|
|
|
#include "nsDirectoryServiceUtils.h"
|
|
|
|
#include "nsDirectoryServiceDefs.h"
|
|
|
|
#include "nsAppDirectoryServiceDefs.h"
|
2015-11-16 02:48:40 +03:00
|
|
|
#include "nsCharSeparatedTokenizer.h"
|
2017-11-02 23:29:33 +03:00
|
|
|
#include "nsXULAppAPI.h"
|
2015-05-13 08:11:25 +03:00
|
|
|
|
2015-09-02 21:12:32 +03:00
|
|
|
#include "mozilla/gfx/HelpersCairo.h"
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
#include <fontconfig/fcfreetype.h>
|
2018-01-18 22:27:41 +03:00
|
|
|
#include <dlfcn.h>
|
2017-11-02 23:29:33 +03:00
|
|
|
#include <unistd.h>
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
#ifdef MOZ_WIDGET_GTK
|
|
|
|
#include <gdk/gdk.h>
|
2015-11-20 07:35:15 +03:00
|
|
|
#include "gfxPlatformGtk.h"
|
2015-05-13 08:11:25 +03:00
|
|
|
#endif
|
|
|
|
|
2016-07-20 21:18:21 +03:00
|
|
|
#ifdef MOZ_X11
|
|
|
|
#include "mozilla/X11Util.h"
|
|
|
|
#endif
|
|
|
|
|
2017-11-09 19:54:30 +03:00
|
|
|
#ifdef MOZ_CONTENT_SANDBOX
|
|
|
|
#include "mozilla/SandboxBrokerPolicyFactory.h"
|
|
|
|
#include "mozilla/SandboxSettings.h"
|
|
|
|
#endif
|
|
|
|
|
2018-01-18 22:27:41 +03:00
|
|
|
#include FT_MULTIPLE_MASTERS_H
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
using namespace mozilla;
|
2017-04-07 00:41:02 +03:00
|
|
|
using namespace mozilla::gfx;
|
2015-05-13 08:11:25 +03:00
|
|
|
using namespace mozilla::unicode;
|
|
|
|
|
2017-11-02 23:29:33 +03:00
|
|
|
using mozilla::dom::SystemFontListEntry;
|
|
|
|
using mozilla::dom::FontPatternListEntry;
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
#ifndef FC_POSTSCRIPT_NAME
|
|
|
|
#define FC_POSTSCRIPT_NAME "postscriptname" /* String */
|
|
|
|
#endif
|
2018-04-25 09:18:23 +03:00
|
|
|
#ifndef FC_VARIABLE
|
|
|
|
#define FC_VARIABLE "variable" /* Bool */
|
|
|
|
#endif
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
#define PRINTING_FC_PROPERTY "gfx.printing"
|
|
|
|
|
2015-05-21 23:22:04 +03:00
|
|
|
#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
|
2015-06-04 01:25:57 +03:00
|
|
|
LogLevel::Debug, args)
|
2015-06-04 01:22:28 +03:00
|
|
|
#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
|
2015-05-13 08:11:25 +03:00
|
|
|
gfxPlatform::GetLog(eGfxLog_fontlist), \
|
2015-06-04 01:25:57 +03:00
|
|
|
LogLevel::Debug)
|
2015-06-04 01:22:28 +03:00
|
|
|
#define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \
|
2015-05-13 08:11:25 +03:00
|
|
|
gfxPlatform::GetLog(eGfxLog_cmapdata), \
|
2015-06-04 01:25:57 +03:00
|
|
|
LogLevel::Debug)
|
2015-05-13 08:11:25 +03:00
|
|
|
|
2017-08-07 23:20:37 +03:00
|
|
|
template <>
|
|
|
|
class nsAutoRefTraits<FcFontSet> : public nsPointerRefTraits<FcFontSet>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static void Release(FcFontSet *ptr) { FcFontSetDestroy(ptr); }
|
|
|
|
};
|
|
|
|
|
|
|
|
template <>
|
|
|
|
class nsAutoRefTraits<FcObjectSet> : public nsPointerRefTraits<FcObjectSet>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static void Release(FcObjectSet *ptr) { FcObjectSetDestroy(ptr); }
|
|
|
|
};
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
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;
|
2015-06-08 09:14:06 +03:00
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-13 22:34:37 +03:00
|
|
|
static FontWeight
|
2015-05-13 08:11:25 +03:00
|
|
|
MapFcWeight(int aFcWeight)
|
|
|
|
{
|
2017-02-09 15:39:27 +03:00
|
|
|
if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) {
|
2018-04-13 22:34:37 +03:00
|
|
|
return FontWeight(100);
|
2017-02-09 15:39:27 +03:00
|
|
|
}
|
|
|
|
if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
|
2018-04-13 22:34:37 +03:00
|
|
|
return FontWeight(200);
|
2017-02-09 15:39:27 +03:00
|
|
|
}
|
|
|
|
if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) {
|
2018-04-13 22:34:37 +03:00
|
|
|
return FontWeight(300);
|
2017-02-09 15:39:27 +03:00
|
|
|
}
|
|
|
|
if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
|
|
|
|
// This includes FC_WEIGHT_BOOK
|
2018-04-13 22:34:37 +03:00
|
|
|
return FontWeight(400);
|
2017-02-09 15:39:27 +03:00
|
|
|
}
|
|
|
|
if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
|
2018-04-13 22:34:37 +03:00
|
|
|
return FontWeight(500);
|
2017-02-09 15:39:27 +03:00
|
|
|
}
|
|
|
|
if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
|
2018-04-13 22:34:37 +03:00
|
|
|
return FontWeight(600);
|
2017-02-09 15:39:27 +03:00
|
|
|
}
|
|
|
|
if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
|
2018-04-13 22:34:37 +03:00
|
|
|
return FontWeight(700);
|
2017-02-09 15:39:27 +03:00
|
|
|
}
|
|
|
|
if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
|
2018-04-13 22:34:37 +03:00
|
|
|
return FontWeight(800);
|
2017-02-09 15:39:27 +03:00
|
|
|
}
|
|
|
|
if (aFcWeight <= FC_WEIGHT_BLACK) {
|
2018-04-13 22:34:37 +03:00
|
|
|
return FontWeight(900);
|
2017-02-09 15:39:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// including FC_WEIGHT_EXTRABLACK
|
2018-04-13 22:34:37 +03:00
|
|
|
return FontWeight(901);
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
2018-04-23 17:52:20 +03:00
|
|
|
// TODO(emilio, jfkthame): I think this can now be more fine-grained.
|
|
|
|
static FontStretch
|
2015-05-13 08:11:25 +03:00
|
|
|
MapFcWidth(int aFcWidth)
|
|
|
|
{
|
|
|
|
if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
|
2018-04-23 17:52:20 +03:00
|
|
|
return FontStretch::UltraCondensed();
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
|
2018-04-23 17:52:20 +03:00
|
|
|
return FontStretch::ExtraCondensed();
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
|
2018-04-23 17:52:20 +03:00
|
|
|
return FontStretch::Condensed();
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
|
2018-04-23 17:52:20 +03:00
|
|
|
return FontStretch::SemiCondensed();
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
|
2018-04-23 17:52:20 +03:00
|
|
|
return FontStretch::Normal();
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
|
2018-04-23 17:52:20 +03:00
|
|
|
return FontStretch::SemiExpanded();
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
|
2018-04-23 17:52:20 +03:00
|
|
|
return FontStretch::Expanded();
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
|
2018-04-23 17:52:20 +03:00
|
|
|
return FontStretch::ExtraExpanded();
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
2018-04-23 17:52:20 +03:00
|
|
|
return FontStretch::UltraExpanded();
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
|
2016-06-15 16:35:05 +03:00
|
|
|
FcPattern* aFontPattern,
|
|
|
|
bool aIgnoreFcCharmap)
|
2015-05-13 08:11:25 +03:00
|
|
|
: gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
|
|
|
|
mFTFace(nullptr), mFTFaceInitialized(false),
|
2016-06-15 16:35:05 +03:00
|
|
|
mIgnoreFcCharmap(aIgnoreFcCharmap),
|
2018-04-25 09:18:23 +03:00
|
|
|
mHasVariationsInitialized(false),
|
2017-12-10 17:01:32 +03:00
|
|
|
mAspect(0.0), mFontData(nullptr), mLength(0)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
// italic
|
|
|
|
int slant;
|
|
|
|
if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
|
|
|
|
slant = FC_SLANT_ROMAN;
|
|
|
|
}
|
2015-10-19 05:16:43 +03:00
|
|
|
if (slant == FC_SLANT_OBLIQUE) {
|
2018-04-25 09:18:23 +03:00
|
|
|
mStyleRange = SlantStyleRange(FontSlantStyle::Oblique());
|
2015-10-19 05:16:43 +03:00
|
|
|
} else if (slant > 0) {
|
2018-04-25 09:18:23 +03:00
|
|
|
mStyleRange = SlantStyleRange(FontSlantStyle::Italic());
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// weight
|
|
|
|
int weight;
|
|
|
|
if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) != FcResultMatch) {
|
|
|
|
weight = FC_WEIGHT_REGULAR;
|
|
|
|
}
|
2018-04-25 09:18:23 +03:00
|
|
|
mWeightRange = WeightRange(MapFcWeight(weight));
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
// width
|
|
|
|
int width;
|
|
|
|
if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
|
|
|
|
width = FC_WIDTH_NORMAL;
|
|
|
|
}
|
2018-04-25 09:18:23 +03:00
|
|
|
mStretchRange = StretchRange(MapFcWidth(width));
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
2017-08-01 13:25:35 +03:00
|
|
|
gfxFontEntry*
|
|
|
|
gfxFontconfigFontEntry::Clone() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
|
|
|
|
return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap);
|
|
|
|
}
|
|
|
|
|
2018-01-06 13:58:23 +03:00
|
|
|
static FcPattern*
|
|
|
|
CreatePatternForFace(FT_Face aFace)
|
|
|
|
{
|
|
|
|
// 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.
|
|
|
|
FcPattern* pattern =
|
|
|
|
FcFreeTypeQueryFace(aFace, ToFcChar8Ptr(""), 0, nullptr);
|
|
|
|
// given that we have a FT_Face, not really sure this is possible...
|
|
|
|
if (!pattern) {
|
|
|
|
pattern = FcPatternCreate();
|
|
|
|
}
|
|
|
|
FcPatternDel(pattern, FC_FILE);
|
|
|
|
FcPatternDel(pattern, FC_INDEX);
|
|
|
|
|
|
|
|
// Make a new pattern and store the face in it so that cairo uses
|
|
|
|
// that when creating a cairo font face.
|
|
|
|
FcPatternAddFTFace(pattern, FC_FT_FACE, aFace);
|
|
|
|
|
|
|
|
return pattern;
|
|
|
|
}
|
|
|
|
|
|
|
|
static FT_Face
|
|
|
|
CreateFaceForPattern(FcPattern* aPattern)
|
|
|
|
{
|
|
|
|
FcChar8 *filename;
|
|
|
|
if (FcPatternGetString(aPattern, FC_FILE, 0, &filename) != FcResultMatch) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
int index;
|
|
|
|
if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) {
|
|
|
|
index = 0; // default to 0 if not found in pattern
|
|
|
|
}
|
|
|
|
return Factory::NewFTFace(nullptr, ToCharPtr(filename), index);
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
|
2018-04-25 09:18:23 +03:00
|
|
|
WeightRange aWeight,
|
|
|
|
StretchRange aStretch,
|
|
|
|
SlantStyleRange aStyle,
|
2015-05-13 08:11:25 +03:00
|
|
|
const uint8_t *aData,
|
2017-12-10 17:01:32 +03:00
|
|
|
uint32_t aLength,
|
2015-05-13 08:11:25 +03:00
|
|
|
FT_Face aFace)
|
2015-07-08 08:44:32 +03:00
|
|
|
: gfxFontEntry(aFaceName),
|
2015-05-13 08:11:25 +03:00
|
|
|
mFTFace(aFace), mFTFaceInitialized(true),
|
2016-06-15 16:35:05 +03:00
|
|
|
mIgnoreFcCharmap(true),
|
2018-04-25 09:18:23 +03:00
|
|
|
mHasVariationsInitialized(false),
|
2017-12-10 17:01:32 +03:00
|
|
|
mAspect(0.0), mFontData(aData), mLength(aLength)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
2018-04-25 09:18:23 +03:00
|
|
|
mWeightRange = aWeight;
|
|
|
|
mStyleRange = aStyle;
|
|
|
|
mStretchRange = aStretch;
|
2015-05-13 08:11:25 +03:00
|
|
|
mIsDataUserFont = true;
|
|
|
|
|
2018-01-06 13:58:23 +03:00
|
|
|
mFontPattern = CreatePatternForFace(mFTFace);
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
mUserFontData = new FTUserFontData(mFTFace, mFontData);
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
|
2015-05-13 08:11:25 +03:00
|
|
|
FcPattern* aFontPattern,
|
2018-04-25 09:18:23 +03:00
|
|
|
WeightRange aWeight,
|
|
|
|
StretchRange aStretch,
|
|
|
|
SlantStyleRange aStyle)
|
2015-05-13 08:11:25 +03:00
|
|
|
: gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
|
|
|
|
mFTFace(nullptr), mFTFaceInitialized(false),
|
2018-04-25 09:18:23 +03:00
|
|
|
mHasVariationsInitialized(false),
|
2017-12-10 17:01:32 +03:00
|
|
|
mAspect(0.0), mFontData(nullptr), mLength(0)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
2018-04-25 09:18:23 +03:00
|
|
|
mWeightRange = aWeight;
|
|
|
|
mStyleRange = aStyle;
|
|
|
|
mStretchRange = aStretch;
|
2015-05-13 08:11:25 +03:00
|
|
|
mIsLocalUserFont = true;
|
2016-06-15 16:35:05 +03:00
|
|
|
|
|
|
|
// The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
|
|
|
|
// via src:local()...
|
|
|
|
// If the local font happens to come from the application fontset,
|
|
|
|
// we want to set it to true so that color/svg fonts will work even
|
|
|
|
// if the default glyphs are blank; but if the local font is a non-
|
|
|
|
// sfnt face (e.g. legacy type 1) then we need to set it to false
|
|
|
|
// because our cmap-reading code will fail and we depend on FT+Fc to
|
|
|
|
// determine the coverage.
|
|
|
|
// We set the flag here, but may flip it the first time TestCharacterMap
|
|
|
|
// is called, at which point we'll look to see whether a 'cmap' is
|
|
|
|
// actually present in the font.
|
|
|
|
mIgnoreFcCharmap = true;
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
2018-01-29 16:24:11 +03:00
|
|
|
typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
|
|
|
|
typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
|
|
|
|
static GetVarFunc sGetVar;
|
|
|
|
static DoneVarFunc sDoneVar;
|
|
|
|
static bool sInitializedVarFuncs = false;
|
|
|
|
|
|
|
|
static void
|
|
|
|
InitializeVarFuncs()
|
|
|
|
{
|
|
|
|
if (sInitializedVarFuncs) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sInitializedVarFuncs = true;
|
2018-02-01 18:28:09 +03:00
|
|
|
#if MOZ_TREE_FREETYPE
|
|
|
|
sGetVar = &FT_Get_MM_Var;
|
|
|
|
sDoneVar = &FT_Done_MM_Var;
|
|
|
|
#else
|
2018-01-29 16:24:11 +03:00
|
|
|
sGetVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
|
|
|
|
sDoneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
|
2018-02-01 18:28:09 +03:00
|
|
|
#endif
|
2018-01-29 16:24:11 +03:00
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::~gfxFontconfigFontEntry()
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
2018-01-29 16:24:11 +03:00
|
|
|
if (mMMVar) {
|
|
|
|
// Prior to freetype 2.9, there was no specific function to free the
|
|
|
|
// FT_MM_Var record, and the docs just said to use free().
|
|
|
|
// InitializeVarFuncs must have been called in order for mMMVar to be
|
|
|
|
// non-null here, so we don't need to do it again.
|
|
|
|
if (sDoneVar) {
|
|
|
|
MOZ_ASSERT(mFTFace, "How did mMMVar get set without a face?");
|
|
|
|
(*sDoneVar)(mFTFace->glyph->library, mMMVar);
|
|
|
|
} else {
|
|
|
|
free(mMMVar);
|
|
|
|
}
|
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
// attempt this once, if errors occur leave a blank cmap
|
|
|
|
if (mCharacterMap) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<gfxCharacterMap> charmap;
|
2015-05-13 08:11:25 +03:00
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
|
2017-09-11 21:23:30 +03:00
|
|
|
mUVSOffset))) {
|
2015-05-13 08:11:25 +03:00
|
|
|
rv = NS_OK;
|
|
|
|
} else {
|
|
|
|
uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
|
|
|
|
charmap = new gfxCharacterMap();
|
|
|
|
AutoTable cmapTable(this, kCMAP);
|
|
|
|
|
|
|
|
if (cmapTable) {
|
|
|
|
uint32_t cmapLen;
|
|
|
|
const uint8_t* cmapData =
|
|
|
|
reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
|
|
|
|
&cmapLen));
|
|
|
|
rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
|
2017-09-11 21:23:30 +03:00
|
|
|
*charmap, mUVSOffset);
|
2015-05-13 08:11:25 +03:00
|
|
|
} 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();
|
|
|
|
}
|
|
|
|
|
2017-07-26 23:03:57 +03:00
|
|
|
LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
|
2015-05-13 08:11:25 +03:00
|
|
|
NS_ConvertUTF16toUTF8(mName).get(),
|
|
|
|
charmap->SizeOfIncludingThis(moz_malloc_size_of),
|
|
|
|
charmap->mHash, mCharacterMap == charmap ? " new" : ""));
|
|
|
|
if (LOG_CMAPDATA_ENABLED()) {
|
|
|
|
char prefix[256];
|
2016-08-17 01:41:12 +03:00
|
|
|
SprintfLiteral(prefix, "(cmapdata) name: %.220s",
|
|
|
|
NS_ConvertUTF16toUTF8(mName).get());
|
2015-05-13 08:11:25 +03:00
|
|
|
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
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
2016-06-15 16:35:05 +03:00
|
|
|
// For user fonts, or for fonts bundled with the app (which might include
|
|
|
|
// color/svg glyphs where the default glyphs may be blank, and thus confuse
|
|
|
|
// fontconfig/freetype's char map checking), we instead check the cmap
|
|
|
|
// directly for character coverage.
|
|
|
|
if (mIgnoreFcCharmap) {
|
|
|
|
// If it does not actually have a cmap, switch our strategy to use
|
|
|
|
// fontconfig's charmap after all (except for data fonts, which must
|
|
|
|
// always have a cmap to have passed OTS validation).
|
|
|
|
if (!mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('c','m','a','p'))) {
|
|
|
|
mIgnoreFcCharmap = false;
|
|
|
|
// ...and continue with HasChar() below.
|
|
|
|
} else {
|
|
|
|
return gfxFontEntry::TestCharacterMap(aCh);
|
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
2016-06-15 16:35:05 +03:00
|
|
|
// otherwise (for system fonts), use the charmap in the pattern
|
|
|
|
return HasChar(mFontPattern, aCh);
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
hb_blob_t*
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
// for data fonts, read directly from the font data
|
|
|
|
if (mFontData) {
|
2016-08-19 15:57:05 +03:00
|
|
|
return gfxFontUtils::GetTableFromFontData(mFontData, aTableTag);
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return gfxFontEntry::GetFontTable(aTableTag);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::MaybeReleaseFTFace()
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
// 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) {
|
2018-01-29 16:24:11 +03:00
|
|
|
if (mMMVar) {
|
|
|
|
if (sDoneVar) {
|
|
|
|
(*sDoneVar)(mFTFace->glyph->library, mMMVar);
|
|
|
|
} else {
|
|
|
|
free(mMMVar);
|
|
|
|
}
|
|
|
|
mMMVar = nullptr;
|
|
|
|
}
|
2017-05-18 04:56:24 +03:00
|
|
|
Factory::ReleaseFTFace(mFTFace);
|
2015-05-13 08:11:25 +03:00
|
|
|
mFTFace = nullptr;
|
|
|
|
}
|
|
|
|
mFTFaceInitialized = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::ForgetHBFace()
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
gfxFontEntry::ForgetHBFace();
|
|
|
|
MaybeReleaseFTFace();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::ReleaseGrFace(gr_face* aFace)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
gfxFontEntry::ReleaseGrFace(aFace);
|
|
|
|
MaybeReleaseFTFace();
|
|
|
|
}
|
|
|
|
|
|
|
|
double
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::GetAspect()
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
2017-07-05 22:18:51 +03:00
|
|
|
if (mAspect != 0.0) {
|
|
|
|
return mAspect;
|
|
|
|
}
|
|
|
|
|
|
|
|
// try to compute aspect from OS/2 metrics if available
|
|
|
|
AutoTable os2Table(this, TRUETYPE_TAG('O','S','/','2'));
|
|
|
|
if (os2Table) {
|
|
|
|
uint16_t upem = UnitsPerEm();
|
|
|
|
if (upem != kInvalidUPEM) {
|
|
|
|
uint32_t len;
|
|
|
|
auto os2 = reinterpret_cast<const OS2Table*>
|
|
|
|
(hb_blob_get_data(os2Table, &len));
|
|
|
|
if (uint16_t(os2->version) >= 2) {
|
|
|
|
if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
|
|
|
|
int16_t(os2->sxHeight) > 0.1 * upem) {
|
|
|
|
mAspect = double(int16_t(os2->sxHeight)) / upem;
|
|
|
|
return mAspect;
|
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-07-05 22:18:51 +03:00
|
|
|
|
|
|
|
// default to aspect = 0.5 if the code below fails
|
|
|
|
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
|
Bug 1449605 - part 1 - Rearrange thebes font code so that the decision whether to apply synthetic-bold is deferred until actually instantiating a font, not made during the font-matching process. r=jwatt
This rearranges how synthetic-bold use is determined in the font selection
& rendering code. Previously, we would decide during the font-selection
algorithm whether we need to apply synthetic-bold to the chosen face, and
then pass that decision through the fontgroup (storing it in the FamilyFace
entries of the mFonts array there) down to the actual rendering code that
instantiates fonts from the faces (font entries) we've selected.
That became a problem for variation fonts because in the case of a user
font, we may not have downloaded the resource yet, so we just have a "user
font container" entry, which carries the descriptors from the @font-face
rule and will fetch the actual resource when needed. But in the case of a
@font-face rule without a weight descriptor, we don't actually know at
font-selection time whether the face will support "true" bold (via a
variation axis) or not, so we can't reliably make the right decision about
applying synthetic bold.
So we now defer that decision until we actually instantiate a platform font
object to shape/measure/draw text. At that point, we have the requested
style and we also have the real font resource, so we can easily determine
whether fake-bold is required.
(This patch should not result in any visible behavior change; that will
come in a second patch now that the architecture supports it.)
2018-05-01 12:30:50 +03:00
|
|
|
RefPtr<gfxFont> font = FindOrMakeFont(&s);
|
2017-07-05 22:18:51 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
return mAspect;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
PrepareFontOptions(FcPattern* aPattern,
|
|
|
|
cairo_font_options_t* aFontOptions)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aFontOptions, "null font options passed to PrepareFontOptions");
|
|
|
|
|
2015-05-20 18:44:09 +03:00
|
|
|
// xxx - taken from the gfxFontconfigFonts code, needs to be reviewed
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
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:
|
2015-11-24 20:39:04 +03:00
|
|
|
MOZ_FALLTHROUGH;
|
2015-05-13 08:11:25 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-05-18 04:56:24 +03:00
|
|
|
static void
|
|
|
|
ReleaseFTUserFontData(void* aData)
|
|
|
|
{
|
|
|
|
static_cast<FTUserFontData*>(aData)->Release();
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
cairo_scaled_font_t*
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry::CreateScaledFont(FcPattern* aRenderPattern,
|
2017-01-13 01:37:07 +03:00
|
|
|
gfxFloat aAdjustedSize,
|
Bug 1449605 - part 1 - Rearrange thebes font code so that the decision whether to apply synthetic-bold is deferred until actually instantiating a font, not made during the font-matching process. r=jwatt
This rearranges how synthetic-bold use is determined in the font selection
& rendering code. Previously, we would decide during the font-selection
algorithm whether we need to apply synthetic-bold to the chosen face, and
then pass that decision through the fontgroup (storing it in the FamilyFace
entries of the mFonts array there) down to the actual rendering code that
instantiates fonts from the faces (font entries) we've selected.
That became a problem for variation fonts because in the case of a user
font, we may not have downloaded the resource yet, so we just have a "user
font container" entry, which carries the descriptors from the @font-face
rule and will fetch the actual resource when needed. But in the case of a
@font-face rule without a weight descriptor, we don't actually know at
font-selection time whether the face will support "true" bold (via a
variation axis) or not, so we can't reliably make the right decision about
applying synthetic bold.
So we now defer that decision until we actually instantiate a platform font
object to shape/measure/draw text. At that point, we have the requested
style and we also have the real font resource, so we can easily determine
whether fake-bold is required.
(This patch should not result in any visible behavior change; that will
come in a second patch now that the architecture supports it.)
2018-05-01 12:30:50 +03:00
|
|
|
const gfxFontStyle *aStyle)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
Bug 1449605 - part 1 - Rearrange thebes font code so that the decision whether to apply synthetic-bold is deferred until actually instantiating a font, not made during the font-matching process. r=jwatt
This rearranges how synthetic-bold use is determined in the font selection
& rendering code. Previously, we would decide during the font-selection
algorithm whether we need to apply synthetic-bold to the chosen face, and
then pass that decision through the fontgroup (storing it in the FamilyFace
entries of the mFonts array there) down to the actual rendering code that
instantiates fonts from the faces (font entries) we've selected.
That became a problem for variation fonts because in the case of a user
font, we may not have downloaded the resource yet, so we just have a "user
font container" entry, which carries the descriptors from the @font-face
rule and will fetch the actual resource when needed. But in the case of a
@font-face rule without a weight descriptor, we don't actually know at
font-selection time whether the face will support "true" bold (via a
variation axis) or not, so we can't reliably make the right decision about
applying synthetic bold.
So we now defer that decision until we actually instantiate a platform font
object to shape/measure/draw text. At that point, we have the requested
style and we also have the real font resource, so we can easily determine
whether fake-bold is required.
(This patch should not result in any visible behavior change; that will
come in a second patch now that the architecture supports it.)
2018-05-01 12:30:50 +03:00
|
|
|
if (aStyle->NeedsSyntheticBold(this)) {
|
2015-05-13 08:11:25 +03:00
|
|
|
FcPatternAddBool(aRenderPattern, FC_EMBOLDEN, FcTrue);
|
|
|
|
}
|
|
|
|
|
2017-11-17 13:54:21 +03:00
|
|
|
// will synthetic oblique be applied using a transform?
|
2015-10-19 05:16:43 +03:00
|
|
|
bool needsOblique = IsUpright() &&
|
2018-04-23 17:52:20 +03:00
|
|
|
aStyle->style != FontSlantStyle::Normal() &&
|
2015-10-19 05:16:43 +03:00
|
|
|
aStyle->allowSyntheticStyle;
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
if (needsOblique) {
|
|
|
|
// disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
|
|
|
|
FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP);
|
|
|
|
FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
|
|
|
|
}
|
|
|
|
|
2017-12-07 16:22:49 +03:00
|
|
|
AutoTArray<FT_Fixed,8> coords;
|
2018-04-25 09:18:23 +03:00
|
|
|
if (HasVariations()) {
|
2017-12-07 16:22:49 +03:00
|
|
|
FT_Face ftFace = GetFTFace();
|
|
|
|
if (ftFace) {
|
2018-04-25 09:18:23 +03:00
|
|
|
AutoTArray<gfxFontVariation,8> settings;
|
|
|
|
GetVariationsForStyle(settings, *aStyle);
|
|
|
|
gfxFT2FontBase::SetupVarCoords(ftFace, settings, &coords);
|
2017-12-07 16:22:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
cairo_font_face_t *face =
|
2017-12-07 16:22:49 +03:00
|
|
|
cairo_ft_font_face_create_for_pattern(aRenderPattern,
|
|
|
|
coords.Elements(),
|
|
|
|
coords.Length());
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
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");
|
2017-10-31 05:31:11 +03:00
|
|
|
mUserFontData.get()->AddRef();
|
2017-05-18 04:56:24 +03:00
|
|
|
if (cairo_font_face_set_user_data(face,
|
|
|
|
&sFcFontlistUserFontDataKey,
|
|
|
|
mUserFontData,
|
|
|
|
ReleaseFTUserFontData) != CAIRO_STATUS_SUCCESS) {
|
|
|
|
NS_WARNING("Failed binding FTUserFontData to Cairo font face");
|
2017-10-31 05:31:11 +03:00
|
|
|
mUserFontData.get()->Release();
|
2017-05-18 04:56:24 +03:00
|
|
|
cairo_font_face_destroy(face);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
cairo_scaled_font_t *scaledFont = nullptr;
|
|
|
|
|
|
|
|
cairo_matrix_t sizeMatrix;
|
|
|
|
cairo_matrix_t identityMatrix;
|
|
|
|
|
2017-01-13 01:37:07 +03:00
|
|
|
cairo_matrix_init_scale(&sizeMatrix, aAdjustedSize, aAdjustedSize);
|
2015-05-13 08:11:25 +03:00
|
|
|
cairo_matrix_init_identity(&identityMatrix);
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2016-07-20 21:18:21 +03:00
|
|
|
#ifdef MOZ_X11
|
|
|
|
static bool
|
|
|
|
GetXftInt(Display* aDisplay, const char* aName, int* aResult)
|
|
|
|
{
|
|
|
|
if (!aDisplay) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
char* value = XGetDefault(aDisplay, "Xft", aName);
|
|
|
|
if (!value) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
char* end;
|
|
|
|
*aResult = strtol(value, &end, 0);
|
|
|
|
if (end != value) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
static void
|
|
|
|
PreparePattern(FcPattern* aPattern, bool aIsPrinterFont)
|
|
|
|
{
|
2015-05-19 08:30:55 +03:00
|
|
|
FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
// 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);
|
2017-04-04 17:22:00 +03:00
|
|
|
} else if (!gfxPlatform::IsHeadless()) {
|
2015-05-13 08:11:25 +03:00
|
|
|
#ifdef MOZ_WIDGET_GTK
|
2017-01-11 20:24:39 +03:00
|
|
|
ApplyGdkScreenFontOptions(aPattern);
|
2016-07-20 21:18:21 +03:00
|
|
|
|
|
|
|
#ifdef MOZ_X11
|
|
|
|
FcValue value;
|
|
|
|
int lcdfilter;
|
2017-01-11 20:24:39 +03:00
|
|
|
if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value) == FcResultNoMatch) {
|
|
|
|
GdkDisplay* dpy = gdk_display_get_default();
|
|
|
|
if (GDK_IS_X11_DISPLAY(dpy) &&
|
|
|
|
GetXftInt(GDK_DISPLAY_XDISPLAY(dpy), "lcdfilter", &lcdfilter)) {
|
|
|
|
FcPatternAddInteger(aPattern, FC_LCD_FILTER, lcdfilter);
|
|
|
|
}
|
2016-07-20 21:18:21 +03:00
|
|
|
}
|
2017-01-11 20:24:39 +03:00
|
|
|
#endif // MOZ_X11
|
|
|
|
#endif // MOZ_WIDGET_GTK
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
FcDefaultSubstitute(aPattern);
|
|
|
|
}
|
|
|
|
|
2017-04-07 00:41:02 +03:00
|
|
|
void
|
|
|
|
gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) {
|
|
|
|
if (aIndex > 0) {
|
2017-10-18 21:22:09 +03:00
|
|
|
ThreadSafeWeakPtr<UnscaledFontFontconfig> front =
|
2018-05-30 22:15:35 +03:00
|
|
|
std::move(mUnscaledFonts[aIndex]);
|
2017-04-07 00:41:02 +03:00
|
|
|
for (size_t i = aIndex; i > 0; i--) {
|
2018-05-30 22:15:35 +03:00
|
|
|
mUnscaledFonts[i] = std::move(mUnscaledFonts[i-1]);
|
2017-04-07 00:41:02 +03:00
|
|
|
}
|
2018-05-30 22:15:35 +03:00
|
|
|
mUnscaledFonts[0] = std::move(front);
|
2017-04-07 00:41:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<UnscaledFontFontconfig>
|
|
|
|
gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const char* aFile, uint32_t aIndex) {
|
|
|
|
for (size_t i = 0; i < kNumEntries; i++) {
|
2017-10-18 21:22:09 +03:00
|
|
|
RefPtr<UnscaledFontFontconfig> entry(mUnscaledFonts[i]);
|
2017-04-07 00:41:02 +03:00
|
|
|
if (entry &&
|
|
|
|
!strcmp(entry->GetFile(), aFile) &&
|
|
|
|
entry->GetIndex() == aIndex) {
|
|
|
|
MoveToFront(i);
|
2017-10-18 21:22:09 +03:00
|
|
|
return entry.forget();
|
2017-04-07 00:41:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-03-29 20:47:46 +03:00
|
|
|
static inline gfxFloat
|
|
|
|
SizeForStyle(gfxFontconfigFontEntry* aEntry, const gfxFontStyle& aStyle)
|
|
|
|
{
|
|
|
|
return aStyle.sizeAdjust >= 0.0 ?
|
|
|
|
aStyle.GetAdjustedSize(aEntry->GetAspect()) :
|
|
|
|
aStyle.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static double
|
|
|
|
ChooseFontSize(gfxFontconfigFontEntry* aEntry,
|
|
|
|
const gfxFontStyle& aStyle)
|
|
|
|
{
|
|
|
|
double requestedSize = SizeForStyle(aEntry, aStyle);
|
|
|
|
double bestDist = -1.0;
|
|
|
|
double bestSize = requestedSize;
|
|
|
|
double size;
|
|
|
|
int v = 0;
|
|
|
|
while (FcPatternGetDouble(aEntry->GetPattern(),
|
|
|
|
FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
|
|
|
|
++v;
|
|
|
|
double dist = fabs(size - requestedSize);
|
|
|
|
if (bestDist < 0.0 || dist < bestDist) {
|
|
|
|
bestDist = dist;
|
|
|
|
bestSize = size;
|
|
|
|
}
|
|
|
|
}
|
2017-05-05 22:46:10 +03:00
|
|
|
// If the font has bitmaps but wants to be scaled, then let it scale.
|
|
|
|
if (bestSize >= 0.0) {
|
|
|
|
FcBool scalable;
|
|
|
|
if (FcPatternGetBool(aEntry->GetPattern(),
|
|
|
|
FC_SCALABLE, 0, &scalable) == FcResultMatch &&
|
|
|
|
scalable) {
|
|
|
|
return requestedSize;
|
|
|
|
}
|
|
|
|
}
|
2017-03-29 20:47:46 +03:00
|
|
|
return bestSize;
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
gfxFont*
|
Bug 1449605 - part 1 - Rearrange thebes font code so that the decision whether to apply synthetic-bold is deferred until actually instantiating a font, not made during the font-matching process. r=jwatt
This rearranges how synthetic-bold use is determined in the font selection
& rendering code. Previously, we would decide during the font-selection
algorithm whether we need to apply synthetic-bold to the chosen face, and
then pass that decision through the fontgroup (storing it in the FamilyFace
entries of the mFonts array there) down to the actual rendering code that
instantiates fonts from the faces (font entries) we've selected.
That became a problem for variation fonts because in the case of a user
font, we may not have downloaded the resource yet, so we just have a "user
font container" entry, which carries the descriptors from the @font-face
rule and will fetch the actual resource when needed. But in the case of a
@font-face rule without a weight descriptor, we don't actually know at
font-selection time whether the face will support "true" bold (via a
variation axis) or not, so we can't reliably make the right decision about
applying synthetic bold.
So we now defer that decision until we actually instantiate a platform font
object to shape/measure/draw text. At that point, we have the requested
style and we also have the real font resource, so we can easily determine
whether fake-bold is required.
(This patch should not result in any visible behavior change; that will
come in a second patch now that the architecture supports it.)
2018-05-01 12:30:50 +03:00
|
|
|
gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
nsAutoRef<FcPattern> pattern(FcPatternCreate());
|
2016-08-17 19:01:01 +03:00
|
|
|
if (!pattern) {
|
|
|
|
NS_WARNING("Failed to create Fontconfig pattern for font instance");
|
|
|
|
return nullptr;
|
|
|
|
}
|
2017-03-29 20:47:46 +03:00
|
|
|
|
|
|
|
double size = ChooseFontSize(this, *aFontStyle);
|
|
|
|
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
|
2015-05-13 08:11:25 +03:00
|
|
|
|
2018-01-06 13:58:23 +03:00
|
|
|
FT_Face face = mFTFace;
|
2017-12-10 17:01:32 +03:00
|
|
|
FcPattern* fontPattern = mFontPattern;
|
2018-01-06 13:58:23 +03:00
|
|
|
if (face && face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
|
2018-01-06 13:58:23 +03:00
|
|
|
// For variation fonts, we create a new FT_Face and FcPattern here
|
2018-01-06 13:58:23 +03:00
|
|
|
// so that variation coordinates from the style can be applied
|
|
|
|
// without affecting other font instances created from the same
|
|
|
|
// entry (font resource).
|
|
|
|
if (mFontData) {
|
2018-01-06 13:58:23 +03:00
|
|
|
// For user fonts: create a new FT_Face from the font data, and then
|
|
|
|
// make a pattern from that.
|
2017-12-10 17:01:32 +03:00
|
|
|
face = Factory::NewFTFaceFromData(nullptr, mFontData, mLength, 0);
|
2018-01-06 13:58:23 +03:00
|
|
|
fontPattern = CreatePatternForFace(face);
|
2018-01-06 13:58:23 +03:00
|
|
|
} else {
|
2018-01-06 13:58:23 +03:00
|
|
|
// For system fonts: create a new FT_Face and store it in a copy of
|
|
|
|
// the original mFontPattern.
|
2018-01-06 13:58:23 +03:00
|
|
|
fontPattern = FcPatternDuplicate(mFontPattern);
|
2018-01-06 13:58:23 +03:00
|
|
|
face = CreateFaceForPattern(fontPattern);
|
|
|
|
if (face) {
|
|
|
|
FcPatternAddFTFace(fontPattern, FC_FT_FACE, face);
|
|
|
|
} else {
|
|
|
|
// I don't think CreateFaceForPattern above should ever fail,
|
|
|
|
// but just in case let's fall back here.
|
|
|
|
face = mFTFace;
|
|
|
|
}
|
2017-12-10 17:01:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
PreparePattern(pattern, aFontStyle->printerFont);
|
|
|
|
nsAutoRef<FcPattern> renderPattern
|
2017-12-10 17:01:32 +03:00
|
|
|
(FcFontRenderPrepare(nullptr, pattern, fontPattern));
|
|
|
|
if (fontPattern != mFontPattern) {
|
|
|
|
// Discard temporary pattern used for variation support
|
|
|
|
FcPatternDestroy(fontPattern);
|
|
|
|
}
|
2016-08-17 19:01:01 +03:00
|
|
|
if (!renderPattern) {
|
|
|
|
NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
cairo_scaled_font_t* scaledFont =
|
Bug 1449605 - part 1 - Rearrange thebes font code so that the decision whether to apply synthetic-bold is deferred until actually instantiating a font, not made during the font-matching process. r=jwatt
This rearranges how synthetic-bold use is determined in the font selection
& rendering code. Previously, we would decide during the font-selection
algorithm whether we need to apply synthetic-bold to the chosen face, and
then pass that decision through the fontgroup (storing it in the FamilyFace
entries of the mFonts array there) down to the actual rendering code that
instantiates fonts from the faces (font entries) we've selected.
That became a problem for variation fonts because in the case of a user
font, we may not have downloaded the resource yet, so we just have a "user
font container" entry, which carries the descriptors from the @font-face
rule and will fetch the actual resource when needed. But in the case of a
@font-face rule without a weight descriptor, we don't actually know at
font-selection time whether the face will support "true" bold (via a
variation axis) or not, so we can't reliably make the right decision about
applying synthetic bold.
So we now defer that decision until we actually instantiate a platform font
object to shape/measure/draw text. At that point, we have the requested
style and we also have the real font resource, so we can easily determine
whether fake-bold is required.
(This patch should not result in any visible behavior change; that will
come in a second patch now that the architecture supports it.)
2018-05-01 12:30:50 +03:00
|
|
|
CreateScaledFont(renderPattern, size, aFontStyle);
|
2017-04-07 00:41:02 +03:00
|
|
|
|
|
|
|
const FcChar8* file = ToFcChar8Ptr("");
|
|
|
|
int index = 0;
|
|
|
|
if (!mFontData) {
|
|
|
|
if (FcPatternGetString(renderPattern, FC_FILE, 0,
|
|
|
|
const_cast<FcChar8**>(&file)) != FcResultMatch ||
|
|
|
|
FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) != FcResultMatch) {
|
|
|
|
NS_WARNING("No file in Fontconfig pattern for font instance");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<UnscaledFontFontconfig> unscaledFont =
|
|
|
|
mUnscaledFontCache.Lookup(ToCharPtr(file), index);
|
|
|
|
if (!unscaledFont) {
|
|
|
|
unscaledFont =
|
|
|
|
mFontData ?
|
2017-12-10 17:01:32 +03:00
|
|
|
new UnscaledFontFontconfig(face) :
|
2017-04-07 00:41:02 +03:00
|
|
|
new UnscaledFontFontconfig(ToCharPtr(file), index);
|
|
|
|
mUnscaledFontCache.Add(unscaledFont);
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
gfxFont* newFont =
|
2017-04-07 00:41:02 +03:00
|
|
|
new gfxFontconfigFont(unscaledFont, scaledFont,
|
|
|
|
renderPattern, size,
|
Bug 1449605 - part 1 - Rearrange thebes font code so that the decision whether to apply synthetic-bold is deferred until actually instantiating a font, not made during the font-matching process. r=jwatt
This rearranges how synthetic-bold use is determined in the font selection
& rendering code. Previously, we would decide during the font-selection
algorithm whether we need to apply synthetic-bold to the chosen face, and
then pass that decision through the fontgroup (storing it in the FamilyFace
entries of the mFonts array there) down to the actual rendering code that
instantiates fonts from the faces (font entries) we've selected.
That became a problem for variation fonts because in the case of a user
font, we may not have downloaded the resource yet, so we just have a "user
font container" entry, which carries the descriptors from the @font-face
rule and will fetch the actual resource when needed. But in the case of a
@font-face rule without a weight descriptor, we don't actually know at
font-selection time whether the face will support "true" bold (via a
variation axis) or not, so we can't reliably make the right decision about
applying synthetic bold.
So we now defer that decision until we actually instantiate a platform font
object to shape/measure/draw text. At that point, we have the requested
style and we also have the real font resource, so we can easily determine
whether fake-bold is required.
(This patch should not result in any visible behavior change; that will
come in a second patch now that the architecture supports it.)
2018-05-01 12:30:50 +03:00
|
|
|
this, aFontStyle);
|
2015-05-13 08:11:25 +03:00
|
|
|
cairo_scaled_font_destroy(scaledFont);
|
|
|
|
|
|
|
|
return newFont;
|
|
|
|
}
|
|
|
|
|
2017-12-07 16:22:49 +03:00
|
|
|
FT_Face
|
|
|
|
gfxFontconfigFontEntry::GetFTFace()
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
if (!mFTFaceInitialized) {
|
|
|
|
mFTFaceInitialized = true;
|
2018-01-06 13:58:23 +03:00
|
|
|
mFTFace = CreateFaceForPattern(mFontPattern);
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
2017-12-07 16:22:49 +03:00
|
|
|
return mFTFace;
|
|
|
|
}
|
|
|
|
|
2018-01-18 22:27:41 +03:00
|
|
|
bool
|
|
|
|
gfxFontconfigFontEntry::HasVariations()
|
|
|
|
{
|
2018-04-25 09:18:23 +03:00
|
|
|
if (mHasVariationsInitialized) {
|
|
|
|
return mHasVariations;
|
|
|
|
}
|
|
|
|
mHasVariationsInitialized = true;
|
|
|
|
mHasVariations = false;
|
|
|
|
|
2018-04-25 16:03:44 +03:00
|
|
|
if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
|
|
|
|
return mHasVariations;
|
|
|
|
}
|
|
|
|
|
2018-04-25 09:18:23 +03:00
|
|
|
// For installed fonts, query the fontconfig pattern rather than paying
|
|
|
|
// the cost of loading a FT_Face that we otherwise might never need.
|
|
|
|
if (!IsUserFont() || IsLocalUserFont()) {
|
|
|
|
FcBool variable;
|
|
|
|
if ((FcPatternGetBool(mFontPattern, FC_VARIABLE, 0,
|
|
|
|
&variable) == FcResultMatch) && variable) {
|
|
|
|
mHasVariations = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
FT_Face face = GetFTFace();
|
|
|
|
if (face) {
|
|
|
|
mHasVariations = face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS;
|
|
|
|
}
|
2018-01-18 22:27:41 +03:00
|
|
|
}
|
2018-04-25 09:18:23 +03:00
|
|
|
|
|
|
|
return mHasVariations;
|
2018-01-18 22:27:41 +03:00
|
|
|
}
|
|
|
|
|
2018-01-29 16:24:11 +03:00
|
|
|
FT_MM_Var*
|
|
|
|
gfxFontconfigFontEntry::GetMMVar()
|
2018-01-18 22:27:41 +03:00
|
|
|
{
|
2018-01-29 16:24:11 +03:00
|
|
|
if (mMMVarInitialized) {
|
|
|
|
return mMMVar;
|
|
|
|
}
|
|
|
|
mMMVarInitialized = true;
|
|
|
|
InitializeVarFuncs();
|
|
|
|
if (!sGetVar) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2018-01-18 22:27:41 +03:00
|
|
|
FT_Face face = GetFTFace();
|
|
|
|
if (!face) {
|
2018-01-29 16:24:11 +03:00
|
|
|
return nullptr;
|
2018-01-18 22:27:41 +03:00
|
|
|
}
|
2018-01-29 16:24:11 +03:00
|
|
|
if (FT_Err_Ok != (*sGetVar)(face, &mMMVar)) {
|
|
|
|
mMMVar = nullptr;
|
2018-01-18 22:27:41 +03:00
|
|
|
}
|
2018-01-29 16:24:11 +03:00
|
|
|
return mMMVar;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFontconfigFontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes)
|
|
|
|
{
|
2018-04-26 19:08:18 +03:00
|
|
|
if (!HasVariations()) {
|
2018-01-18 22:27:41 +03:00
|
|
|
return;
|
|
|
|
}
|
2018-04-26 19:08:18 +03:00
|
|
|
gfxFT2Utils::GetVariationAxes(GetMMVar(), aAxes);
|
2018-01-29 16:24:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFontconfigFontEntry::GetVariationInstances(
|
|
|
|
nsTArray<gfxFontVariationInstance>& aInstances)
|
|
|
|
{
|
2018-04-26 19:08:18 +03:00
|
|
|
if (!HasVariations()) {
|
2018-01-29 16:24:11 +03:00
|
|
|
return;
|
|
|
|
}
|
2018-04-26 19:08:18 +03:00
|
|
|
gfxFT2Utils::GetVariationInstances(this, GetMMVar(), aInstances);
|
2018-01-18 22:27:41 +03:00
|
|
|
}
|
|
|
|
|
2017-12-07 16:22:49 +03:00
|
|
|
nsresult
|
|
|
|
gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
|
|
|
|
nsTArray<uint8_t>& aBuffer)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(!mIsDataUserFont,
|
|
|
|
"data fonts should be reading tables directly from memory");
|
2015-05-13 08:11:25 +03:00
|
|
|
|
2017-12-07 16:22:49 +03:00
|
|
|
FT_Face face = GetFTFace();
|
|
|
|
if (!face) {
|
2015-05-13 08:11:25 +03:00
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
FT_ULong length = 0;
|
2017-12-07 16:22:49 +03:00
|
|
|
if (FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &length) != 0) {
|
2015-05-13 08:11:25 +03:00
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
2015-06-11 00:30:41 +03:00
|
|
|
if (!aBuffer.SetLength(length, fallible)) {
|
2015-05-13 08:11:25 +03:00
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
2017-12-07 16:22:49 +03:00
|
|
|
if (FT_Load_Sfnt_Table(face, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
|
2015-05-13 08:11:25 +03:00
|
|
|
aBuffer.Clear();
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
if (mHasStyles) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add font entries for each of the faces
|
|
|
|
uint32_t numFonts = mFontPatterns.Length();
|
|
|
|
NS_ASSERTION(numFonts, "font family containing no faces!!");
|
2015-11-11 15:13:33 +03:00
|
|
|
uint32_t numRegularFaces = 0;
|
2015-05-13 08:11:25 +03:00
|
|
|
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;
|
|
|
|
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontEntry *fontEntry =
|
2016-06-15 16:35:05 +03:00
|
|
|
new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);
|
2018-04-25 16:03:44 +03:00
|
|
|
|
|
|
|
if (gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
|
|
|
|
fontEntry->SetupVariationRanges();
|
|
|
|
}
|
2018-04-25 09:18:23 +03:00
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
AddFontEntry(fontEntry);
|
|
|
|
|
2017-10-28 15:21:38 +03:00
|
|
|
if (fontEntry->IsNormalStyle()) {
|
2015-11-11 15:13:33 +03:00
|
|
|
numRegularFaces++;
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
if (LOG_FONTLIST_ENABLED()) {
|
2018-04-25 09:18:23 +03:00
|
|
|
nsAutoCString weightString;
|
|
|
|
fontEntry->Weight().ToString(weightString);
|
2018-04-25 09:18:23 +03:00
|
|
|
nsAutoCString stretchString;
|
|
|
|
fontEntry->Stretch().ToString(stretchString);
|
|
|
|
nsAutoCString styleString;
|
|
|
|
fontEntry->SlantStyle().ToString(styleString);
|
2015-05-13 08:11:25 +03:00
|
|
|
LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
|
2018-04-25 09:18:23 +03:00
|
|
|
" with style: %s weight: %s stretch: %s"
|
2015-05-13 08:11:25 +03:00
|
|
|
" psname: %s fullname: %s",
|
|
|
|
NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
|
|
|
|
NS_ConvertUTF16toUTF8(Name()).get(),
|
2018-04-25 09:18:23 +03:00
|
|
|
styleString.get(),
|
2018-04-25 09:18:23 +03:00
|
|
|
weightString.get(),
|
2018-04-25 09:18:23 +03:00
|
|
|
stretchString.get(),
|
2015-05-13 08:11:25 +03:00
|
|
|
NS_ConvertUTF16toUTF8(psname).get(),
|
|
|
|
NS_ConvertUTF16toUTF8(fullname).get()));
|
|
|
|
}
|
|
|
|
}
|
2015-11-11 15:13:33 +03:00
|
|
|
|
|
|
|
// somewhat arbitrary, but define a family with two or more regular
|
|
|
|
// faces as a family for which intra-family fallback should be used
|
|
|
|
if (numRegularFaces > 1) {
|
|
|
|
mCheckForFallbackFaces = true;
|
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
mFaceNamesInitialized = true;
|
|
|
|
mFontPatterns.Clear();
|
|
|
|
SetHasStyles(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
NS_ASSERTION(!mHasStyles,
|
|
|
|
"font patterns must not be added to already enumerated families");
|
|
|
|
|
2017-05-04 03:45:39 +03:00
|
|
|
FcBool outline;
|
|
|
|
if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) != FcResultMatch ||
|
|
|
|
!outline) {
|
2017-03-29 20:47:46 +03:00
|
|
|
mHasNonScalableFaces = true;
|
2017-05-05 22:46:10 +03:00
|
|
|
|
|
|
|
FcBool scalable;
|
|
|
|
if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) == FcResultMatch &&
|
|
|
|
scalable) {
|
|
|
|
mForceScalable = true;
|
|
|
|
}
|
2017-03-29 20:47:46 +03:00
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
nsCountedRef<FcPattern> pattern(aFontPattern);
|
|
|
|
mFontPatterns.AppendElement(pattern);
|
|
|
|
}
|
|
|
|
|
2017-03-29 20:47:46 +03:00
|
|
|
static const double kRejectDistance = 10000.0;
|
|
|
|
|
|
|
|
// Calculate a distance score representing the size disparity between the
|
|
|
|
// requested style's size and the font entry's size.
|
|
|
|
static double
|
2017-05-05 22:46:10 +03:00
|
|
|
SizeDistance(gfxFontconfigFontEntry* aEntry,
|
|
|
|
const gfxFontStyle& aStyle,
|
|
|
|
bool aForceScalable)
|
2017-03-29 20:47:46 +03:00
|
|
|
{
|
|
|
|
double requestedSize = SizeForStyle(aEntry, aStyle);
|
|
|
|
double bestDist = -1.0;
|
|
|
|
double size;
|
|
|
|
int v = 0;
|
|
|
|
while (FcPatternGetDouble(aEntry->GetPattern(),
|
|
|
|
FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
|
|
|
|
++v;
|
|
|
|
double dist = fabs(size - requestedSize);
|
|
|
|
if (bestDist < 0.0 || dist < bestDist) {
|
|
|
|
bestDist = dist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bestDist < 0.0) {
|
|
|
|
// No size means scalable
|
|
|
|
return -1.0;
|
2017-05-05 22:46:10 +03:00
|
|
|
} else if (aForceScalable || 5.0 * bestDist < requestedSize) {
|
2017-03-29 20:47:46 +03:00
|
|
|
// fontconfig prefers a matching family or lang to pixelsize of bitmap
|
|
|
|
// fonts. CSS suggests a tolerance of 20% on pixelsize.
|
|
|
|
return bestDist;
|
|
|
|
} else {
|
|
|
|
// Reject any non-scalable fonts that are not within tolerance.
|
|
|
|
return kRejectDistance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFontconfigFontFamily::FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
|
|
|
|
nsTArray<gfxFontEntry*>& aFontEntryList,
|
2017-08-07 19:27:33 +03:00
|
|
|
bool aIgnoreSizeTolerance)
|
2017-03-29 20:47:46 +03:00
|
|
|
{
|
|
|
|
gfxFontFamily::FindAllFontsForStyle(aFontStyle,
|
|
|
|
aFontEntryList,
|
2017-08-07 19:27:33 +03:00
|
|
|
aIgnoreSizeTolerance);
|
2017-03-29 20:47:46 +03:00
|
|
|
|
|
|
|
if (!mHasNonScalableFaces) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate over the the available fonts while compacting any groups
|
|
|
|
// of unscalable fonts with matching styles into a single entry
|
|
|
|
// corresponding to the closest available size. If the closest
|
2017-08-07 19:27:33 +03:00
|
|
|
// available size is rejected for being outside tolerance, then the
|
2017-03-29 20:47:46 +03:00
|
|
|
// entire group will be skipped.
|
|
|
|
size_t skipped = 0;
|
|
|
|
gfxFontconfigFontEntry* bestEntry = nullptr;
|
|
|
|
double bestDist = -1.0;
|
|
|
|
for (size_t i = 0; i < aFontEntryList.Length(); i++) {
|
|
|
|
gfxFontconfigFontEntry* entry =
|
|
|
|
static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
|
2017-08-07 19:27:33 +03:00
|
|
|
double dist = SizeDistance(entry, aFontStyle,
|
|
|
|
mForceScalable || aIgnoreSizeTolerance);
|
2017-03-29 20:47:46 +03:00
|
|
|
// If the entry is scalable or has a style that does not match
|
|
|
|
// the group of unscalable fonts, then start a new group.
|
|
|
|
if (dist < 0.0 ||
|
|
|
|
!bestEntry ||
|
|
|
|
bestEntry->Stretch() != entry->Stretch() ||
|
2018-04-25 09:18:23 +03:00
|
|
|
bestEntry->Weight() != entry->Weight() ||
|
|
|
|
bestEntry->SlantStyle() != entry->SlantStyle()) {
|
2017-03-29 20:47:46 +03:00
|
|
|
// If the best entry in this group is still outside the tolerance,
|
|
|
|
// then skip the entire group.
|
|
|
|
if (bestDist >= kRejectDistance) {
|
|
|
|
skipped++;
|
|
|
|
}
|
|
|
|
// Remove any compacted entries from the previous group.
|
|
|
|
if (skipped) {
|
|
|
|
i -= skipped;
|
|
|
|
aFontEntryList.RemoveElementsAt(i, skipped);
|
|
|
|
skipped = 0;
|
|
|
|
}
|
|
|
|
// Mark the start of the new group.
|
|
|
|
bestEntry = entry;
|
|
|
|
bestDist = dist;
|
|
|
|
} else {
|
|
|
|
// If this entry more closely matches the requested size than the
|
|
|
|
// current best in the group, then take this entry instead.
|
|
|
|
if (dist < bestDist) {
|
|
|
|
aFontEntryList[i-1-skipped] = entry;
|
|
|
|
bestEntry = entry;
|
|
|
|
bestDist = dist;
|
|
|
|
}
|
|
|
|
skipped++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If the best entry in this group is still outside the tolerance,
|
|
|
|
// then skip the entire group.
|
|
|
|
if (bestDist >= kRejectDistance) {
|
|
|
|
skipped++;
|
|
|
|
}
|
|
|
|
// Remove any compacted entries from the current group.
|
|
|
|
if (skipped) {
|
|
|
|
aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-11 21:22:15 +03:00
|
|
|
static bool
|
|
|
|
PatternHasLang(const FcPattern *aPattern, const FcChar8 *aLang)
|
|
|
|
{
|
|
|
|
FcLangSet *langset;
|
|
|
|
|
|
|
|
if (FcPatternGetLangSet(aPattern, FC_LANG, 0, &langset) != FcResultMatch) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FcLangSetHasLang(langset, aLang) != FcLangDifferentLang) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2017-10-03 01:05:19 +03:00
|
|
|
gfxFontconfigFontFamily::SupportsLangGroup(nsAtom *aLangGroup) const
|
2017-09-11 21:22:15 +03:00
|
|
|
{
|
|
|
|
if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoCString fcLang;
|
|
|
|
gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
|
|
|
|
pfl->GetSampleLangForGroup(aLangGroup, fcLang);
|
|
|
|
if (fcLang.IsEmpty()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Before FindStyleVariations has been called, mFontPatterns will contain
|
|
|
|
// the font patterns. Afterward, it'll be empty, but mAvailableFonts
|
|
|
|
// will contain the font entries, each of which holds a reference to its
|
|
|
|
// pattern. We only check the first pattern in each list, because support
|
|
|
|
// for langs is considered to be consistent across all faces in a family.
|
|
|
|
FcPattern* fontPattern;
|
|
|
|
if (mFontPatterns.Length()) {
|
|
|
|
fontPattern = mFontPatterns[0];
|
|
|
|
} else if (mAvailableFonts.Length()) {
|
|
|
|
fontPattern = static_cast<gfxFontconfigFontEntry*>
|
|
|
|
(mAvailableFonts[0].get())->GetPattern();
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// is lang included in the underlying pattern?
|
|
|
|
return PatternHasLang(fontPattern, ToFcChar8Ptr(fcLang.get()));
|
|
|
|
}
|
|
|
|
|
2017-04-08 01:49:44 +03:00
|
|
|
/* virtual */
|
|
|
|
gfxFontconfigFontFamily::~gfxFontconfigFontFamily()
|
|
|
|
{
|
|
|
|
// Should not be dropped by stylo
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
}
|
|
|
|
|
2017-11-02 23:29:33 +03:00
|
|
|
template<typename Func>
|
|
|
|
void
|
|
|
|
gfxFontconfigFontFamily::AddFacesToFontList(Func aAddPatternFunc)
|
|
|
|
{
|
|
|
|
if (HasStyles()) {
|
|
|
|
for (auto& fe : mAvailableFonts) {
|
|
|
|
if (!fe) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
auto fce = static_cast<gfxFontconfigFontEntry*>(fe.get());
|
|
|
|
aAddPatternFunc(fce->GetPattern(), mContainsAppFonts);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (auto& pat : mFontPatterns) {
|
|
|
|
aAddPatternFunc(pat, mContainsAppFonts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-07 00:41:02 +03:00
|
|
|
gfxFontconfigFont::gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
|
|
|
|
cairo_scaled_font_t *aScaledFont,
|
2016-07-20 22:07:11 +03:00
|
|
|
FcPattern *aPattern,
|
2017-01-13 01:37:07 +03:00
|
|
|
gfxFloat aAdjustedSize,
|
2015-05-13 08:11:25 +03:00
|
|
|
gfxFontEntry *aFontEntry,
|
Bug 1449605 - part 1 - Rearrange thebes font code so that the decision whether to apply synthetic-bold is deferred until actually instantiating a font, not made during the font-matching process. r=jwatt
This rearranges how synthetic-bold use is determined in the font selection
& rendering code. Previously, we would decide during the font-selection
algorithm whether we need to apply synthetic-bold to the chosen face, and
then pass that decision through the fontgroup (storing it in the FamilyFace
entries of the mFonts array there) down to the actual rendering code that
instantiates fonts from the faces (font entries) we've selected.
That became a problem for variation fonts because in the case of a user
font, we may not have downloaded the resource yet, so we just have a "user
font container" entry, which carries the descriptors from the @font-face
rule and will fetch the actual resource when needed. But in the case of a
@font-face rule without a weight descriptor, we don't actually know at
font-selection time whether the face will support "true" bold (via a
variation axis) or not, so we can't reliably make the right decision about
applying synthetic bold.
So we now defer that decision until we actually instantiate a platform font
object to shape/measure/draw text. At that point, we have the requested
style and we also have the real font resource, so we can easily determine
whether fake-bold is required.
(This patch should not result in any visible behavior change; that will
come in a second patch now that the architecture supports it.)
2018-05-01 12:30:50 +03:00
|
|
|
const gfxFontStyle *aFontStyle)
|
|
|
|
: gfxFT2FontBase(aUnscaledFont, aScaledFont, aFontEntry, aFontStyle)
|
2017-08-07 23:20:37 +03:00
|
|
|
, mPattern(aPattern)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
2017-01-13 01:37:07 +03:00
|
|
|
mAdjustedSize = aAdjustedSize;
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFontconfigFont::~gfxFontconfigFont()
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-08-07 23:20:44 +03:00
|
|
|
already_AddRefed<ScaledFont>
|
|
|
|
gfxFontconfigFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
|
|
|
|
{
|
|
|
|
if (!mAzureScaledFont) {
|
|
|
|
mAzureScaledFont =
|
|
|
|
Factory::CreateScaledFontForFontconfigFont(GetCairoScaledFont(),
|
|
|
|
GetPattern(),
|
|
|
|
GetUnscaledFont(),
|
2018-01-17 15:51:20 +03:00
|
|
|
GetAdjustedSize());
|
2017-08-07 23:20:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
|
|
|
|
return scaledFont.forget();
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:26 +03:00
|
|
|
gfxFcPlatformFontList::gfxFcPlatformFontList()
|
2015-06-10 09:23:45 +03:00
|
|
|
: mLocalNames(64)
|
|
|
|
, mGenericMappings(32)
|
|
|
|
, mFcSubstituteCache(64)
|
|
|
|
, mLastConfig(nullptr)
|
2015-11-16 02:48:40 +03:00
|
|
|
, mAlwaysUseFontconfigGenerics(true)
|
2015-05-13 08:11:26 +03:00
|
|
|
{
|
2017-11-02 23:29:33 +03:00
|
|
|
if (XRE_IsParentProcess()) {
|
|
|
|
// if the rescan interval is set, start the timer
|
|
|
|
int rescanInterval = FcConfigGetRescanInterval(nullptr);
|
|
|
|
if (rescanInterval) {
|
|
|
|
mLastConfig = FcConfigGetCurrent();
|
|
|
|
NS_NewTimerWithFuncCallback(getter_AddRefs(mCheckFontUpdatesTimer),
|
|
|
|
CheckFontUpdates,
|
|
|
|
this,
|
|
|
|
(rescanInterval + 1) * 1000,
|
|
|
|
nsITimer::TYPE_REPEATING_SLACK,
|
|
|
|
"gfxFcPlatformFontList::gfxFcPlatformFontList");
|
|
|
|
if (!mCheckFontUpdatesTimer) {
|
|
|
|
NS_WARNING("Failure to create font updates timer");
|
|
|
|
}
|
2015-05-13 08:11:26 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MOZ_BUNDLED_FONTS
|
|
|
|
mBundledFontsInitialized = false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
gfxFcPlatformFontList::~gfxFcPlatformFontList()
|
|
|
|
{
|
2015-05-13 08:11:26 +03:00
|
|
|
if (mCheckFontUpdatesTimer) {
|
|
|
|
mCheckFontUpdatesTimer->Cancel();
|
|
|
|
mCheckFontUpdatesTimer = nullptr;
|
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-11-09 19:54:30 +03:00
|
|
|
gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet,
|
|
|
|
const SandboxPolicy* aPolicy,
|
|
|
|
bool aAppFonts)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
// This iterates over the fonts in a font set and adds in gfxFontFamily
|
2017-11-02 23:29:33 +03:00
|
|
|
// objects for each family. Individual gfxFontEntry objects for each face
|
|
|
|
// are not created here; the patterns are just stored in the family. When
|
|
|
|
// a family is actually used, it will be populated with gfxFontEntry
|
|
|
|
// records and the patterns moved to those.
|
2015-05-13 08:11:25 +03:00
|
|
|
|
2015-05-18 06:33:46 +03:00
|
|
|
if (!aFontSet) {
|
|
|
|
NS_WARNING("AddFontSetFamilies called with a null font set.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
FcChar8* lastFamilyName = (FcChar8*)"";
|
2016-08-23 03:06:07 +03:00
|
|
|
RefPtr<gfxFontconfigFontFamily> fontFamily;
|
2015-05-13 08:11:25 +03:00
|
|
|
nsAutoString familyName;
|
|
|
|
for (int f = 0; f < aFontSet->nfont; f++) {
|
2017-11-02 23:29:33 +03:00
|
|
|
FcPattern* pattern = aFontSet->fonts[f];
|
2015-05-13 08:11:25 +03:00
|
|
|
|
2017-11-02 23:29:33 +03:00
|
|
|
// Skip any fonts that aren't readable for us (e.g. due to restrictive
|
|
|
|
// file ownership/permissions).
|
|
|
|
FcChar8* path;
|
|
|
|
if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
|
2015-05-13 08:11:25 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-11-09 19:54:30 +03:00
|
|
|
#ifdef MOZ_CONTENT_SANDBOX
|
|
|
|
// Skip any fonts that will be blocked by the content-process sandbox
|
|
|
|
// policy.
|
|
|
|
if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
|
|
|
|
SandboxBroker::Perms::MAY_READ)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
2017-11-02 23:29:33 +03:00
|
|
|
|
2017-11-02 23:29:33 +03:00
|
|
|
AddPatternToFontList(pattern, lastFamilyName,
|
|
|
|
familyName, fontFamily, aAppFonts);
|
|
|
|
}
|
|
|
|
}
|
2017-11-02 23:29:33 +03:00
|
|
|
|
2017-11-02 23:29:33 +03:00
|
|
|
void
|
|
|
|
gfxFcPlatformFontList::AddPatternToFontList(FcPattern* aFont,
|
|
|
|
FcChar8*& aLastFamilyName,
|
|
|
|
nsAString& aFamilyName,
|
|
|
|
RefPtr<gfxFontconfigFontFamily>& aFontFamily,
|
|
|
|
bool aAppFonts)
|
|
|
|
{
|
|
|
|
// get canonical name
|
|
|
|
uint32_t cIndex = FindCanonicalNameIndex(aFont, FC_FAMILYLANG);
|
|
|
|
FcChar8* canonical = nullptr;
|
|
|
|
FcPatternGetString(aFont, FC_FAMILY, cIndex, &canonical);
|
|
|
|
if (!canonical) {
|
|
|
|
return;
|
|
|
|
}
|
2017-11-02 23:29:33 +03:00
|
|
|
|
2017-11-02 23:29:33 +03:00
|
|
|
// same as the last one? no need to add a new family, skip
|
|
|
|
if (FcStrCmp(canonical, aLastFamilyName) != 0) {
|
|
|
|
aLastFamilyName = canonical;
|
|
|
|
|
|
|
|
// add new family if one doesn't already exist
|
|
|
|
aFamilyName.Truncate();
|
|
|
|
AppendUTF8toUTF16(ToCharPtr(canonical), aFamilyName);
|
|
|
|
nsAutoString keyName(aFamilyName);
|
|
|
|
ToLowerCase(keyName);
|
|
|
|
|
|
|
|
aFontFamily = static_cast<gfxFontconfigFontFamily*>
|
|
|
|
(mFontFamilies.GetWeak(keyName));
|
|
|
|
if (!aFontFamily) {
|
|
|
|
aFontFamily = new gfxFontconfigFontFamily(aFamilyName);
|
|
|
|
mFontFamilies.Put(keyName, aFontFamily);
|
2017-11-10 19:05:33 +03:00
|
|
|
}
|
2017-11-02 23:29:33 +03:00
|
|
|
// Record if the family contains fonts from the app font set
|
|
|
|
// (in which case we won't rely on fontconfig's charmap, due to
|
|
|
|
// bug 1276594).
|
|
|
|
if (aAppFonts) {
|
|
|
|
aFontFamily->SetFamilyContainsAppFonts(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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(aFont, FC_FAMILY, n, &otherName) ==
|
|
|
|
FcResultMatch) {
|
|
|
|
NS_ConvertUTF8toUTF16 otherFamilyName(ToCharPtr(otherName));
|
|
|
|
AddOtherFamilyName(aFontFamily, otherFamilyName);
|
|
|
|
n++;
|
|
|
|
if (n == int(cIndex)) {
|
|
|
|
n++; // skip over canonical name
|
|
|
|
}
|
2017-11-10 19:05:33 +03:00
|
|
|
}
|
2017-11-02 23:29:33 +03:00
|
|
|
}
|
2017-11-02 23:29:33 +03:00
|
|
|
|
|
|
|
MOZ_ASSERT(aFontFamily, "font must belong to a font family");
|
|
|
|
aFontFamily->AddFontPattern(aFont);
|
|
|
|
|
|
|
|
// map the psname, fullname ==> font family for local font lookups
|
|
|
|
nsAutoString psname, fullname;
|
|
|
|
GetFaceNames(aFont, aFamilyName, psname, fullname);
|
|
|
|
if (!psname.IsEmpty()) {
|
|
|
|
ToLowerCase(psname);
|
|
|
|
mLocalNames.Put(psname, aFont);
|
|
|
|
}
|
|
|
|
if (!fullname.IsEmpty()) {
|
|
|
|
ToLowerCase(fullname);
|
|
|
|
mLocalNames.Put(fullname, aFont);
|
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2016-08-23 03:06:07 +03:00
|
|
|
gfxFcPlatformFontList::InitFontListForPlatform()
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
2017-11-02 23:29:33 +03:00
|
|
|
#ifdef MOZ_BUNDLED_FONTS
|
|
|
|
ActivateBundledFonts();
|
|
|
|
#endif
|
2015-05-13 08:11:26 +03:00
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
mLocalNames.Clear();
|
2015-06-10 09:23:45 +03:00
|
|
|
mFcSubstituteCache.Clear();
|
2015-05-13 08:11:25 +03:00
|
|
|
|
2017-11-02 23:29:33 +03:00
|
|
|
mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
|
|
|
|
mOtherFamilyNamesInitialized = true;
|
|
|
|
|
|
|
|
if (XRE_IsContentProcess()) {
|
|
|
|
// Content process: use the font list passed from the chrome process,
|
|
|
|
// because we can't rely on fontconfig in the presence of sandboxing;
|
|
|
|
// it may report fonts that we can't actually access.
|
|
|
|
|
|
|
|
FcChar8* lastFamilyName = (FcChar8*)"";
|
|
|
|
RefPtr<gfxFontconfigFontFamily> fontFamily;
|
|
|
|
nsAutoString familyName;
|
|
|
|
|
|
|
|
// Get font list that was passed during XPCOM startup
|
|
|
|
// or in an UpdateFontList message.
|
|
|
|
auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
|
|
|
|
|
2017-11-13 00:35:25 +03:00
|
|
|
// For fontconfig versions between 2.10.94 and 2.11.1 inclusive,
|
|
|
|
// we need to escape any leading space in the charset element,
|
|
|
|
// otherwise FcNameParse will fail. :(
|
|
|
|
//
|
|
|
|
// The bug was introduced on 2013-05-24 by
|
|
|
|
// https://cgit.freedesktop.org/fontconfig/commit/?id=cd9b1033a68816a7acfbba1718ba0aa5888f6ec7
|
|
|
|
// "Bug 64906 - FcNameParse() should ignore leading whitespace in parameters"
|
|
|
|
// because ignoring a leading space in the encoded value of charset
|
|
|
|
// causes erroneous decoding of the whole element.
|
|
|
|
// This first shipped in version 2.10.94, and was eventually fixed as
|
|
|
|
// a side-effect of switching to the "human-readable" representation of
|
|
|
|
// charsets on 2014-07-03 in
|
|
|
|
// https://cgit.freedesktop.org/fontconfig/commit/?id=e708e97c351d3bc9f7030ef22ac2f007d5114730
|
|
|
|
// "Change charset parse/unparse format to be human readable"
|
|
|
|
// (with a followup fix next day) which means a leading space is no
|
|
|
|
// longer significant. This fix landed after 2.11.1 had been shipped,
|
|
|
|
// so the first version tag without the bug is 2.11.91.
|
|
|
|
int fcVersion = FcGetVersion();
|
|
|
|
bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
|
|
|
|
|
2017-11-02 23:29:33 +03:00
|
|
|
for (SystemFontListEntry& fle : fontList) {
|
|
|
|
MOZ_ASSERT(fle.type() ==
|
|
|
|
SystemFontListEntry::Type::TFontPatternListEntry);
|
|
|
|
FontPatternListEntry& fpe(fle);
|
2017-11-13 00:35:25 +03:00
|
|
|
nsCString& patternStr = fpe.pattern();
|
|
|
|
if (fcCharsetParseBug) {
|
|
|
|
int32_t index = patternStr.Find(":charset= ");
|
|
|
|
if (index != kNotFound) {
|
|
|
|
// insert backslash after the =, before the space
|
|
|
|
patternStr.Insert('\\', index + 9);
|
|
|
|
}
|
|
|
|
}
|
2017-11-02 23:29:33 +03:00
|
|
|
FcPattern* pattern =
|
2017-11-13 00:35:25 +03:00
|
|
|
FcNameParse((const FcChar8*)patternStr.get());
|
2017-11-02 23:29:33 +03:00
|
|
|
AddPatternToFontList(pattern, lastFamilyName, familyName,
|
|
|
|
fontFamily, fpe.appFontFamily());
|
|
|
|
FcPatternDestroy(pattern);
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_FONTLIST(("got font list from chrome process: "
|
|
|
|
"%u faces in %u families",
|
|
|
|
(unsigned)fontList.Length(), mFontFamilies.Count()));
|
|
|
|
|
|
|
|
fontList.Clear();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
mLastConfig = FcConfigGetCurrent();
|
|
|
|
|
2017-11-09 19:54:30 +03:00
|
|
|
UniquePtr<SandboxPolicy> policy;
|
|
|
|
|
|
|
|
#ifdef MOZ_CONTENT_SANDBOX
|
2017-11-22 14:10:20 +03:00
|
|
|
// If read sandboxing is enabled, create a temporary SandboxPolicy to
|
|
|
|
// check font paths; use a fake PID to avoid picking up any PID-specific
|
|
|
|
// rules by accident.
|
2017-11-09 19:54:30 +03:00
|
|
|
SandboxBrokerPolicyFactory policyFactory;
|
2017-11-22 14:10:20 +03:00
|
|
|
if (GetEffectiveContentSandboxLevel() > 2 &&
|
2017-11-09 19:54:30 +03:00
|
|
|
!PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
|
|
|
|
policy = policyFactory.GetContentPolicy(-1, false);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
// iterate over available fonts
|
|
|
|
FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
|
2017-11-09 19:54:30 +03:00
|
|
|
AddFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
#ifdef MOZ_BUNDLED_FONTS
|
|
|
|
FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
|
2017-11-09 19:54:30 +03:00
|
|
|
AddFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
|
2015-05-13 08:11:25 +03:00
|
|
|
#endif
|
|
|
|
|
2017-11-10 19:05:33 +03:00
|
|
|
return NS_OK;
|
2017-11-02 23:29:33 +03:00
|
|
|
}
|
|
|
|
|
2017-11-02 23:29:33 +03:00
|
|
|
void
|
|
|
|
gfxFcPlatformFontList::ReadSystemFontList(
|
|
|
|
InfallibleTArray<SystemFontListEntry>* retValue)
|
|
|
|
{
|
|
|
|
// Fontconfig versions below 2.9 drop the FC_FILE element in FcNameUnparse
|
|
|
|
// (see https://bugs.freedesktop.org/show_bug.cgi?id=26718), so when using
|
|
|
|
// an older version, we manually append it to the unparsed pattern.
|
|
|
|
if (FcGetVersion() < 20900) {
|
|
|
|
for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
|
|
|
|
auto family =
|
|
|
|
static_cast<gfxFontconfigFontFamily*>(iter.Data().get());
|
|
|
|
family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
|
|
|
|
char* s = (char*)FcNameUnparse(aPat);
|
|
|
|
nsAutoCString patternStr(s);
|
|
|
|
free(s);
|
|
|
|
if (FcResultMatch ==
|
|
|
|
FcPatternGetString(aPat, FC_FILE, 0, (FcChar8**)&s)) {
|
|
|
|
patternStr.Append(":file=");
|
|
|
|
patternStr.Append(s);
|
|
|
|
}
|
|
|
|
retValue->AppendElement(FontPatternListEntry(patternStr,
|
|
|
|
aAppFonts));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
|
|
|
|
auto family =
|
|
|
|
static_cast<gfxFontconfigFontFamily*>(iter.Data().get());
|
|
|
|
family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
|
|
|
|
char* s = (char*)FcNameUnparse(aPat);
|
|
|
|
nsDependentCString patternStr(s);
|
|
|
|
retValue->AppendElement(FontPatternListEntry(patternStr,
|
|
|
|
aAppFonts));
|
|
|
|
free(s);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
// 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
|
2017-10-03 01:05:19 +03:00
|
|
|
GetSystemFontList(nsTArray<nsString>& aListOfFonts, nsAtom *aLangGroup)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
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;
|
2015-11-17 05:39:58 +03:00
|
|
|
gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
|
2018-05-28 12:09:28 +03:00
|
|
|
pfl->GetSampleLangForGroup(aLangGroup, fcLang,
|
|
|
|
/*aForFontEnumerationThread*/ true);
|
2015-05-13 08:11:25 +03:00
|
|
|
if (!fcLang.IsEmpty()) {
|
|
|
|
FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2017-10-03 01:05:19 +03:00
|
|
|
gfxFcPlatformFontList::GetFontList(nsAtom *aLangGroup,
|
2015-05-13 08:11:25 +03:00
|
|
|
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
|
2015-05-19 08:41:48 +03:00
|
|
|
// FontBuilder.readFontSelection() if the preference-selected font is not
|
2015-05-13 08:11:25 +03:00
|
|
|
// 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*
|
2016-08-23 03:06:07 +03:00
|
|
|
gfxFcPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
2015-06-02 11:20:15 +03:00
|
|
|
// Get the default font by using a fake name to retrieve the first
|
|
|
|
// scalable font that fontconfig suggests for the given language.
|
2015-11-16 02:48:40 +03:00
|
|
|
PrefFontList* prefFonts =
|
|
|
|
FindGenericFamilies(NS_LITERAL_STRING("-moz-default"), aStyle->language);
|
|
|
|
NS_ASSERTION(prefFonts, "null list of generic fonts");
|
|
|
|
if (prefFonts && !prefFonts->IsEmpty()) {
|
|
|
|
return (*prefFonts)[0];
|
|
|
|
}
|
|
|
|
return nullptr;
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
gfxFontEntry*
|
|
|
|
gfxFcPlatformFontList::LookupLocalFont(const nsAString& aFontName,
|
2018-04-25 09:18:23 +03:00
|
|
|
WeightRange aWeightForEntry,
|
|
|
|
StretchRange aStretchForEntry,
|
|
|
|
SlantStyleRange aStyleForEntry)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
2015-05-27 10:19:19 +03:00
|
|
|
nsAutoString keyName(aFontName);
|
|
|
|
ToLowerCase(keyName);
|
2015-05-13 08:11:25 +03:00
|
|
|
|
2015-05-27 10:19:19 +03:00
|
|
|
// if name is not in the global list, done
|
|
|
|
FcPattern* fontPattern = mLocalNames.Get(keyName);
|
|
|
|
if (!fontPattern) {
|
|
|
|
return nullptr;
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
2016-06-15 16:35:05 +03:00
|
|
|
return new gfxFontconfigFontEntry(aFontName, fontPattern,
|
2018-04-25 09:18:23 +03:00
|
|
|
aWeightForEntry,
|
|
|
|
aStretchForEntry,
|
|
|
|
aStyleForEntry);
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
gfxFontEntry*
|
|
|
|
gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
|
2018-04-25 09:18:23 +03:00
|
|
|
WeightRange aWeightForEntry,
|
|
|
|
StretchRange aStretchForEntry,
|
|
|
|
SlantStyleRange aStyleForEntry,
|
2015-05-13 08:11:25 +03:00
|
|
|
const uint8_t* aFontData,
|
|
|
|
uint32_t aLength)
|
|
|
|
{
|
2017-05-18 04:56:24 +03:00
|
|
|
FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0);
|
|
|
|
if (!face) {
|
2017-10-12 05:08:44 +03:00
|
|
|
free((void*)aFontData);
|
2015-05-13 08:11:25 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
|
2017-05-18 04:56:24 +03:00
|
|
|
Factory::ReleaseFTFace(face);
|
2017-10-12 05:08:44 +03:00
|
|
|
free((void*)aFontData);
|
2015-05-13 08:11:25 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-04-25 09:18:23 +03:00
|
|
|
return new gfxFontconfigFontEntry(aFontName,
|
|
|
|
aWeightForEntry,
|
|
|
|
aStretchForEntry,
|
|
|
|
aStyleForEntry,
|
|
|
|
aFontData, aLength, face);
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
2016-04-12 09:06:22 +03:00
|
|
|
bool
|
|
|
|
gfxFcPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
|
2018-06-01 19:07:24 +03:00
|
|
|
nsTArray<gfxFontFamily*>* aOutput,
|
2017-08-01 13:25:35 +03:00
|
|
|
FindFamiliesFlags aFlags,
|
2016-04-12 09:06:22 +03:00
|
|
|
gfxFontStyle* aStyle,
|
|
|
|
gfxFloat aDevToCssSize)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
nsAutoString familyName(aFamily);
|
|
|
|
ToLowerCase(familyName);
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom* language = (aStyle ? aStyle->language.get() : nullptr);
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
// 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()) {
|
2015-11-16 02:48:40 +03:00
|
|
|
PrefFontList* prefFonts = FindGenericFamilies(familyName, language);
|
|
|
|
if (prefFonts && !prefFonts->IsEmpty()) {
|
2016-04-09 13:09:08 +03:00
|
|
|
aOutput->AppendElements(*prefFonts);
|
2016-04-12 09:06:22 +03:00
|
|
|
return true;
|
2015-11-16 02:48:40 +03:00
|
|
|
}
|
2016-04-12 09:06:22 +03:00
|
|
|
return false;
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
2015-06-10 09:23:45 +03:00
|
|
|
// Because the FcConfigSubstitute call is quite expensive, we cache the
|
2016-04-09 13:09:08 +03:00
|
|
|
// actual font families found via this process. So check the cache first:
|
2015-06-10 09:23:45 +03:00
|
|
|
NS_ConvertUTF16toUTF8 familyToFind(familyName);
|
2018-06-01 19:07:24 +03:00
|
|
|
AutoTArray<gfxFontFamily*,10> cachedFamilies;
|
2016-04-09 13:09:08 +03:00
|
|
|
if (mFcSubstituteCache.Get(familyToFind, &cachedFamilies)) {
|
|
|
|
if (cachedFamilies.IsEmpty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
aOutput->AppendElements(cachedFamilies);
|
2016-04-12 09:06:22 +03:00
|
|
|
return true;
|
2015-06-10 09:23:45 +03:00
|
|
|
}
|
|
|
|
|
2016-04-09 13:09:08 +03:00
|
|
|
// It wasn't in the cache, so we need to ask fontconfig...
|
2015-05-13 08:11:25 +03:00
|
|
|
const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
|
2015-06-20 10:44:21 +03:00
|
|
|
FcChar8* sentinelFirstFamily = nullptr;
|
|
|
|
nsAutoRef<FcPattern> sentinelSubst(FcPatternCreate());
|
|
|
|
FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName);
|
|
|
|
FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern);
|
|
|
|
FcPatternGetString(sentinelSubst, FC_FAMILY, 0, &sentinelFirstFamily);
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
// substitutions for font, -moz-sentinel pattern
|
|
|
|
nsAutoRef<FcPattern> fontWithSentinel(FcPatternCreate());
|
2015-06-20 10:44:21 +03:00
|
|
|
FcPatternAddString(fontWithSentinel, FC_FAMILY,
|
|
|
|
ToFcChar8Ptr(familyToFind.get()));
|
2015-05-13 08:11:25 +03:00
|
|
|
FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
|
|
|
|
FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
|
|
|
|
|
2016-04-09 13:09:08 +03:00
|
|
|
// Add all font family matches until reaching the sentinel.
|
2015-05-13 08:11:25 +03:00
|
|
|
FcChar8* substName = nullptr;
|
|
|
|
for (int i = 0;
|
|
|
|
FcPatternGetString(fontWithSentinel, FC_FAMILY,
|
|
|
|
i, &substName) == FcResultMatch;
|
|
|
|
i++)
|
|
|
|
{
|
|
|
|
NS_ConvertUTF8toUTF16 subst(ToCharPtr(substName));
|
2015-06-20 10:44:21 +03:00
|
|
|
if (sentinelFirstFamily &&
|
|
|
|
FcStrCmp(substName, sentinelFirstFamily) == 0) {
|
2015-05-13 08:11:25 +03:00
|
|
|
break;
|
|
|
|
}
|
2017-08-03 03:33:00 +03:00
|
|
|
gfxPlatformFontList::FindAndAddFamilies(subst,
|
|
|
|
&cachedFamilies,
|
2017-08-01 13:25:35 +03:00
|
|
|
aFlags);
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
2016-04-09 13:09:08 +03:00
|
|
|
// Cache the resulting list, so we don't have to do this again.
|
|
|
|
mFcSubstituteCache.Put(familyToFind, cachedFamilies);
|
|
|
|
|
|
|
|
if (cachedFamilies.IsEmpty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
aOutput->AppendElements(cachedFamilies);
|
|
|
|
return true;
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
gfxFcPlatformFontList::GetStandardFamilyName(const nsAString& aFontName,
|
|
|
|
nsAString& aFamilyName)
|
|
|
|
{
|
2015-11-16 02:48:36 +03:00
|
|
|
aFamilyName.Truncate();
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2015-11-16 02:48:36 +03:00
|
|
|
nsAutoRef<FcPattern> pat(FcPatternCreate());
|
|
|
|
if (!pat) {
|
2015-05-13 08:11:25 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-16 02:48:36 +03:00
|
|
|
nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
|
|
|
|
if (!os) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the family name to the pattern
|
|
|
|
NS_ConvertUTF16toUTF8 familyName(aFontName);
|
|
|
|
FcPatternAddString(pat, FC_FAMILY, ToFcChar8Ptr(familyName.get()));
|
|
|
|
|
|
|
|
nsAutoRef<FcFontSet> givenFS(FcFontList(nullptr, pat, os));
|
|
|
|
if (!givenFS) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if there is a font face with first family equal to the given family
|
|
|
|
// (needs to be in sync with names coming from GetFontList())
|
|
|
|
nsTArray<nsCString> candidates;
|
|
|
|
for (int i = 0; i < givenFS->nfont; i++) {
|
|
|
|
char* firstFamily;
|
|
|
|
|
|
|
|
if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
|
|
|
|
(FcChar8 **) &firstFamily) != FcResultMatch)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsDependentCString first(firstFamily);
|
|
|
|
if (!candidates.Contains(first)) {
|
|
|
|
candidates.AppendElement(first);
|
|
|
|
|
|
|
|
if (familyName.Equals(first)) {
|
|
|
|
aFamilyName.Assign(aFontName);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Because fontconfig conflates different family name types, need to
|
|
|
|
// double check that the candidate name is not simply a different
|
|
|
|
// name type. For example, if a font with nameID=16 "Minion Pro" and
|
|
|
|
// nameID=21 "Minion Pro Caption" exists, calling FcFontList with
|
|
|
|
// family="Minion Pro" will return a set of patterns some of which
|
|
|
|
// will have a first family of "Minion Pro Caption". Ignore these
|
|
|
|
// patterns and use the first candidate that maps to a font set with
|
|
|
|
// the same number of faces and an identical set of patterns.
|
|
|
|
for (uint32_t j = 0; j < candidates.Length(); ++j) {
|
|
|
|
FcPatternDel(pat, FC_FAMILY);
|
|
|
|
FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j].get());
|
|
|
|
|
|
|
|
nsAutoRef<FcFontSet> candidateFS(FcFontList(nullptr, pat, os));
|
|
|
|
if (!candidateFS) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (candidateFS->nfont != givenFS->nfont) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool equal = true;
|
|
|
|
for (int i = 0; i < givenFS->nfont; ++i) {
|
|
|
|
if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
|
|
|
|
equal = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (equal) {
|
|
|
|
AppendUTF8toUTF16(candidates[j], aFamilyName);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// didn't find localized name, leave family name blank
|
|
|
|
return true;
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
2015-11-16 02:48:40 +03:00
|
|
|
void
|
|
|
|
gfxFcPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom* aLanguage,
|
2018-06-01 19:07:24 +03:00
|
|
|
nsTArray<gfxFontFamily*>& aFamilyList)
|
2015-11-16 02:48:40 +03:00
|
|
|
{
|
|
|
|
bool usePrefFontList = false;
|
|
|
|
|
|
|
|
// treat -moz-fixed as monospace
|
|
|
|
if (aGenericType == eFamily_moz_fixed) {
|
|
|
|
aGenericType = eFamily_monospace;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* generic = GetGenericName(aGenericType);
|
|
|
|
NS_ASSERTION(generic, "weird generic font type");
|
|
|
|
if (!generic) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// By default, most font prefs on Linux map to "use fontconfig"
|
|
|
|
// keywords. So only need to explicitly lookup font pref if
|
|
|
|
// non-default settings exist
|
|
|
|
NS_ConvertASCIItoUTF16 genericToLookup(generic);
|
|
|
|
if ((!mAlwaysUseFontconfigGenerics && aLanguage) ||
|
|
|
|
aLanguage == nsGkAtoms::x_math) {
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom* langGroup = GetLangGroup(aLanguage);
|
2017-07-31 07:23:50 +03:00
|
|
|
nsAutoString fontlistValue;
|
|
|
|
Preferences::GetString(NamePref(generic, langGroup).get(),
|
|
|
|
fontlistValue);
|
|
|
|
nsresult rv;
|
2017-03-29 17:21:47 +03:00
|
|
|
if (fontlistValue.IsEmpty()) {
|
|
|
|
// The font name list may have two or more family names as comma
|
|
|
|
// separated list. In such case, not matching with generic font
|
|
|
|
// name is fine because if the list prefers specific font, we
|
|
|
|
// should try to use the pref with complicated path.
|
2017-07-31 07:23:50 +03:00
|
|
|
rv = Preferences::GetString(NameListPref(generic, langGroup).get(),
|
|
|
|
fontlistValue);
|
|
|
|
} else {
|
|
|
|
rv = NS_OK;
|
2017-03-29 17:21:47 +03:00
|
|
|
}
|
2017-07-31 07:23:50 +03:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
2015-11-16 02:48:40 +03:00
|
|
|
if (!fontlistValue.EqualsLiteral("serif") &&
|
|
|
|
!fontlistValue.EqualsLiteral("sans-serif") &&
|
|
|
|
!fontlistValue.EqualsLiteral("monospace")) {
|
|
|
|
usePrefFontList = true;
|
|
|
|
} else {
|
|
|
|
// serif, sans-serif or monospace was specified
|
|
|
|
genericToLookup.Assign(fontlistValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// when pref fonts exist, use standard pref font lookup
|
|
|
|
if (usePrefFontList) {
|
|
|
|
return gfxPlatformFontList::AddGenericFonts(aGenericType,
|
|
|
|
aLanguage,
|
|
|
|
aFamilyList);
|
|
|
|
}
|
|
|
|
|
|
|
|
PrefFontList* prefFonts = FindGenericFamilies(genericToLookup, aLanguage);
|
|
|
|
NS_ASSERTION(prefFonts, "null generic font list");
|
2018-06-01 19:07:24 +03:00
|
|
|
aFamilyList.AppendElements(*prefFonts);
|
2015-11-16 02:48:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFcPlatformFontList::ClearLangGroupPrefFonts()
|
|
|
|
{
|
2015-11-20 07:35:15 +03:00
|
|
|
ClearGenericMappings();
|
2015-11-16 02:48:40 +03:00
|
|
|
gfxPlatformFontList::ClearLangGroupPrefFonts();
|
|
|
|
mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
/* 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.
|
|
|
|
|
2018-04-17 23:45:29 +03:00
|
|
|
FcPattern* pat =
|
|
|
|
FcPatternBuild(0, FC_FAMILY, FcTypeString, "serif", (char*)0);
|
|
|
|
cairo_font_face_t* face =
|
|
|
|
cairo_ft_font_face_create_for_pattern(pat, nullptr, 0);
|
|
|
|
FcPatternDestroy(pat);
|
|
|
|
|
|
|
|
cairo_matrix_t identity;
|
|
|
|
cairo_matrix_init_identity(&identity);
|
|
|
|
cairo_font_options_t* options = cairo_font_options_create();
|
|
|
|
cairo_scaled_font_t* sf =
|
|
|
|
cairo_scaled_font_create(face, &identity, &identity, options);
|
|
|
|
cairo_font_options_destroy(options);
|
|
|
|
cairo_font_face_destroy(face);
|
|
|
|
|
|
|
|
FT_Face ft = cairo_ft_scaled_font_lock_face(sf);
|
|
|
|
|
|
|
|
sCairoFTLibrary = ft->glyph->library;
|
|
|
|
|
|
|
|
cairo_ft_scaled_font_unlock_face(sf);
|
|
|
|
cairo_scaled_font_destroy(sf);
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return sCairoFTLibrary;
|
|
|
|
}
|
|
|
|
|
2015-11-16 02:48:40 +03:00
|
|
|
gfxPlatformFontList::PrefFontList*
|
|
|
|
gfxFcPlatformFontList::FindGenericFamilies(const nsAString& aGeneric,
|
2017-10-03 01:05:19 +03:00
|
|
|
nsAtom* aLanguage)
|
2015-05-13 08:11:25 +03:00
|
|
|
{
|
|
|
|
// set up name
|
|
|
|
NS_ConvertUTF16toUTF8 generic(aGeneric);
|
|
|
|
|
|
|
|
nsAutoCString fcLang;
|
|
|
|
GetSampleLangForGroup(aLanguage, fcLang);
|
2015-11-16 02:48:40 +03:00
|
|
|
ToLowerCase(fcLang);
|
2015-05-13 08:11:25 +03:00
|
|
|
|
|
|
|
nsAutoCString genericLang(generic);
|
2015-11-16 02:48:40 +03:00
|
|
|
if (fcLang.Length() > 0) {
|
|
|
|
genericLang.Append('-');
|
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
genericLang.Append(fcLang);
|
|
|
|
|
|
|
|
// try to get the family from the cache
|
2015-11-16 02:48:40 +03:00
|
|
|
PrefFontList* prefFonts = mGenericMappings.Get(genericLang);
|
|
|
|
if (prefFonts) {
|
|
|
|
return prefFonts;
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// if not found, ask fontconfig to pick the appropriate font
|
|
|
|
nsAutoRef<FcPattern> genericPattern(FcPatternCreate());
|
|
|
|
FcPatternAddString(genericPattern, FC_FAMILY,
|
|
|
|
ToFcChar8Ptr(generic.get()));
|
|
|
|
|
2015-05-27 10:19:27 +03:00
|
|
|
// -- prefer scalable fonts
|
|
|
|
FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue);
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
// -- 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));
|
|
|
|
|
2015-07-31 21:37:19 +03:00
|
|
|
if (!faces) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2015-11-16 02:48:40 +03:00
|
|
|
// -- select the fonts to be used for the generic
|
|
|
|
prefFonts = new PrefFontList; // can be empty but in practice won't happen
|
2015-11-20 07:35:15 +03:00
|
|
|
uint32_t limit = gfxPlatformGtk::GetPlatform()->MaxGenericSubstitions();
|
2015-11-20 07:35:15 +03:00
|
|
|
bool foundFontWithLang = false;
|
2015-05-13 08:11:25 +03:00
|
|
|
for (int i = 0; i < faces->nfont; i++) {
|
|
|
|
FcPattern* font = faces->fonts[i];
|
|
|
|
FcChar8* mappedGeneric = nullptr;
|
|
|
|
|
|
|
|
FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
|
|
|
|
if (mappedGeneric) {
|
|
|
|
NS_ConvertUTF8toUTF16 mappedGenericName(ToCharPtr(mappedGeneric));
|
2018-06-01 19:07:24 +03:00
|
|
|
AutoTArray<gfxFontFamily*,1> genericFamilies;
|
2016-04-12 09:06:22 +03:00
|
|
|
if (gfxPlatformFontList::FindAndAddFamilies(mappedGenericName,
|
2017-08-03 03:33:00 +03:00
|
|
|
&genericFamilies,
|
2017-08-01 13:25:35 +03:00
|
|
|
FindFamiliesFlags(0))) {
|
2016-04-12 09:06:22 +03:00
|
|
|
MOZ_ASSERT(genericFamilies.Length() == 1,
|
|
|
|
"expected a single family");
|
2018-06-01 19:07:24 +03:00
|
|
|
if (!prefFonts->Contains(genericFamilies[0])) {
|
|
|
|
prefFonts->AppendElement(genericFamilies[0]);
|
2016-04-12 09:06:22 +03:00
|
|
|
bool foundLang =
|
|
|
|
!fcLang.IsEmpty() &&
|
|
|
|
PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
|
|
|
|
foundFontWithLang = foundFontWithLang || foundLang;
|
|
|
|
// check to see if the list is full
|
|
|
|
if (prefFonts->Length() >= limit) {
|
|
|
|
break;
|
|
|
|
}
|
2015-11-16 02:48:40 +03:00
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-11-20 07:35:15 +03:00
|
|
|
|
|
|
|
// if no font in the list matches the lang, trim all but the first one
|
|
|
|
if (!prefFonts->IsEmpty() && !foundFontWithLang) {
|
|
|
|
prefFonts->TruncateLength(1);
|
|
|
|
}
|
|
|
|
|
2015-11-16 02:48:40 +03:00
|
|
|
mGenericMappings.Put(genericLang, prefFonts);
|
|
|
|
return prefFonts;
|
|
|
|
}
|
2015-05-13 08:11:25 +03:00
|
|
|
|
2015-11-16 02:48:40 +03:00
|
|
|
bool
|
|
|
|
gfxFcPlatformFontList::PrefFontListsUseOnlyGenerics()
|
|
|
|
{
|
2017-04-12 13:32:19 +03:00
|
|
|
static const char kFontNamePrefix[] = "font.name.";
|
|
|
|
|
2015-11-16 02:48:40 +03:00
|
|
|
bool prefFontsUseOnlyGenerics = true;
|
|
|
|
uint32_t count;
|
|
|
|
char** names;
|
|
|
|
nsresult rv = Preferences::GetRootBranch()->
|
|
|
|
GetChildList(kFontNamePrefix, &count, &names);
|
|
|
|
if (NS_SUCCEEDED(rv) && count) {
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
// Check whether all font.name prefs map to generic keywords
|
|
|
|
// and that the pref name and keyword match.
|
|
|
|
// Ex: font.name.serif.ar ==> "serif" (ok)
|
|
|
|
// Ex: font.name.serif.ar ==> "monospace" (return false)
|
|
|
|
// Ex: font.name.serif.ar ==> "DejaVu Serif" (return false)
|
2017-03-29 17:21:47 +03:00
|
|
|
// Ex: font.name.serif.ar ==> "" and
|
|
|
|
// font.name-list.serif.ar ==> "serif" (ok)
|
|
|
|
// Ex: font.name.serif.ar ==> "" and
|
|
|
|
// font.name-list.serif.ar ==> "Something, serif"
|
|
|
|
// (return false)
|
2015-11-16 02:48:40 +03:00
|
|
|
|
|
|
|
nsDependentCString prefName(names[i] +
|
|
|
|
ArrayLength(kFontNamePrefix) - 1);
|
|
|
|
nsCCharSeparatedTokenizer tokenizer(prefName, '.');
|
|
|
|
const nsDependentCSubstring& generic = tokenizer.nextToken();
|
|
|
|
const nsDependentCSubstring& langGroup = tokenizer.nextToken();
|
2017-07-31 07:28:48 +03:00
|
|
|
nsAutoCString fontPrefValue;
|
|
|
|
Preferences::GetCString(names[i], fontPrefValue);
|
2017-03-29 17:21:47 +03:00
|
|
|
if (fontPrefValue.IsEmpty()) {
|
|
|
|
// The font name list may have two or more family names as comma
|
|
|
|
// separated list. In such case, not matching with generic font
|
|
|
|
// name is fine because if the list prefers specific font, this
|
|
|
|
// should return false.
|
2017-07-31 07:28:48 +03:00
|
|
|
Preferences::GetCString(NameListPref(generic, langGroup).get(),
|
|
|
|
fontPrefValue);
|
2017-03-29 17:21:47 +03:00
|
|
|
}
|
2015-11-16 02:48:40 +03:00
|
|
|
|
|
|
|
if (!langGroup.EqualsLiteral("x-math") &&
|
|
|
|
!generic.Equals(fontPrefValue)) {
|
|
|
|
prefFontsUseOnlyGenerics = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
|
|
|
|
}
|
|
|
|
return prefFontsUseOnlyGenerics;
|
2015-05-13 08:11:25 +03:00
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:26 +03:00
|
|
|
/* static */ void
|
|
|
|
gfxFcPlatformFontList::CheckFontUpdates(nsITimer *aTimer, void *aThis)
|
|
|
|
{
|
2017-11-02 23:29:33 +03:00
|
|
|
// A content process is not supposed to check this directly;
|
|
|
|
// it will be notified by the parent when the font list changes.
|
|
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
|
|
|
2015-05-13 08:11:26 +03:00
|
|
|
// 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();
|
2017-11-02 23:29:33 +03:00
|
|
|
|
|
|
|
mozilla::dom::ContentParent::NotifyUpdatedFonts();
|
2015-05-13 08:11:26 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 13:25:35 +03:00
|
|
|
gfxFontFamily*
|
|
|
|
gfxFcPlatformFontList::CreateFontFamily(const nsAString& aName) const
|
|
|
|
{
|
|
|
|
return new gfxFontconfigFontFamily(aName);
|
|
|
|
}
|
|
|
|
|
2018-05-25 23:18:54 +03:00
|
|
|
// mapping of moz lang groups ==> default lang
|
|
|
|
struct MozLangGroupData {
|
|
|
|
nsAtom* 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 }
|
|
|
|
};
|
|
|
|
|
|
|
|
bool
|
|
|
|
gfxFcPlatformFontList::TryLangForGroup(const nsACString& aOSLang,
|
|
|
|
nsAtom* aLangGroup,
|
2018-05-28 12:09:28 +03:00
|
|
|
nsACString& aFcLang,
|
|
|
|
bool aForFontEnumerationThread)
|
2018-05-25 23:18:54 +03:00
|
|
|
{
|
|
|
|
// Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
|
|
|
|
// aOSLang is in the form "language[_territory][.codeset][@modifier]".
|
|
|
|
// fontconfig takes languages in the form "language-territory".
|
|
|
|
// nsLanguageAtomService takes languages in the form language-subtag,
|
|
|
|
// where subtag may be a territory. fontconfig and nsLanguageAtomService
|
|
|
|
// handle case-conversion for us.
|
|
|
|
const char *pos, *end;
|
|
|
|
aOSLang.BeginReading(pos);
|
|
|
|
aOSLang.EndReading(end);
|
|
|
|
aFcLang.Truncate();
|
|
|
|
while (pos < end) {
|
|
|
|
switch (*pos) {
|
|
|
|
case '.':
|
|
|
|
case '@':
|
|
|
|
end = pos;
|
|
|
|
break;
|
|
|
|
case '_':
|
|
|
|
aFcLang.Append('-');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
aFcLang.Append(*pos);
|
|
|
|
}
|
|
|
|
++pos;
|
|
|
|
}
|
|
|
|
|
2018-05-28 12:09:28 +03:00
|
|
|
if (!aForFontEnumerationThread) {
|
|
|
|
nsAtom *atom = mLangService->LookupLanguage(aFcLang);
|
|
|
|
return atom == aLangGroup;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we were called by the font enumeration thread, we can't use
|
|
|
|
// mLangService->LookupLanguage because it is not thread-safe.
|
|
|
|
// Use GetUncachedLanguageGroup to avoid unsafe access to the lang-group
|
|
|
|
// mapping cache hashtable.
|
|
|
|
nsAutoCString lowered(aFcLang);
|
|
|
|
ToLowerCase(lowered);
|
|
|
|
RefPtr<nsAtom> lang = NS_Atomize(lowered);
|
|
|
|
RefPtr<nsAtom> group = mLangService->GetUncachedLanguageGroup(lang);
|
|
|
|
return group.get() == aLangGroup;
|
2018-05-25 23:18:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFcPlatformFontList::GetSampleLangForGroup(nsAtom* aLanguage,
|
2018-05-28 12:09:28 +03:00
|
|
|
nsACString& aLangStr,
|
|
|
|
bool aForFontEnumerationThread)
|
2018-05-25 23:18:54 +03:00
|
|
|
{
|
|
|
|
aLangStr.Truncate();
|
|
|
|
if (!aLanguage) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -- not a mozilla lang group? Just return the BCP47 string
|
|
|
|
// representation of the lang group
|
|
|
|
if (!mozLangGroup) {
|
|
|
|
// Not a special mozilla language group.
|
|
|
|
// Use aLanguage as a language code.
|
|
|
|
aLanguage->ToUTF8String(aLangStr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -- check the environment for the user's preferred language that
|
|
|
|
// corresponds to this mozilla lang group.
|
2018-05-28 12:09:25 +03:00
|
|
|
const char *languages = getenv("LANGUAGE");
|
|
|
|
if (languages) {
|
|
|
|
const char separator = ':';
|
|
|
|
|
|
|
|
for (const char *pos = languages; true; ++pos) {
|
|
|
|
if (*pos == '\0' || *pos == separator) {
|
|
|
|
if (languages < pos &&
|
|
|
|
TryLangForGroup(Substring(languages, pos),
|
2018-05-28 12:09:28 +03:00
|
|
|
aLanguage, aLangStr,
|
|
|
|
aForFontEnumerationThread)) {
|
2018-05-28 12:09:25 +03:00
|
|
|
return;
|
|
|
|
}
|
2018-05-25 23:18:54 +03:00
|
|
|
|
2018-05-28 12:09:25 +03:00
|
|
|
if (*pos == '\0') {
|
|
|
|
break;
|
2018-05-25 23:18:54 +03:00
|
|
|
}
|
2018-05-28 12:09:25 +03:00
|
|
|
|
|
|
|
languages = pos + 1;
|
2018-05-25 23:18:54 +03:00
|
|
|
}
|
|
|
|
}
|
2018-05-28 12:09:25 +03:00
|
|
|
}
|
|
|
|
const char *ctype = setlocale(LC_CTYPE, nullptr);
|
|
|
|
if (ctype &&
|
2018-05-28 12:09:28 +03:00
|
|
|
TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr,
|
|
|
|
aForFontEnumerationThread)) {
|
2018-05-28 12:09:25 +03:00
|
|
|
return;
|
2018-05-25 23:18:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mozLangGroup->defaultLang) {
|
|
|
|
aLangStr.Assign(mozLangGroup->defaultLang);
|
|
|
|
} else {
|
|
|
|
aLangStr.Truncate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 08:11:25 +03:00
|
|
|
#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);
|
|
|
|
}
|
|
|
|
|
2016-03-16 07:54:15 +03:00
|
|
|
#endif // MOZ_WIDGET_GTK
|