summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxFontconfigFonts.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /gfx/thebes/gfxFontconfigFonts.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/thebes/gfxFontconfigFonts.cpp')
-rw-r--r--gfx/thebes/gfxFontconfigFonts.cpp2262
1 files changed, 2262 insertions, 0 deletions
diff --git a/gfx/thebes/gfxFontconfigFonts.cpp b/gfx/thebes/gfxFontconfigFonts.cpp
new file mode 100644
index 000000000..bbcbbabf9
--- /dev/null
+++ b/gfx/thebes/gfxFontconfigFonts.cpp
@@ -0,0 +1,2262 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prlink.h"
+#include "gfxTypes.h"
+
+#include "nsTArray.h"
+
+#include "gfxContext.h"
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+#include "gfxFontconfigFonts.h"
+#include "gfxFT2FontBase.h"
+#include "gfxFT2Utils.h"
+#include "harfbuzz/hb.h"
+#include "harfbuzz/hb-glib.h"
+#include "harfbuzz/hb-ot.h"
+#include "nsUnicodeProperties.h"
+#include "nsUnicodeScriptCodes.h"
+#include "gfxFontconfigUtils.h"
+#include "gfxUserFontSet.h"
+#include "gfxFontConstants.h"
+#include "nsGkAtoms.h"
+#include "nsILanguageAtomService.h"
+#include "nsServiceManagerUtils.h"
+
+#include <cairo.h>
+#include <cairo-ft.h>
+#include "mozilla/gfx/HelpersCairo.h"
+
+#include <fontconfig/fcfreetype.h>
+#include <pango/pango.h>
+
+#include FT_TRUETYPE_TABLES_H
+
+#ifdef MOZ_WIDGET_GTK
+#include <gdk/gdk.h>
+#endif
+
+#include <math.h>
+
+using namespace mozilla;
+using namespace mozilla::unicode;
+
+#define PRINTING_FC_PROPERTY "gfx.printing"
+
+static PangoLanguage *GuessPangoLanguage(nsIAtom *aLanguage);
+
+static cairo_scaled_font_t *
+CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace);
+
+static FT_Library gFTLibrary;
+
+// FC_FAMILYLANG and FC_FULLNAME were introduced in fontconfig-2.2.97
+// and so fontconfig-2.3.0 (2005).
+#ifndef FC_FAMILYLANG
+#define FC_FAMILYLANG "familylang"
+#endif
+#ifndef FC_FULLNAME
+#define FC_FULLNAME "fullname"
+#endif
+
+static PRFuncPtr
+FindFunctionSymbol(const char *name)
+{
+ PRLibrary *lib = nullptr;
+ PRFuncPtr result = PR_FindFunctionSymbolAndLibrary(name, &lib);
+ if (lib) {
+ PR_UnloadLibrary(lib);
+ }
+
+ return result;
+}
+
+static bool HasChar(FcPattern *aFont, FcChar32 wc)
+{
+ FcCharSet *charset = nullptr;
+ FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
+
+ return charset && FcCharSetHasChar(charset, wc);
+}
+
+/**
+ * gfxFcFontEntry:
+ *
+ * An abstract base class of for gfxFontEntry implementations used by
+ * gfxFcFont and gfxUserFontSet.
+ */
+
+class gfxFcFontEntry : public gfxFontEntry {
+public:
+ // For all FontEntrys attached to gfxFcFonts, there will be only one
+ // pattern in this array. This is always a font pattern, not a fully
+ // resolved pattern. gfxFcFont only uses this to construct a PangoFont.
+ //
+ // FontEntrys for src:local() fonts in gfxUserFontSet may return more than
+ // one pattern. (See comment in gfxUserFcFontEntry.)
+ const nsTArray< nsCountedRef<FcPattern> >& GetPatterns()
+ {
+ return mPatterns;
+ }
+
+ static gfxFcFontEntry *LookupFontEntry(cairo_font_face_t *aFace)
+ {
+ return static_cast<gfxFcFontEntry*>
+ (cairo_font_face_get_user_data(aFace, &sFontEntryKey));
+ }
+
+ // override the gfxFontEntry impl to read the name from fontconfig
+ // instead of trying to get the 'name' table, as we don't implement
+ // GetFontTable() here
+ virtual nsString RealFaceName();
+
+ // This is needed to make gfxFontEntry::HasCharacter(aCh) work.
+ virtual bool TestCharacterMap(uint32_t aCh)
+ {
+ for (uint32_t i = 0; i < mPatterns.Length(); ++i) {
+ if (HasChar(mPatterns[i], aCh)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+protected:
+ explicit gfxFcFontEntry(const nsAString& aName)
+ : gfxFontEntry(aName)
+ {
+ }
+
+ // One pattern is the common case and some subclasses rely on successful
+ // addition of the first element to the array.
+ AutoTArray<nsCountedRef<FcPattern>,1> mPatterns;
+
+ static cairo_user_data_key_t sFontEntryKey;
+};
+
+cairo_user_data_key_t gfxFcFontEntry::sFontEntryKey;
+
+nsString
+gfxFcFontEntry::RealFaceName()
+{
+ FcChar8 *name;
+ if (!mPatterns.IsEmpty()) {
+ if (FcPatternGetString(mPatterns[0],
+ FC_FULLNAME, 0, &name) == FcResultMatch) {
+ return NS_ConvertUTF8toUTF16((const char*)name);
+ }
+ if (FcPatternGetString(mPatterns[0],
+ FC_FAMILY, 0, &name) == FcResultMatch) {
+ NS_ConvertUTF8toUTF16 result((const char*)name);
+ if (FcPatternGetString(mPatterns[0],
+ FC_STYLE, 0, &name) == FcResultMatch) {
+ result.Append(' ');
+ AppendUTF8toUTF16((const char*)name, result);
+ }
+ return result;
+ }
+ }
+ // fall back to gfxFontEntry implementation (only works for sfnt fonts)
+ return gfxFontEntry::RealFaceName();
+}
+
+/**
+ * gfxSystemFcFontEntry:
+ *
+ * An implementation of gfxFcFontEntry used by gfxFcFonts for system fonts,
+ * including those from regular family-name based font selection as well as
+ * those from src:local().
+ *
+ * All gfxFcFonts using the same cairo_font_face_t share the same FontEntry.
+ */
+
+class gfxSystemFcFontEntry : public gfxFcFontEntry {
+public:
+ // For memory efficiency, aFontPattern should be a font pattern,
+ // not a fully resolved pattern.
+ gfxSystemFcFontEntry(cairo_font_face_t *aFontFace,
+ FcPattern *aFontPattern,
+ const nsAString& aName)
+ : gfxFcFontEntry(aName), mFontFace(aFontFace),
+ mFTFace(nullptr), mFTFaceInitialized(false)
+ {
+ cairo_font_face_reference(mFontFace);
+ cairo_font_face_set_user_data(mFontFace, &sFontEntryKey, this, nullptr);
+
+ // mPatterns is an AutoTArray with 1 space always available, so the
+ // AppendElement always succeeds.
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(mPatterns.AppendElement(fallible));
+ mPatterns[0] = aFontPattern;
+
+ FcChar8 *name;
+ if (FcPatternGetString(aFontPattern,
+ FC_FAMILY, 0, &name) == FcResultMatch) {
+ mFamilyName = NS_ConvertUTF8toUTF16((const char*)name);
+ }
+ }
+
+ ~gfxSystemFcFontEntry()
+ {
+ cairo_font_face_set_user_data(mFontFace,
+ &sFontEntryKey,
+ nullptr,
+ nullptr);
+ cairo_font_face_destroy(mFontFace);
+ }
+
+ virtual void ForgetHBFace() override;
+ virtual void ReleaseGrFace(gr_face* aFace) override;
+
+protected:
+ virtual nsresult
+ CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>& aBuffer) override;
+
+ void MaybeReleaseFTFace();
+
+private:
+ cairo_font_face_t *mFontFace;
+ FT_Face mFTFace;
+ bool mFTFaceInitialized;
+};
+
+nsresult
+gfxSystemFcFontEntry::CopyFontTable(uint32_t aTableTag,
+ nsTArray<uint8_t>& aBuffer)
+{
+ if (!mFTFaceInitialized) {
+ mFTFaceInitialized = true;
+ FcChar8 *filename;
+ if (FcPatternGetString(mPatterns[0], FC_FILE, 0, &filename) != FcResultMatch) {
+ return NS_ERROR_FAILURE;
+ }
+ int index;
+ if (FcPatternGetInteger(mPatterns[0], FC_INDEX, 0, &index) != FcResultMatch) {
+ index = 0; // default to 0 if not found in pattern
+ }
+ if (FT_New_Face(gfxPangoFontGroup::GetFTLibrary(),
+ (const char*)filename, index, &mFTFace) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (!mFTFace) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ FT_ULong length = 0;
+ if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, nullptr, &length) != 0) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ if (!aBuffer.SetLength(length, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
+ aBuffer.Clear();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+void
+gfxSystemFcFontEntry::MaybeReleaseFTFace()
+{
+ // don't release if either HB or Gr face still exists
+ if (mHBFace || mGrFace) {
+ return;
+ }
+ if (mFTFace) {
+ FT_Done_Face(mFTFace);
+ mFTFace = nullptr;
+ }
+ mFTFaceInitialized = false;
+}
+
+void
+gfxSystemFcFontEntry::ForgetHBFace()
+{
+ gfxFontEntry::ForgetHBFace();
+ MaybeReleaseFTFace();
+}
+
+void
+gfxSystemFcFontEntry::ReleaseGrFace(gr_face* aFace)
+{
+ gfxFontEntry::ReleaseGrFace(aFace);
+ MaybeReleaseFTFace();
+}
+
+// A namespace for @font-face family names in FcPatterns so that fontconfig
+// aliases do not pick up families from @font-face rules and so that
+// fontconfig rules can distinguish between web fonts and platform fonts.
+// http://lists.freedesktop.org/archives/fontconfig/2008-November/003037.html
+#define FONT_FACE_FAMILY_PREFIX "@font-face:"
+
+/**
+ * gfxUserFcFontEntry:
+ *
+ * An abstract class for objects in a gfxUserFontSet that can provide
+ * FcPattern* handles to fonts.
+ *
+ * Separate implementations of this class support local fonts from src:local()
+ * and web fonts from src:url().
+ */
+
+// There is a one-to-one correspondence between gfxUserFcFontEntry objects and
+// @font-face rules, but sometimes a one-to-many correspondence between font
+// entries and font patterns.
+//
+// http://www.w3.org/TR/2002/WD-css3-webfonts-20020802#font-descriptions
+// provided a font-size descriptor to specify the sizes supported by the face,
+// but the "Editor's Draft 27 June 2008"
+// http://dev.w3.org/csswg/css3-fonts/#font-resources does not provide such a
+// descriptor, and Mozilla does not recognize such a descriptor.
+//
+// Font face names used in src:local() also do not usually specify a size.
+//
+// PCF format fonts have each size in a different file, and each of these
+// files is referenced by its own pattern, but really these are each
+// different sizes of one face with one name.
+//
+// Multiple patterns in an entry also effectively deals with a set of
+// PostScript Type 1 font files that all have the same face name but are in
+// several files because of the limit on the number of glyphs in a Type 1 font
+// file. (e.g. Computer Modern.)
+
+class gfxUserFcFontEntry : public gfxFcFontEntry {
+protected:
+ explicit gfxUserFcFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+ : gfxFcFontEntry(aFontName)
+ {
+ mStyle = aStyle;
+ mWeight = aWeight;
+ mStretch = aStretch;
+ }
+
+ // Helper function to change a pattern so that it matches the CSS style
+ // descriptors and so gets properly sorted in font selection. This also
+ // avoids synthetic style effects being added by the renderer when the
+ // style of the font itself does not match the descriptor provided by the
+ // author.
+ void AdjustPatternToCSS(FcPattern *aPattern);
+};
+
+void
+gfxUserFcFontEntry::AdjustPatternToCSS(FcPattern *aPattern)
+{
+ int fontWeight = -1;
+ FcPatternGetInteger(aPattern, FC_WEIGHT, 0, &fontWeight);
+ int cssWeight = gfxFontconfigUtils::FcWeightForBaseWeight(mWeight / 100);
+ if (cssWeight != fontWeight) {
+ FcPatternDel(aPattern, FC_WEIGHT);
+ FcPatternAddInteger(aPattern, FC_WEIGHT, cssWeight);
+ }
+
+ int fontSlant;
+ FcResult res = FcPatternGetInteger(aPattern, FC_SLANT, 0, &fontSlant);
+ // gfxFontEntry doesn't understand the difference between oblique
+ // and italic.
+ if (res != FcResultMatch ||
+ IsItalic() != (fontSlant != FC_SLANT_ROMAN)) {
+ FcPatternDel(aPattern, FC_SLANT);
+ FcPatternAddInteger(aPattern, FC_SLANT,
+ IsItalic() ? FC_SLANT_OBLIQUE : FC_SLANT_ROMAN);
+ }
+
+ int fontWidth = -1;
+ FcPatternGetInteger(aPattern, FC_WIDTH, 0, &fontWidth);
+ int cssWidth = gfxFontconfigUtils::FcWidthForThebesStretch(mStretch);
+ if (cssWidth != fontWidth) {
+ FcPatternDel(aPattern, FC_WIDTH);
+ FcPatternAddInteger(aPattern, FC_WIDTH, cssWidth);
+ }
+
+ // Ensure that there is a fullname property (if there is a family
+ // property) so that fontconfig rules can identify the real name of the
+ // font, because the family property will be replaced.
+ FcChar8 *unused;
+ if (FcPatternGetString(aPattern,
+ FC_FULLNAME, 0, &unused) == FcResultNoMatch) {
+ nsAutoCString fullname;
+ if (gfxFontconfigUtils::GetFullnameFromFamilyAndStyle(aPattern,
+ &fullname)) {
+ FcPatternAddString(aPattern, FC_FULLNAME,
+ gfxFontconfigUtils::ToFcChar8(fullname));
+ }
+ }
+
+ nsAutoCString family;
+ family.Append(FONT_FACE_FAMILY_PREFIX);
+ AppendUTF16toUTF8(Name(), family);
+
+ FcPatternDel(aPattern, FC_FAMILY);
+ FcPatternDel(aPattern, FC_FAMILYLANG);
+ FcPatternAddString(aPattern, FC_FAMILY,
+ gfxFontconfigUtils::ToFcChar8(family));
+}
+
+/**
+ * gfxLocalFcFontEntry:
+ *
+ * An implementation of gfxUserFcFontEntry for local fonts from src:local().
+ *
+ * This class is used only in gfxUserFontSet and for providing FcPattern*
+ * handles to system fonts for font selection. gfxFcFonts created from these
+ * patterns will use gfxSystemFcFontEntrys, which may be shared with
+ * gfxFcFonts from regular family-name based font selection.
+ */
+
+class gfxLocalFcFontEntry : public gfxUserFcFontEntry {
+public:
+ gfxLocalFcFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const nsTArray< nsCountedRef<FcPattern> >& aPatterns)
+ : gfxUserFcFontEntry(aFontName, aWeight, aStretch, aStyle)
+ {
+ if (!mPatterns.SetCapacity(aPatterns.Length(), fallible))
+ return; // OOM
+
+ for (uint32_t i = 0; i < aPatterns.Length(); ++i) {
+ FcPattern *pattern = FcPatternDuplicate(aPatterns.ElementAt(i));
+ if (!pattern)
+ return; // OOM
+
+ AdjustPatternToCSS(pattern);
+
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(mPatterns.AppendElement(fallible));
+ mPatterns[i].own(pattern);
+ }
+ mIsLocalUserFont = true;
+ }
+};
+
+/**
+ * gfxDownloadedFcFontEntry:
+ *
+ * An implementation of gfxFcFontEntry for web fonts from src:url().
+ *
+ * When a cairo_font_face_t is created for these fonts, the cairo_font_face_t
+ * keeps a reference to the FontEntry to keep the font data alive.
+ */
+
+class gfxDownloadedFcFontEntry : public gfxUserFcFontEntry {
+public:
+ // This takes ownership of the face and its underlying data
+ gfxDownloadedFcFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t *aData, FT_Face aFace)
+ : gfxUserFcFontEntry(aFontName, aWeight, aStretch, aStyle),
+ mFontData(aData), mFace(aFace)
+ {
+ NS_PRECONDITION(aFace != nullptr, "aFace is NULL!");
+ mIsDataUserFont = true;
+ InitPattern();
+ }
+
+ virtual ~gfxDownloadedFcFontEntry();
+
+ // Returns true on success
+ bool SetCairoFace(cairo_font_face_t *aFace);
+
+ virtual hb_blob_t* GetFontTable(uint32_t aTableTag) override;
+
+protected:
+ void InitPattern();
+
+ // mFontData holds the data used to instantiate the FT_Face;
+ // this has to persist until we are finished with the face,
+ // then be released with free().
+ const uint8_t* mFontData;
+
+ FT_Face mFace;
+};
+
+// A property for recording gfxDownloadedFcFontEntrys on FcPatterns.
+static const char *kFontEntryFcProp = "-moz-font-entry";
+
+static FcBool AddDownloadedFontEntry(FcPattern *aPattern,
+ gfxDownloadedFcFontEntry *aFontEntry)
+{
+ FcValue value;
+ value.type = FcTypeFTFace; // void* field of union
+ value.u.f = aFontEntry;
+
+ return FcPatternAdd(aPattern, kFontEntryFcProp, value, FcFalse);
+}
+
+static FcBool DelDownloadedFontEntry(FcPattern *aPattern)
+{
+ return FcPatternDel(aPattern, kFontEntryFcProp);
+}
+
+static gfxDownloadedFcFontEntry *GetDownloadedFontEntry(FcPattern *aPattern)
+{
+ FcValue value;
+ if (FcPatternGet(aPattern, kFontEntryFcProp, 0, &value) != FcResultMatch)
+ return nullptr;
+
+ if (value.type != FcTypeFTFace) {
+ NS_NOTREACHED("Wrong type for -moz-font-entry font property");
+ return nullptr;
+ }
+
+ return static_cast<gfxDownloadedFcFontEntry*>(value.u.f);
+}
+
+gfxDownloadedFcFontEntry::~gfxDownloadedFcFontEntry()
+{
+ if (mPatterns.Length() != 0) {
+ // Remove back reference to this font entry and the face in case
+ // anyone holds a reference to the pattern.
+ NS_ASSERTION(mPatterns.Length() == 1,
+ "More than one pattern in gfxDownloadedFcFontEntry!");
+ DelDownloadedFontEntry(mPatterns[0]);
+ FcPatternDel(mPatterns[0], FC_FT_FACE);
+ }
+ FT_Done_Face(mFace);
+ free((void*)mFontData);
+}
+
+typedef FcPattern* (*QueryFaceFunction)(const FT_Face face,
+ const FcChar8 *file, int id,
+ FcBlanks *blanks);
+
+void
+gfxDownloadedFcFontEntry::InitPattern()
+{
+ static QueryFaceFunction sQueryFacePtr =
+ reinterpret_cast<QueryFaceFunction>
+ (FindFunctionSymbol("FcFreeTypeQueryFace"));
+ FcPattern *pattern;
+
+ // FcFreeTypeQueryFace is the same function used to construct patterns for
+ // system fonts and so is the preferred function to use for this purpose.
+ // This will set up the langset property, which helps with sorting, and
+ // the foundry, fullname, and fontversion properties, which properly
+ // identify the font to fontconfig rules. However, FcFreeTypeQueryFace is
+ // available only from fontconfig-2.4.2 (December 2006). (CentOS 5.0 has
+ // fontconfig-2.4.1.)
+ if (sQueryFacePtr) {
+ // 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.
+ pattern =
+ (*sQueryFacePtr)(mFace,
+ gfxFontconfigUtils::ToFcChar8(""),
+ 0,
+ nullptr);
+ if (!pattern)
+ // Either OOM, or fontconfig chose to skip this font because it
+ // has "no encoded characters", which I think means "BDF and PCF
+ // fonts which are not in Unicode (or the effectively equivalent
+ // ISO Latin-1) encoding".
+ return;
+
+ // These properties don't make sense for this face without a file.
+ FcPatternDel(pattern, FC_FILE);
+ FcPatternDel(pattern, FC_INDEX);
+
+ } else {
+ // Do the minimum necessary to construct a pattern for sorting.
+
+ // FC_CHARSET is vital to determine which characters are supported.
+ nsAutoRef<FcCharSet> charset(FcFreeTypeCharSet(mFace, nullptr));
+ // If there are no characters then assume we don't know how to read
+ // this font.
+ if (!charset || FcCharSetCount(charset) == 0)
+ return;
+
+ pattern = FcPatternCreate();
+ FcPatternAddCharSet(pattern, FC_CHARSET, charset);
+
+ // FC_PIXEL_SIZE can be important for font selection of fixed-size
+ // fonts.
+ if (!(mFace->face_flags & FT_FACE_FLAG_SCALABLE)) {
+ for (FT_Int i = 0; i < mFace->num_fixed_sizes; ++i) {
+#if HAVE_FT_BITMAP_SIZE_Y_PPEM
+ double size = FLOAT_FROM_26_6(mFace->available_sizes[i].y_ppem);
+#else
+ double size = mFace->available_sizes[i].height;
+#endif
+ FcPatternAddDouble (pattern, FC_PIXEL_SIZE, size);
+ }
+
+ // Not sure whether this is important;
+ // imitating FcFreeTypeQueryFace:
+ FcPatternAddBool (pattern, FC_ANTIALIAS, FcFalse);
+ }
+
+ // Setting up the FC_LANGSET property is very difficult with the APIs
+ // available prior to FcFreeTypeQueryFace. Having no FC_LANGSET
+ // property seems better than having a property with an empty LangSet.
+ // With no FC_LANGSET property, fontconfig sort functions will
+ // consider this face to have the same priority as (otherwise equal)
+ // faces that have support for the primary requested language, but
+ // will not consider any language to have been satisfied (and so will
+ // continue to look for a face with language support in fallback
+ // fonts).
+ }
+
+ AdjustPatternToCSS(pattern);
+
+ FcPatternAddFTFace(pattern, FC_FT_FACE, mFace);
+ AddDownloadedFontEntry(pattern, this);
+
+ // There is never more than one pattern
+ // FIXME: Make this infallible after bug 968520 is done.
+ MOZ_ALWAYS_TRUE(mPatterns.AppendElement(fallible));
+ mPatterns[0].own(pattern);
+}
+
+static void ReleaseDownloadedFontEntry(void *data)
+{
+ gfxDownloadedFcFontEntry *downloadedFontEntry =
+ static_cast<gfxDownloadedFcFontEntry*>(data);
+ NS_RELEASE(downloadedFontEntry);
+}
+
+bool gfxDownloadedFcFontEntry::SetCairoFace(cairo_font_face_t *aFace)
+{
+ if (CAIRO_STATUS_SUCCESS !=
+ cairo_font_face_set_user_data(aFace, &sFontEntryKey, this,
+ ReleaseDownloadedFontEntry))
+ return false;
+
+ // Hold a reference to this font entry to keep the font face data.
+ NS_ADDREF(this);
+ return true;
+}
+
+hb_blob_t *
+gfxDownloadedFcFontEntry::GetFontTable(uint32_t aTableTag)
+{
+ // The entry already owns the (sanitized) sfnt data in mFontData,
+ // so we can just return a blob that "wraps" the appropriate chunk of it.
+ // The blob should not attempt to free its data, as the entire sfnt data
+ // will be freed when the font entry is deleted.
+ return gfxFontUtils::GetTableFromFontData(mFontData, aTableTag);
+}
+
+/*
+ * gfxFcFont
+ *
+ * This is a gfxFont implementation using a CAIRO_FONT_TYPE_FT
+ * cairo_scaled_font created from an FcPattern.
+ */
+
+class gfxFcFont : public gfxFontconfigFontBase {
+public:
+ virtual ~gfxFcFont();
+ static already_AddRefed<gfxFcFont>
+ GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern,
+ const gfxFontStyle *aFontStyle);
+
+ // return a cloned font resized and offset to simulate sub/superscript glyphs
+ virtual already_AddRefed<gfxFont>
+ GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) override;
+
+protected:
+ virtual already_AddRefed<gfxFont> MakeScaledFont(gfxFontStyle *aFontStyle,
+ gfxFloat aFontScale);
+ virtual already_AddRefed<gfxFont> GetSmallCapsFont() override;
+
+private:
+ gfxFcFont(cairo_scaled_font_t *aCairoFont,
+ FcPattern *aPattern,
+ gfxFcFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle);
+
+ // key for locating a gfxFcFont corresponding to a cairo_scaled_font
+ static cairo_user_data_key_t sGfxFontKey;
+};
+
+/**
+ * gfxFcFontSet:
+ *
+ * Translation from a desired FcPattern to a sorted set of font references
+ * (fontconfig cache data) and (when needed) fonts.
+ */
+
+class gfxFcFontSet final {
+public:
+ NS_INLINE_DECL_REFCOUNTING(gfxFcFontSet)
+
+ explicit gfxFcFontSet(FcPattern *aPattern,
+ gfxUserFontSet *aUserFontSet)
+ : mSortPattern(aPattern), mUserFontSet(aUserFontSet),
+ mFcFontsTrimmed(0),
+ mHaveFallbackFonts(false)
+ {
+ bool waitForUserFont;
+ mFcFontSet = SortPreferredFonts(waitForUserFont);
+ mWaitingForUserFont = waitForUserFont;
+ }
+
+ // A reference is held by the FontSet.
+ // The caller may add a ref to keep the font alive longer than the FontSet.
+ gfxFcFont *GetFontAt(uint32_t i, const gfxFontStyle *aFontStyle)
+ {
+ if (i >= mFonts.Length() || !mFonts[i].mFont) {
+ // GetFontPatternAt sets up mFonts
+ FcPattern *fontPattern = GetFontPatternAt(i);
+ if (!fontPattern)
+ return nullptr;
+
+ mFonts[i].mFont =
+ gfxFcFont::GetOrMakeFont(mSortPattern, fontPattern,
+ aFontStyle);
+ }
+ return mFonts[i].mFont;
+ }
+
+ FcPattern *GetFontPatternAt(uint32_t i);
+
+ bool WaitingForUserFont() const {
+ return mWaitingForUserFont;
+ }
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~gfxFcFontSet()
+ {
+ }
+
+ nsReturnRef<FcFontSet> SortPreferredFonts(bool& aWaitForUserFont);
+ nsReturnRef<FcFontSet> SortFallbackFonts();
+
+ struct FontEntry {
+ explicit FontEntry(FcPattern *aPattern) : mPattern(aPattern) {}
+ nsCountedRef<FcPattern> mPattern;
+ RefPtr<gfxFcFont> mFont;
+ };
+
+ struct LangSupportEntry {
+ LangSupportEntry(FcChar8 *aLang, FcLangResult aSupport) :
+ mLang(aLang), mBestSupport(aSupport) {}
+ FcChar8 *mLang;
+ FcLangResult mBestSupport;
+ };
+
+public:
+ // public for nsTArray
+ class LangComparator {
+ public:
+ bool Equals(const LangSupportEntry& a, const FcChar8 *b) const
+ {
+ return FcStrCmpIgnoreCase(a.mLang, b) == 0;
+ }
+ };
+
+private:
+ // The requested pattern
+ nsCountedRef<FcPattern> mSortPattern;
+ // Fonts from @font-face rules
+ RefPtr<gfxUserFontSet> mUserFontSet;
+ // A (trimmed) list of font patterns and fonts that is built up as
+ // required.
+ nsTArray<FontEntry> mFonts;
+ // Holds a list of font patterns that will be trimmed. This is first set
+ // to a list of preferred fonts. Then, if/when all the preferred fonts
+ // have been trimmed and added to mFonts, this is set to a list of
+ // fallback fonts.
+ nsAutoRef<FcFontSet> mFcFontSet;
+ // The set of characters supported by the fonts in mFonts.
+ nsAutoRef<FcCharSet> mCharSet;
+ // The index of the next font in mFcFontSet that has not yet been
+ // considered for mFonts.
+ int mFcFontsTrimmed;
+ // True iff fallback fonts are either stored in mFcFontSet or have been
+ // trimmed and added to mFonts (so that mFcFontSet is nullptr).
+ bool mHaveFallbackFonts;
+ // True iff there was a user font set with pending downloads,
+ // so the set may be updated when downloads complete
+ bool mWaitingForUserFont;
+};
+
+// Find the FcPattern for an @font-face font suitable for CSS family |aFamily|
+// and style |aStyle| properties.
+static const nsTArray< nsCountedRef<FcPattern> >*
+FindFontPatterns(gfxUserFontSet *mUserFontSet,
+ const nsACString &aFamily, uint8_t aStyle,
+ uint16_t aWeight, int16_t aStretch,
+ bool& aWaitForUserFont)
+{
+ // Convert to UTF16
+ NS_ConvertUTF8toUTF16 utf16Family(aFamily);
+
+ // needsBold is not used here. Instead synthetic bold is enabled through
+ // FcFontRenderPrepare when the weight in the requested pattern is
+ // compared against the weight in the font pattern.
+ bool needsBold;
+
+ gfxFontStyle style;
+ style.style = aStyle;
+ style.weight = aWeight;
+ style.stretch = aStretch;
+
+ gfxUserFcFontEntry *fontEntry = nullptr;
+ gfxFontFamily *family = mUserFontSet->LookupFamily(utf16Family);
+ if (family) {
+ gfxUserFontEntry* userFontEntry =
+ mUserFontSet->FindUserFontEntryAndLoad(family, style, needsBold,
+ aWaitForUserFont);
+ if (userFontEntry) {
+ fontEntry = static_cast<gfxUserFcFontEntry*>
+ (userFontEntry->GetPlatformFontEntry());
+ }
+
+ // Accept synthetic oblique for italic and oblique.
+ // xxx - this isn't really ideal behavior, for docs that only use a
+ // single italic face it will also pull down the normal face
+ // and probably never use it
+ if (!fontEntry && aStyle != NS_FONT_STYLE_NORMAL) {
+ style.style = NS_FONT_STYLE_NORMAL;
+ userFontEntry =
+ mUserFontSet->FindUserFontEntryAndLoad(family, style,
+ needsBold,
+ aWaitForUserFont);
+ if (userFontEntry) {
+ fontEntry = static_cast<gfxUserFcFontEntry*>
+ (userFontEntry->GetPlatformFontEntry());
+ }
+ }
+ }
+
+ if (!fontEntry) {
+ return nullptr;
+ }
+
+ return &fontEntry->GetPatterns();
+}
+
+typedef FcBool (*FcPatternRemoveFunction)(FcPattern *p, const char *object,
+ int id);
+
+// FcPatternRemove is available in fontconfig-2.3.0 (2005)
+static FcBool
+moz_FcPatternRemove(FcPattern *p, const char *object, int id)
+{
+ static FcPatternRemoveFunction sFcPatternRemovePtr =
+ reinterpret_cast<FcPatternRemoveFunction>
+ (FindFunctionSymbol("FcPatternRemove"));
+
+ if (!sFcPatternRemovePtr)
+ return FcFalse;
+
+ return (*sFcPatternRemovePtr)(p, object, id);
+}
+
+// fontconfig prefers a matching family or lang to pixelsize of bitmap
+// fonts. CSS suggests a tolerance of 20% on pixelsize.
+static bool
+SizeIsAcceptable(FcPattern *aFont, double aRequestedSize)
+{
+ double size;
+ int v = 0;
+ while (FcPatternGetDouble(aFont,
+ FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
+ ++v;
+ if (5.0 * fabs(size - aRequestedSize) < aRequestedSize)
+ return true;
+ }
+
+ // No size means scalable
+ return v == 0;
+}
+
+// Sorting only the preferred fonts first usually saves having to sort through
+// every font on the system.
+nsReturnRef<FcFontSet>
+gfxFcFontSet::SortPreferredFonts(bool &aWaitForUserFont)
+{
+ aWaitForUserFont = false;
+
+ gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils();
+ if (!utils)
+ return nsReturnRef<FcFontSet>();
+
+ // The list of families in mSortPattern has values with both weak and
+ // strong bindings. Values with strong bindings should be preferred.
+ // Values with weak bindings are default fonts that should be considered
+ // only when the font provides the best support for a requested language
+ // or after other fonts have satisfied all the requested languages.
+ //
+ // There are no direct fontconfig APIs to get the binding type. The
+ // binding only takes effect in the sort and match functions.
+
+ // |requiredLangs| is a list of requested languages that have not yet been
+ // satisfied. gfxFontconfigUtils only sets one FC_LANG property value,
+ // but FcConfigSubstitute may add more values (e.g. prepending "en" to
+ // "ja" will use western fonts to render Latin/Arabic numerals in Japanese
+ // text.)
+ AutoTArray<LangSupportEntry,10> requiredLangs;
+ for (int v = 0; ; ++v) {
+ FcChar8 *lang;
+ FcResult result = FcPatternGetString(mSortPattern, FC_LANG, v, &lang);
+ if (result != FcResultMatch) {
+ // No need to check FcPatternGetLangSet() because
+ // gfxFontconfigUtils sets only a string value for FC_LANG and
+ // FcConfigSubstitute cannot add LangSets.
+ NS_ASSERTION(result != FcResultTypeMismatch,
+ "Expected a string for FC_LANG");
+ break;
+ }
+
+ if (!requiredLangs.Contains(lang, LangComparator())) {
+ FcLangResult bestLangSupport = utils->GetBestLangSupport(lang);
+ if (bestLangSupport != FcLangDifferentLang) {
+ requiredLangs.
+ AppendElement(LangSupportEntry(lang, bestLangSupport));
+ }
+ }
+ }
+
+ nsAutoRef<FcFontSet> fontSet(FcFontSetCreate());
+ if (!fontSet)
+ return fontSet.out();
+
+ // FcDefaultSubstitute() ensures a slant on mSortPattern, but, if that ever
+ // doesn't happen, Roman will be used.
+ int requestedSlant = FC_SLANT_ROMAN;
+ FcPatternGetInteger(mSortPattern, FC_SLANT, 0, &requestedSlant);
+ double requestedSize = -1.0;
+ FcPatternGetDouble(mSortPattern, FC_PIXEL_SIZE, 0, &requestedSize);
+
+ nsTHashtable<gfxFontconfigUtils::DepFcStrEntry> existingFamilies(32);
+ FcChar8 *family;
+ for (int v = 0;
+ FcPatternGetString(mSortPattern,
+ FC_FAMILY, v, &family) == FcResultMatch; ++v) {
+ const nsTArray< nsCountedRef<FcPattern> > *familyFonts = nullptr;
+
+ // Is this an @font-face family?
+ bool isUserFont = false;
+ if (mUserFontSet) {
+ // Have some @font-face definitions
+
+ nsDependentCString cFamily(gfxFontconfigUtils::ToCString(family));
+ NS_NAMED_LITERAL_CSTRING(userPrefix, FONT_FACE_FAMILY_PREFIX);
+
+ if (StringBeginsWith(cFamily, userPrefix)) {
+ isUserFont = true;
+
+ // Trim off the prefix
+ nsDependentCSubstring cssFamily(cFamily, userPrefix.Length());
+
+ uint8_t thebesStyle =
+ gfxFontconfigUtils::FcSlantToThebesStyle(requestedSlant);
+ uint16_t thebesWeight =
+ gfxFontconfigUtils::GetThebesWeight(mSortPattern);
+ int16_t thebesStretch =
+ gfxFontconfigUtils::GetThebesStretch(mSortPattern);
+
+ bool waitForUserFont;
+ familyFonts = FindFontPatterns(mUserFontSet, cssFamily,
+ thebesStyle,
+ thebesWeight, thebesStretch,
+ waitForUserFont);
+ if (waitForUserFont) {
+ aWaitForUserFont = true;
+ }
+ }
+ }
+
+ if (!isUserFont) {
+ familyFonts = &utils->GetFontsForFamily(family);
+ }
+
+ if (!familyFonts || familyFonts->Length() == 0) {
+ // There are no fonts matching this family, so there is no point
+ // in searching for this family in the FontSort.
+ //
+ // Perhaps the original pattern should be retained for
+ // FcFontRenderPrepare. However, the only a useful config
+ // substitution test against missing families that i can imagine
+ // would only be interested in the preferred family
+ // (qual="first"), so always keep the first family and use the
+ // same pattern for Sort and RenderPrepare.
+ if (v != 0 && moz_FcPatternRemove(mSortPattern, FC_FAMILY, v)) {
+ --v;
+ }
+ continue;
+ }
+
+ // Aliases seem to often end up occurring more than once, but
+ // duplicate families can't be removed from the sort pattern without
+ // knowing whether duplicates have the same binding.
+ gfxFontconfigUtils::DepFcStrEntry *familyEntry =
+ existingFamilies.PutEntry(family);
+ if (familyEntry) {
+ if (familyEntry->mKey) // old entry
+ continue;
+
+ familyEntry->mKey = family; // initialize new entry
+ }
+
+ for (uint32_t f = 0; f < familyFonts->Length(); ++f) {
+ FcPattern *font = familyFonts->ElementAt(f);
+
+ // Fix up the family name of user-font patterns, as the same
+ // font entry may be used (via the UserFontCache) for multiple
+ // CSS family names
+ if (isUserFont) {
+ font = FcPatternDuplicate(font);
+ FcPatternDel(font, FC_FAMILY);
+ FcPatternAddString(font, FC_FAMILY, family);
+ }
+
+ // User fonts are already filtered by slant (but not size) in
+ // mUserFontSet->FindUserFontEntry().
+ if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
+ continue;
+
+ for (uint32_t r = 0; r < requiredLangs.Length(); ++r) {
+ const LangSupportEntry& langEntry = requiredLangs[r];
+ FcLangResult support =
+ gfxFontconfigUtils::GetLangSupport(font, langEntry.mLang);
+ if (support <= langEntry.mBestSupport) { // lower is better
+ requiredLangs.RemoveElementAt(r);
+ --r;
+ }
+ }
+
+ // FcFontSetDestroy will remove a reference but FcFontSetAdd
+ // does _not_ take a reference!
+ if (FcFontSetAdd(fontSet, font)) {
+ // We don't add a reference here for user fonts, because we're
+ // using a local clone of the pattern (see above) in order to
+ // override the family name
+ if (!isUserFont) {
+ FcPatternReference(font);
+ }
+ }
+ }
+ }
+
+ FcPattern *truncateMarker = nullptr;
+ for (uint32_t r = 0; r < requiredLangs.Length(); ++r) {
+ const nsTArray< nsCountedRef<FcPattern> >& langFonts =
+ utils->GetFontsForLang(requiredLangs[r].mLang);
+
+ bool haveLangFont = false;
+ for (uint32_t f = 0; f < langFonts.Length(); ++f) {
+ FcPattern *font = langFonts[f];
+ if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
+ continue;
+
+ haveLangFont = true;
+ if (FcFontSetAdd(fontSet, font)) {
+ FcPatternReference(font);
+ }
+ }
+
+ if (!haveLangFont && langFonts.Length() > 0) {
+ // There is a font that supports this language but it didn't pass
+ // the slant and size criteria. Weak default font families should
+ // not be considered until the language has been satisfied.
+ //
+ // Insert a font that supports the language so that it will mark
+ // the position of fonts from weak families in the sorted set and
+ // they can be removed. The language and weak families will be
+ // considered in the fallback fonts, which use fontconfig's
+ // algorithm.
+ //
+ // Of the fonts that don't meet slant and size criteria, strong
+ // default font families should be considered before (other) fonts
+ // for this language, so this marker font will be removed (as well
+ // as the fonts from weak families), and strong families will be
+ // reconsidered in the fallback fonts.
+ FcPattern *font = langFonts[0];
+ if (FcFontSetAdd(fontSet, font)) {
+ FcPatternReference(font);
+ truncateMarker = font;
+ }
+ break;
+ }
+ }
+
+ FcFontSet *sets[1] = { fontSet };
+ FcResult result;
+#ifdef SOLARIS
+ // Get around a crash of FcFontSetSort when FcConfig is nullptr
+ // Solaris's FcFontSetSort needs an FcConfig (bug 474758)
+ fontSet.own(FcFontSetSort(FcConfigGetCurrent(), sets, 1, mSortPattern,
+ FcFalse, nullptr, &result));
+#else
+ fontSet.own(FcFontSetSort(nullptr, sets, 1, mSortPattern,
+ FcFalse, nullptr, &result));
+#endif
+
+ if (truncateMarker != nullptr && fontSet) {
+ nsAutoRef<FcFontSet> truncatedSet(FcFontSetCreate());
+
+ for (int f = 0; f < fontSet->nfont; ++f) {
+ FcPattern *font = fontSet->fonts[f];
+ if (font == truncateMarker)
+ break;
+
+ if (FcFontSetAdd(truncatedSet, font)) {
+ FcPatternReference(font);
+ }
+ }
+
+ fontSet.steal(truncatedSet);
+ }
+
+ return fontSet.out();
+}
+
+nsReturnRef<FcFontSet>
+gfxFcFontSet::SortFallbackFonts()
+{
+ // Setting trim to FcTrue would provide a much smaller (~ 1/10) FcFontSet,
+ // but would take much longer due to comparing all the character sets.
+ //
+ // The references to fonts in this FcFontSet are almost free
+ // as they are pointers into mmaped cache files.
+ //
+ // GetFontPatternAt() will trim lazily if and as needed, which will also
+ // remove duplicates of preferred fonts.
+ FcResult result;
+ return nsReturnRef<FcFontSet>(FcFontSort(nullptr, mSortPattern,
+ FcFalse, nullptr, &result));
+}
+
+// GetFontAt relies on this setting up all patterns up to |i|.
+FcPattern *
+gfxFcFontSet::GetFontPatternAt(uint32_t i)
+{
+ while (i >= mFonts.Length()) {
+ while (!mFcFontSet) {
+ if (mHaveFallbackFonts)
+ return nullptr;
+
+ mFcFontSet = SortFallbackFonts();
+ mHaveFallbackFonts = true;
+ mFcFontsTrimmed = 0;
+ // Loop to test that mFcFontSet is non-nullptr.
+ }
+
+ while (mFcFontsTrimmed < mFcFontSet->nfont) {
+ FcPattern *font = mFcFontSet->fonts[mFcFontsTrimmed];
+ ++mFcFontsTrimmed;
+
+ if (mFonts.Length() != 0) {
+ // See if the next font provides support for any extra
+ // characters. Most often the next font is not going to
+ // support more characters so check for a SubSet first before
+ // allocating a new CharSet with Union.
+ FcCharSet *supportedChars = mCharSet;
+ if (!supportedChars) {
+ FcPatternGetCharSet(mFonts[mFonts.Length() - 1].mPattern,
+ FC_CHARSET, 0, &supportedChars);
+ }
+
+ if (supportedChars) {
+ FcCharSet *newChars = nullptr;
+ FcPatternGetCharSet(font, FC_CHARSET, 0, &newChars);
+ if (newChars) {
+ if (FcCharSetIsSubset(newChars, supportedChars))
+ continue;
+
+ mCharSet.own(FcCharSetUnion(supportedChars, newChars));
+ } else if (!mCharSet) {
+ mCharSet.own(FcCharSetCopy(supportedChars));
+ }
+ }
+ }
+
+ mFonts.AppendElement(font);
+ if (mFonts.Length() >= i)
+ break;
+ }
+
+ if (mFcFontsTrimmed == mFcFontSet->nfont) {
+ // finished with this font set
+ mFcFontSet.reset();
+ }
+ }
+
+ return mFonts[i].mPattern;
+}
+
+#ifdef MOZ_WIDGET_GTK
+static void ApplyGdkScreenFontOptions(FcPattern *aPattern);
+#endif
+
+// Apply user settings and defaults to pattern in preparation for matching.
+static void
+PrepareSortPattern(FcPattern *aPattern, double aFallbackSize,
+ double aSizeAdjustFactor, bool aIsPrinterFont)
+{
+ FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
+
+ // This gets cairo_font_options_t for the Screen. We should have
+ // different font options for printing (no hinting) but we are not told
+ // what we are measuring for.
+ //
+ // If cairo adds support for lcd_filter, gdk will not provide the default
+ // setting for that option. We could get the default setting by creating
+ // an xlib surface once, recording its font_options, and then merging the
+ // gdk options.
+ //
+ // Using an xlib surface would also be an option to get Screen font
+ // options for non-GTK X11 toolkits, but less efficient than using GDK to
+ // pick up dynamic changes.
+ if(aIsPrinterFont) {
+ cairo_font_options_t *options = cairo_font_options_create();
+ cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+ cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
+ cairo_ft_font_options_substitute(options, aPattern);
+ cairo_font_options_destroy(options);
+ FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
+ } else {
+#ifdef MOZ_WIDGET_GTK
+ ApplyGdkScreenFontOptions(aPattern);
+#endif
+ }
+
+ // Protect against any fontconfig settings that may have incorrectly
+ // modified the pixelsize, and consider aSizeAdjustFactor.
+ double size = aFallbackSize;
+ if (FcPatternGetDouble(aPattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch
+ || aSizeAdjustFactor != 1.0) {
+ FcPatternDel(aPattern, FC_PIXEL_SIZE);
+ FcPatternAddDouble(aPattern, FC_PIXEL_SIZE, size * aSizeAdjustFactor);
+ }
+
+ FcDefaultSubstitute(aPattern);
+}
+
+/**
+ ** gfxPangoFontGroup
+ **/
+
+gfxPangoFontGroup::gfxPangoFontGroup(const FontFamilyList& aFontFamilyList,
+ const gfxFontStyle *aStyle,
+ gfxUserFontSet *aUserFontSet,
+ gfxFloat aDevToCssSize)
+ : gfxFontGroup(aFontFamilyList, aStyle, nullptr, aUserFontSet, aDevToCssSize),
+ mPangoLanguage(GuessPangoLanguage(aStyle->language))
+{
+ // This language is passed to the font for shaping.
+ // Shaping doesn't know about lang groups so make it a real language.
+ if (mPangoLanguage) {
+ mStyle.language = NS_Atomize(pango_language_to_string(mPangoLanguage));
+ }
+
+ // dummy entry, will be replaced when actually needed
+ mFonts.AppendElement(FamilyFace());
+ mSkipUpdateUserFonts = true;
+}
+
+gfxPangoFontGroup::~gfxPangoFontGroup()
+{
+}
+
+gfxFontGroup *
+gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle)
+{
+ return new gfxPangoFontGroup(mFamilyList, aStyle, mUserFontSet, mDevToCssSize);
+}
+
+void
+gfxPangoFontGroup::FindGenericFontsPFG(FontFamilyType aGenericType,
+ nsIAtom *aLanguage,
+ void *aClosure)
+{
+ AutoTArray<nsString, 5> resolvedGenerics;
+ ResolveGenericFontNamesPFG(aGenericType, aLanguage, resolvedGenerics);
+ uint32_t g = 0, numGenerics = resolvedGenerics.Length();
+ for (g = 0; g < numGenerics; g++) {
+ FindPlatformFontPFG(resolvedGenerics[g], false, aClosure);
+ }
+}
+
+/* static */ void
+gfxPangoFontGroup::ResolveGenericFontNamesPFG(FontFamilyType aGenericType,
+ nsIAtom *aLanguage,
+ nsTArray<nsString>& aGenericFamilies)
+{
+ static const char kGeneric_serif[] = "serif";
+ static const char kGeneric_sans_serif[] = "sans-serif";
+ static const char kGeneric_monospace[] = "monospace";
+ static const char kGeneric_cursive[] = "cursive";
+ static const char kGeneric_fantasy[] = "fantasy";
+
+ // treat -moz-fixed as monospace
+ if (aGenericType == eFamily_moz_fixed) {
+ aGenericType = eFamily_monospace;
+ }
+
+ // type should be standard generic type at this point
+ NS_ASSERTION(aGenericType >= eFamily_serif &&
+ aGenericType <= eFamily_fantasy,
+ "standard generic font family type required");
+
+ // create the lang string
+ nsIAtom *langGroupAtom = nullptr;
+ nsAutoCString langGroupString;
+ if (aLanguage) {
+ if (!gLangService) {
+ CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
+ }
+ if (gLangService) {
+ nsresult rv;
+ langGroupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
+ }
+ }
+ if (!langGroupAtom) {
+ langGroupAtom = nsGkAtoms::Unicode;
+ }
+ langGroupAtom->ToUTF8String(langGroupString);
+
+ // map generic type to string
+ const char *generic = nullptr;
+ switch (aGenericType) {
+ case eFamily_serif:
+ generic = kGeneric_serif;
+ break;
+ case eFamily_sans_serif:
+ generic = kGeneric_sans_serif;
+ break;
+ case eFamily_monospace:
+ generic = kGeneric_monospace;
+ break;
+ case eFamily_cursive:
+ generic = kGeneric_cursive;
+ break;
+ case eFamily_fantasy:
+ generic = kGeneric_fantasy;
+ break;
+ default:
+ break;
+ }
+
+ if (!generic) {
+ return;
+ }
+
+ aGenericFamilies.Clear();
+
+ // load family for "font.name.generic.lang"
+ nsAutoCString prefFontName("font.name.");
+ prefFontName.Append(generic);
+ prefFontName.Append('.');
+ prefFontName.Append(langGroupString);
+ gfxFontUtils::AppendPrefsFontList(prefFontName.get(),
+ aGenericFamilies);
+
+ // if lang has pref fonts, also load fonts for "font.name-list.generic.lang"
+ if (!aGenericFamilies.IsEmpty()) {
+ nsAutoCString prefFontListName("font.name-list.");
+ prefFontListName.Append(generic);
+ prefFontListName.Append('.');
+ prefFontListName.Append(langGroupString);
+ gfxFontUtils::AppendPrefsFontList(prefFontListName.get(),
+ aGenericFamilies);
+ }
+
+#if 0 // dump out generic mappings
+ printf("%s ===> ", prefFontName.get());
+ for (uint32_t k = 0; k < aGenericFamilies.Length(); k++) {
+ if (k > 0) printf(", ");
+ printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]).get());
+ }
+ printf("\n");
+#endif
+}
+
+void gfxPangoFontGroup::EnumerateFontListPFG(nsIAtom *aLanguage, void *aClosure)
+{
+ // initialize fonts in the font family list
+ const nsTArray<FontFamilyName>& fontlist = mFamilyList.GetFontlist();
+
+ // lookup fonts in the fontlist
+ uint32_t i, numFonts = fontlist.Length();
+ for (i = 0; i < numFonts; i++) {
+ const FontFamilyName& name = fontlist[i];
+ if (name.IsNamed()) {
+ FindPlatformFontPFG(name.mName, true, aClosure);
+ } else {
+ FindGenericFontsPFG(name.mType, aLanguage, aClosure);
+ }
+ }
+
+ // if necessary, append default generic onto the end
+ if (mFamilyList.GetDefaultFontType() != eFamily_none &&
+ !mFamilyList.HasDefaultGeneric()) {
+ FindGenericFontsPFG(mFamilyList.GetDefaultFontType(),
+ aLanguage, aClosure);
+ }
+}
+
+void
+gfxPangoFontGroup::FindPlatformFontPFG(const nsAString& fontName,
+ bool aUseFontSet,
+ void *aClosure)
+{
+ nsTArray<nsString> *list = static_cast<nsTArray<nsString>*>(aClosure);
+
+ if (!list->Contains(fontName)) {
+ // names present in the user fontset are not matched against system fonts
+ if (aUseFontSet && mUserFontSet && mUserFontSet->HasFamily(fontName)) {
+ nsAutoString userFontName =
+ NS_LITERAL_STRING(FONT_FACE_FAMILY_PREFIX) + fontName;
+ list->AppendElement(userFontName);
+ } else {
+ list->AppendElement(fontName);
+ }
+ }
+}
+
+gfxFcFont *
+gfxPangoFontGroup::GetBaseFont()
+{
+ if (mFonts[0].Font() == nullptr) {
+ gfxFont* font = GetBaseFontSet()->GetFontAt(0, GetStyle());
+ mFonts[0] = FamilyFace(nullptr, font);
+ }
+
+ return static_cast<gfxFcFont*>(mFonts[0].Font());
+}
+
+gfxFont*
+gfxPangoFontGroup::GetFirstValidFont(uint32_t aCh)
+{
+ return GetFontAt(0);
+}
+
+gfxFont *
+gfxPangoFontGroup::GetFontAt(int32_t i, uint32_t aCh)
+{
+ // If it turns out to be hard for all clients that cache font
+ // groups to call UpdateUserFonts at appropriate times, we could
+ // instead consider just calling UpdateUserFonts from someplace
+ // more central (such as here).
+ NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
+ "Whoever was caching this font group should have "
+ "called UpdateUserFonts on it");
+
+ NS_PRECONDITION(i == 0, "Only have one font");
+
+ return GetBaseFont();
+}
+
+void
+gfxPangoFontGroup::UpdateUserFonts()
+{
+ uint64_t newGeneration = GetGeneration();
+ if (newGeneration == mCurrGeneration)
+ return;
+
+ mFonts[0] = FamilyFace();
+ mFontSets.Clear();
+ ClearCachedData();
+ mCurrGeneration = newGeneration;
+}
+
+already_AddRefed<gfxFcFontSet>
+gfxPangoFontGroup::MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
+ nsAutoRef<FcPattern> *aMatchPattern)
+{
+ const char *lang = pango_language_to_string(aLang);
+
+ RefPtr<nsIAtom> langGroup;
+ if (aLang != mPangoLanguage) {
+ // Set up langGroup for Mozilla's font prefs.
+ langGroup = NS_Atomize(lang);
+ }
+
+ AutoTArray<nsString, 20> fcFamilyList;
+ EnumerateFontListPFG(langGroup ? langGroup.get() : mStyle.language.get(),
+ &fcFamilyList);
+
+ // To consider: A fontset cache here could be helpful.
+
+ // Get a pattern suitable for matching.
+ nsAutoRef<FcPattern> pattern
+ (gfxFontconfigUtils::NewPattern(fcFamilyList, mStyle, lang));
+
+ PrepareSortPattern(pattern, mStyle.size, aSizeAdjustFactor, mStyle.printerFont);
+
+ RefPtr<gfxFcFontSet> fontset =
+ new gfxFcFontSet(pattern, mUserFontSet);
+
+ mSkipDrawing = fontset->WaitingForUserFont();
+
+ if (aMatchPattern)
+ aMatchPattern->steal(pattern);
+
+ return fontset.forget();
+}
+
+gfxPangoFontGroup::
+FontSetByLangEntry::FontSetByLangEntry(PangoLanguage *aLang,
+ gfxFcFontSet *aFontSet)
+ : mLang(aLang), mFontSet(aFontSet)
+{
+}
+
+gfxFcFontSet *
+gfxPangoFontGroup::GetFontSet(PangoLanguage *aLang)
+{
+ GetBaseFontSet(); // sets mSizeAdjustFactor and mFontSets[0]
+
+ if (!aLang)
+ return mFontSets[0].mFontSet;
+
+ for (uint32_t i = 0; i < mFontSets.Length(); ++i) {
+ if (mFontSets[i].mLang == aLang)
+ return mFontSets[i].mFontSet;
+ }
+
+ RefPtr<gfxFcFontSet> fontSet =
+ MakeFontSet(aLang, mSizeAdjustFactor);
+ mFontSets.AppendElement(FontSetByLangEntry(aLang, fontSet));
+
+ return fontSet;
+}
+
+already_AddRefed<gfxFont>
+gfxPangoFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
+ uint32_t aNextCh, Script aRunScript,
+ gfxFont *aPrevMatchedFont,
+ uint8_t *aMatchType)
+{
+ if (aPrevMatchedFont) {
+ // Don't switch fonts for control characters, regardless of
+ // whether they are present in the current font, as they won't
+ // actually be rendered (see bug 716229)
+ uint8_t category = GetGeneralCategory(aCh);
+ if (category == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
+ return RefPtr<gfxFont>(aPrevMatchedFont).forget();
+ }
+
+ // if this character is a join-control or the previous is a join-causer,
+ // use the same font as the previous range if we can
+ if (gfxFontUtils::IsJoinControl(aCh) ||
+ gfxFontUtils::IsJoinCauser(aPrevCh)) {
+ if (aPrevMatchedFont->HasCharacter(aCh)) {
+ return RefPtr<gfxFont>(aPrevMatchedFont).forget();
+ }
+ }
+ }
+
+ // if this character is a variation selector,
+ // use the previous font regardless of whether it supports VS or not.
+ // otherwise the text run will be divided.
+ if (gfxFontUtils::IsVarSelector(aCh)) {
+ if (aPrevMatchedFont) {
+ return RefPtr<gfxFont>(aPrevMatchedFont).forget();
+ }
+ // VS alone. it's meaningless to search different fonts
+ return nullptr;
+ }
+
+ // The real fonts that fontconfig provides for generic/fallback families
+ // depend on the language used, so a different FontSet is used for each
+ // language (except for the variation below).
+ //
+ // With most fontconfig configurations any real family names prior to a
+ // fontconfig generic with corresponding fonts installed will still lead
+ // to the same leading fonts in each FontSet.
+ //
+ // There is an inefficiency here therefore because the same base FontSet
+ // could often be used if these real families support the character.
+ // However, with fontconfig aliases, it is difficult to distinguish
+ // where exactly alias fonts end and generic/fallback fonts begin.
+ //
+ // The variation from pure language-based matching used here is that the
+ // same primary/base font is always used irrespective of the language.
+ // This provides that SCRIPT_COMMON characters are consistently rendered
+ // with the same font (bug 339513 and bug 416725). This is particularly
+ // important with the word cache as script can't be reliably determined
+ // from surrounding words. It also often avoids the unnecessary extra
+ // FontSet efficiency mentioned above.
+ //
+ // However, in two situations, the base font is not checked before the
+ // language-specific FontSet.
+ //
+ // 1. When we don't have a language to make a good choice for
+ // the base font.
+ //
+ // 2. For system fonts, use the default Pango behavior to give
+ // consistency with other apps. This is relevant when un-localized
+ // builds are run in non-Latin locales. This special-case probably
+ // wouldn't be necessary but for bug 91190.
+
+ gfxFcFontSet *fontSet = GetBaseFontSet();
+ uint32_t nextFont = 0;
+ FcPattern *basePattern = nullptr;
+ if (!mStyle.systemFont && mPangoLanguage) {
+ basePattern = fontSet->GetFontPatternAt(0);
+ if (HasChar(basePattern, aCh)) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return RefPtr<gfxFont>(GetBaseFont()).forget();
+ }
+
+ nextFont = 1;
+ }
+
+ // Our MOZ_SCRIPT_* codes may not match the PangoScript enumeration values
+ // (if we're using ICU's codes), so convert by mapping through ISO 15924 tag.
+ // Note that PangoScript is defined to be compatible with GUnicodeScript:
+ // https://developer.gnome.org/pango/stable/pango-Scripts-and-Languages.html#PangoScript
+ const hb_tag_t scriptTag = GetScriptTagForCode(aRunScript);
+ const PangoScript script =
+ (const PangoScript)hb_glib_script_from_script(hb_script_from_iso15924_tag(scriptTag));
+
+ // Might be nice to call pango_language_includes_script only once for the
+ // run rather than for each character.
+ PangoLanguage *scriptLang;
+ if ((!basePattern ||
+ !pango_language_includes_script(mPangoLanguage, script)) &&
+ (scriptLang = pango_script_get_sample_language(script))) {
+ fontSet = GetFontSet(scriptLang);
+ nextFont = 0;
+ }
+
+ for (uint32_t i = nextFont;
+ FcPattern *pattern = fontSet->GetFontPatternAt(i);
+ ++i) {
+ if (pattern == basePattern) {
+ continue; // already checked basePattern
+ }
+
+ if (HasChar(pattern, aCh)) {
+ *aMatchType = gfxTextRange::kFontGroup;
+ return RefPtr<gfxFont>(fontSet->GetFontAt(i, GetStyle())).forget();
+ }
+ }
+
+ return nullptr;
+}
+
+/**
+ ** gfxFcFont
+ **/
+
+cairo_user_data_key_t gfxFcFont::sGfxFontKey;
+
+gfxFcFont::gfxFcFont(cairo_scaled_font_t *aCairoFont,
+ FcPattern *aPattern,
+ gfxFcFontEntry *aFontEntry,
+ const gfxFontStyle *aFontStyle)
+ : gfxFontconfigFontBase(aCairoFont, aPattern, aFontEntry, aFontStyle)
+{
+ cairo_scaled_font_set_user_data(mScaledFont, &sGfxFontKey, this, nullptr);
+}
+
+gfxFcFont::~gfxFcFont()
+{
+ cairo_scaled_font_set_user_data(mScaledFont,
+ &sGfxFontKey,
+ nullptr,
+ nullptr);
+}
+
+already_AddRefed<gfxFont>
+gfxFcFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)
+{
+ gfxFontStyle style(*GetStyle());
+ style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
+ return MakeScaledFont(&style, style.size / GetStyle()->size);
+}
+
+already_AddRefed<gfxFont>
+gfxFcFont::MakeScaledFont(gfxFontStyle *aFontStyle, gfxFloat aScaleFactor)
+{
+ gfxFcFontEntry* fe = static_cast<gfxFcFontEntry*>(GetFontEntry());
+ RefPtr<gfxFont> font =
+ gfxFontCache::GetCache()->Lookup(fe, aFontStyle, nullptr);
+ if (font) {
+ return font.forget();
+ }
+
+ cairo_matrix_t fontMatrix;
+ cairo_scaled_font_get_font_matrix(mScaledFont, &fontMatrix);
+ cairo_matrix_scale(&fontMatrix, aScaleFactor, aScaleFactor);
+
+ cairo_matrix_t ctm;
+ cairo_scaled_font_get_ctm(mScaledFont, &ctm);
+
+ cairo_font_options_t *options = cairo_font_options_create();
+ cairo_scaled_font_get_font_options(mScaledFont, options);
+
+ cairo_scaled_font_t *newFont =
+ cairo_scaled_font_create(cairo_scaled_font_get_font_face(mScaledFont),
+ &fontMatrix, &ctm, options);
+ cairo_font_options_destroy(options);
+
+ font = new gfxFcFont(newFont, GetPattern(), fe, aFontStyle);
+ gfxFontCache::GetCache()->AddNew(font);
+ cairo_scaled_font_destroy(newFont);
+
+ return font.forget();
+}
+
+already_AddRefed<gfxFont>
+gfxFcFont::GetSmallCapsFont()
+{
+ gfxFontStyle style(*GetStyle());
+ style.size *= SMALL_CAPS_SCALE_FACTOR;
+ style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
+ return MakeScaledFont(&style, SMALL_CAPS_SCALE_FACTOR);
+}
+
+/* static */ void
+gfxPangoFontGroup::Shutdown()
+{
+ // Resetting gFTLibrary in case this is wanted again after a
+ // cairo_debug_reset_static_data.
+ gFTLibrary = nullptr;
+}
+
+/* static */ gfxFontEntry *
+gfxPangoFontGroup::NewFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle)
+{
+ gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils();
+ if (!utils)
+ return nullptr;
+
+ // The font face name from @font-face { src: local() } is not well
+ // defined.
+ //
+ // On MS Windows, this name gets compared with
+ // ENUMLOGFONTEXW::elfFullName, which for OpenType fonts seems to be the
+ // full font name from the name table. For CFF OpenType fonts this is the
+ // same as the PostScript name, but for TrueType fonts it is usually
+ // different.
+ //
+ // On Mac, the font face name is compared with the PostScript name, even
+ // for TrueType fonts.
+ //
+ // Fontconfig only records the full font names, so the behavior here
+ // follows that on MS Windows. However, to provide the possibility
+ // of aliases to compensate for variations, the font face name is passed
+ // through FcConfigSubstitute.
+
+ nsAutoRef<FcPattern> pattern(FcPatternCreate());
+ if (!pattern)
+ return nullptr;
+
+ NS_ConvertUTF16toUTF8 fullname(aFontName);
+ FcPatternAddString(pattern, FC_FULLNAME,
+ gfxFontconfigUtils::ToFcChar8(fullname));
+ FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
+
+ FcChar8 *name;
+ for (int v = 0;
+ FcPatternGetString(pattern, FC_FULLNAME, v, &name) == FcResultMatch;
+ ++v) {
+ const nsTArray< nsCountedRef<FcPattern> >& fonts =
+ utils->GetFontsForFullname(name);
+
+ if (fonts.Length() != 0)
+ return new gfxLocalFcFontEntry(aFontName,
+ aWeight,
+ aStretch,
+ aStyle,
+ fonts);
+ }
+
+ return nullptr;
+}
+
+/* static */ FT_Library
+gfxPangoFontGroup::GetFTLibrary()
+{
+ if (!gFTLibrary) {
+ // 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's FT_Library can be obtained from any cairo_scaled_font. The
+ // font properties requested here are chosen to get an FT_Face that is
+ // likely to be also used elsewhere.
+ gfxFontStyle style;
+ RefPtr<gfxPangoFontGroup> fontGroup =
+ new gfxPangoFontGroup(FontFamilyList(eFamily_sans_serif),
+ &style, nullptr, 1.0);
+
+ gfxFcFont *font = fontGroup->GetBaseFont();
+ if (!font)
+ return nullptr;
+
+ gfxFT2LockedFace face(font);
+ if (!face.get())
+ return nullptr;
+
+ gFTLibrary = face.get()->glyph->library;
+ }
+
+ return gFTLibrary;
+}
+
+/* static */ gfxFontEntry *
+gfxPangoFontGroup::NewFontEntry(const nsAString& aFontName,
+ uint16_t aWeight,
+ int16_t aStretch,
+ uint8_t aStyle,
+ const uint8_t* aFontData,
+ uint32_t aLength)
+{
+ // Ownership of aFontData is passed in here, and transferred to the
+ // new fontEntry, which will release it when no longer needed.
+
+ // Using face_index = 0 for the first face in the font, as we have no
+ // other information. FT_New_Memory_Face checks for a nullptr FT_Library.
+ FT_Face face;
+ FT_Error error =
+ FT_New_Memory_Face(GetFTLibrary(), aFontData, aLength, 0, &face);
+ if (error != 0) {
+ free((void*)aFontData);
+ return nullptr;
+ }
+
+ return new gfxDownloadedFcFontEntry(aFontName, aWeight,
+ aStretch, aStyle,
+ aFontData, face);
+}
+
+
+static double
+GetPixelSize(FcPattern *aPattern)
+{
+ double size;
+ if (FcPatternGetDouble(aPattern,
+ FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
+ return size;
+
+ NS_NOTREACHED("No size on pattern");
+ return 0.0;
+}
+
+/**
+ * The following gfxFcFonts are accessed from the cairo_scaled_font or created
+ * from the FcPattern, not from the gfxFontCache hash table. The gfxFontCache
+ * hash table is keyed by desired family and style, whereas here we only know
+ * actual family and style. There may be more than one of these fonts with
+ * the same family and style, but different PangoFont and actual font face.
+ *
+ * The point of this is to record the exact font face for gfxTextRun glyph
+ * indices. The style of this font does not necessarily represent the exact
+ * gfxFontStyle used to build the text run. Notably, the language is not
+ * recorded.
+ */
+
+/* static */
+already_AddRefed<gfxFcFont>
+gfxFcFont::GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern,
+ const gfxFontStyle *aFontStyle)
+{
+ nsAutoRef<FcPattern> renderPattern
+ (FcFontRenderPrepare(nullptr, aRequestedPattern, aFontPattern));
+
+ // If synthetic bold/italic is not allowed by the style, adjust the
+ // resulting pattern to match the actual properties of the font.
+ if (!aFontStyle->allowSyntheticWeight) {
+ int weight;
+ if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0,
+ &weight) == FcResultMatch) {
+ FcPatternDel(renderPattern, FC_WEIGHT);
+ FcPatternAddInteger(renderPattern, FC_WEIGHT, weight);
+ }
+ }
+ if (!aFontStyle->allowSyntheticStyle) {
+ int slant;
+ if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0,
+ &slant) == FcResultMatch) {
+ FcPatternDel(renderPattern, FC_SLANT);
+ FcPatternAddInteger(renderPattern, FC_SLANT, slant);
+ }
+ }
+
+ cairo_font_face_t *face =
+ cairo_ft_font_face_create_for_pattern(renderPattern);
+
+ // Reuse an existing font entry if available.
+ RefPtr<gfxFcFontEntry> fe = gfxFcFontEntry::LookupFontEntry(face);
+ if (!fe) {
+ gfxDownloadedFcFontEntry *downloadedFontEntry =
+ GetDownloadedFontEntry(aFontPattern);
+ if (downloadedFontEntry) {
+ // Web font
+ fe = downloadedFontEntry;
+ if (cairo_font_face_status(face) == CAIRO_STATUS_SUCCESS) {
+ // cairo_font_face_t is using the web font data.
+ // Hold a reference to the font entry to keep the font face
+ // data.
+ if (!downloadedFontEntry->SetCairoFace(face)) {
+ // OOM. Let cairo pick a fallback font
+ cairo_font_face_destroy(face);
+ face = cairo_ft_font_face_create_for_pattern(aRequestedPattern);
+ fe = gfxFcFontEntry::LookupFontEntry(face);
+ }
+ }
+ }
+ if (!fe) {
+ // Get a unique name for the font face from the file and id.
+ nsAutoString name;
+ FcChar8 *fc_file;
+ if (FcPatternGetString(renderPattern,
+ FC_FILE, 0, &fc_file) == FcResultMatch) {
+ int index;
+ if (FcPatternGetInteger(renderPattern,
+ FC_INDEX, 0, &index) != FcResultMatch) {
+ // cairo defaults to 0.
+ index = 0;
+ }
+
+ AppendUTF8toUTF16(gfxFontconfigUtils::ToCString(fc_file), name);
+ if (index != 0) {
+ name.Append('/');
+ name.AppendInt(index);
+ }
+ }
+
+ fe = new gfxSystemFcFontEntry(face, aFontPattern, name);
+ }
+ }
+
+ gfxFontStyle style(*aFontStyle);
+ style.size = GetPixelSize(renderPattern);
+ style.style = gfxFontconfigUtils::GetThebesStyle(renderPattern);
+ style.weight = gfxFontconfigUtils::GetThebesWeight(renderPattern);
+
+ RefPtr<gfxFont> font =
+ gfxFontCache::GetCache()->Lookup(fe, &style, nullptr);
+ if (!font) {
+ // Note that a file/index pair (or FT_Face) and the gfxFontStyle are
+ // not necessarily enough to provide a key that will describe a unique
+ // font. cairoFont contains information from renderPattern, which is a
+ // fully resolved pattern from FcFontRenderPrepare.
+ // FcFontRenderPrepare takes the requested pattern and the face
+ // pattern as input and can modify elements of the resulting pattern
+ // that affect rendering but are not included in the gfxFontStyle.
+ cairo_scaled_font_t *cairoFont = CreateScaledFont(renderPattern, face);
+ font = new gfxFcFont(cairoFont, renderPattern, fe, &style);
+ gfxFontCache::GetCache()->AddNew(font);
+ cairo_scaled_font_destroy(cairoFont);
+ }
+
+ cairo_font_face_destroy(face);
+
+ RefPtr<gfxFcFont> retval(static_cast<gfxFcFont*>(font.get()));
+ return retval.forget();
+}
+
+gfxFcFontSet *
+gfxPangoFontGroup::GetBaseFontSet()
+{
+ if (mFontSets.Length() > 0)
+ return mFontSets[0].mFontSet;
+
+ mSizeAdjustFactor = 1.0; // will be adjusted below if necessary
+ nsAutoRef<FcPattern> pattern;
+ RefPtr<gfxFcFontSet> fontSet =
+ MakeFontSet(mPangoLanguage, mSizeAdjustFactor, &pattern);
+
+ double size = GetPixelSize(pattern);
+ if (size != 0.0 && mStyle.sizeAdjust > 0.0) {
+ gfxFcFont *font = fontSet->GetFontAt(0, GetStyle());
+ if (font) {
+ const gfxFont::Metrics& metrics =
+ font->GetMetrics(gfxFont::eHorizontal); // XXX vertical?
+
+ // 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) {
+ mSizeAdjustFactor =
+ mStyle.sizeAdjust * metrics.emHeight / metrics.xHeight;
+
+ size *= mSizeAdjustFactor;
+ FcPatternDel(pattern, FC_PIXEL_SIZE);
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
+
+ fontSet = new gfxFcFontSet(pattern, mUserFontSet);
+ }
+ }
+ }
+
+ PangoLanguage *pangoLang = mPangoLanguage;
+ FcChar8 *fcLang;
+ if (!pangoLang &&
+ FcPatternGetString(pattern, FC_LANG, 0, &fcLang) == FcResultMatch) {
+ pangoLang =
+ pango_language_from_string(gfxFontconfigUtils::ToCString(fcLang));
+ }
+
+ mFontSets.AppendElement(FontSetByLangEntry(pangoLang, fontSet));
+
+ return fontSet;
+}
+
+/**
+ ** gfxTextRun
+ *
+ * A serious problem:
+ *
+ * -- We draw with a font that's hinted for the CTM, but we measure with a font
+ * hinted to the identity matrix, so our "bounding metrics" may not be accurate.
+ *
+ **/
+
+// This will fetch an existing scaled_font if one exists.
+static cairo_scaled_font_t *
+CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace)
+{
+ double size = GetPixelSize(aPattern);
+
+ cairo_matrix_t fontMatrix;
+ FcMatrix *fcMatrix;
+ if (FcPatternGetMatrix(aPattern, FC_MATRIX, 0, &fcMatrix) == FcResultMatch)
+ cairo_matrix_init(&fontMatrix, fcMatrix->xx, -fcMatrix->yx, -fcMatrix->xy, fcMatrix->yy, 0, 0);
+ else
+ cairo_matrix_init_identity(&fontMatrix);
+ cairo_matrix_scale(&fontMatrix, size, size);
+
+ FcBool printing;
+ if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) != FcResultMatch) {
+ printing = FcFalse;
+ }
+
+ // The cairo_scaled_font is created with a unit ctm so that metrics and
+ // positions are in user space, but this means that hinting effects will
+ // not be estimated accurately for non-unit transformations.
+ cairo_matrix_t identityMatrix;
+ cairo_matrix_init_identity(&identityMatrix);
+
+ // 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.
+ cairo_font_options_t *fontOptions = cairo_font_options_create();
+
+ // 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(fontOptions, CAIRO_HINT_METRICS_OFF);
+ } else {
+ cairo_font_options_set_hint_metrics(fontOptions, 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 {
+#ifdef FC_HINT_STYLE // FC_HINT_STYLE is available from fontconfig 2.2.91.
+ 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;
+ }
+#else // no FC_HINT_STYLE
+ hint_style = CAIRO_HINT_STYLE_FULL;
+#endif
+ }
+ cairo_font_options_set_hint_style(fontOptions, 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:
+ MOZ_FALLTHROUGH;
+ 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(fontOptions, 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(fontOptions, antialias);
+
+ cairo_scaled_font_t *scaledFont =
+ cairo_scaled_font_create(aFace, &fontMatrix, &identityMatrix,
+ fontOptions);
+
+ cairo_font_options_destroy(fontOptions);
+
+ NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
+ "Failed to create scaled font");
+ return scaledFont;
+}
+
+/* static */
+PangoLanguage *
+GuessPangoLanguage(nsIAtom *aLanguage)
+{
+ if (!aLanguage)
+ return nullptr;
+
+ // Pango and fontconfig won't understand mozilla's internal langGroups, so
+ // find a real language.
+ nsAutoCString lang;
+ gfxFontconfigUtils::GetSampleLangForGroup(aLanguage, &lang);
+ if (lang.IsEmpty())
+ return nullptr;
+
+ return pango_language_from_string(lang.get());
+}
+
+#ifdef MOZ_WIDGET_GTK
+/***************************************************************************
+ *
+ * This function must be last in the file because it uses the system cairo
+ * library. Above this point the cairo library used is the tree cairo if
+ * MOZ_TREE_CAIRO.
+ */
+
+#if MOZ_TREE_CAIRO
+// Tree cairo symbols have different names. Disable their activation through
+// preprocessor macros.
+#undef cairo_ft_font_options_substitute
+
+// The system cairo functions are not declared because the include paths cause
+// the gdk headers to pick up the tree cairo.h.
+extern "C" {
+NS_VISIBILITY_DEFAULT void
+cairo_ft_font_options_substitute (const cairo_font_options_t *options,
+ FcPattern *pattern);
+}
+#endif
+
+static void
+ApplyGdkScreenFontOptions(FcPattern *aPattern)
+{
+ const cairo_font_options_t *options =
+ gdk_screen_get_font_options(gdk_screen_get_default());
+
+ cairo_ft_font_options_substitute(options, aPattern);
+}
+
+#endif // MOZ_WIDGET_GTK
+