michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "prlink.h" michael@0: #include "gfxTypes.h" michael@0: michael@0: #include "nsTArray.h" michael@0: michael@0: #include "gfxContext.h" michael@0: #ifdef MOZ_WIDGET_GTK michael@0: #include "gfxPlatformGtk.h" michael@0: #endif michael@0: #ifdef MOZ_WIDGET_QT michael@0: #include "gfxQtPlatform.h" michael@0: #endif michael@0: #include "gfxPangoFonts.h" michael@0: #include "gfxFT2FontBase.h" michael@0: #include "gfxFT2Utils.h" michael@0: #include "harfbuzz/hb.h" michael@0: #include "harfbuzz/hb-ot.h" michael@0: #include "gfxHarfBuzzShaper.h" michael@0: #include "gfxGraphiteShaper.h" michael@0: #include "nsUnicodeProperties.h" michael@0: #include "nsUnicodeScriptCodes.h" michael@0: #include "gfxFontconfigUtils.h" michael@0: #include "gfxUserFontSet.h" michael@0: #include "gfxFontConstants.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include FT_TRUETYPE_TABLES_H michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::unicode; michael@0: michael@0: #define PRINTING_FC_PROPERTY "gfx.printing" michael@0: michael@0: static PangoLanguage *GuessPangoLanguage(nsIAtom *aLanguage); michael@0: michael@0: static cairo_scaled_font_t * michael@0: CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace); michael@0: michael@0: static FT_Library gFTLibrary; michael@0: michael@0: // FC_FAMILYLANG and FC_FULLNAME were introduced in fontconfig-2.2.97 michael@0: // and so fontconfig-2.3.0 (2005). michael@0: #ifndef FC_FAMILYLANG michael@0: #define FC_FAMILYLANG "familylang" michael@0: #endif michael@0: #ifndef FC_FULLNAME michael@0: #define FC_FULLNAME "fullname" michael@0: #endif michael@0: michael@0: static PRFuncPtr michael@0: FindFunctionSymbol(const char *name) michael@0: { michael@0: PRLibrary *lib = nullptr; michael@0: PRFuncPtr result = PR_FindFunctionSymbolAndLibrary(name, &lib); michael@0: if (lib) { michael@0: PR_UnloadLibrary(lib); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: static bool HasChar(FcPattern *aFont, FcChar32 wc) michael@0: { michael@0: FcCharSet *charset = nullptr; michael@0: FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset); michael@0: michael@0: return charset && FcCharSetHasChar(charset, wc); michael@0: } michael@0: michael@0: /** michael@0: * gfxFcFontEntry: michael@0: * michael@0: * An abstract base class of for gfxFontEntry implementations used by michael@0: * gfxFcFont and gfxUserFontSet. michael@0: */ michael@0: michael@0: class gfxFcFontEntry : public gfxFontEntry { michael@0: public: michael@0: // For all FontEntrys attached to gfxFcFonts, there will be only one michael@0: // pattern in this array. This is always a font pattern, not a fully michael@0: // resolved pattern. gfxFcFont only uses this to construct a PangoFont. michael@0: // michael@0: // FontEntrys for src:local() fonts in gfxUserFontSet may return more than michael@0: // one pattern. (See comment in gfxUserFcFontEntry.) michael@0: const nsTArray< nsCountedRef >& GetPatterns() michael@0: { michael@0: return mPatterns; michael@0: } michael@0: michael@0: static gfxFcFontEntry *LookupFontEntry(cairo_font_face_t *aFace) michael@0: { michael@0: return static_cast michael@0: (cairo_font_face_get_user_data(aFace, &sFontEntryKey)); michael@0: } michael@0: michael@0: // override the gfxFontEntry impl to read the name from fontconfig michael@0: // instead of trying to get the 'name' table, as we don't implement michael@0: // GetFontTable() here michael@0: virtual nsString RealFaceName(); michael@0: michael@0: // This is needed to make gfxFontEntry::HasCharacter(aCh) work. michael@0: virtual bool TestCharacterMap(uint32_t aCh) michael@0: { michael@0: for (uint32_t i = 0; i < mPatterns.Length(); ++i) { michael@0: if (HasChar(mPatterns[i], aCh)) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: protected: michael@0: gfxFcFontEntry(const nsAString& aName) michael@0: : gfxFontEntry(aName) michael@0: { michael@0: } michael@0: michael@0: // One pattern is the common case and some subclasses rely on successful michael@0: // addition of the first element to the array. michael@0: AutoFallibleTArray,1> mPatterns; michael@0: michael@0: static cairo_user_data_key_t sFontEntryKey; michael@0: }; michael@0: michael@0: cairo_user_data_key_t gfxFcFontEntry::sFontEntryKey; michael@0: michael@0: nsString michael@0: gfxFcFontEntry::RealFaceName() michael@0: { michael@0: FcChar8 *name; michael@0: if (!mPatterns.IsEmpty()) { michael@0: if (FcPatternGetString(mPatterns[0], michael@0: FC_FULLNAME, 0, &name) == FcResultMatch) { michael@0: return NS_ConvertUTF8toUTF16((const char*)name); michael@0: } michael@0: if (FcPatternGetString(mPatterns[0], michael@0: FC_FAMILY, 0, &name) == FcResultMatch) { michael@0: NS_ConvertUTF8toUTF16 result((const char*)name); michael@0: if (FcPatternGetString(mPatterns[0], michael@0: FC_STYLE, 0, &name) == FcResultMatch) { michael@0: result.AppendLiteral(" "); michael@0: AppendUTF8toUTF16((const char*)name, result); michael@0: } michael@0: return result; michael@0: } michael@0: } michael@0: // fall back to gfxFontEntry implementation (only works for sfnt fonts) michael@0: return gfxFontEntry::RealFaceName(); michael@0: } michael@0: michael@0: /** michael@0: * gfxSystemFcFontEntry: michael@0: * michael@0: * An implementation of gfxFcFontEntry used by gfxFcFonts for system fonts, michael@0: * including those from regular family-name based font selection as well as michael@0: * those from src:local(). michael@0: * michael@0: * All gfxFcFonts using the same cairo_font_face_t share the same FontEntry. michael@0: */ michael@0: michael@0: class gfxSystemFcFontEntry : public gfxFcFontEntry { michael@0: public: michael@0: // For memory efficiency, aFontPattern should be a font pattern, michael@0: // not a fully resolved pattern. michael@0: gfxSystemFcFontEntry(cairo_font_face_t *aFontFace, michael@0: FcPattern *aFontPattern, michael@0: const nsAString& aName) michael@0: : gfxFcFontEntry(aName), mFontFace(aFontFace), michael@0: mFTFace(nullptr), mFTFaceInitialized(false) michael@0: { michael@0: cairo_font_face_reference(mFontFace); michael@0: cairo_font_face_set_user_data(mFontFace, &sFontEntryKey, this, nullptr); michael@0: mPatterns.AppendElement(); michael@0: // mPatterns is an nsAutoTArray with 1 space always available, so the michael@0: // AppendElement always succeeds. michael@0: mPatterns[0] = aFontPattern; michael@0: michael@0: FcChar8 *name; michael@0: if (FcPatternGetString(aFontPattern, michael@0: FC_FAMILY, 0, &name) == FcResultMatch) { michael@0: mFamilyName = NS_ConvertUTF8toUTF16((const char*)name); michael@0: } michael@0: } michael@0: michael@0: ~gfxSystemFcFontEntry() michael@0: { michael@0: cairo_font_face_set_user_data(mFontFace, michael@0: &sFontEntryKey, michael@0: nullptr, michael@0: nullptr); michael@0: cairo_font_face_destroy(mFontFace); michael@0: } michael@0: michael@0: virtual void ForgetHBFace(); michael@0: virtual void ReleaseGrFace(gr_face* aFace); michael@0: michael@0: protected: michael@0: virtual nsresult michael@0: CopyFontTable(uint32_t aTableTag, FallibleTArray& aBuffer) MOZ_OVERRIDE; michael@0: michael@0: void MaybeReleaseFTFace(); michael@0: michael@0: private: michael@0: cairo_font_face_t *mFontFace; michael@0: FT_Face mFTFace; michael@0: bool mFTFaceInitialized; michael@0: }; michael@0: michael@0: nsresult michael@0: gfxSystemFcFontEntry::CopyFontTable(uint32_t aTableTag, michael@0: FallibleTArray& aBuffer) michael@0: { michael@0: if (!mFTFaceInitialized) { michael@0: mFTFaceInitialized = true; michael@0: FcChar8 *filename; michael@0: if (FcPatternGetString(mPatterns[0], FC_FILE, 0, &filename) != FcResultMatch) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: int index; michael@0: if (FcPatternGetInteger(mPatterns[0], FC_INDEX, 0, &index) != FcResultMatch) { michael@0: index = 0; // default to 0 if not found in pattern michael@0: } michael@0: if (FT_New_Face(gfxPangoFontGroup::GetFTLibrary(), michael@0: (const char*)filename, index, &mFTFace) != 0) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: if (!mFTFace) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: FT_ULong length = 0; michael@0: if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, nullptr, &length) != 0) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: if (!aBuffer.SetLength(length)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, aBuffer.Elements(), &length) != 0) { michael@0: aBuffer.Clear(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: gfxSystemFcFontEntry::MaybeReleaseFTFace() michael@0: { michael@0: // don't release if either HB or Gr face still exists michael@0: if (mHBFace || mGrFace) { michael@0: return; michael@0: } michael@0: if (mFTFace) { michael@0: FT_Done_Face(mFTFace); michael@0: mFTFace = nullptr; michael@0: } michael@0: mFTFaceInitialized = false; michael@0: } michael@0: michael@0: void michael@0: gfxSystemFcFontEntry::ForgetHBFace() michael@0: { michael@0: gfxFontEntry::ForgetHBFace(); michael@0: MaybeReleaseFTFace(); michael@0: } michael@0: michael@0: void michael@0: gfxSystemFcFontEntry::ReleaseGrFace(gr_face* aFace) michael@0: { michael@0: gfxFontEntry::ReleaseGrFace(aFace); michael@0: MaybeReleaseFTFace(); michael@0: } michael@0: michael@0: // A namespace for @font-face family names in FcPatterns so that fontconfig michael@0: // aliases do not pick up families from @font-face rules and so that michael@0: // fontconfig rules can distinguish between web fonts and platform fonts. michael@0: // http://lists.freedesktop.org/archives/fontconfig/2008-November/003037.html michael@0: #define FONT_FACE_FAMILY_PREFIX "@font-face:" michael@0: michael@0: /** michael@0: * gfxUserFcFontEntry: michael@0: * michael@0: * An abstract class for objects in a gfxUserFontSet that can provide michael@0: * FcPattern* handles to fonts. michael@0: * michael@0: * Separate implementations of this class support local fonts from src:local() michael@0: * and web fonts from src:url(). michael@0: */ michael@0: michael@0: // There is a one-to-one correspondence between gfxUserFcFontEntry objects and michael@0: // @font-face rules, but sometimes a one-to-many correspondence between font michael@0: // entries and font patterns. michael@0: // michael@0: // http://www.w3.org/TR/2002/WD-css3-webfonts-20020802#font-descriptions michael@0: // provided a font-size descriptor to specify the sizes supported by the face, michael@0: // but the "Editor's Draft 27 June 2008" michael@0: // http://dev.w3.org/csswg/css3-fonts/#font-resources does not provide such a michael@0: // descriptor, and Mozilla does not recognize such a descriptor. michael@0: // michael@0: // Font face names used in src:local() also do not usually specify a size. michael@0: // michael@0: // PCF format fonts have each size in a different file, and each of these michael@0: // files is referenced by its own pattern, but really these are each michael@0: // different sizes of one face with one name. michael@0: // michael@0: // Multiple patterns in an entry also effectively deals with a set of michael@0: // PostScript Type 1 font files that all have the same face name but are in michael@0: // several files because of the limit on the number of glyphs in a Type 1 font michael@0: // file. (e.g. Computer Modern.) michael@0: michael@0: class gfxUserFcFontEntry : public gfxFcFontEntry { michael@0: protected: michael@0: gfxUserFcFontEntry(const gfxProxyFontEntry &aProxyEntry) michael@0: : gfxFcFontEntry(aProxyEntry.Name()) michael@0: { michael@0: mItalic = aProxyEntry.mItalic; michael@0: mWeight = aProxyEntry.mWeight; michael@0: mStretch = aProxyEntry.mStretch; michael@0: mIsUserFont = true; michael@0: } michael@0: michael@0: // Helper function to change a pattern so that it matches the CSS style michael@0: // descriptors and so gets properly sorted in font selection. This also michael@0: // avoids synthetic style effects being added by the renderer when the michael@0: // style of the font itself does not match the descriptor provided by the michael@0: // author. michael@0: void AdjustPatternToCSS(FcPattern *aPattern); michael@0: }; michael@0: michael@0: void michael@0: gfxUserFcFontEntry::AdjustPatternToCSS(FcPattern *aPattern) michael@0: { michael@0: int fontWeight = -1; michael@0: FcPatternGetInteger(aPattern, FC_WEIGHT, 0, &fontWeight); michael@0: int cssWeight = gfxFontconfigUtils::FcWeightForBaseWeight(mWeight / 100); michael@0: if (cssWeight != fontWeight) { michael@0: FcPatternDel(aPattern, FC_WEIGHT); michael@0: FcPatternAddInteger(aPattern, FC_WEIGHT, cssWeight); michael@0: } michael@0: michael@0: int fontSlant; michael@0: FcResult res = FcPatternGetInteger(aPattern, FC_SLANT, 0, &fontSlant); michael@0: // gfxFontEntry doesn't understand the difference between oblique michael@0: // and italic. michael@0: if (res != FcResultMatch || michael@0: IsItalic() != (fontSlant != FC_SLANT_ROMAN)) { michael@0: FcPatternDel(aPattern, FC_SLANT); michael@0: FcPatternAddInteger(aPattern, FC_SLANT, michael@0: IsItalic() ? FC_SLANT_OBLIQUE : FC_SLANT_ROMAN); michael@0: } michael@0: michael@0: int fontWidth = -1; michael@0: FcPatternGetInteger(aPattern, FC_WIDTH, 0, &fontWidth); michael@0: int cssWidth = gfxFontconfigUtils::FcWidthForThebesStretch(mStretch); michael@0: if (cssWidth != fontWidth) { michael@0: FcPatternDel(aPattern, FC_WIDTH); michael@0: FcPatternAddInteger(aPattern, FC_WIDTH, cssWidth); michael@0: } michael@0: michael@0: // Ensure that there is a fullname property (if there is a family michael@0: // property) so that fontconfig rules can identify the real name of the michael@0: // font, because the family property will be replaced. michael@0: FcChar8 *unused; michael@0: if (FcPatternGetString(aPattern, michael@0: FC_FULLNAME, 0, &unused) == FcResultNoMatch) { michael@0: nsAutoCString fullname; michael@0: if (gfxFontconfigUtils::GetFullnameFromFamilyAndStyle(aPattern, michael@0: &fullname)) { michael@0: FcPatternAddString(aPattern, FC_FULLNAME, michael@0: gfxFontconfigUtils::ToFcChar8(fullname)); michael@0: } michael@0: } michael@0: michael@0: nsAutoCString family; michael@0: family.Append(FONT_FACE_FAMILY_PREFIX); michael@0: AppendUTF16toUTF8(Name(), family); michael@0: michael@0: FcPatternDel(aPattern, FC_FAMILY); michael@0: FcPatternDel(aPattern, FC_FAMILYLANG); michael@0: FcPatternAddString(aPattern, FC_FAMILY, michael@0: gfxFontconfigUtils::ToFcChar8(family)); michael@0: } michael@0: michael@0: /** michael@0: * gfxLocalFcFontEntry: michael@0: * michael@0: * An implementation of gfxUserFcFontEntry for local fonts from src:local(). michael@0: * michael@0: * This class is used only in gfxUserFontSet and for providing FcPattern* michael@0: * handles to system fonts for font selection. gfxFcFonts created from these michael@0: * patterns will use gfxSystemFcFontEntrys, which may be shared with michael@0: * gfxFcFonts from regular family-name based font selection. michael@0: */ michael@0: michael@0: class gfxLocalFcFontEntry : public gfxUserFcFontEntry { michael@0: public: michael@0: gfxLocalFcFontEntry(const gfxProxyFontEntry &aProxyEntry, michael@0: const nsTArray< nsCountedRef >& aPatterns) michael@0: : gfxUserFcFontEntry(aProxyEntry) michael@0: { michael@0: if (!mPatterns.SetCapacity(aPatterns.Length())) michael@0: return; // OOM michael@0: michael@0: for (uint32_t i = 0; i < aPatterns.Length(); ++i) { michael@0: FcPattern *pattern = FcPatternDuplicate(aPatterns.ElementAt(i)); michael@0: if (!pattern) michael@0: return; // OOM michael@0: michael@0: AdjustPatternToCSS(pattern); michael@0: michael@0: mPatterns.AppendElement(); michael@0: mPatterns[i].own(pattern); michael@0: } michael@0: mIsLocalUserFont = true; michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * gfxDownloadedFcFontEntry: michael@0: * michael@0: * An implementation of gfxFcFontEntry for web fonts from src:url(). michael@0: * michael@0: * When a cairo_font_face_t is created for these fonts, the cairo_font_face_t michael@0: * keeps a reference to the FontEntry to keep the font data alive. michael@0: */ michael@0: michael@0: class gfxDownloadedFcFontEntry : public gfxUserFcFontEntry { michael@0: public: michael@0: // This takes ownership of the face and its underlying data michael@0: gfxDownloadedFcFontEntry(const gfxProxyFontEntry &aProxyEntry, michael@0: const uint8_t *aData, FT_Face aFace) michael@0: : gfxUserFcFontEntry(aProxyEntry), mFontData(aData), mFace(aFace) michael@0: { michael@0: NS_PRECONDITION(aFace != nullptr, "aFace is NULL!"); michael@0: InitPattern(); michael@0: } michael@0: michael@0: virtual ~gfxDownloadedFcFontEntry(); michael@0: michael@0: // Returns true on success michael@0: bool SetCairoFace(cairo_font_face_t *aFace); michael@0: michael@0: virtual hb_blob_t* GetFontTable(uint32_t aTableTag) MOZ_OVERRIDE; michael@0: michael@0: protected: michael@0: void InitPattern(); michael@0: michael@0: // mFontData holds the data used to instantiate the FT_Face; michael@0: // this has to persist until we are finished with the face, michael@0: // then be released with NS_Free(). michael@0: const uint8_t* mFontData; michael@0: michael@0: FT_Face mFace; michael@0: }; michael@0: michael@0: // A property for recording gfxDownloadedFcFontEntrys on FcPatterns. michael@0: static const char *kFontEntryFcProp = "-moz-font-entry"; michael@0: michael@0: static FcBool AddDownloadedFontEntry(FcPattern *aPattern, michael@0: gfxDownloadedFcFontEntry *aFontEntry) michael@0: { michael@0: FcValue value; michael@0: value.type = FcTypeFTFace; // void* field of union michael@0: value.u.f = aFontEntry; michael@0: michael@0: return FcPatternAdd(aPattern, kFontEntryFcProp, value, FcFalse); michael@0: } michael@0: michael@0: static FcBool DelDownloadedFontEntry(FcPattern *aPattern) michael@0: { michael@0: return FcPatternDel(aPattern, kFontEntryFcProp); michael@0: } michael@0: michael@0: static gfxDownloadedFcFontEntry *GetDownloadedFontEntry(FcPattern *aPattern) michael@0: { michael@0: FcValue value; michael@0: if (FcPatternGet(aPattern, kFontEntryFcProp, 0, &value) != FcResultMatch) michael@0: return nullptr; michael@0: michael@0: if (value.type != FcTypeFTFace) { michael@0: NS_NOTREACHED("Wrong type for -moz-font-entry font property"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return static_cast(value.u.f); michael@0: } michael@0: michael@0: gfxDownloadedFcFontEntry::~gfxDownloadedFcFontEntry() michael@0: { michael@0: if (mPatterns.Length() != 0) { michael@0: // Remove back reference to this font entry and the face in case michael@0: // anyone holds a reference to the pattern. michael@0: NS_ASSERTION(mPatterns.Length() == 1, michael@0: "More than one pattern in gfxDownloadedFcFontEntry!"); michael@0: DelDownloadedFontEntry(mPatterns[0]); michael@0: FcPatternDel(mPatterns[0], FC_FT_FACE); michael@0: } michael@0: FT_Done_Face(mFace); michael@0: NS_Free((void*)mFontData); michael@0: } michael@0: michael@0: typedef FcPattern* (*QueryFaceFunction)(const FT_Face face, michael@0: const FcChar8 *file, int id, michael@0: FcBlanks *blanks); michael@0: michael@0: void michael@0: gfxDownloadedFcFontEntry::InitPattern() michael@0: { michael@0: static QueryFaceFunction sQueryFacePtr = michael@0: reinterpret_cast michael@0: (FindFunctionSymbol("FcFreeTypeQueryFace")); michael@0: FcPattern *pattern; michael@0: michael@0: // FcFreeTypeQueryFace is the same function used to construct patterns for michael@0: // system fonts and so is the preferred function to use for this purpose. michael@0: // This will set up the langset property, which helps with sorting, and michael@0: // the foundry, fullname, and fontversion properties, which properly michael@0: // identify the font to fontconfig rules. However, FcFreeTypeQueryFace is michael@0: // available only from fontconfig-2.4.2 (December 2006). (CentOS 5.0 has michael@0: // fontconfig-2.4.1.) michael@0: if (sQueryFacePtr) { michael@0: // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at michael@0: // least). The dummy file passed here is removed below. michael@0: // michael@0: // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr) michael@0: // is passed as the "blanks" argument, which provides that unexpectedly michael@0: // blank glyphs are elided. Here, however, we pass nullptr for michael@0: // "blanks", effectively assuming that, if the font has a blank glyph, michael@0: // then the author intends any associated character to be rendered michael@0: // blank. michael@0: pattern = michael@0: (*sQueryFacePtr)(mFace, michael@0: gfxFontconfigUtils::ToFcChar8(""), michael@0: 0, michael@0: nullptr); michael@0: if (!pattern) michael@0: // Either OOM, or fontconfig chose to skip this font because it michael@0: // has "no encoded characters", which I think means "BDF and PCF michael@0: // fonts which are not in Unicode (or the effectively equivalent michael@0: // ISO Latin-1) encoding". michael@0: return; michael@0: michael@0: // These properties don't make sense for this face without a file. michael@0: FcPatternDel(pattern, FC_FILE); michael@0: FcPatternDel(pattern, FC_INDEX); michael@0: michael@0: } else { michael@0: // Do the minimum necessary to construct a pattern for sorting. michael@0: michael@0: // FC_CHARSET is vital to determine which characters are supported. michael@0: nsAutoRef charset(FcFreeTypeCharSet(mFace, nullptr)); michael@0: // If there are no characters then assume we don't know how to read michael@0: // this font. michael@0: if (!charset || FcCharSetCount(charset) == 0) michael@0: return; michael@0: michael@0: pattern = FcPatternCreate(); michael@0: FcPatternAddCharSet(pattern, FC_CHARSET, charset); michael@0: michael@0: // FC_PIXEL_SIZE can be important for font selection of fixed-size michael@0: // fonts. michael@0: if (!(mFace->face_flags & FT_FACE_FLAG_SCALABLE)) { michael@0: for (FT_Int i = 0; i < mFace->num_fixed_sizes; ++i) { michael@0: #if HAVE_FT_BITMAP_SIZE_Y_PPEM michael@0: double size = FLOAT_FROM_26_6(mFace->available_sizes[i].y_ppem); michael@0: #else michael@0: double size = mFace->available_sizes[i].height; michael@0: #endif michael@0: FcPatternAddDouble (pattern, FC_PIXEL_SIZE, size); michael@0: } michael@0: michael@0: // Not sure whether this is important; michael@0: // imitating FcFreeTypeQueryFace: michael@0: FcPatternAddBool (pattern, FC_ANTIALIAS, FcFalse); michael@0: } michael@0: michael@0: // Setting up the FC_LANGSET property is very difficult with the APIs michael@0: // available prior to FcFreeTypeQueryFace. Having no FC_LANGSET michael@0: // property seems better than having a property with an empty LangSet. michael@0: // With no FC_LANGSET property, fontconfig sort functions will michael@0: // consider this face to have the same priority as (otherwise equal) michael@0: // faces that have support for the primary requested language, but michael@0: // will not consider any language to have been satisfied (and so will michael@0: // continue to look for a face with language support in fallback michael@0: // fonts). michael@0: } michael@0: michael@0: AdjustPatternToCSS(pattern); michael@0: michael@0: FcPatternAddFTFace(pattern, FC_FT_FACE, mFace); michael@0: AddDownloadedFontEntry(pattern, this); michael@0: michael@0: // There is never more than one pattern michael@0: mPatterns.AppendElement(); michael@0: mPatterns[0].own(pattern); michael@0: } michael@0: michael@0: static void ReleaseDownloadedFontEntry(void *data) michael@0: { michael@0: gfxDownloadedFcFontEntry *downloadedFontEntry = michael@0: static_cast(data); michael@0: NS_RELEASE(downloadedFontEntry); michael@0: } michael@0: michael@0: bool gfxDownloadedFcFontEntry::SetCairoFace(cairo_font_face_t *aFace) michael@0: { michael@0: if (CAIRO_STATUS_SUCCESS != michael@0: cairo_font_face_set_user_data(aFace, &sFontEntryKey, this, michael@0: ReleaseDownloadedFontEntry)) michael@0: return false; michael@0: michael@0: // Hold a reference to this font entry to keep the font face data. michael@0: NS_ADDREF(this); michael@0: return true; michael@0: } michael@0: michael@0: hb_blob_t * michael@0: gfxDownloadedFcFontEntry::GetFontTable(uint32_t aTableTag) michael@0: { michael@0: // The entry already owns the (sanitized) sfnt data in mFontData, michael@0: // so we can just return a blob that "wraps" the appropriate chunk of it. michael@0: // The blob should not attempt to free its data, as the entire sfnt data michael@0: // will be freed when the font entry is deleted. michael@0: return GetTableFromFontData(mFontData, aTableTag); michael@0: } michael@0: michael@0: /* michael@0: * gfxFcFont michael@0: * michael@0: * This is a gfxFont implementation using a CAIRO_FONT_TYPE_FT michael@0: * cairo_scaled_font created from an FcPattern. michael@0: */ michael@0: michael@0: class gfxFcFont : public gfxFT2FontBase { michael@0: public: michael@0: virtual ~gfxFcFont(); michael@0: static already_AddRefed michael@0: GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern, michael@0: const gfxFontStyle *aFontStyle); michael@0: michael@0: #ifdef USE_SKIA michael@0: virtual mozilla::TemporaryRef GetGlyphRenderingOptions(); michael@0: #endif michael@0: michael@0: protected: michael@0: virtual bool ShapeText(gfxContext *aContext, michael@0: const char16_t *aText, michael@0: uint32_t aOffset, michael@0: uint32_t aLength, michael@0: int32_t aScript, michael@0: gfxShapedText *aShapedText, michael@0: bool aPreferPlatformShaping); michael@0: michael@0: bool InitGlyphRunWithPango(const char16_t *aString, michael@0: uint32_t aOffset, michael@0: uint32_t aLength, michael@0: int32_t aScript, michael@0: gfxShapedText *aShapedText); michael@0: michael@0: private: michael@0: gfxFcFont(cairo_scaled_font_t *aCairoFont, gfxFcFontEntry *aFontEntry, michael@0: const gfxFontStyle *aFontStyle); michael@0: michael@0: // key for locating a gfxFcFont corresponding to a cairo_scaled_font michael@0: static cairo_user_data_key_t sGfxFontKey; michael@0: }; michael@0: michael@0: /** michael@0: * gfxFcFontSet: michael@0: * michael@0: * Translation from a desired FcPattern to a sorted set of font references michael@0: * (fontconfig cache data) and (when needed) fonts. michael@0: */ michael@0: michael@0: class gfxFcFontSet MOZ_FINAL { michael@0: public: michael@0: NS_INLINE_DECL_REFCOUNTING(gfxFcFontSet) michael@0: michael@0: explicit gfxFcFontSet(FcPattern *aPattern, michael@0: gfxUserFontSet *aUserFontSet) michael@0: : mSortPattern(aPattern), mUserFontSet(aUserFontSet), michael@0: mFcFontsTrimmed(0), michael@0: mHaveFallbackFonts(false) michael@0: { michael@0: bool waitForUserFont; michael@0: mFcFontSet = SortPreferredFonts(waitForUserFont); michael@0: mWaitingForUserFont = waitForUserFont; michael@0: } michael@0: michael@0: // A reference is held by the FontSet. michael@0: // The caller may add a ref to keep the font alive longer than the FontSet. michael@0: gfxFcFont *GetFontAt(uint32_t i, const gfxFontStyle *aFontStyle) michael@0: { michael@0: if (i >= mFonts.Length() || !mFonts[i].mFont) { michael@0: // GetFontPatternAt sets up mFonts michael@0: FcPattern *fontPattern = GetFontPatternAt(i); michael@0: if (!fontPattern) michael@0: return nullptr; michael@0: michael@0: mFonts[i].mFont = michael@0: gfxFcFont::GetOrMakeFont(mSortPattern, fontPattern, michael@0: aFontStyle); michael@0: } michael@0: return mFonts[i].mFont; michael@0: } michael@0: michael@0: FcPattern *GetFontPatternAt(uint32_t i); michael@0: michael@0: bool WaitingForUserFont() const { michael@0: return mWaitingForUserFont; michael@0: } michael@0: michael@0: private: michael@0: // Private destructor, to discourage deletion outside of Release(): michael@0: ~gfxFcFontSet() michael@0: { michael@0: } michael@0: michael@0: nsReturnRef SortPreferredFonts(bool& aWaitForUserFont); michael@0: nsReturnRef SortFallbackFonts(); michael@0: michael@0: struct FontEntry { michael@0: explicit FontEntry(FcPattern *aPattern) : mPattern(aPattern) {} michael@0: nsCountedRef mPattern; michael@0: nsRefPtr mFont; michael@0: }; michael@0: michael@0: struct LangSupportEntry { michael@0: LangSupportEntry(FcChar8 *aLang, FcLangResult aSupport) : michael@0: mLang(aLang), mBestSupport(aSupport) {} michael@0: FcChar8 *mLang; michael@0: FcLangResult mBestSupport; michael@0: }; michael@0: michael@0: public: michael@0: // public for nsTArray michael@0: class LangComparator { michael@0: public: michael@0: bool Equals(const LangSupportEntry& a, const FcChar8 *b) const michael@0: { michael@0: return FcStrCmpIgnoreCase(a.mLang, b) == 0; michael@0: } michael@0: }; michael@0: michael@0: private: michael@0: // The requested pattern michael@0: nsCountedRef mSortPattern; michael@0: // Fonts from @font-face rules michael@0: nsRefPtr mUserFontSet; michael@0: // A (trimmed) list of font patterns and fonts that is built up as michael@0: // required. michael@0: nsTArray mFonts; michael@0: // Holds a list of font patterns that will be trimmed. This is first set michael@0: // to a list of preferred fonts. Then, if/when all the preferred fonts michael@0: // have been trimmed and added to mFonts, this is set to a list of michael@0: // fallback fonts. michael@0: nsAutoRef mFcFontSet; michael@0: // The set of characters supported by the fonts in mFonts. michael@0: nsAutoRef mCharSet; michael@0: // The index of the next font in mFcFontSet that has not yet been michael@0: // considered for mFonts. michael@0: int mFcFontsTrimmed; michael@0: // True iff fallback fonts are either stored in mFcFontSet or have been michael@0: // trimmed and added to mFonts (so that mFcFontSet is nullptr). michael@0: bool mHaveFallbackFonts; michael@0: // True iff there was a user font set with pending downloads, michael@0: // so the set may be updated when downloads complete michael@0: bool mWaitingForUserFont; michael@0: }; michael@0: michael@0: // Find the FcPattern for an @font-face font suitable for CSS family |aFamily| michael@0: // and style |aStyle| properties. michael@0: static const nsTArray< nsCountedRef >* michael@0: FindFontPatterns(gfxUserFontSet *mUserFontSet, michael@0: const nsACString &aFamily, uint8_t aStyle, michael@0: uint16_t aWeight, int16_t aStretch, michael@0: bool& aWaitForUserFont) michael@0: { michael@0: // Convert to UTF16 michael@0: NS_ConvertUTF8toUTF16 utf16Family(aFamily); michael@0: michael@0: // needsBold is not used here. Instead synthetic bold is enabled through michael@0: // FcFontRenderPrepare when the weight in the requested pattern is michael@0: // compared against the weight in the font pattern. michael@0: bool needsBold; michael@0: michael@0: gfxFontStyle style; michael@0: style.style = aStyle; michael@0: style.weight = aWeight; michael@0: style.stretch = aStretch; michael@0: michael@0: gfxUserFcFontEntry *fontEntry = nullptr; michael@0: gfxFontFamily *family = mUserFontSet->GetFamily(utf16Family); michael@0: if (family) { michael@0: fontEntry = static_cast michael@0: (mUserFontSet->FindFontEntry(family, style, needsBold, michael@0: aWaitForUserFont)); michael@0: michael@0: // Accept synthetic oblique for italic and oblique. michael@0: if (!fontEntry && aStyle != NS_FONT_STYLE_NORMAL) { michael@0: style.style = NS_FONT_STYLE_NORMAL; michael@0: fontEntry = static_cast michael@0: (mUserFontSet->FindFontEntry(family, style, needsBold, michael@0: aWaitForUserFont)); michael@0: } michael@0: } michael@0: michael@0: if (!fontEntry) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return &fontEntry->GetPatterns(); michael@0: } michael@0: michael@0: typedef FcBool (*FcPatternRemoveFunction)(FcPattern *p, const char *object, michael@0: int id); michael@0: michael@0: // FcPatternRemove is available in fontconfig-2.3.0 (2005) michael@0: static FcBool michael@0: moz_FcPatternRemove(FcPattern *p, const char *object, int id) michael@0: { michael@0: static FcPatternRemoveFunction sFcPatternRemovePtr = michael@0: reinterpret_cast michael@0: (FindFunctionSymbol("FcPatternRemove")); michael@0: michael@0: if (!sFcPatternRemovePtr) michael@0: return FcFalse; michael@0: michael@0: return (*sFcPatternRemovePtr)(p, object, id); michael@0: } michael@0: michael@0: // fontconfig prefers a matching family or lang to pixelsize of bitmap michael@0: // fonts. CSS suggests a tolerance of 20% on pixelsize. michael@0: static bool michael@0: SizeIsAcceptable(FcPattern *aFont, double aRequestedSize) michael@0: { michael@0: double size; michael@0: int v = 0; michael@0: while (FcPatternGetDouble(aFont, michael@0: FC_PIXEL_SIZE, v, &size) == FcResultMatch) { michael@0: ++v; michael@0: if (5.0 * fabs(size - aRequestedSize) < aRequestedSize) michael@0: return true; michael@0: } michael@0: michael@0: // No size means scalable michael@0: return v == 0; michael@0: } michael@0: michael@0: // Sorting only the preferred fonts first usually saves having to sort through michael@0: // every font on the system. michael@0: nsReturnRef michael@0: gfxFcFontSet::SortPreferredFonts(bool &aWaitForUserFont) michael@0: { michael@0: aWaitForUserFont = false; michael@0: michael@0: gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils(); michael@0: if (!utils) michael@0: return nsReturnRef(); michael@0: michael@0: // The list of families in mSortPattern has values with both weak and michael@0: // strong bindings. Values with strong bindings should be preferred. michael@0: // Values with weak bindings are default fonts that should be considered michael@0: // only when the font provides the best support for a requested language michael@0: // or after other fonts have satisfied all the requested languages. michael@0: // michael@0: // There are no direct fontconfig APIs to get the binding type. The michael@0: // binding only takes effect in the sort and match functions. michael@0: michael@0: // |requiredLangs| is a list of requested languages that have not yet been michael@0: // satisfied. gfxFontconfigUtils only sets one FC_LANG property value, michael@0: // but FcConfigSubstitute may add more values (e.g. prepending "en" to michael@0: // "ja" will use western fonts to render Latin/Arabic numerals in Japanese michael@0: // text.) michael@0: nsAutoTArray requiredLangs; michael@0: for (int v = 0; ; ++v) { michael@0: FcChar8 *lang; michael@0: FcResult result = FcPatternGetString(mSortPattern, FC_LANG, v, &lang); michael@0: if (result != FcResultMatch) { michael@0: // No need to check FcPatternGetLangSet() because michael@0: // gfxFontconfigUtils sets only a string value for FC_LANG and michael@0: // FcConfigSubstitute cannot add LangSets. michael@0: NS_ASSERTION(result != FcResultTypeMismatch, michael@0: "Expected a string for FC_LANG"); michael@0: break; michael@0: } michael@0: michael@0: if (!requiredLangs.Contains(lang, LangComparator())) { michael@0: FcLangResult bestLangSupport = utils->GetBestLangSupport(lang); michael@0: if (bestLangSupport != FcLangDifferentLang) { michael@0: requiredLangs. michael@0: AppendElement(LangSupportEntry(lang, bestLangSupport)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsAutoRef fontSet(FcFontSetCreate()); michael@0: if (!fontSet) michael@0: return fontSet.out(); michael@0: michael@0: // FcDefaultSubstitute() ensures a slant on mSortPattern, but, if that ever michael@0: // doesn't happen, Roman will be used. michael@0: int requestedSlant = FC_SLANT_ROMAN; michael@0: FcPatternGetInteger(mSortPattern, FC_SLANT, 0, &requestedSlant); michael@0: double requestedSize = -1.0; michael@0: FcPatternGetDouble(mSortPattern, FC_PIXEL_SIZE, 0, &requestedSize); michael@0: michael@0: nsTHashtable existingFamilies(50); michael@0: FcChar8 *family; michael@0: for (int v = 0; michael@0: FcPatternGetString(mSortPattern, michael@0: FC_FAMILY, v, &family) == FcResultMatch; ++v) { michael@0: const nsTArray< nsCountedRef > *familyFonts = nullptr; michael@0: michael@0: // Is this an @font-face family? michael@0: // XXX: Make use of this + pass to nsFont?? michael@0: bool isUserFont = false; michael@0: if (mUserFontSet) { michael@0: // Have some @font-face definitions michael@0: michael@0: nsDependentCString cFamily(gfxFontconfigUtils::ToCString(family)); michael@0: NS_NAMED_LITERAL_CSTRING(userPrefix, FONT_FACE_FAMILY_PREFIX); michael@0: michael@0: if (StringBeginsWith(cFamily, userPrefix)) { michael@0: isUserFont = true; michael@0: michael@0: // Trim off the prefix michael@0: nsDependentCSubstring cssFamily(cFamily, userPrefix.Length()); michael@0: michael@0: uint8_t thebesStyle = michael@0: gfxFontconfigUtils::FcSlantToThebesStyle(requestedSlant); michael@0: uint16_t thebesWeight = michael@0: gfxFontconfigUtils::GetThebesWeight(mSortPattern); michael@0: int16_t thebesStretch = michael@0: gfxFontconfigUtils::GetThebesStretch(mSortPattern); michael@0: michael@0: bool waitForUserFont; michael@0: familyFonts = FindFontPatterns(mUserFontSet, cssFamily, michael@0: thebesStyle, michael@0: thebesWeight, thebesStretch, michael@0: waitForUserFont); michael@0: if (waitForUserFont) { michael@0: aWaitForUserFont = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!isUserFont) { michael@0: familyFonts = &utils->GetFontsForFamily(family); michael@0: } michael@0: michael@0: if (!familyFonts || familyFonts->Length() == 0) { michael@0: // There are no fonts matching this family, so there is no point michael@0: // in searching for this family in the FontSort. michael@0: // michael@0: // Perhaps the original pattern should be retained for michael@0: // FcFontRenderPrepare. However, the only a useful config michael@0: // substitution test against missing families that i can imagine michael@0: // would only be interested in the preferred family michael@0: // (qual="first"), so always keep the first family and use the michael@0: // same pattern for Sort and RenderPrepare. michael@0: if (v != 0 && moz_FcPatternRemove(mSortPattern, FC_FAMILY, v)) { michael@0: --v; michael@0: } michael@0: continue; michael@0: } michael@0: michael@0: // Aliases seem to often end up occurring more than once, but michael@0: // duplicate families can't be removed from the sort pattern without michael@0: // knowing whether duplicates have the same binding. michael@0: gfxFontconfigUtils::DepFcStrEntry *entry = michael@0: existingFamilies.PutEntry(family); michael@0: if (entry) { michael@0: if (entry->mKey) // old entry michael@0: continue; michael@0: michael@0: entry->mKey = family; // initialize new entry michael@0: } michael@0: michael@0: for (uint32_t f = 0; f < familyFonts->Length(); ++f) { michael@0: FcPattern *font = familyFonts->ElementAt(f); michael@0: michael@0: // Fix up the family name of user-font patterns, as the same michael@0: // font entry may be used (via the UserFontCache) for multiple michael@0: // CSS family names michael@0: if (isUserFont) { michael@0: font = FcPatternDuplicate(font); michael@0: FcPatternDel(font, FC_FAMILY); michael@0: FcPatternAddString(font, FC_FAMILY, family); michael@0: } michael@0: michael@0: // User fonts are already filtered by slant (but not size) in michael@0: // mUserFontSet->FindFontEntry(). michael@0: if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize)) michael@0: continue; michael@0: michael@0: for (uint32_t r = 0; r < requiredLangs.Length(); ++r) { michael@0: const LangSupportEntry& entry = requiredLangs[r]; michael@0: FcLangResult support = michael@0: gfxFontconfigUtils::GetLangSupport(font, entry.mLang); michael@0: if (support <= entry.mBestSupport) { // lower is better michael@0: requiredLangs.RemoveElementAt(r); michael@0: --r; michael@0: } michael@0: } michael@0: michael@0: // FcFontSetDestroy will remove a reference but FcFontSetAdd michael@0: // does _not_ take a reference! michael@0: if (FcFontSetAdd(fontSet, font)) { michael@0: // We don't add a reference here for user fonts, because we're michael@0: // using a local clone of the pattern (see above) in order to michael@0: // override the family name michael@0: if (!isUserFont) { michael@0: FcPatternReference(font); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: FcPattern *truncateMarker = nullptr; michael@0: for (uint32_t r = 0; r < requiredLangs.Length(); ++r) { michael@0: const nsTArray< nsCountedRef >& langFonts = michael@0: utils->GetFontsForLang(requiredLangs[r].mLang); michael@0: michael@0: bool haveLangFont = false; michael@0: for (uint32_t f = 0; f < langFonts.Length(); ++f) { michael@0: FcPattern *font = langFonts[f]; michael@0: if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize)) michael@0: continue; michael@0: michael@0: haveLangFont = true; michael@0: if (FcFontSetAdd(fontSet, font)) { michael@0: FcPatternReference(font); michael@0: } michael@0: } michael@0: michael@0: if (!haveLangFont && langFonts.Length() > 0) { michael@0: // There is a font that supports this language but it didn't pass michael@0: // the slant and size criteria. Weak default font families should michael@0: // not be considered until the language has been satisfied. michael@0: // michael@0: // Insert a font that supports the language so that it will mark michael@0: // the position of fonts from weak families in the sorted set and michael@0: // they can be removed. The language and weak families will be michael@0: // considered in the fallback fonts, which use fontconfig's michael@0: // algorithm. michael@0: // michael@0: // Of the fonts that don't meet slant and size criteria, strong michael@0: // default font families should be considered before (other) fonts michael@0: // for this language, so this marker font will be removed (as well michael@0: // as the fonts from weak families), and strong families will be michael@0: // reconsidered in the fallback fonts. michael@0: FcPattern *font = langFonts[0]; michael@0: if (FcFontSetAdd(fontSet, font)) { michael@0: FcPatternReference(font); michael@0: truncateMarker = font; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: FcFontSet *sets[1] = { fontSet }; michael@0: FcResult result; michael@0: #ifdef SOLARIS michael@0: // Get around a crash of FcFontSetSort when FcConfig is nullptr michael@0: // Solaris's FcFontSetSort needs an FcConfig (bug 474758) michael@0: fontSet.own(FcFontSetSort(FcConfigGetCurrent(), sets, 1, mSortPattern, michael@0: FcFalse, nullptr, &result)); michael@0: #else michael@0: fontSet.own(FcFontSetSort(nullptr, sets, 1, mSortPattern, michael@0: FcFalse, nullptr, &result)); michael@0: #endif michael@0: michael@0: if (truncateMarker != nullptr && fontSet) { michael@0: nsAutoRef truncatedSet(FcFontSetCreate()); michael@0: michael@0: for (int f = 0; f < fontSet->nfont; ++f) { michael@0: FcPattern *font = fontSet->fonts[f]; michael@0: if (font == truncateMarker) michael@0: break; michael@0: michael@0: if (FcFontSetAdd(truncatedSet, font)) { michael@0: FcPatternReference(font); michael@0: } michael@0: } michael@0: michael@0: fontSet.steal(truncatedSet); michael@0: } michael@0: michael@0: return fontSet.out(); michael@0: } michael@0: michael@0: nsReturnRef michael@0: gfxFcFontSet::SortFallbackFonts() michael@0: { michael@0: // Setting trim to FcTrue would provide a much smaller (~ 1/10) FcFontSet, michael@0: // but would take much longer due to comparing all the character sets. michael@0: // michael@0: // The references to fonts in this FcFontSet are almost free michael@0: // as they are pointers into mmaped cache files. michael@0: // michael@0: // GetFontPatternAt() will trim lazily if and as needed, which will also michael@0: // remove duplicates of preferred fonts. michael@0: FcResult result; michael@0: return nsReturnRef(FcFontSort(nullptr, mSortPattern, michael@0: FcFalse, nullptr, &result)); michael@0: } michael@0: michael@0: // GetFontAt relies on this setting up all patterns up to |i|. michael@0: FcPattern * michael@0: gfxFcFontSet::GetFontPatternAt(uint32_t i) michael@0: { michael@0: while (i >= mFonts.Length()) { michael@0: while (!mFcFontSet) { michael@0: if (mHaveFallbackFonts) michael@0: return nullptr; michael@0: michael@0: mFcFontSet = SortFallbackFonts(); michael@0: mHaveFallbackFonts = true; michael@0: mFcFontsTrimmed = 0; michael@0: // Loop to test that mFcFontSet is non-nullptr. michael@0: } michael@0: michael@0: while (mFcFontsTrimmed < mFcFontSet->nfont) { michael@0: FcPattern *font = mFcFontSet->fonts[mFcFontsTrimmed]; michael@0: ++mFcFontsTrimmed; michael@0: michael@0: if (mFonts.Length() != 0) { michael@0: // See if the next font provides support for any extra michael@0: // characters. Most often the next font is not going to michael@0: // support more characters so check for a SubSet first before michael@0: // allocating a new CharSet with Union. michael@0: FcCharSet *supportedChars = mCharSet; michael@0: if (!supportedChars) { michael@0: FcPatternGetCharSet(mFonts[mFonts.Length() - 1].mPattern, michael@0: FC_CHARSET, 0, &supportedChars); michael@0: } michael@0: michael@0: if (supportedChars) { michael@0: FcCharSet *newChars = nullptr; michael@0: FcPatternGetCharSet(font, FC_CHARSET, 0, &newChars); michael@0: if (newChars) { michael@0: if (FcCharSetIsSubset(newChars, supportedChars)) michael@0: continue; michael@0: michael@0: mCharSet.own(FcCharSetUnion(supportedChars, newChars)); michael@0: } else if (!mCharSet) { michael@0: mCharSet.own(FcCharSetCopy(supportedChars)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: mFonts.AppendElement(font); michael@0: if (mFonts.Length() >= i) michael@0: break; michael@0: } michael@0: michael@0: if (mFcFontsTrimmed == mFcFontSet->nfont) { michael@0: // finished with this font set michael@0: mFcFontSet.reset(); michael@0: } michael@0: } michael@0: michael@0: return mFonts[i].mPattern; michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: static void ApplyGdkScreenFontOptions(FcPattern *aPattern); michael@0: #endif michael@0: michael@0: // Apply user settings and defaults to pattern in preparation for matching. michael@0: static void michael@0: PrepareSortPattern(FcPattern *aPattern, double aFallbackSize, michael@0: double aSizeAdjustFactor, bool aIsPrinterFont) michael@0: { michael@0: FcConfigSubstitute(nullptr, aPattern, FcMatchPattern); michael@0: michael@0: // This gets cairo_font_options_t for the Screen. We should have michael@0: // different font options for printing (no hinting) but we are not told michael@0: // what we are measuring for. michael@0: // michael@0: // If cairo adds support for lcd_filter, gdk will not provide the default michael@0: // setting for that option. We could get the default setting by creating michael@0: // an xlib surface once, recording its font_options, and then merging the michael@0: // gdk options. michael@0: // michael@0: // Using an xlib surface would also be an option to get Screen font michael@0: // options for non-GTK X11 toolkits, but less efficient than using GDK to michael@0: // pick up dynamic changes. michael@0: if(aIsPrinterFont) { michael@0: cairo_font_options_t *options = cairo_font_options_create(); michael@0: cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); michael@0: cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); michael@0: cairo_ft_font_options_substitute(options, aPattern); michael@0: cairo_font_options_destroy(options); michael@0: FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue); michael@0: } else { michael@0: #ifdef MOZ_GFX_OPTIMIZE_MOBILE michael@0: cairo_font_options_t *options = cairo_font_options_create(); michael@0: cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); michael@0: cairo_ft_font_options_substitute(options, aPattern); michael@0: cairo_font_options_destroy(options); michael@0: #endif michael@0: #ifdef MOZ_WIDGET_GTK michael@0: ApplyGdkScreenFontOptions(aPattern); michael@0: #endif michael@0: } michael@0: michael@0: // Protect against any fontconfig settings that may have incorrectly michael@0: // modified the pixelsize, and consider aSizeAdjustFactor. michael@0: double size = aFallbackSize; michael@0: if (FcPatternGetDouble(aPattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch michael@0: || aSizeAdjustFactor != 1.0) { michael@0: FcPatternDel(aPattern, FC_PIXEL_SIZE); michael@0: FcPatternAddDouble(aPattern, FC_PIXEL_SIZE, size * aSizeAdjustFactor); michael@0: } michael@0: michael@0: FcDefaultSubstitute(aPattern); michael@0: } michael@0: michael@0: /** michael@0: ** gfxPangoFontGroup michael@0: **/ michael@0: michael@0: struct FamilyCallbackData { michael@0: FamilyCallbackData(nsTArray *aFcFamilyList, michael@0: gfxUserFontSet *aUserFontSet) michael@0: : mFcFamilyList(aFcFamilyList), mUserFontSet(aUserFontSet) michael@0: { michael@0: } michael@0: nsTArray *mFcFamilyList; michael@0: const gfxUserFontSet *mUserFontSet; michael@0: }; michael@0: michael@0: static int michael@0: FFRECountHyphens (const nsAString &aFFREName) michael@0: { michael@0: int h = 0; michael@0: int32_t hyphen = 0; michael@0: while ((hyphen = aFFREName.FindChar('-', hyphen)) >= 0) { michael@0: ++h; michael@0: ++hyphen; michael@0: } michael@0: return h; michael@0: } michael@0: michael@0: static bool michael@0: FamilyCallback (const nsAString& fontName, const nsACString& genericName, michael@0: bool aUseFontSet, void *closure) michael@0: { michael@0: FamilyCallbackData *data = static_cast(closure); michael@0: nsTArray *list = data->mFcFamilyList; michael@0: michael@0: // We ignore prefs that have three hypens since they are X style prefs. michael@0: if (genericName.Length() && FFRECountHyphens(fontName) >= 3) michael@0: return true; michael@0: michael@0: if (!list->Contains(fontName)) { michael@0: // The family properties of FcPatterns for @font-face fonts have a michael@0: // namespace to identify them among system fonts. (see michael@0: // FONT_FACE_FAMILY_PREFIX.) michael@0: // michael@0: // Earlier versions of this code allowed the CSS family name to match michael@0: // either the @font-face family or the system font family, so both michael@0: // were added here. This was in accordance with earlier versions of michael@0: // the W3C specifications regarding @font-face. michael@0: // michael@0: // The current (2011-02-27) draft of CSS3 Fonts says michael@0: // michael@0: // (Section 4.2: Font family: the font-family descriptor): michael@0: // "If the font family name is the same as a font family available in michael@0: // a given user's environment, it effectively hides the underlying michael@0: // font for documents that use the stylesheet." michael@0: // michael@0: // (Section 5: Font matching algorithm) michael@0: // "... the user agent attempts to find the family name among fonts michael@0: // defined via @font-face rules and then among available system fonts, michael@0: // .... If a font family defined via @font-face rules contains only michael@0: // invalid font data, it should be considered as if a font was present michael@0: // but contained an empty character map; matching a platform font with michael@0: // the same name must not occur in this case." michael@0: // michael@0: // Therefore, for names present in the user font set, this code no michael@0: // longer includes the family name for matching against system fonts. michael@0: // michael@0: const gfxUserFontSet *userFontSet = data->mUserFontSet; michael@0: if (aUseFontSet && genericName.Length() == 0 && michael@0: userFontSet && userFontSet->HasFamily(fontName)) { michael@0: nsAutoString userFontName = michael@0: NS_LITERAL_STRING(FONT_FACE_FAMILY_PREFIX) + fontName; michael@0: list->AppendElement(userFontName); michael@0: } else { michael@0: list->AppendElement(fontName); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: gfxPangoFontGroup::gfxPangoFontGroup (const nsAString& families, michael@0: const gfxFontStyle *aStyle, michael@0: gfxUserFontSet *aUserFontSet) michael@0: : gfxFontGroup(families, aStyle, aUserFontSet), michael@0: mPangoLanguage(GuessPangoLanguage(aStyle->language)) michael@0: { michael@0: // This language is passed to the font for shaping. michael@0: // Shaping doesn't know about lang groups so make it a real language. michael@0: if (mPangoLanguage) { michael@0: mStyle.language = do_GetAtom(pango_language_to_string(mPangoLanguage)); michael@0: } michael@0: michael@0: // dummy entry, will be replaced when actually needed michael@0: mFonts.AppendElement(FamilyFace()); michael@0: } michael@0: michael@0: gfxPangoFontGroup::~gfxPangoFontGroup() michael@0: { michael@0: } michael@0: michael@0: gfxFontGroup * michael@0: gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle) michael@0: { michael@0: return new gfxPangoFontGroup(mFamilies, aStyle, mUserFontSet); michael@0: } michael@0: michael@0: // An array of family names suitable for fontconfig michael@0: void michael@0: gfxPangoFontGroup::GetFcFamilies(nsTArray *aFcFamilyList, michael@0: nsIAtom *aLanguage) michael@0: { michael@0: FamilyCallbackData data(aFcFamilyList, mUserFontSet); michael@0: // Leave non-existing fonts in the list so that fontconfig can get the michael@0: // best match. michael@0: ForEachFontInternal(mFamilies, aLanguage, true, false, true, michael@0: FamilyCallback, &data); michael@0: } michael@0: michael@0: gfxFcFont * michael@0: gfxPangoFontGroup::GetBaseFont() michael@0: { michael@0: if (mFonts[0].Font() == nullptr) { michael@0: gfxFont* font = GetBaseFontSet()->GetFontAt(0, GetStyle()); michael@0: mFonts[0] = FamilyFace(nullptr, font); michael@0: } michael@0: michael@0: return static_cast(mFonts[0].Font()); michael@0: } michael@0: michael@0: gfxFont * michael@0: gfxPangoFontGroup::GetFontAt(int32_t i) michael@0: { michael@0: // If it turns out to be hard for all clients that cache font michael@0: // groups to call UpdateFontList at appropriate times, we could michael@0: // instead consider just calling UpdateFontList from someplace michael@0: // more central (such as here). michael@0: NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(), michael@0: "Whoever was caching this font group should have " michael@0: "called UpdateFontList on it"); michael@0: michael@0: NS_PRECONDITION(i == 0, "Only have one font"); michael@0: michael@0: return GetBaseFont(); michael@0: } michael@0: michael@0: void michael@0: gfxPangoFontGroup::UpdateFontList() michael@0: { michael@0: uint64_t newGeneration = GetGeneration(); michael@0: if (newGeneration == mCurrGeneration) michael@0: return; michael@0: michael@0: mFonts[0] = FamilyFace(); michael@0: mFontSets.Clear(); michael@0: mCachedEllipsisTextRun = nullptr; michael@0: mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET; michael@0: mCurrGeneration = newGeneration; michael@0: mSkipDrawing = false; michael@0: } michael@0: michael@0: already_AddRefed michael@0: gfxPangoFontGroup::MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor, michael@0: nsAutoRef *aMatchPattern) michael@0: { michael@0: const char *lang = pango_language_to_string(aLang); michael@0: michael@0: nsRefPtr langGroup; michael@0: if (aLang != mPangoLanguage) { michael@0: // Set up langGroup for Mozilla's font prefs. michael@0: langGroup = do_GetAtom(lang); michael@0: } michael@0: michael@0: nsAutoTArray fcFamilyList; michael@0: GetFcFamilies(&fcFamilyList, michael@0: langGroup ? langGroup.get() : mStyle.language.get()); michael@0: michael@0: // To consider: A fontset cache here could be helpful. michael@0: michael@0: // Get a pattern suitable for matching. michael@0: nsAutoRef pattern michael@0: (gfxFontconfigUtils::NewPattern(fcFamilyList, mStyle, lang)); michael@0: michael@0: PrepareSortPattern(pattern, mStyle.size, aSizeAdjustFactor, mStyle.printerFont); michael@0: michael@0: nsRefPtr fontset = michael@0: new gfxFcFontSet(pattern, mUserFontSet); michael@0: michael@0: mSkipDrawing = fontset->WaitingForUserFont(); michael@0: michael@0: if (aMatchPattern) michael@0: aMatchPattern->steal(pattern); michael@0: michael@0: return fontset.forget(); michael@0: } michael@0: michael@0: gfxPangoFontGroup:: michael@0: FontSetByLangEntry::FontSetByLangEntry(PangoLanguage *aLang, michael@0: gfxFcFontSet *aFontSet) michael@0: : mLang(aLang), mFontSet(aFontSet) michael@0: { michael@0: } michael@0: michael@0: gfxFcFontSet * michael@0: gfxPangoFontGroup::GetFontSet(PangoLanguage *aLang) michael@0: { michael@0: GetBaseFontSet(); // sets mSizeAdjustFactor and mFontSets[0] michael@0: michael@0: if (!aLang) michael@0: return mFontSets[0].mFontSet; michael@0: michael@0: for (uint32_t i = 0; i < mFontSets.Length(); ++i) { michael@0: if (mFontSets[i].mLang == aLang) michael@0: return mFontSets[i].mFontSet; michael@0: } michael@0: michael@0: nsRefPtr fontSet = michael@0: MakeFontSet(aLang, mSizeAdjustFactor); michael@0: mFontSets.AppendElement(FontSetByLangEntry(aLang, fontSet)); michael@0: michael@0: return fontSet; michael@0: } michael@0: michael@0: already_AddRefed michael@0: gfxPangoFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, michael@0: int32_t aRunScript, michael@0: gfxFont *aPrevMatchedFont, michael@0: uint8_t *aMatchType) michael@0: { michael@0: if (aPrevMatchedFont) { michael@0: // Don't switch fonts for control characters, regardless of michael@0: // whether they are present in the current font, as they won't michael@0: // actually be rendered (see bug 716229) michael@0: uint8_t category = GetGeneralCategory(aCh); michael@0: if (category == HB_UNICODE_GENERAL_CATEGORY_CONTROL) { michael@0: return nsRefPtr(aPrevMatchedFont).forget(); michael@0: } michael@0: michael@0: // if this character is a join-control or the previous is a join-causer, michael@0: // use the same font as the previous range if we can michael@0: if (gfxFontUtils::IsJoinControl(aCh) || michael@0: gfxFontUtils::IsJoinCauser(aPrevCh)) { michael@0: if (aPrevMatchedFont->HasCharacter(aCh)) { michael@0: return nsRefPtr(aPrevMatchedFont).forget(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // if this character is a variation selector, michael@0: // use the previous font regardless of whether it supports VS or not. michael@0: // otherwise the text run will be divided. michael@0: if (gfxFontUtils::IsVarSelector(aCh)) { michael@0: if (aPrevMatchedFont) { michael@0: return nsRefPtr(aPrevMatchedFont).forget(); michael@0: } michael@0: // VS alone. it's meaningless to search different fonts michael@0: return nullptr; michael@0: } michael@0: michael@0: // The real fonts that fontconfig provides for generic/fallback families michael@0: // depend on the language used, so a different FontSet is used for each michael@0: // language (except for the variation below). michael@0: // michael@0: // With most fontconfig configurations any real family names prior to a michael@0: // fontconfig generic with corresponding fonts installed will still lead michael@0: // to the same leading fonts in each FontSet. michael@0: // michael@0: // There is an inefficiency here therefore because the same base FontSet michael@0: // could often be used if these real families support the character. michael@0: // However, with fontconfig aliases, it is difficult to distinguish michael@0: // where exactly alias fonts end and generic/fallback fonts begin. michael@0: // michael@0: // The variation from pure language-based matching used here is that the michael@0: // same primary/base font is always used irrespective of the language. michael@0: // This provides that SCRIPT_COMMON characters are consistently rendered michael@0: // with the same font (bug 339513 and bug 416725). This is particularly michael@0: // important with the word cache as script can't be reliably determined michael@0: // from surrounding words. It also often avoids the unnecessary extra michael@0: // FontSet efficiency mentioned above. michael@0: // michael@0: // However, in two situations, the base font is not checked before the michael@0: // language-specific FontSet. michael@0: // michael@0: // 1. When we don't have a language to make a good choice for michael@0: // the base font. michael@0: // michael@0: // 2. For system fonts, use the default Pango behavior to give michael@0: // consistency with other apps. This is relevant when un-localized michael@0: // builds are run in non-Latin locales. This special-case probably michael@0: // wouldn't be necessary but for bug 91190. michael@0: michael@0: gfxFcFontSet *fontSet = GetBaseFontSet(); michael@0: uint32_t nextFont = 0; michael@0: FcPattern *basePattern = nullptr; michael@0: if (!mStyle.systemFont && mPangoLanguage) { michael@0: basePattern = fontSet->GetFontPatternAt(0); michael@0: if (HasChar(basePattern, aCh)) { michael@0: *aMatchType = gfxTextRange::kFontGroup; michael@0: return nsRefPtr(GetBaseFont()).forget(); michael@0: } michael@0: michael@0: nextFont = 1; michael@0: } michael@0: michael@0: // Pango, GLib, and Thebes (but not harfbuzz!) all happen to use the same michael@0: // script codes, so we can just cast the value here. michael@0: const PangoScript script = static_cast(aRunScript); michael@0: // Might be nice to call pango_language_includes_script only once for the michael@0: // run rather than for each character. michael@0: PangoLanguage *scriptLang; michael@0: if ((!basePattern || michael@0: !pango_language_includes_script(mPangoLanguage, script)) && michael@0: (scriptLang = pango_script_get_sample_language(script))) { michael@0: fontSet = GetFontSet(scriptLang); michael@0: nextFont = 0; michael@0: } michael@0: michael@0: for (uint32_t i = nextFont; michael@0: FcPattern *pattern = fontSet->GetFontPatternAt(i); michael@0: ++i) { michael@0: if (pattern == basePattern) { michael@0: continue; // already checked basePattern michael@0: } michael@0: michael@0: if (HasChar(pattern, aCh)) { michael@0: *aMatchType = gfxTextRange::kFontGroup; michael@0: return nsRefPtr(fontSet->GetFontAt(i, GetStyle())).forget(); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: // Sanity-check: spot-check a few constants to confirm that Thebes and michael@0: // Pango script codes really do match michael@0: #define CHECK_SCRIPT_CODE(script) \ michael@0: PR_STATIC_ASSERT(int32_t(MOZ_SCRIPT_##script) == \ michael@0: int32_t(PANGO_SCRIPT_##script)) michael@0: michael@0: CHECK_SCRIPT_CODE(COMMON); michael@0: CHECK_SCRIPT_CODE(INHERITED); michael@0: CHECK_SCRIPT_CODE(ARABIC); michael@0: CHECK_SCRIPT_CODE(LATIN); michael@0: CHECK_SCRIPT_CODE(UNKNOWN); michael@0: CHECK_SCRIPT_CODE(NKO); michael@0: michael@0: /** michael@0: ** gfxFcFont michael@0: **/ michael@0: michael@0: cairo_user_data_key_t gfxFcFont::sGfxFontKey; michael@0: michael@0: gfxFcFont::gfxFcFont(cairo_scaled_font_t *aCairoFont, michael@0: gfxFcFontEntry *aFontEntry, michael@0: const gfxFontStyle *aFontStyle) michael@0: : gfxFT2FontBase(aCairoFont, aFontEntry, aFontStyle) michael@0: { michael@0: cairo_scaled_font_set_user_data(mScaledFont, &sGfxFontKey, this, nullptr); michael@0: } michael@0: michael@0: gfxFcFont::~gfxFcFont() michael@0: { michael@0: cairo_scaled_font_set_user_data(mScaledFont, michael@0: &sGfxFontKey, michael@0: nullptr, michael@0: nullptr); michael@0: } michael@0: michael@0: bool michael@0: gfxFcFont::ShapeText(gfxContext *aContext, michael@0: const char16_t *aText, michael@0: uint32_t aOffset, michael@0: uint32_t aLength, michael@0: int32_t aScript, michael@0: gfxShapedText *aShapedText, michael@0: bool aPreferPlatformShaping) michael@0: { michael@0: bool ok = false; michael@0: michael@0: if (FontCanSupportGraphite()) { michael@0: if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) { michael@0: if (!mGraphiteShaper) { michael@0: mGraphiteShaper = new gfxGraphiteShaper(this); michael@0: } michael@0: ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength, michael@0: aScript, aShapedText); michael@0: } michael@0: } michael@0: michael@0: if (!ok) { michael@0: if (!mHarfBuzzShaper) { michael@0: mHarfBuzzShaper = new gfxHarfBuzzShaper(this); michael@0: } michael@0: ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength, michael@0: aScript, aShapedText); michael@0: } michael@0: michael@0: NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text"); michael@0: michael@0: PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText); michael@0: michael@0: return ok; michael@0: } michael@0: michael@0: /* static */ void michael@0: gfxPangoFontGroup::Shutdown() michael@0: { michael@0: // Resetting gFTLibrary in case this is wanted again after a michael@0: // cairo_debug_reset_static_data. michael@0: gFTLibrary = nullptr; michael@0: } michael@0: michael@0: /* static */ gfxFontEntry * michael@0: gfxPangoFontGroup::NewFontEntry(const gfxProxyFontEntry &aProxyEntry, michael@0: const nsAString& aFullname) michael@0: { michael@0: gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils(); michael@0: if (!utils) michael@0: return nullptr; michael@0: michael@0: // The font face name from @font-face { src: local() } is not well michael@0: // defined. michael@0: // michael@0: // On MS Windows, this name gets compared with michael@0: // ENUMLOGFONTEXW::elfFullName, which for OpenType fonts seems to be the michael@0: // full font name from the name table. For CFF OpenType fonts this is the michael@0: // same as the PostScript name, but for TrueType fonts it is usually michael@0: // different. michael@0: // michael@0: // On Mac, the font face name is compared with the PostScript name, even michael@0: // for TrueType fonts. michael@0: // michael@0: // Fontconfig only records the full font names, so the behavior here michael@0: // follows that on MS Windows. However, to provide the possibility michael@0: // of aliases to compensate for variations, the font face name is passed michael@0: // through FcConfigSubstitute. michael@0: michael@0: nsAutoRef pattern(FcPatternCreate()); michael@0: if (!pattern) michael@0: return nullptr; michael@0: michael@0: NS_ConvertUTF16toUTF8 fullname(aFullname); michael@0: FcPatternAddString(pattern, FC_FULLNAME, michael@0: gfxFontconfigUtils::ToFcChar8(fullname)); michael@0: FcConfigSubstitute(nullptr, pattern, FcMatchPattern); michael@0: michael@0: FcChar8 *name; michael@0: for (int v = 0; michael@0: FcPatternGetString(pattern, FC_FULLNAME, v, &name) == FcResultMatch; michael@0: ++v) { michael@0: const nsTArray< nsCountedRef >& fonts = michael@0: utils->GetFontsForFullname(name); michael@0: michael@0: if (fonts.Length() != 0) michael@0: return new gfxLocalFcFontEntry(aProxyEntry, fonts); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: /* static */ FT_Library michael@0: gfxPangoFontGroup::GetFTLibrary() michael@0: { michael@0: if (!gFTLibrary) { michael@0: // Use cairo's FT_Library so that cairo takes care of shutdown of the michael@0: // FT_Library after it has destroyed its font_faces, and FT_Done_Face michael@0: // has been called on each FT_Face, at least until this bug is fixed: michael@0: // https://bugs.freedesktop.org/show_bug.cgi?id=18857 michael@0: // michael@0: // Cairo's FT_Library can be obtained from any cairo_scaled_font. The michael@0: // font properties requested here are chosen to get an FT_Face that is michael@0: // likely to be also used elsewhere. michael@0: gfxFontStyle style; michael@0: nsRefPtr fontGroup = michael@0: new gfxPangoFontGroup(NS_LITERAL_STRING("sans-serif"), michael@0: &style, nullptr); michael@0: michael@0: gfxFcFont *font = fontGroup->GetBaseFont(); michael@0: if (!font) michael@0: return nullptr; michael@0: michael@0: gfxFT2LockedFace face(font); michael@0: if (!face.get()) michael@0: return nullptr; michael@0: michael@0: gFTLibrary = face.get()->glyph->library; michael@0: } michael@0: michael@0: return gFTLibrary; michael@0: } michael@0: michael@0: /* static */ gfxFontEntry * michael@0: gfxPangoFontGroup::NewFontEntry(const gfxProxyFontEntry &aProxyEntry, michael@0: const uint8_t *aFontData, uint32_t aLength) michael@0: { michael@0: // Ownership of aFontData is passed in here, and transferred to the michael@0: // new fontEntry, which will release it when no longer needed. michael@0: michael@0: // Using face_index = 0 for the first face in the font, as we have no michael@0: // other information. FT_New_Memory_Face checks for a nullptr FT_Library. michael@0: FT_Face face; michael@0: FT_Error error = michael@0: FT_New_Memory_Face(GetFTLibrary(), aFontData, aLength, 0, &face); michael@0: if (error != 0) { michael@0: NS_Free((void*)aFontData); michael@0: return nullptr; michael@0: } michael@0: michael@0: return new gfxDownloadedFcFontEntry(aProxyEntry, aFontData, face); michael@0: } michael@0: michael@0: michael@0: static double michael@0: GetPixelSize(FcPattern *aPattern) michael@0: { michael@0: double size; michael@0: if (FcPatternGetDouble(aPattern, michael@0: FC_PIXEL_SIZE, 0, &size) == FcResultMatch) michael@0: return size; michael@0: michael@0: NS_NOTREACHED("No size on pattern"); michael@0: return 0.0; michael@0: } michael@0: michael@0: /** michael@0: * The following gfxFcFonts are accessed from the cairo_scaled_font or created michael@0: * from the FcPattern, not from the gfxFontCache hash table. The gfxFontCache michael@0: * hash table is keyed by desired family and style, whereas here we only know michael@0: * actual family and style. There may be more than one of these fonts with michael@0: * the same family and style, but different PangoFont and actual font face. michael@0: * michael@0: * The point of this is to record the exact font face for gfxTextRun glyph michael@0: * indices. The style of this font does not necessarily represent the exact michael@0: * gfxFontStyle used to build the text run. Notably, the language is not michael@0: * recorded. michael@0: */ michael@0: michael@0: /* static */ michael@0: already_AddRefed michael@0: gfxFcFont::GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern, michael@0: const gfxFontStyle *aFontStyle) michael@0: { michael@0: nsAutoRef renderPattern michael@0: (FcFontRenderPrepare(nullptr, aRequestedPattern, aFontPattern)); michael@0: cairo_font_face_t *face = michael@0: cairo_ft_font_face_create_for_pattern(renderPattern); michael@0: michael@0: // Reuse an existing font entry if available. michael@0: nsRefPtr fe = gfxFcFontEntry::LookupFontEntry(face); michael@0: if (!fe) { michael@0: gfxDownloadedFcFontEntry *downloadedFontEntry = michael@0: GetDownloadedFontEntry(aFontPattern); michael@0: if (downloadedFontEntry) { michael@0: // Web font michael@0: fe = downloadedFontEntry; michael@0: if (cairo_font_face_status(face) == CAIRO_STATUS_SUCCESS) { michael@0: // cairo_font_face_t is using the web font data. michael@0: // Hold a reference to the font entry to keep the font face michael@0: // data. michael@0: if (!downloadedFontEntry->SetCairoFace(face)) { michael@0: // OOM. Let cairo pick a fallback font michael@0: cairo_font_face_destroy(face); michael@0: face = cairo_ft_font_face_create_for_pattern(aRequestedPattern); michael@0: fe = gfxFcFontEntry::LookupFontEntry(face); michael@0: } michael@0: } michael@0: } michael@0: if (!fe) { michael@0: // Get a unique name for the font face from the file and id. michael@0: nsAutoString name; michael@0: FcChar8 *fc_file; michael@0: if (FcPatternGetString(renderPattern, michael@0: FC_FILE, 0, &fc_file) == FcResultMatch) { michael@0: int index; michael@0: if (FcPatternGetInteger(renderPattern, michael@0: FC_INDEX, 0, &index) != FcResultMatch) { michael@0: // cairo defaults to 0. michael@0: index = 0; michael@0: } michael@0: michael@0: AppendUTF8toUTF16(gfxFontconfigUtils::ToCString(fc_file), name); michael@0: if (index != 0) { michael@0: name.AppendLiteral("/"); michael@0: name.AppendInt(index); michael@0: } michael@0: } michael@0: michael@0: fe = new gfxSystemFcFontEntry(face, aFontPattern, name); michael@0: } michael@0: } michael@0: michael@0: gfxFontStyle style(*aFontStyle); michael@0: style.size = GetPixelSize(renderPattern); michael@0: style.style = gfxFontconfigUtils::GetThebesStyle(renderPattern); michael@0: style.weight = gfxFontconfigUtils::GetThebesWeight(renderPattern); michael@0: michael@0: nsRefPtr font = gfxFontCache::GetCache()->Lookup(fe, &style); michael@0: if (!font) { michael@0: // Note that a file/index pair (or FT_Face) and the gfxFontStyle are michael@0: // not necessarily enough to provide a key that will describe a unique michael@0: // font. cairoFont contains information from renderPattern, which is a michael@0: // fully resolved pattern from FcFontRenderPrepare. michael@0: // FcFontRenderPrepare takes the requested pattern and the face michael@0: // pattern as input and can modify elements of the resulting pattern michael@0: // that affect rendering but are not included in the gfxFontStyle. michael@0: cairo_scaled_font_t *cairoFont = CreateScaledFont(renderPattern, face); michael@0: font = new gfxFcFont(cairoFont, fe, &style); michael@0: gfxFontCache::GetCache()->AddNew(font); michael@0: cairo_scaled_font_destroy(cairoFont); michael@0: } michael@0: michael@0: cairo_font_face_destroy(face); michael@0: michael@0: nsRefPtr retval(static_cast(font.get())); michael@0: return retval.forget(); michael@0: } michael@0: michael@0: gfxFcFontSet * michael@0: gfxPangoFontGroup::GetBaseFontSet() michael@0: { michael@0: if (mFontSets.Length() > 0) michael@0: return mFontSets[0].mFontSet; michael@0: michael@0: mSizeAdjustFactor = 1.0; // will be adjusted below if necessary michael@0: nsAutoRef pattern; michael@0: nsRefPtr fontSet = michael@0: MakeFontSet(mPangoLanguage, mSizeAdjustFactor, &pattern); michael@0: michael@0: double size = GetPixelSize(pattern); michael@0: if (size != 0.0 && mStyle.sizeAdjust != 0.0) { michael@0: gfxFcFont *font = fontSet->GetFontAt(0, GetStyle()); michael@0: if (font) { michael@0: const gfxFont::Metrics& metrics = font->GetMetrics(); michael@0: michael@0: // The factor of 0.1 ensures that xHeight is sane so fonts don't michael@0: // become huge. Strictly ">" ensures that xHeight and emHeight are michael@0: // not both zero. michael@0: if (metrics.xHeight > 0.1 * metrics.emHeight) { michael@0: mSizeAdjustFactor = michael@0: mStyle.sizeAdjust * metrics.emHeight / metrics.xHeight; michael@0: michael@0: size *= mSizeAdjustFactor; michael@0: FcPatternDel(pattern, FC_PIXEL_SIZE); michael@0: FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size); michael@0: michael@0: fontSet = new gfxFcFontSet(pattern, mUserFontSet); michael@0: } michael@0: } michael@0: } michael@0: michael@0: PangoLanguage *pangoLang = mPangoLanguage; michael@0: FcChar8 *fcLang; michael@0: if (!pangoLang && michael@0: FcPatternGetString(pattern, FC_LANG, 0, &fcLang) == FcResultMatch) { michael@0: pangoLang = michael@0: pango_language_from_string(gfxFontconfigUtils::ToCString(fcLang)); michael@0: } michael@0: michael@0: mFontSets.AppendElement(FontSetByLangEntry(pangoLang, fontSet)); michael@0: michael@0: return fontSet; michael@0: } michael@0: michael@0: /** michael@0: ** gfxTextRun michael@0: * michael@0: * A serious problem: michael@0: * michael@0: * -- We draw with a font that's hinted for the CTM, but we measure with a font michael@0: * hinted to the identity matrix, so our "bounding metrics" may not be accurate. michael@0: * michael@0: **/ michael@0: michael@0: // This will fetch an existing scaled_font if one exists. michael@0: static cairo_scaled_font_t * michael@0: CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace) michael@0: { michael@0: double size = GetPixelSize(aPattern); michael@0: michael@0: cairo_matrix_t fontMatrix; michael@0: FcMatrix *fcMatrix; michael@0: if (FcPatternGetMatrix(aPattern, FC_MATRIX, 0, &fcMatrix) == FcResultMatch) michael@0: cairo_matrix_init(&fontMatrix, fcMatrix->xx, -fcMatrix->yx, -fcMatrix->xy, fcMatrix->yy, 0, 0); michael@0: else michael@0: cairo_matrix_init_identity(&fontMatrix); michael@0: cairo_matrix_scale(&fontMatrix, size, size); michael@0: michael@0: FcBool printing; michael@0: if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) != FcResultMatch) { michael@0: printing = FcFalse; michael@0: } michael@0: michael@0: // The cairo_scaled_font is created with a unit ctm so that metrics and michael@0: // positions are in user space, but this means that hinting effects will michael@0: // not be estimated accurately for non-unit transformations. michael@0: cairo_matrix_t identityMatrix; michael@0: cairo_matrix_init_identity(&identityMatrix); michael@0: michael@0: // Font options are set explicitly here to improve cairo's caching michael@0: // behavior and to record the relevant parts of the pattern for michael@0: // SetupCairoFont (so that the pattern can be released). michael@0: // michael@0: // Most font_options have already been set as defaults on the FcPattern michael@0: // with cairo_ft_font_options_substitute(), then user and system michael@0: // fontconfig configurations were applied. The resulting font_options michael@0: // have been recorded on the face during michael@0: // cairo_ft_font_face_create_for_pattern(). michael@0: // michael@0: // None of the settings here cause this scaled_font to behave any michael@0: // differently from how it would behave if it were created from the same michael@0: // face with default font_options. michael@0: // michael@0: // We set options explicitly so that the same scaled_font will be found in michael@0: // the cairo_scaled_font_map when cairo loads glyphs from a context with michael@0: // the same font_face, font_matrix, ctm, and surface font_options. michael@0: // michael@0: // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the michael@0: // font_options on the cairo_ft_font_face, and doesn't consider default michael@0: // option values to not match any explicit values. michael@0: // michael@0: // Even after cairo_set_scaled_font is used to set font_options for the michael@0: // cairo context, when cairo looks for a scaled_font for the context, it michael@0: // will look for a font with some option values from the target surface if michael@0: // any values are left default on the context font_options. If this michael@0: // scaled_font is created with default font_options, cairo will not find michael@0: // it. michael@0: cairo_font_options_t *fontOptions = cairo_font_options_create(); michael@0: michael@0: // The one option not recorded in the pattern is hint_metrics, which will michael@0: // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON. michael@0: // We should be considering the font_options of the surface on which this michael@0: // font will be used, but currently we don't have different gfxFonts for michael@0: // different surface font_options, so we'll create a font suitable for the michael@0: // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON. michael@0: #ifdef MOZ_GFX_OPTIMIZE_MOBILE michael@0: cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF); michael@0: #else michael@0: if (printing) { michael@0: cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF); michael@0: } else { michael@0: cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_ON); michael@0: } michael@0: #endif michael@0: michael@0: // The remaining options have been recorded on the pattern and the face. michael@0: // _cairo_ft_options_merge has some logic to decide which options from the michael@0: // scaled_font or from the cairo_ft_font_face take priority in the way the michael@0: // font behaves. michael@0: // michael@0: // In the majority of cases, _cairo_ft_options_merge uses the options from michael@0: // the cairo_ft_font_face, so sometimes it is not so important which michael@0: // values are set here so long as they are not defaults, but we'll set michael@0: // them to the exact values that we expect from the font, to be consistent michael@0: // and to protect against changes in cairo. michael@0: // michael@0: // In some cases, _cairo_ft_options_merge uses some options from the michael@0: // scaled_font's font_options rather than options on the michael@0: // cairo_ft_font_face (from fontconfig). michael@0: // https://bugs.freedesktop.org/show_bug.cgi?id=11838 michael@0: // michael@0: // Surface font options were set on the pattern in michael@0: // cairo_ft_font_options_substitute. If fontconfig has changed the michael@0: // hint_style then that is what the user (or distribution) wants, so we michael@0: // use the setting from the FcPattern. michael@0: // michael@0: // Fallback values here mirror treatment of defaults in cairo-ft-font.c. michael@0: FcBool hinting = FcFalse; michael@0: #ifndef MOZ_GFX_OPTIMIZE_MOBILE michael@0: if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) { michael@0: hinting = FcTrue; michael@0: } michael@0: #endif michael@0: cairo_hint_style_t hint_style; michael@0: if (printing || !hinting) { michael@0: hint_style = CAIRO_HINT_STYLE_NONE; michael@0: } else { michael@0: #ifdef FC_HINT_STYLE // FC_HINT_STYLE is available from fontconfig 2.2.91. michael@0: int fc_hintstyle; michael@0: if (FcPatternGetInteger(aPattern, FC_HINT_STYLE, michael@0: 0, &fc_hintstyle ) != FcResultMatch) { michael@0: fc_hintstyle = FC_HINT_FULL; michael@0: } michael@0: switch (fc_hintstyle) { michael@0: case FC_HINT_NONE: michael@0: hint_style = CAIRO_HINT_STYLE_NONE; michael@0: break; michael@0: case FC_HINT_SLIGHT: michael@0: hint_style = CAIRO_HINT_STYLE_SLIGHT; michael@0: break; michael@0: case FC_HINT_MEDIUM: michael@0: default: // This fallback mirrors _get_pattern_ft_options in cairo. michael@0: hint_style = CAIRO_HINT_STYLE_MEDIUM; michael@0: break; michael@0: case FC_HINT_FULL: michael@0: hint_style = CAIRO_HINT_STYLE_FULL; michael@0: break; michael@0: } michael@0: #else // no FC_HINT_STYLE michael@0: hint_style = CAIRO_HINT_STYLE_FULL; michael@0: #endif michael@0: } michael@0: cairo_font_options_set_hint_style(fontOptions, hint_style); michael@0: michael@0: int rgba; michael@0: if (FcPatternGetInteger(aPattern, michael@0: FC_RGBA, 0, &rgba) != FcResultMatch) { michael@0: rgba = FC_RGBA_UNKNOWN; michael@0: } michael@0: cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; michael@0: switch (rgba) { michael@0: case FC_RGBA_UNKNOWN: michael@0: case FC_RGBA_NONE: michael@0: default: michael@0: // There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing michael@0: // is disabled through cairo_antialias_t. michael@0: rgba = FC_RGBA_NONE; michael@0: // subpixel_order won't be used by the font as we won't use michael@0: // CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for michael@0: // caching reasons described above. Fall through: michael@0: case FC_RGBA_RGB: michael@0: subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; michael@0: break; michael@0: case FC_RGBA_BGR: michael@0: subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; michael@0: break; michael@0: case FC_RGBA_VRGB: michael@0: subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; michael@0: break; michael@0: case FC_RGBA_VBGR: michael@0: subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; michael@0: break; michael@0: } michael@0: cairo_font_options_set_subpixel_order(fontOptions, subpixel_order); michael@0: michael@0: FcBool fc_antialias; michael@0: if (FcPatternGetBool(aPattern, michael@0: FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) { michael@0: fc_antialias = FcTrue; michael@0: } michael@0: cairo_antialias_t antialias; michael@0: if (!fc_antialias) { michael@0: antialias = CAIRO_ANTIALIAS_NONE; michael@0: } else if (rgba == FC_RGBA_NONE) { michael@0: antialias = CAIRO_ANTIALIAS_GRAY; michael@0: } else { michael@0: antialias = CAIRO_ANTIALIAS_SUBPIXEL; michael@0: } michael@0: cairo_font_options_set_antialias(fontOptions, antialias); michael@0: michael@0: cairo_scaled_font_t *scaledFont = michael@0: cairo_scaled_font_create(aFace, &fontMatrix, &identityMatrix, michael@0: fontOptions); michael@0: michael@0: cairo_font_options_destroy(fontOptions); michael@0: michael@0: NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS, michael@0: "Failed to create scaled font"); michael@0: return scaledFont; michael@0: } michael@0: michael@0: /* static */ michael@0: PangoLanguage * michael@0: GuessPangoLanguage(nsIAtom *aLanguage) michael@0: { michael@0: if (!aLanguage) michael@0: return nullptr; michael@0: michael@0: // Pango and fontconfig won't understand mozilla's internal langGroups, so michael@0: // find a real language. michael@0: nsAutoCString lang; michael@0: gfxFontconfigUtils::GetSampleLangForGroup(aLanguage, &lang); michael@0: if (lang.IsEmpty()) michael@0: return nullptr; michael@0: michael@0: return pango_language_from_string(lang.get()); michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: /*************************************************************************** michael@0: * michael@0: * This function must be last in the file because it uses the system cairo michael@0: * library. Above this point the cairo library used is the tree cairo if michael@0: * MOZ_TREE_CAIRO. michael@0: */ michael@0: michael@0: #if MOZ_TREE_CAIRO michael@0: // Tree cairo symbols have different names. Disable their activation through michael@0: // preprocessor macros. michael@0: #undef cairo_ft_font_options_substitute michael@0: michael@0: // The system cairo functions are not declared because the include paths cause michael@0: // the gdk headers to pick up the tree cairo.h. michael@0: extern "C" { michael@0: NS_VISIBILITY_DEFAULT void michael@0: cairo_ft_font_options_substitute (const cairo_font_options_t *options, michael@0: FcPattern *pattern); michael@0: } michael@0: #endif michael@0: michael@0: static void michael@0: ApplyGdkScreenFontOptions(FcPattern *aPattern) michael@0: { michael@0: const cairo_font_options_t *options = michael@0: gdk_screen_get_font_options(gdk_screen_get_default()); michael@0: michael@0: cairo_ft_font_options_substitute(options, aPattern); michael@0: } michael@0: michael@0: #endif // MOZ_WIDGET_GTK2 michael@0: michael@0: #ifdef USE_SKIA michael@0: mozilla::TemporaryRef michael@0: gfxFcFont::GetGlyphRenderingOptions() michael@0: { michael@0: cairo_scaled_font_t *scaled_font = CairoScaledFont(); michael@0: cairo_font_options_t *options = cairo_font_options_create(); michael@0: cairo_scaled_font_get_font_options(scaled_font, options); michael@0: cairo_hint_style_t hint_style = cairo_font_options_get_hint_style(options); michael@0: cairo_font_options_destroy(options); michael@0: michael@0: mozilla::gfx::FontHinting hinting; michael@0: michael@0: switch (hint_style) { michael@0: case CAIRO_HINT_STYLE_NONE: michael@0: hinting = mozilla::gfx::FontHinting::NONE; michael@0: break; michael@0: case CAIRO_HINT_STYLE_SLIGHT: michael@0: hinting = mozilla::gfx::FontHinting::LIGHT; michael@0: break; michael@0: case CAIRO_HINT_STYLE_FULL: michael@0: hinting = mozilla::gfx::FontHinting::FULL; michael@0: break; michael@0: default: michael@0: hinting = mozilla::gfx::FontHinting::NORMAL; michael@0: break; michael@0: } michael@0: michael@0: // We don't want to force the use of the autohinter over the font's built in hints michael@0: return mozilla::gfx::Factory::CreateCairoGlyphRenderingOptions(hinting, false); michael@0: } michael@0: #endif michael@0: