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: #ifndef GFX_FONT_H michael@0: #define GFX_FONT_H michael@0: michael@0: #include "gfxTypes.h" michael@0: #include "nsString.h" michael@0: #include "gfxPoint.h" michael@0: #include "gfxFontUtils.h" michael@0: #include "nsTArray.h" michael@0: #include "nsTHashtable.h" michael@0: #include "nsHashKeys.h" michael@0: #include "gfxSkipChars.h" michael@0: #include "gfxRect.h" michael@0: #include "nsExpirationTracker.h" michael@0: #include "gfxPlatform.h" michael@0: #include "nsIAtom.h" michael@0: #include "mozilla/HashFunctions.h" michael@0: #include "nsIMemoryReporter.h" michael@0: #include "nsIObserver.h" michael@0: #include "gfxFontFeatures.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include michael@0: #include "DrawMode.h" michael@0: #include "nsUnicodeScriptCodes.h" michael@0: #include "nsDataHashtable.h" michael@0: #include "harfbuzz/hb.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: michael@0: typedef struct _cairo_scaled_font cairo_scaled_font_t; michael@0: typedef struct gr_face gr_face; michael@0: michael@0: #ifdef DEBUG michael@0: #include michael@0: #endif michael@0: michael@0: class gfxContext; michael@0: class gfxTextRun; michael@0: class gfxFont; michael@0: class gfxFontFamily; michael@0: class gfxFontGroup; michael@0: class gfxGraphiteShaper; michael@0: class gfxHarfBuzzShaper; michael@0: class gfxUserFontSet; michael@0: class gfxUserFontData; michael@0: class gfxShapedText; michael@0: class gfxShapedWord; michael@0: class gfxSVGGlyphs; michael@0: class gfxMathTable; michael@0: class gfxTextContextPaint; michael@0: class FontInfoData; michael@0: michael@0: class nsILanguageAtomService; michael@0: michael@0: #define FONT_MAX_SIZE 2000.0 michael@0: michael@0: #define NO_FONT_LANGUAGE_OVERRIDE 0 michael@0: michael@0: struct FontListSizes; michael@0: struct gfxTextRunDrawCallbacks; michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: class GlyphRenderingOptions; michael@0: } michael@0: } michael@0: michael@0: struct gfxFontStyle { michael@0: gfxFontStyle(); michael@0: gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch, michael@0: gfxFloat aSize, nsIAtom *aLanguage, michael@0: float aSizeAdjust, bool aSystemFont, michael@0: bool aPrinterFont, michael@0: const nsString& aLanguageOverride); michael@0: gfxFontStyle(const gfxFontStyle& aStyle); michael@0: michael@0: // the language (may be an internal langGroup code rather than an actual michael@0: // language code) specified in the document or element's lang property, michael@0: // or inferred from the charset michael@0: nsRefPtr language; michael@0: michael@0: // Features are composed of (1) features from style rules (2) features michael@0: // from feature setttings rules and (3) family-specific features. (1) and michael@0: // (3) are guaranteed to be mutually exclusive michael@0: michael@0: // custom opentype feature settings michael@0: nsTArray featureSettings; michael@0: michael@0: // Some font-variant property values require font-specific settings michael@0: // defined via @font-feature-values rules. These are resolved after michael@0: // font matching occurs. michael@0: michael@0: // -- list of value tags for specific alternate features michael@0: nsTArray alternateValues; michael@0: michael@0: // -- object used to look these up once the font is matched michael@0: nsRefPtr featureValueLookup; michael@0: michael@0: // The logical size of the font, in pixels michael@0: gfxFloat size; michael@0: michael@0: // The aspect-value (ie., the ratio actualsize:actualxheight) that any michael@0: // actual physical font created from this font structure must have when michael@0: // rendering or measuring a string. A value of 0 means no adjustment michael@0: // needs to be done. michael@0: float sizeAdjust; michael@0: michael@0: // Language system tag, to override document language; michael@0: // an OpenType "language system" tag represented as a 32-bit integer michael@0: // (see http://www.microsoft.com/typography/otspec/languagetags.htm). michael@0: // Normally 0, so font rendering will use the document or element language michael@0: // (see above) to control any language-specific rendering, but the author michael@0: // can override this for cases where the options implemented in the font michael@0: // do not directly match the actual language. (E.g. lang may be Macedonian, michael@0: // but the font in use does not explicitly support this; the author can michael@0: // use font-language-override to request the Serbian option in the font michael@0: // in order to get correct glyph shapes.) michael@0: uint32_t languageOverride; michael@0: michael@0: // The weight of the font: 100, 200, ... 900. michael@0: uint16_t weight; michael@0: michael@0: // The stretch of the font (the sum of various NS_FONT_STRETCH_* michael@0: // constants; see gfxFontConstants.h). michael@0: int8_t stretch; michael@0: michael@0: // Say that this font is a system font and therefore does not michael@0: // require certain fixup that we do for fonts from untrusted michael@0: // sources. michael@0: bool systemFont : 1; michael@0: michael@0: // Say that this font is used for print or print preview. michael@0: bool printerFont : 1; michael@0: michael@0: // Used to imitate -webkit-font-smoothing: antialiased michael@0: bool useGrayscaleAntialiasing : 1; michael@0: michael@0: // The style of font (normal, italic, oblique) michael@0: uint8_t style : 2; michael@0: michael@0: // Return the final adjusted font size for the given aspect ratio. michael@0: // Not meant to be called when sizeAdjust = 0. michael@0: gfxFloat GetAdjustedSize(gfxFloat aspect) const { michael@0: NS_ASSERTION(sizeAdjust != 0.0, "Not meant to be called when sizeAdjust = 0"); michael@0: gfxFloat adjustedSize = std::max(NS_round(size*(sizeAdjust/aspect)), 1.0); michael@0: return std::min(adjustedSize, FONT_MAX_SIZE); michael@0: } michael@0: michael@0: PLDHashNumber Hash() const { michael@0: return ((style + (systemFont << 7) + michael@0: (weight << 8)) + uint32_t(size*1000) + uint32_t(sizeAdjust*1000)) ^ michael@0: nsISupportsHashKey::HashKey(language); michael@0: } michael@0: michael@0: int8_t ComputeWeight() const; michael@0: michael@0: bool Equals(const gfxFontStyle& other) const { michael@0: return michael@0: (*reinterpret_cast(&size) == michael@0: *reinterpret_cast(&other.size)) && michael@0: (style == other.style) && michael@0: (systemFont == other.systemFont) && michael@0: (printerFont == other.printerFont) && michael@0: (useGrayscaleAntialiasing == other.useGrayscaleAntialiasing) && michael@0: (weight == other.weight) && michael@0: (stretch == other.stretch) && michael@0: (language == other.language) && michael@0: (*reinterpret_cast(&sizeAdjust) == michael@0: *reinterpret_cast(&other.sizeAdjust)) && michael@0: (featureSettings == other.featureSettings) && michael@0: (languageOverride == other.languageOverride) && michael@0: (alternateValues == other.alternateValues) && michael@0: (featureValueLookup == other.featureValueLookup); michael@0: } michael@0: michael@0: static void ParseFontFeatureSettings(const nsString& aFeatureString, michael@0: nsTArray& aFeatures); michael@0: michael@0: static uint32_t ParseFontLanguageOverride(const nsString& aLangTag); michael@0: }; michael@0: michael@0: class gfxCharacterMap : public gfxSparseBitSet { michael@0: public: michael@0: nsrefcnt AddRef() { michael@0: NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt"); michael@0: ++mRefCnt; michael@0: NS_LOG_ADDREF(this, mRefCnt, "gfxCharacterMap", sizeof(*this)); michael@0: return mRefCnt; michael@0: } michael@0: michael@0: nsrefcnt Release() { michael@0: NS_PRECONDITION(0 != mRefCnt, "dup release"); michael@0: --mRefCnt; michael@0: NS_LOG_RELEASE(this, mRefCnt, "gfxCharacterMap"); michael@0: if (mRefCnt == 0) { michael@0: NotifyReleased(); michael@0: // |this| has been deleted. michael@0: return 0; michael@0: } michael@0: return mRefCnt; michael@0: } michael@0: michael@0: gfxCharacterMap() : michael@0: mHash(0), mBuildOnTheFly(false), mShared(false) michael@0: { } michael@0: michael@0: void CalcHash() { mHash = GetChecksum(); } michael@0: michael@0: size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { michael@0: return gfxSparseBitSet::SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: // hash of the cmap bitvector michael@0: uint32_t mHash; michael@0: michael@0: // if cmap is built on the fly it's never shared michael@0: bool mBuildOnTheFly; michael@0: michael@0: // cmap is shared globally michael@0: bool mShared; michael@0: michael@0: protected: michael@0: void NotifyReleased(); michael@0: michael@0: nsAutoRefCnt mRefCnt; michael@0: michael@0: private: michael@0: gfxCharacterMap(const gfxCharacterMap&); michael@0: gfxCharacterMap& operator=(const gfxCharacterMap&); michael@0: }; michael@0: michael@0: class gfxFontEntry { michael@0: public: michael@0: NS_INLINE_DECL_REFCOUNTING(gfxFontEntry) michael@0: michael@0: gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false); michael@0: michael@0: // unique name for the face, *not* the family; not necessarily the michael@0: // "real" or user-friendly name, may be an internal identifier michael@0: const nsString& Name() const { return mName; } michael@0: michael@0: // family name michael@0: const nsString& FamilyName() const { return mFamilyName; } michael@0: michael@0: // The following two methods may be relatively expensive, as they michael@0: // will (usually, except on Linux) load and parse the 'name' table; michael@0: // they are intended only for the font-inspection API, not for michael@0: // perf-critical layout/drawing work. michael@0: michael@0: // The "real" name of the face, if available from the font resource; michael@0: // returns Name() if nothing better is available. michael@0: virtual nsString RealFaceName(); michael@0: michael@0: uint16_t Weight() const { return mWeight; } michael@0: int16_t Stretch() const { return mStretch; } michael@0: michael@0: bool IsUserFont() const { return mIsUserFont; } michael@0: bool IsLocalUserFont() const { return mIsLocalUserFont; } michael@0: bool IsFixedPitch() const { return mFixedPitch; } michael@0: bool IsItalic() const { return mItalic; } michael@0: bool IsBold() const { return mWeight >= 600; } // bold == weights 600 and above michael@0: bool IgnoreGDEF() const { return mIgnoreGDEF; } michael@0: bool IgnoreGSUB() const { return mIgnoreGSUB; } michael@0: michael@0: virtual bool IsSymbolFont(); michael@0: michael@0: virtual bool HasFontTable(uint32_t aTableTag); michael@0: michael@0: inline bool HasGraphiteTables() { michael@0: if (!mCheckedForGraphiteTables) { michael@0: CheckForGraphiteTables(); michael@0: mCheckedForGraphiteTables = true; michael@0: } michael@0: return mHasGraphiteTables; michael@0: } michael@0: michael@0: inline bool HasCmapTable() { michael@0: if (!mCharacterMap) { michael@0: ReadCMAP(); michael@0: NS_ASSERTION(mCharacterMap, "failed to initialize character map"); michael@0: } michael@0: return mHasCmapTable; michael@0: } michael@0: michael@0: inline bool HasCharacter(uint32_t ch) { michael@0: if (mCharacterMap && mCharacterMap->test(ch)) { michael@0: return true; michael@0: } michael@0: return TestCharacterMap(ch); michael@0: } michael@0: michael@0: virtual bool SkipDuringSystemFallback() { return false; } michael@0: virtual bool TestCharacterMap(uint32_t aCh); michael@0: nsresult InitializeUVSMap(); michael@0: uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS); michael@0: michael@0: // All concrete gfxFontEntry subclasses (except gfxProxyFontEntry) need michael@0: // to override this, otherwise the font will never be used as it will michael@0: // be considered to support no characters. michael@0: // ReadCMAP() must *always* set the mCharacterMap pointer to a valid michael@0: // gfxCharacterMap, even if empty, as other code assumes this pointer michael@0: // can be safely dereferenced. michael@0: virtual nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr); michael@0: michael@0: bool TryGetSVGData(gfxFont* aFont); michael@0: bool HasSVGGlyph(uint32_t aGlyphId); michael@0: bool GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId, michael@0: gfxRect *aResult); michael@0: bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode, michael@0: gfxTextContextPaint *aContextPaint); michael@0: // Call this when glyph geometry or rendering has changed michael@0: // (e.g. animated SVG glyphs) michael@0: void NotifyGlyphsChanged(); michael@0: michael@0: enum MathConstant { michael@0: // The order of the constants must match the order of the fields michael@0: // defined in the MATH table. michael@0: ScriptPercentScaleDown, michael@0: ScriptScriptPercentScaleDown, michael@0: DelimitedSubFormulaMinHeight, michael@0: DisplayOperatorMinHeight, michael@0: MathLeading, michael@0: AxisHeight, michael@0: AccentBaseHeight, michael@0: FlattenedAccentBaseHeight, michael@0: SubscriptShiftDown, michael@0: SubscriptTopMax, michael@0: SubscriptBaselineDropMin, michael@0: SuperscriptShiftUp, michael@0: SuperscriptShiftUpCramped, michael@0: SuperscriptBottomMin, michael@0: SuperscriptBaselineDropMax, michael@0: SubSuperscriptGapMin, michael@0: SuperscriptBottomMaxWithSubscript, michael@0: SpaceAfterScript, michael@0: UpperLimitGapMin, michael@0: UpperLimitBaselineRiseMin, michael@0: LowerLimitGapMin, michael@0: LowerLimitBaselineDropMin, michael@0: StackTopShiftUp, michael@0: StackTopDisplayStyleShiftUp, michael@0: StackBottomShiftDown, michael@0: StackBottomDisplayStyleShiftDown, michael@0: StackGapMin, michael@0: StackDisplayStyleGapMin, michael@0: StretchStackTopShiftUp, michael@0: StretchStackBottomShiftDown, michael@0: StretchStackGapAboveMin, michael@0: StretchStackGapBelowMin, michael@0: FractionNumeratorShiftUp, michael@0: FractionNumeratorDisplayStyleShiftUp, michael@0: FractionDenominatorShiftDown, michael@0: FractionDenominatorDisplayStyleShiftDown, michael@0: FractionNumeratorGapMin, michael@0: FractionNumDisplayStyleGapMin, michael@0: FractionRuleThickness, michael@0: FractionDenominatorGapMin, michael@0: FractionDenomDisplayStyleGapMin, michael@0: SkewedFractionHorizontalGap, michael@0: SkewedFractionVerticalGap, michael@0: OverbarVerticalGap, michael@0: OverbarRuleThickness, michael@0: OverbarExtraAscender, michael@0: UnderbarVerticalGap, michael@0: UnderbarRuleThickness, michael@0: UnderbarExtraDescender, michael@0: RadicalVerticalGap, michael@0: RadicalDisplayStyleVerticalGap, michael@0: RadicalRuleThickness, michael@0: RadicalExtraAscender, michael@0: RadicalKernBeforeDegree, michael@0: RadicalKernAfterDegree, michael@0: RadicalDegreeBottomRaisePercent michael@0: }; michael@0: michael@0: // Call TryGetMathTable to try to load the Open Type MATH table. The other michael@0: // functions forward the call to the gfxMathTable class. The GetMath...() michael@0: // functions MUST NOT be called unless TryGetMathTable() has returned true. michael@0: bool TryGetMathTable(gfxFont* aFont); michael@0: gfxFloat GetMathConstant(MathConstant aConstant); michael@0: bool GetMathItalicsCorrection(uint32_t aGlyphID, michael@0: gfxFloat* aItalicCorrection); michael@0: uint32_t GetMathVariantsSize(uint32_t aGlyphID, bool aVertical, michael@0: uint16_t aSize); michael@0: bool GetMathVariantsParts(uint32_t aGlyphID, bool aVertical, michael@0: uint32_t aGlyphs[4]); michael@0: michael@0: virtual bool MatchesGenericFamily(const nsACString& aGeneric) const { michael@0: return true; michael@0: } michael@0: virtual bool SupportsLangGroup(nsIAtom *aLangGroup) const { michael@0: return true; michael@0: } michael@0: michael@0: // Access to raw font table data (needed for Harfbuzz): michael@0: // returns a pointer to data owned by the fontEntry or the OS, michael@0: // which will remain valid until the blob is destroyed. michael@0: // The data MUST be treated as read-only; we may be getting a michael@0: // reference to a shared system font cache. michael@0: // michael@0: // The default implementation uses CopyFontTable to get the data michael@0: // into a byte array, and maintains a cache of loaded tables. michael@0: // michael@0: // Subclasses should override this if they can provide more efficient michael@0: // access than copying table data into our own buffers. michael@0: // michael@0: // Get blob that encapsulates a specific font table, or nullptr if michael@0: // the table doesn't exist in the font. michael@0: // michael@0: // Caller is responsible to call hb_blob_destroy() on the returned blob michael@0: // (if non-nullptr) when no longer required. For transient access to a michael@0: // table, use of AutoTable (below) is generally preferred. michael@0: virtual hb_blob_t *GetFontTable(uint32_t aTag); michael@0: michael@0: // Stack-based utility to return a specified table, automatically releasing michael@0: // the blob when the AutoTable goes out of scope. michael@0: class AutoTable { michael@0: public: michael@0: AutoTable(gfxFontEntry* aFontEntry, uint32_t aTag) michael@0: { michael@0: mBlob = aFontEntry->GetFontTable(aTag); michael@0: } michael@0: ~AutoTable() { michael@0: if (mBlob) { michael@0: hb_blob_destroy(mBlob); michael@0: } michael@0: } michael@0: operator hb_blob_t*() const { return mBlob; } michael@0: private: michael@0: hb_blob_t* mBlob; michael@0: // not implemented: michael@0: AutoTable(const AutoTable&) MOZ_DELETE; michael@0: AutoTable& operator=(const AutoTable&) MOZ_DELETE; michael@0: }; michael@0: michael@0: already_AddRefed FindOrMakeFont(const gfxFontStyle *aStyle, michael@0: bool aNeedsBold); michael@0: michael@0: // Get an existing font table cache entry in aBlob if it has been michael@0: // registered, or return false if not. Callers must call michael@0: // hb_blob_destroy on aBlob if true is returned. michael@0: // michael@0: // Note that some gfxFont implementations may not call this at all, michael@0: // if it is more efficient to get the table from the OS at that level. michael@0: bool GetExistingFontTable(uint32_t aTag, hb_blob_t** aBlob); michael@0: michael@0: // Elements of aTable are transferred (not copied) to and returned in a michael@0: // new hb_blob_t which is registered on the gfxFontEntry, but the initial michael@0: // reference is owned by the caller. Removing the last reference michael@0: // unregisters the table from the font entry. michael@0: // michael@0: // Pass nullptr for aBuffer to indicate that the table is not present and michael@0: // nullptr will be returned. Also returns nullptr on OOM. michael@0: hb_blob_t *ShareFontTableAndGetBlob(uint32_t aTag, michael@0: FallibleTArray* aTable); michael@0: michael@0: // Get the font's unitsPerEm from the 'head' table, in the case of an michael@0: // sfnt resource. Will return kInvalidUPEM for non-sfnt fonts, michael@0: // if present on the platform. michael@0: uint16_t UnitsPerEm(); michael@0: enum { michael@0: kMinUPEM = 16, // Limits on valid unitsPerEm range, from the michael@0: kMaxUPEM = 16384, // OpenType spec michael@0: kInvalidUPEM = uint16_t(-1) michael@0: }; michael@0: michael@0: // Shaper face accessors: michael@0: // NOTE that harfbuzz and graphite handle ownership/lifetime of the face michael@0: // object in completely different ways. michael@0: michael@0: // Get HarfBuzz face corresponding to this font file. michael@0: // Caller must release with hb_face_destroy() when finished with it, michael@0: // and the font entry will be notified via ForgetHBFace. michael@0: hb_face_t* GetHBFace(); michael@0: virtual void ForgetHBFace(); michael@0: michael@0: // Get Graphite face corresponding to this font file. michael@0: // Caller must call gfxFontEntry::ReleaseGrFace when finished with it. michael@0: gr_face* GetGrFace(); michael@0: virtual void ReleaseGrFace(gr_face* aFace); michael@0: michael@0: // Release any SVG-glyphs document this font may have loaded. michael@0: void DisconnectSVG(); michael@0: michael@0: // Called to notify that aFont is being destroyed. Needed when we're tracking michael@0: // the fonts belonging to this font entry. michael@0: void NotifyFontDestroyed(gfxFont* aFont); michael@0: michael@0: // For memory reporting michael@0: virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const; michael@0: virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const; michael@0: michael@0: // Used when checking for complex script support, to mask off cmap ranges michael@0: struct ScriptRange { michael@0: uint32_t rangeStart; michael@0: uint32_t rangeEnd; michael@0: hb_tag_t tags[3]; // one or two OpenType script tags to check, michael@0: // plus a NULL terminator michael@0: }; michael@0: michael@0: bool SupportsScriptInGSUB(const hb_tag_t* aScriptTags); michael@0: michael@0: nsString mName; michael@0: nsString mFamilyName; michael@0: michael@0: bool mItalic : 1; michael@0: bool mFixedPitch : 1; michael@0: bool mIsProxy : 1; michael@0: bool mIsValid : 1; michael@0: bool mIsBadUnderlineFont : 1; michael@0: bool mIsUserFont : 1; michael@0: bool mIsLocalUserFont : 1; michael@0: bool mStandardFace : 1; michael@0: bool mSymbolFont : 1; michael@0: bool mIgnoreGDEF : 1; michael@0: bool mIgnoreGSUB : 1; michael@0: bool mSVGInitialized : 1; michael@0: bool mMathInitialized : 1; michael@0: bool mHasSpaceFeaturesInitialized : 1; michael@0: bool mHasSpaceFeatures : 1; michael@0: bool mHasSpaceFeaturesKerning : 1; michael@0: bool mHasSpaceFeaturesNonKerning : 1; michael@0: bool mSkipDefaultFeatureSpaceCheck : 1; michael@0: bool mHasGraphiteTables : 1; michael@0: bool mCheckedForGraphiteTables : 1; michael@0: bool mHasCmapTable : 1; michael@0: bool mGrFaceInitialized : 1; michael@0: michael@0: // bitvector of substitution space features per script, one each michael@0: // for default and non-default features michael@0: uint32_t mDefaultSubSpaceFeatures[(MOZ_NUM_SCRIPT_CODES + 31) / 32]; michael@0: uint32_t mNonDefaultSubSpaceFeatures[(MOZ_NUM_SCRIPT_CODES + 31) / 32]; michael@0: michael@0: uint16_t mWeight; michael@0: int16_t mStretch; michael@0: michael@0: nsRefPtr mCharacterMap; michael@0: uint32_t mUVSOffset; michael@0: nsAutoArrayPtr mUVSData; michael@0: nsAutoPtr mUserFontData; michael@0: nsAutoPtr mSVGGlyphs; michael@0: // list of gfxFonts that are using SVG glyphs michael@0: nsTArray mFontsUsingSVGGlyphs; michael@0: nsAutoPtr mMathTable; michael@0: nsTArray mFeatureSettings; michael@0: uint32_t mLanguageOverride; michael@0: michael@0: protected: michael@0: friend class gfxPlatformFontList; michael@0: friend class gfxMacPlatformFontList; michael@0: friend class gfxUserFcFontEntry; michael@0: friend class gfxFontFamily; michael@0: friend class gfxSingleFaceMacFontFamily; michael@0: michael@0: gfxFontEntry(); michael@0: michael@0: // Protected destructor, to discourage deletion outside of Release(): michael@0: virtual ~gfxFontEntry(); michael@0: michael@0: virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) { michael@0: NS_NOTREACHED("oops, somebody didn't override CreateFontInstance"); michael@0: return nullptr; michael@0: } michael@0: michael@0: virtual void CheckForGraphiteTables(); michael@0: michael@0: // Copy a font table into aBuffer. michael@0: // The caller will be responsible for ownership of the data. michael@0: virtual nsresult CopyFontTable(uint32_t aTableTag, michael@0: FallibleTArray& aBuffer) { michael@0: NS_NOTREACHED("forgot to override either GetFontTable or CopyFontTable?"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Return a blob that wraps a table found within a buffer of font data. michael@0: // The blob does NOT own its data; caller guarantees that the buffer michael@0: // will remain valid at least as long as the blob. michael@0: // Returns null if the specified table is not found. michael@0: // This method assumes aFontData is valid 'sfnt' data; before using this, michael@0: // caller is responsible to do any sanitization/validation necessary. michael@0: hb_blob_t* GetTableFromFontData(const void* aFontData, uint32_t aTableTag); michael@0: michael@0: // lookup the cmap in cached font data michael@0: virtual already_AddRefed michael@0: GetCMAPFromFontInfo(FontInfoData *aFontInfoData, michael@0: uint32_t& aUVSOffset, michael@0: bool& aSymbolFont); michael@0: michael@0: // Font's unitsPerEm from the 'head' table, if available (will be set to michael@0: // kInvalidUPEM for non-sfnt font formats) michael@0: uint16_t mUnitsPerEm; michael@0: michael@0: // Shaper-specific face objects, shared by all instantiations of the same michael@0: // physical font, regardless of size. michael@0: // Usually, only one of these will actually be created for any given font michael@0: // entry, depending on the font tables that are present. michael@0: michael@0: // hb_face_t is refcounted internally, so each shaper that's using it will michael@0: // bump the ref count when it acquires the face, and "destroy" (release) it michael@0: // in its destructor. The font entry has only this non-owning reference to michael@0: // the face; when the face is deleted, it will tell the font entry to forget michael@0: // it, so that a new face will be created next time it is needed. michael@0: hb_face_t* mHBFace; michael@0: michael@0: static hb_blob_t* HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData); michael@0: michael@0: // Callback that the hb_face will use to tell us when it is being deleted. michael@0: static void HBFaceDeletedCallback(void *aUserData); michael@0: michael@0: // gr_face is -not- refcounted, so it will be owned directly by the font michael@0: // entry, and we'll keep a count of how many references we've handed out; michael@0: // each shaper is responsible to call ReleaseGrFace on its entry when michael@0: // finished with it, so that we know when it can be deleted. michael@0: gr_face* mGrFace; michael@0: michael@0: // hashtable to map raw table data ptr back to its owning blob, for use by michael@0: // graphite table-release callback michael@0: nsDataHashtable,void*>* mGrTableMap; michael@0: michael@0: // number of current users of this entry's mGrFace michael@0: nsrefcnt mGrFaceRefCnt; michael@0: michael@0: static const void* GrGetTable(const void *aAppFaceHandle, michael@0: unsigned int aName, michael@0: size_t *aLen); michael@0: static void GrReleaseTable(const void *aAppFaceHandle, michael@0: const void *aTableBuffer); michael@0: michael@0: private: michael@0: /** michael@0: * Font table hashtable, to support GetFontTable for harfbuzz. michael@0: * michael@0: * The harfbuzz shaper (and potentially other clients) needs access to raw michael@0: * font table data. This needs to be cached so that it can be used michael@0: * repeatedly (each time we construct a text run; in some cases, for michael@0: * each character/glyph within the run) without re-fetching large tables michael@0: * every time. michael@0: * michael@0: * Because we may instantiate many gfxFonts for the same physical font michael@0: * file (at different sizes), we should ensure that they can share a michael@0: * single cached copy of the font tables. To do this, we implement table michael@0: * access and sharing on the fontEntry rather than the font itself. michael@0: * michael@0: * The default implementation uses GetFontTable() to read font table michael@0: * data into byte arrays, and wraps them in blobs which are registered in michael@0: * a hashtable. The hashtable can then return pre-existing blobs to michael@0: * harfbuzz. michael@0: * michael@0: * Harfbuzz will "destroy" the blobs when it is finished with them. When michael@0: * the last blob reference is removed, the FontTableBlobData user data michael@0: * will remove the blob from the hashtable if still registered. michael@0: */ michael@0: michael@0: class FontTableBlobData; michael@0: michael@0: /** michael@0: * FontTableHashEntry manages the entries of hb_blob_t's containing font michael@0: * table data. michael@0: * michael@0: * This is used to share font tables across fonts with the same michael@0: * font entry (but different sizes) for use by HarfBuzz. The hashtable michael@0: * does not own a strong reference to the blob, but keeps a weak pointer, michael@0: * managed by FontTableBlobData. Similarly FontTableBlobData keeps only a michael@0: * weak pointer to the hashtable, managed by FontTableHashEntry. michael@0: */ michael@0: michael@0: class FontTableHashEntry : public nsUint32HashKey michael@0: { michael@0: public: michael@0: // Declarations for nsTHashtable michael@0: michael@0: typedef nsUint32HashKey KeyClass; michael@0: typedef KeyClass::KeyType KeyType; michael@0: typedef KeyClass::KeyTypePointer KeyTypePointer; michael@0: michael@0: FontTableHashEntry(KeyTypePointer aTag) michael@0: : KeyClass(aTag) michael@0: , mSharedBlobData(nullptr) michael@0: , mBlob(nullptr) michael@0: { } michael@0: michael@0: // NOTE: This assumes the new entry belongs to the same hashtable as michael@0: // the old, because the mHashtable pointer in mSharedBlobData (if michael@0: // present) will not be updated. michael@0: FontTableHashEntry(FontTableHashEntry&& toMove) michael@0: : KeyClass(mozilla::Move(toMove)) michael@0: , mSharedBlobData(mozilla::Move(toMove.mSharedBlobData)) michael@0: , mBlob(mozilla::Move(toMove.mBlob)) michael@0: { michael@0: toMove.mSharedBlobData = nullptr; michael@0: toMove.mBlob = nullptr; michael@0: } michael@0: michael@0: ~FontTableHashEntry() { Clear(); } michael@0: michael@0: // FontTable/Blob API michael@0: michael@0: // Transfer (not copy) elements of aTable to a new hb_blob_t and michael@0: // return ownership to the caller. A weak reference to the blob is michael@0: // recorded in the hashtable entry so that others may use the same michael@0: // table. michael@0: hb_blob_t * michael@0: ShareTableAndGetBlob(FallibleTArray& aTable, michael@0: nsTHashtable *aHashtable); michael@0: michael@0: // Return a strong reference to the blob. michael@0: // Callers must hb_blob_destroy the returned blob. michael@0: hb_blob_t *GetBlob() const; michael@0: michael@0: void Clear(); michael@0: michael@0: static size_t michael@0: SizeOfEntryExcludingThis(FontTableHashEntry *aEntry, michael@0: mozilla::MallocSizeOf aMallocSizeOf, michael@0: void* aUserArg); michael@0: michael@0: private: michael@0: static void DeleteFontTableBlobData(void *aBlobData); michael@0: // not implemented michael@0: FontTableHashEntry& operator=(FontTableHashEntry& toCopy); michael@0: michael@0: FontTableBlobData *mSharedBlobData; michael@0: hb_blob_t *mBlob; michael@0: }; michael@0: michael@0: nsAutoPtr > mFontTableCache; michael@0: michael@0: gfxFontEntry(const gfxFontEntry&); michael@0: gfxFontEntry& operator=(const gfxFontEntry&); michael@0: }; michael@0: michael@0: michael@0: // used when iterating over all fonts looking for a match for a given character michael@0: struct GlobalFontMatch { michael@0: GlobalFontMatch(const uint32_t aCharacter, michael@0: int32_t aRunScript, michael@0: const gfxFontStyle *aStyle) : michael@0: mCh(aCharacter), mRunScript(aRunScript), mStyle(aStyle), michael@0: mMatchRank(0), mCount(0), mCmapsTested(0) michael@0: { michael@0: michael@0: } michael@0: michael@0: const uint32_t mCh; // codepoint to be matched michael@0: int32_t mRunScript; // Unicode script for the codepoint michael@0: const gfxFontStyle* mStyle; // style to match michael@0: int32_t mMatchRank; // metric indicating closest match michael@0: nsRefPtr mBestMatch; // current best match michael@0: nsRefPtr mMatchedFamily; // the family it belongs to michael@0: uint32_t mCount; // number of fonts matched michael@0: uint32_t mCmapsTested; // number of cmaps tested michael@0: }; michael@0: michael@0: class gfxFontFamily { michael@0: public: michael@0: NS_INLINE_DECL_REFCOUNTING(gfxFontFamily) michael@0: michael@0: gfxFontFamily(const nsAString& aName) : michael@0: mName(aName), michael@0: mOtherFamilyNamesInitialized(false), michael@0: mHasOtherFamilyNames(false), michael@0: mFaceNamesInitialized(false), michael@0: mHasStyles(false), michael@0: mIsSimpleFamily(false), michael@0: mIsBadUnderlineFamily(false), michael@0: mFamilyCharacterMapInitialized(false), michael@0: mSkipDefaultFeatureSpaceCheck(false) michael@0: { } michael@0: michael@0: const nsString& Name() { return mName; } michael@0: michael@0: virtual void LocalizedName(nsAString& aLocalizedName); michael@0: virtual bool HasOtherFamilyNames(); michael@0: michael@0: nsTArray >& GetFontList() { return mAvailableFonts; } michael@0: michael@0: void AddFontEntry(nsRefPtr aFontEntry) { michael@0: // bug 589682 - set the IgnoreGDEF flag on entries for Italic faces michael@0: // of Times New Roman, because of buggy table in those fonts michael@0: if (aFontEntry->IsItalic() && !aFontEntry->IsUserFont() && michael@0: Name().EqualsLiteral("Times New Roman")) michael@0: { michael@0: aFontEntry->mIgnoreGDEF = true; michael@0: } michael@0: aFontEntry->mFamilyName = Name(); michael@0: aFontEntry->mSkipDefaultFeatureSpaceCheck = mSkipDefaultFeatureSpaceCheck; michael@0: mAvailableFonts.AppendElement(aFontEntry); michael@0: } michael@0: michael@0: // note that the styles for this family have been added michael@0: bool HasStyles() { return mHasStyles; } michael@0: void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; } michael@0: michael@0: // choose a specific face to match a style using CSS font matching michael@0: // rules (weight matching occurs here). may return a face that doesn't michael@0: // precisely match (e.g. normal face when no italic face exists). michael@0: // aNeedsSyntheticBold is set to true when synthetic bolding is michael@0: // needed, false otherwise michael@0: gfxFontEntry *FindFontForStyle(const gfxFontStyle& aFontStyle, michael@0: bool& aNeedsSyntheticBold); michael@0: michael@0: // checks for a matching font within the family michael@0: // used as part of the font fallback process michael@0: void FindFontForChar(GlobalFontMatch *aMatchData); michael@0: michael@0: // checks all fonts for a matching font within the family michael@0: void SearchAllFontsForChar(GlobalFontMatch *aMatchData); michael@0: michael@0: // read in other family names, if any, and use functor to add each into cache michael@0: virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList); michael@0: michael@0: // helper method for reading localized family names from the name table michael@0: // of a single face michael@0: static void ReadOtherFamilyNamesForFace(const nsAString& aFamilyName, michael@0: const char *aNameData, michael@0: uint32_t aDataLength, michael@0: nsTArray& aOtherFamilyNames, michael@0: bool useFullName); michael@0: michael@0: // set when other family names have been read in michael@0: void SetOtherFamilyNamesInitialized() { michael@0: mOtherFamilyNamesInitialized = true; michael@0: } michael@0: michael@0: // read in other localized family names, fullnames and Postscript names michael@0: // for all faces and append to lookup tables michael@0: virtual void ReadFaceNames(gfxPlatformFontList *aPlatformFontList, michael@0: bool aNeedFullnamePostscriptNames, michael@0: FontInfoData *aFontInfoData = nullptr); michael@0: michael@0: // find faces belonging to this family (platform implementations override this; michael@0: // should be made pure virtual once all subclasses have been updated) michael@0: virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) { } michael@0: michael@0: // search for a specific face using the Postscript name michael@0: gfxFontEntry* FindFont(const nsAString& aPostscriptName); michael@0: michael@0: // read in cmaps for all the faces michael@0: void ReadAllCMAPs(FontInfoData *aFontInfoData = nullptr); michael@0: michael@0: bool TestCharacterMap(uint32_t aCh) { michael@0: if (!mFamilyCharacterMapInitialized) { michael@0: ReadAllCMAPs(); michael@0: } michael@0: return mFamilyCharacterMap.test(aCh); michael@0: } michael@0: michael@0: void ResetCharacterMap() { michael@0: mFamilyCharacterMap.reset(); michael@0: mFamilyCharacterMapInitialized = false; michael@0: } michael@0: michael@0: // mark this family as being in the "bad" underline offset blacklist michael@0: void SetBadUnderlineFamily() { michael@0: mIsBadUnderlineFamily = true; michael@0: if (mHasStyles) { michael@0: SetBadUnderlineFonts(); michael@0: } michael@0: } michael@0: michael@0: bool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; } michael@0: michael@0: // sort available fonts to put preferred (standard) faces towards the end michael@0: void SortAvailableFonts(); michael@0: michael@0: // check whether the family fits into the simple 4-face model, michael@0: // so we can use simplified style-matching; michael@0: // if so set the mIsSimpleFamily flag (defaults to False before we've checked) michael@0: void CheckForSimpleFamily(); michael@0: michael@0: // For memory reporter michael@0: virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const; michael@0: virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const; michael@0: michael@0: // Only used for debugging checks - does a linear search michael@0: bool ContainsFace(gfxFontEntry* aFontEntry) { michael@0: uint32_t i, numFonts = mAvailableFonts.Length(); michael@0: for (i = 0; i < numFonts; i++) { michael@0: if (mAvailableFonts[i] == aFontEntry) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void SetSkipSpaceFeatureCheck(bool aSkipCheck) { michael@0: mSkipDefaultFeatureSpaceCheck = aSkipCheck; michael@0: } michael@0: michael@0: protected: michael@0: // Protected destructor, to discourage deletion outside of Release(): michael@0: virtual ~gfxFontFamily() michael@0: { michael@0: } michael@0: michael@0: // fills in an array with weights of faces that match style, michael@0: // returns whether any matching entries found michael@0: virtual bool FindWeightsForStyle(gfxFontEntry* aFontsForWeights[], michael@0: bool anItalic, int16_t aStretch); michael@0: michael@0: bool ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList, michael@0: hb_blob_t *aNameTable, michael@0: bool useFullName = false); michael@0: michael@0: // set whether this font family is in "bad" underline offset blacklist. michael@0: void SetBadUnderlineFonts() { michael@0: uint32_t i, numFonts = mAvailableFonts.Length(); michael@0: for (i = 0; i < numFonts; i++) { michael@0: if (mAvailableFonts[i]) { michael@0: mAvailableFonts[i]->mIsBadUnderlineFont = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsString mName; michael@0: nsTArray > mAvailableFonts; michael@0: gfxSparseBitSet mFamilyCharacterMap; michael@0: bool mOtherFamilyNamesInitialized : 1; michael@0: bool mHasOtherFamilyNames : 1; michael@0: bool mFaceNamesInitialized : 1; michael@0: bool mHasStyles : 1; michael@0: bool mIsSimpleFamily : 1; michael@0: bool mIsBadUnderlineFamily : 1; michael@0: bool mFamilyCharacterMapInitialized : 1; michael@0: bool mSkipDefaultFeatureSpaceCheck : 1; michael@0: michael@0: enum { michael@0: // for "simple" families, the faces are stored in mAvailableFonts michael@0: // with fixed positions: michael@0: kRegularFaceIndex = 0, michael@0: kBoldFaceIndex = 1, michael@0: kItalicFaceIndex = 2, michael@0: kBoldItalicFaceIndex = 3, michael@0: // mask values for selecting face with bold and/or italic attributes michael@0: kBoldMask = 0x01, michael@0: kItalicMask = 0x02 michael@0: }; michael@0: }; michael@0: michael@0: struct gfxTextRange { michael@0: enum { michael@0: // flags for recording the kind of font-matching that was used michael@0: kFontGroup = 0x0001, michael@0: kPrefsFallback = 0x0002, michael@0: kSystemFallback = 0x0004 michael@0: }; michael@0: gfxTextRange(uint32_t aStart, uint32_t aEnd, michael@0: gfxFont* aFont, uint8_t aMatchType) michael@0: : start(aStart), michael@0: end(aEnd), michael@0: font(aFont), michael@0: matchType(aMatchType) michael@0: { } michael@0: uint32_t Length() const { return end - start; } michael@0: uint32_t start, end; michael@0: nsRefPtr font; michael@0: uint8_t matchType; michael@0: }; michael@0: michael@0: michael@0: /** michael@0: * Font cache design: michael@0: * michael@0: * The mFonts hashtable contains most fonts, indexed by (gfxFontEntry*, style). michael@0: * It does not add a reference to the fonts it contains. michael@0: * When a font's refcount decreases to zero, instead of deleting it we michael@0: * add it to our expiration tracker. michael@0: * The expiration tracker tracks fonts with zero refcount. After a certain michael@0: * period of time, such fonts expire and are deleted. michael@0: * michael@0: * We're using 3 generations with a ten-second generation interval, so michael@0: * zero-refcount fonts will be deleted 20-30 seconds after their refcount michael@0: * goes to zero, if timer events fire in a timely manner. michael@0: * michael@0: * The font cache also handles timed expiration of cached ShapedWords michael@0: * for "persistent" fonts: it has a repeating timer, and notifies michael@0: * each cached font to "age" its shaped words. The words will be released michael@0: * by the fonts if they get aged three times without being re-used in the michael@0: * meantime. michael@0: * michael@0: * Note that the ShapedWord timeout is much larger than the font timeout, michael@0: * so that in the case of a short-lived font, we'll discard the gfxFont michael@0: * completely, with all its words, and avoid the cost of aging the words michael@0: * individually. That only happens with longer-lived fonts. michael@0: */ michael@0: struct FontCacheSizes { michael@0: FontCacheSizes() michael@0: : mFontInstances(0), mShapedWords(0) michael@0: { } michael@0: michael@0: size_t mFontInstances; // memory used by instances of gfxFont subclasses michael@0: size_t mShapedWords; // memory used by the per-font shapedWord caches michael@0: }; michael@0: michael@0: class gfxFontCache MOZ_FINAL : public nsExpirationTracker { michael@0: public: michael@0: enum { michael@0: FONT_TIMEOUT_SECONDS = 10, michael@0: SHAPED_WORD_TIMEOUT_SECONDS = 60 michael@0: }; michael@0: michael@0: gfxFontCache(); michael@0: ~gfxFontCache(); michael@0: michael@0: /* michael@0: * Get the global gfxFontCache. You must call Init() before michael@0: * calling this method --- the result will not be null. michael@0: */ michael@0: static gfxFontCache* GetCache() { michael@0: return gGlobalCache; michael@0: } michael@0: michael@0: static nsresult Init(); michael@0: // It's OK to call this even if Init() has not been called. michael@0: static void Shutdown(); michael@0: michael@0: // Look up a font in the cache. Returns an addrefed pointer, or null michael@0: // if there's nothing matching in the cache michael@0: already_AddRefed Lookup(const gfxFontEntry *aFontEntry, michael@0: const gfxFontStyle *aStyle); michael@0: // We created a new font (presumably because Lookup returned null); michael@0: // put it in the cache. The font's refcount should be nonzero. It is michael@0: // allowable to add a new font even if there is one already in the michael@0: // cache with the same key; we'll forget about the old one. michael@0: void AddNew(gfxFont *aFont); michael@0: michael@0: // The font's refcount has gone to zero; give ownership of it to michael@0: // the cache. We delete it if it's not acquired again after a certain michael@0: // amount of time. michael@0: void NotifyReleased(gfxFont *aFont); michael@0: michael@0: // This gets called when the timeout has expired on a zero-refcount michael@0: // font; we just delete it. michael@0: virtual void NotifyExpired(gfxFont *aFont); michael@0: michael@0: // Cleans out the hashtable and removes expired fonts waiting for cleanup. michael@0: // Other gfxFont objects may be still in use but they will be pushed michael@0: // into the expiration queues and removed. michael@0: void Flush() { michael@0: mFonts.Clear(); michael@0: AgeAllGenerations(); michael@0: } michael@0: michael@0: void FlushShapedWordCaches() { michael@0: mFonts.EnumerateEntries(ClearCachedWordsForFont, nullptr); michael@0: } michael@0: michael@0: void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, michael@0: FontCacheSizes* aSizes) const; michael@0: void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, michael@0: FontCacheSizes* aSizes) const; michael@0: michael@0: protected: michael@0: class MemoryReporter MOZ_FINAL : public nsIMemoryReporter michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIMEMORYREPORTER michael@0: }; michael@0: michael@0: // Observer for notifications that the font cache cares about michael@0: class Observer MOZ_FINAL michael@0: : public nsIObserver michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: }; michael@0: michael@0: void DestroyFont(gfxFont *aFont); michael@0: michael@0: static gfxFontCache *gGlobalCache; michael@0: michael@0: struct Key { michael@0: const gfxFontEntry* mFontEntry; michael@0: const gfxFontStyle* mStyle; michael@0: Key(const gfxFontEntry* aFontEntry, const gfxFontStyle* aStyle) michael@0: : mFontEntry(aFontEntry), mStyle(aStyle) {} michael@0: }; michael@0: michael@0: class HashEntry : public PLDHashEntryHdr { michael@0: public: michael@0: typedef const Key& KeyType; michael@0: typedef const Key* KeyTypePointer; michael@0: michael@0: // When constructing a new entry in the hashtable, we'll leave this michael@0: // blank. The caller of Put() will fill this in. michael@0: HashEntry(KeyTypePointer aStr) : mFont(nullptr) { } michael@0: HashEntry(const HashEntry& toCopy) : mFont(toCopy.mFont) { } michael@0: ~HashEntry() { } michael@0: michael@0: bool KeyEquals(const KeyTypePointer aKey) const; michael@0: static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } michael@0: static PLDHashNumber HashKey(const KeyTypePointer aKey) { michael@0: return mozilla::HashGeneric(aKey->mStyle->Hash(), aKey->mFontEntry); michael@0: } michael@0: enum { ALLOW_MEMMOVE = true }; michael@0: michael@0: gfxFont* mFont; michael@0: }; michael@0: michael@0: static size_t AddSizeOfFontEntryExcludingThis(HashEntry* aHashEntry, michael@0: mozilla::MallocSizeOf aMallocSizeOf, michael@0: void* aUserArg); michael@0: michael@0: nsTHashtable mFonts; michael@0: michael@0: static PLDHashOperator ClearCachedWordsForFont(HashEntry* aHashEntry, void*); michael@0: static PLDHashOperator AgeCachedWordsForFont(HashEntry* aHashEntry, void*); michael@0: static void WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache); michael@0: nsCOMPtr mWordCacheExpirationTimer; michael@0: }; michael@0: michael@0: class gfxTextPerfMetrics { michael@0: public: michael@0: michael@0: struct TextCounts { michael@0: uint32_t numContentTextRuns; michael@0: uint32_t numChromeTextRuns; michael@0: uint32_t numChars; michael@0: uint32_t maxTextRunLen; michael@0: uint32_t wordCacheSpaceRules; michael@0: uint32_t wordCacheLong; michael@0: uint32_t wordCacheHit; michael@0: uint32_t wordCacheMiss; michael@0: uint32_t fallbackPrefs; michael@0: uint32_t fallbackSystem; michael@0: uint32_t textrunConst; michael@0: uint32_t textrunDestr; michael@0: }; michael@0: michael@0: uint32_t reflowCount; michael@0: michael@0: // counts per reflow operation michael@0: TextCounts current; michael@0: michael@0: // totals for the lifetime of a document michael@0: TextCounts cumulative; michael@0: michael@0: gfxTextPerfMetrics() { michael@0: memset(this, 0, sizeof(gfxTextPerfMetrics)); michael@0: } michael@0: michael@0: // add current totals to cumulative ones michael@0: void Accumulate() { michael@0: if (current.numChars == 0) { michael@0: return; michael@0: } michael@0: cumulative.numContentTextRuns += current.numContentTextRuns; michael@0: cumulative.numChromeTextRuns += current.numChromeTextRuns; michael@0: cumulative.numChars += current.numChars; michael@0: if (current.maxTextRunLen > cumulative.maxTextRunLen) { michael@0: cumulative.maxTextRunLen = current.maxTextRunLen; michael@0: } michael@0: cumulative.wordCacheSpaceRules += current.wordCacheSpaceRules; michael@0: cumulative.wordCacheLong += current.wordCacheLong; michael@0: cumulative.wordCacheHit += current.wordCacheHit; michael@0: cumulative.wordCacheMiss += current.wordCacheMiss; michael@0: cumulative.fallbackPrefs += current.fallbackPrefs; michael@0: cumulative.fallbackSystem += current.fallbackSystem; michael@0: cumulative.textrunConst += current.textrunConst; michael@0: cumulative.textrunDestr += current.textrunDestr; michael@0: memset(¤t, 0, sizeof(current)); michael@0: } michael@0: }; michael@0: michael@0: class gfxTextRunFactory { michael@0: NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory) michael@0: michael@0: public: michael@0: // Flags in the mask 0xFFFF0000 are reserved for textrun clients michael@0: // Flags in the mask 0x0000F000 are reserved for per-platform fonts michael@0: // Flags in the mask 0x00000FFF are set by the textrun creator. michael@0: enum { michael@0: CACHE_TEXT_FLAGS = 0xF0000000, michael@0: USER_TEXT_FLAGS = 0x0FFF0000, michael@0: PLATFORM_TEXT_FLAGS = 0x0000F000, michael@0: TEXTRUN_TEXT_FLAGS = 0x00000FFF, michael@0: SETTABLE_FLAGS = CACHE_TEXT_FLAGS | USER_TEXT_FLAGS, michael@0: michael@0: /** michael@0: * When set, the text string pointer used to create the text run michael@0: * is guaranteed to be available during the lifetime of the text run. michael@0: */ michael@0: TEXT_IS_PERSISTENT = 0x0001, michael@0: /** michael@0: * When set, the text is known to be all-ASCII (< 128). michael@0: */ michael@0: TEXT_IS_ASCII = 0x0002, michael@0: /** michael@0: * When set, the text is RTL. michael@0: */ michael@0: TEXT_IS_RTL = 0x0004, michael@0: /** michael@0: * When set, spacing is enabled and the textrun needs to call GetSpacing michael@0: * on the spacing provider. michael@0: */ michael@0: TEXT_ENABLE_SPACING = 0x0008, michael@0: /** michael@0: * When set, GetHyphenationBreaks may return true for some character michael@0: * positions, otherwise it will always return false for all characters. michael@0: */ michael@0: TEXT_ENABLE_HYPHEN_BREAKS = 0x0010, michael@0: /** michael@0: * When set, the text has no characters above 255 and it is stored michael@0: * in the textrun in 8-bit format. michael@0: */ michael@0: TEXT_IS_8BIT = 0x0020, michael@0: /** michael@0: * When set, the RunMetrics::mBoundingBox field will be initialized michael@0: * properly based on glyph extents, in particular, glyph extents that michael@0: * overflow the standard font-box (the box defined by the ascent, descent michael@0: * and advance width of the glyph). When not set, it may just be the michael@0: * standard font-box even if glyphs overflow. michael@0: */ michael@0: TEXT_NEED_BOUNDING_BOX = 0x0040, michael@0: /** michael@0: * When set, optional ligatures are disabled. Ligatures that are michael@0: * required for legible text should still be enabled. michael@0: */ michael@0: TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0080, michael@0: /** michael@0: * When set, the textrun should favour speed of construction over michael@0: * quality. This may involve disabling ligatures and/or kerning or michael@0: * other effects. michael@0: */ michael@0: TEXT_OPTIMIZE_SPEED = 0x0100, michael@0: /** michael@0: * For internal use by the memory reporter when accounting for michael@0: * storage used by textruns. michael@0: * Because the reporter may visit each textrun multiple times while michael@0: * walking the frame trees and textrun cache, it needs to mark michael@0: * textruns that have been seen so as to avoid multiple-accounting. michael@0: */ michael@0: TEXT_RUN_SIZE_ACCOUNTED = 0x0200, michael@0: /** michael@0: * When set, the textrun should discard control characters instead of michael@0: * turning them into hexboxes. michael@0: */ michael@0: TEXT_HIDE_CONTROL_CHARACTERS = 0x0400, michael@0: michael@0: /** michael@0: * nsTextFrameThebes sets these, but they're defined here rather than michael@0: * in nsTextFrameUtils.h because ShapedWord creation/caching also needs michael@0: * to check the _INCOMING flag michael@0: */ michael@0: TEXT_TRAILING_ARABICCHAR = 0x20000000, michael@0: /** michael@0: * When set, the previous character for this textrun was an Arabic michael@0: * character. This is used for the context detection necessary for michael@0: * bidi.numeral implementation. michael@0: */ michael@0: TEXT_INCOMING_ARABICCHAR = 0x40000000, michael@0: michael@0: // Set if the textrun should use the OpenType 'math' script. michael@0: TEXT_USE_MATH_SCRIPT = 0x80000000, michael@0: michael@0: TEXT_UNUSED_FLAGS = 0x10000000 michael@0: }; michael@0: michael@0: /** michael@0: * This record contains all the parameters needed to initialize a textrun. michael@0: */ michael@0: struct Parameters { michael@0: // A reference context suggesting where the textrun will be rendered michael@0: gfxContext *mContext; michael@0: // Pointer to arbitrary user data (which should outlive the textrun) michael@0: void *mUserData; michael@0: // A description of which characters have been stripped from the original michael@0: // DOM string to produce the characters in the textrun. May be null michael@0: // if that information is not relevant. michael@0: gfxSkipChars *mSkipChars; michael@0: // A list of where linebreaks are currently placed in the textrun. May michael@0: // be null if mInitialBreakCount is zero. michael@0: uint32_t *mInitialBreaks; michael@0: uint32_t mInitialBreakCount; michael@0: // The ratio to use to convert device pixels to application layout units michael@0: int32_t mAppUnitsPerDevUnit; michael@0: }; michael@0: michael@0: protected: michael@0: // Protected destructor, to discourage deletion outside of Release(): michael@0: virtual ~gfxTextRunFactory() {} michael@0: }; michael@0: michael@0: /** michael@0: * This stores glyph bounds information for a particular gfxFont, at michael@0: * a particular appunits-per-dev-pixel ratio (because the compressed glyph michael@0: * width array is stored in appunits). michael@0: * michael@0: * We store a hashtable from glyph IDs to float bounding rects. For the michael@0: * common case where the glyph has no horizontal left bearing, and no michael@0: * y overflow above the font ascent or below the font descent, and tight michael@0: * bounding boxes are not required, we avoid storing the glyph ID in the hashtable michael@0: * and instead consult an array of 16-bit glyph XMost values (in appunits). michael@0: * This array always has an entry for the font's space glyph --- the width is michael@0: * assumed to be zero. michael@0: */ michael@0: class gfxGlyphExtents { michael@0: public: michael@0: gfxGlyphExtents(int32_t aAppUnitsPerDevUnit) : michael@0: mAppUnitsPerDevUnit(aAppUnitsPerDevUnit) { michael@0: MOZ_COUNT_CTOR(gfxGlyphExtents); michael@0: } michael@0: ~gfxGlyphExtents(); michael@0: michael@0: enum { INVALID_WIDTH = 0xFFFF }; michael@0: michael@0: void NotifyGlyphsChanged() { michael@0: mTightGlyphExtents.Clear(); michael@0: } michael@0: michael@0: // returns INVALID_WIDTH => not a contained glyph michael@0: // Otherwise the glyph has no before-bearing or vertical bearings, michael@0: // and the result is its width measured from the baseline origin, in michael@0: // appunits. michael@0: uint16_t GetContainedGlyphWidthAppUnits(uint32_t aGlyphID) const { michael@0: return mContainedGlyphWidths.Get(aGlyphID); michael@0: } michael@0: michael@0: bool IsGlyphKnown(uint32_t aGlyphID) const { michael@0: return mContainedGlyphWidths.Get(aGlyphID) != INVALID_WIDTH || michael@0: mTightGlyphExtents.GetEntry(aGlyphID) != nullptr; michael@0: } michael@0: michael@0: bool IsGlyphKnownWithTightExtents(uint32_t aGlyphID) const { michael@0: return mTightGlyphExtents.GetEntry(aGlyphID) != nullptr; michael@0: } michael@0: michael@0: // Get glyph extents; a rectangle relative to the left baseline origin michael@0: // Returns true on success. Can fail on OOM or when aContext is null michael@0: // and extents were not (successfully) prefetched. michael@0: bool GetTightGlyphExtentsAppUnits(gfxFont *aFont, gfxContext *aContext, michael@0: uint32_t aGlyphID, gfxRect *aExtents); michael@0: michael@0: void SetContainedGlyphWidthAppUnits(uint32_t aGlyphID, uint16_t aWidth) { michael@0: mContainedGlyphWidths.Set(aGlyphID, aWidth); michael@0: } michael@0: void SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits); michael@0: michael@0: int32_t GetAppUnitsPerDevUnit() { return mAppUnitsPerDevUnit; } michael@0: michael@0: size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: private: michael@0: class HashEntry : public nsUint32HashKey { michael@0: public: michael@0: // When constructing a new entry in the hashtable, we'll leave this michael@0: // blank. The caller of Put() will fill this in. michael@0: HashEntry(KeyTypePointer aPtr) : nsUint32HashKey(aPtr) {} michael@0: HashEntry(const HashEntry& toCopy) : nsUint32HashKey(toCopy) { michael@0: x = toCopy.x; y = toCopy.y; width = toCopy.width; height = toCopy.height; michael@0: } michael@0: michael@0: float x, y, width, height; michael@0: }; michael@0: michael@0: enum { BLOCK_SIZE_BITS = 7, BLOCK_SIZE = 1 << BLOCK_SIZE_BITS }; // 128-glyph blocks michael@0: michael@0: class GlyphWidths { michael@0: public: michael@0: void Set(uint32_t aIndex, uint16_t aValue); michael@0: uint16_t Get(uint32_t aIndex) const { michael@0: uint32_t block = aIndex >> BLOCK_SIZE_BITS; michael@0: if (block >= mBlocks.Length()) michael@0: return INVALID_WIDTH; michael@0: uintptr_t bits = mBlocks[block]; michael@0: if (!bits) michael@0: return INVALID_WIDTH; michael@0: uint32_t indexInBlock = aIndex & (BLOCK_SIZE - 1); michael@0: if (bits & 0x1) { michael@0: if (GetGlyphOffset(bits) != indexInBlock) michael@0: return INVALID_WIDTH; michael@0: return GetWidth(bits); michael@0: } michael@0: uint16_t *widths = reinterpret_cast(bits); michael@0: return widths[indexInBlock]; michael@0: } michael@0: michael@0: uint32_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: ~GlyphWidths(); michael@0: michael@0: private: michael@0: static uint32_t GetGlyphOffset(uintptr_t aBits) { michael@0: NS_ASSERTION(aBits & 0x1, "This is really a pointer..."); michael@0: return (aBits >> 1) & ((1 << BLOCK_SIZE_BITS) - 1); michael@0: } michael@0: static uint32_t GetWidth(uintptr_t aBits) { michael@0: NS_ASSERTION(aBits & 0x1, "This is really a pointer..."); michael@0: return aBits >> (1 + BLOCK_SIZE_BITS); michael@0: } michael@0: static uintptr_t MakeSingle(uint32_t aGlyphOffset, uint16_t aWidth) { michael@0: return (aWidth << (1 + BLOCK_SIZE_BITS)) + (aGlyphOffset << 1) + 1; michael@0: } michael@0: michael@0: nsTArray mBlocks; michael@0: }; michael@0: michael@0: GlyphWidths mContainedGlyphWidths; michael@0: nsTHashtable mTightGlyphExtents; michael@0: int32_t mAppUnitsPerDevUnit; michael@0: michael@0: private: michael@0: // not implemented: michael@0: gfxGlyphExtents(const gfxGlyphExtents& aOther) MOZ_DELETE; michael@0: gfxGlyphExtents& operator=(const gfxGlyphExtents& aOther) MOZ_DELETE; michael@0: }; michael@0: michael@0: /** michael@0: * gfxFontShaper michael@0: * michael@0: * This class implements text shaping (character to glyph mapping and michael@0: * glyph layout). There is a gfxFontShaper subclass for each text layout michael@0: * technology (uniscribe, core text, harfbuzz,....) we support. michael@0: * michael@0: * The shaper is responsible for setting up glyph data in gfxTextRuns. michael@0: * michael@0: * A generic, platform-independent shaper relies only on the standard michael@0: * gfxFont interface and can work with any concrete subclass of gfxFont. michael@0: * michael@0: * Platform-specific implementations designed to interface to platform michael@0: * shaping APIs such as Uniscribe or CoreText may rely on features of a michael@0: * specific font subclass to access native font references michael@0: * (such as CTFont, HFONT, DWriteFont, etc). michael@0: */ michael@0: michael@0: class gfxFontShaper { michael@0: public: michael@0: gfxFontShaper(gfxFont *aFont) michael@0: : mFont(aFont) michael@0: { michael@0: NS_ASSERTION(aFont, "shaper requires a valid font!"); michael@0: } michael@0: michael@0: virtual ~gfxFontShaper() { } michael@0: michael@0: // Shape a piece of text and store the resulting glyph data into michael@0: // aShapedText. Parameters aOffset/aLength indicate the range of michael@0: // aShapedText to be updated; aLength is also the length of aText. 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) = 0; michael@0: michael@0: gfxFont *GetFont() const { return mFont; } michael@0: michael@0: // returns true if features exist in output, false otherwise michael@0: static bool michael@0: MergeFontFeatures(const gfxFontStyle *aStyle, michael@0: const nsTArray& aFontFeatures, michael@0: bool aDisableLigatures, michael@0: const nsAString& aFamilyName, michael@0: nsDataHashtable& aMergedFeatures); michael@0: michael@0: protected: michael@0: // the font this shaper is working with michael@0: gfxFont * mFont; michael@0: }; michael@0: michael@0: /* a SPECIFIC single font family */ michael@0: class gfxFont { michael@0: michael@0: friend class gfxHarfBuzzShaper; michael@0: friend class gfxGraphiteShaper; michael@0: michael@0: public: michael@0: nsrefcnt AddRef(void) { michael@0: NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt"); michael@0: if (mExpirationState.IsTracked()) { michael@0: gfxFontCache::GetCache()->RemoveObject(this); michael@0: } michael@0: ++mRefCnt; michael@0: NS_LOG_ADDREF(this, mRefCnt, "gfxFont", sizeof(*this)); michael@0: return mRefCnt; michael@0: } michael@0: nsrefcnt Release(void) { michael@0: NS_PRECONDITION(0 != mRefCnt, "dup release"); michael@0: --mRefCnt; michael@0: NS_LOG_RELEASE(this, mRefCnt, "gfxFont"); michael@0: if (mRefCnt == 0) { michael@0: NotifyReleased(); michael@0: // |this| may have been deleted. michael@0: return 0; michael@0: } michael@0: return mRefCnt; michael@0: } michael@0: michael@0: int32_t GetRefCount() { return mRefCnt; } michael@0: michael@0: // options to specify the kind of AA to be used when creating a font michael@0: typedef enum { michael@0: kAntialiasDefault, michael@0: kAntialiasNone, michael@0: kAntialiasGrayscale, michael@0: kAntialiasSubpixel michael@0: } AntialiasOption; michael@0: michael@0: protected: michael@0: nsAutoRefCnt mRefCnt; michael@0: cairo_scaled_font_t *mScaledFont; michael@0: michael@0: void NotifyReleased() { michael@0: gfxFontCache *cache = gfxFontCache::GetCache(); michael@0: if (cache) { michael@0: // Don't delete just yet; return the object to the cache for michael@0: // possibly recycling within some time limit michael@0: cache->NotifyReleased(this); michael@0: } else { michael@0: // The cache may have already been shut down. michael@0: delete this; michael@0: } michael@0: } michael@0: michael@0: gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle, michael@0: AntialiasOption anAAOption = kAntialiasDefault, michael@0: cairo_scaled_font_t *aScaledFont = nullptr); michael@0: michael@0: public: michael@0: virtual ~gfxFont(); michael@0: michael@0: bool Valid() const { michael@0: return mIsValid; michael@0: } michael@0: michael@0: // options for the kind of bounding box to return from measurement michael@0: typedef enum { michael@0: LOOSE_INK_EXTENTS, michael@0: // A box that encloses all the painted pixels, and may michael@0: // include sidebearings and/or additional ascent/descent michael@0: // within the glyph cell even if the ink is smaller. michael@0: TIGHT_INK_EXTENTS, michael@0: // A box that tightly encloses all the painted pixels michael@0: // (although actually on Windows, at least, it may be michael@0: // slightly larger than strictly necessary because michael@0: // we can't get precise extents with ClearType). michael@0: TIGHT_HINTED_OUTLINE_EXTENTS michael@0: // A box that tightly encloses the glyph outline, michael@0: // ignoring possible antialiasing pixels that extend michael@0: // beyond this. michael@0: // NOTE: The default implementation of gfxFont::Measure(), michael@0: // which works with the glyph extents cache, does not michael@0: // differentiate between this and TIGHT_INK_EXTENTS. michael@0: // Whether the distinction is important depends on the michael@0: // antialiasing behavior of the platform; currently the michael@0: // distinction is only implemented in the gfxWindowsFont michael@0: // subclass, because of ClearType's tendency to paint michael@0: // outside the hinted outline. michael@0: // Also NOTE: it is relatively expensive to request this, michael@0: // as it does not use cached glyph extents in the font. michael@0: } BoundingBoxType; michael@0: michael@0: const nsString& GetName() const { return mFontEntry->Name(); } michael@0: const gfxFontStyle *GetStyle() const { return &mStyle; } michael@0: michael@0: virtual cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; } michael@0: michael@0: virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption) { michael@0: // platforms where this actually matters should override michael@0: return nullptr; michael@0: } michael@0: michael@0: virtual gfxFloat GetAdjustedSize() { michael@0: return mAdjustedSize > 0.0 ? mAdjustedSize : mStyle.size; michael@0: } michael@0: michael@0: float FUnitsToDevUnitsFactor() const { michael@0: // check this was set up during font initialization michael@0: NS_ASSERTION(mFUnitsConvFactor > 0.0f, "mFUnitsConvFactor not valid"); michael@0: return mFUnitsConvFactor; michael@0: } michael@0: michael@0: // check whether this is an sfnt we can potentially use with harfbuzz michael@0: bool FontCanSupportHarfBuzz() { michael@0: return mFontEntry->HasCmapTable(); michael@0: } michael@0: michael@0: // check whether this is an sfnt we can potentially use with Graphite michael@0: bool FontCanSupportGraphite() { michael@0: return mFontEntry->HasGraphiteTables(); michael@0: } michael@0: michael@0: // Subclasses may choose to look up glyph ids for characters. michael@0: // If they do not override this, gfxHarfBuzzShaper will fetch the cmap michael@0: // table and use that. michael@0: virtual bool ProvidesGetGlyph() const { michael@0: return false; michael@0: } michael@0: // Map unicode character to glyph ID. michael@0: // Only used if ProvidesGetGlyph() returns true. michael@0: virtual uint32_t GetGlyph(uint32_t unicode, uint32_t variation_selector) { michael@0: return 0; michael@0: } michael@0: // Return the horizontal advance of a glyph. michael@0: gfxFloat GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID); michael@0: michael@0: // Return Azure GlyphRenderingOptions for drawing this font. michael@0: virtual mozilla::TemporaryRef michael@0: GetGlyphRenderingOptions() { return nullptr; } michael@0: michael@0: gfxFloat SynthesizeSpaceWidth(uint32_t aCh); michael@0: michael@0: // Font metrics michael@0: struct Metrics { michael@0: gfxFloat xHeight; michael@0: gfxFloat superscriptOffset; michael@0: gfxFloat subscriptOffset; michael@0: gfxFloat strikeoutSize; michael@0: gfxFloat strikeoutOffset; michael@0: gfxFloat underlineSize; michael@0: gfxFloat underlineOffset; michael@0: michael@0: gfxFloat internalLeading; michael@0: gfxFloat externalLeading; michael@0: michael@0: gfxFloat emHeight; michael@0: gfxFloat emAscent; michael@0: gfxFloat emDescent; michael@0: gfxFloat maxHeight; michael@0: gfxFloat maxAscent; michael@0: gfxFloat maxDescent; michael@0: gfxFloat maxAdvance; michael@0: michael@0: gfxFloat aveCharWidth; michael@0: gfxFloat spaceWidth; michael@0: gfxFloat zeroOrAveCharWidth; // width of '0', or if there is michael@0: // no '0' glyph in this font, michael@0: // equal to .aveCharWidth michael@0: }; michael@0: virtual const gfxFont::Metrics& GetMetrics() = 0; michael@0: michael@0: /** michael@0: * We let layout specify spacing on either side of any michael@0: * character. We need to specify both before and after michael@0: * spacing so that substring measurement can do the right things. michael@0: * These values are in appunits. They're always an integral number of michael@0: * appunits, but we specify them in floats in case very large spacing michael@0: * values are required. michael@0: */ michael@0: struct Spacing { michael@0: gfxFloat mBefore; michael@0: gfxFloat mAfter; michael@0: }; michael@0: /** michael@0: * Metrics for a particular string michael@0: */ michael@0: struct RunMetrics { michael@0: RunMetrics() { michael@0: mAdvanceWidth = mAscent = mDescent = 0.0; michael@0: } michael@0: michael@0: void CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft); michael@0: michael@0: // can be negative (partly due to negative spacing). michael@0: // Advance widths should be additive: the advance width of the michael@0: // (offset1, length1) plus the advance width of (offset1 + length1, michael@0: // length2) should be the advance width of (offset1, length1 + length2) michael@0: gfxFloat mAdvanceWidth; michael@0: michael@0: // For zero-width substrings, these must be zero! michael@0: gfxFloat mAscent; // always non-negative michael@0: gfxFloat mDescent; // always non-negative michael@0: michael@0: // Bounding box that is guaranteed to include everything drawn. michael@0: // If a tight boundingBox was requested when these metrics were michael@0: // generated, this will tightly wrap the glyphs, otherwise it is michael@0: // "loose" and may be larger than the true bounding box. michael@0: // Coordinates are relative to the baseline left origin, so typically michael@0: // mBoundingBox.y == -mAscent michael@0: gfxRect mBoundingBox; michael@0: }; michael@0: michael@0: /** michael@0: * Draw a series of glyphs to aContext. The direction of aTextRun must michael@0: * be honoured. michael@0: * @param aStart the first character to draw michael@0: * @param aEnd draw characters up to here michael@0: * @param aBaselineOrigin the baseline origin; the left end of the baseline michael@0: * for LTR textruns, the right end of the baseline for RTL textruns. On return, michael@0: * this should be updated to the other end of the baseline. In application michael@0: * units, really! michael@0: * @param aSpacing spacing to insert before and after characters (for RTL michael@0: * glyphs, before-spacing is inserted to the right of characters). There michael@0: * are aEnd - aStart elements in this array, unless it's null to indicate michael@0: * that there is no spacing. michael@0: * @param aDrawMode specifies whether the fill or stroke of the glyph should be michael@0: * drawn, or if it should be drawn into the current path michael@0: * @param aContextPaint information about how to construct the fill and michael@0: * stroke pattern. Can be nullptr if we are not stroking the text, which michael@0: * indicates that the current source from aContext should be used for filling michael@0: * michael@0: * Callers guarantee: michael@0: * -- aStart and aEnd are aligned to cluster and ligature boundaries michael@0: * -- all glyphs use this font michael@0: * michael@0: * The default implementation builds a cairo glyph array and michael@0: * calls cairo_show_glyphs or cairo_glyph_path. michael@0: */ michael@0: virtual void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, michael@0: gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aBaselineOrigin, michael@0: Spacing *aSpacing, gfxTextContextPaint *aContextPaint, michael@0: gfxTextRunDrawCallbacks *aCallbacks); michael@0: michael@0: /** michael@0: * Measure a run of characters. See gfxTextRun::Metrics. michael@0: * @param aTight if false, then return the union of the glyph extents michael@0: * with the font-box for the characters (the rectangle with x=0,width= michael@0: * the advance width for the character run,y=-(font ascent), and height= michael@0: * font ascent + font descent). Otherwise, we must return as tight as possible michael@0: * an approximation to the area actually painted by glyphs. michael@0: * @param aContextForTightBoundingBox when aTight is true, this must michael@0: * be non-null. michael@0: * @param aSpacing spacing to insert before and after glyphs. The bounding box michael@0: * need not include the spacing itself, but the spacing affects the glyph michael@0: * positions. null if there is no spacing. michael@0: * michael@0: * Callers guarantee: michael@0: * -- aStart and aEnd are aligned to cluster and ligature boundaries michael@0: * -- all glyphs use this font michael@0: * michael@0: * The default implementation just uses font metrics and aTextRun's michael@0: * advances, and assumes no characters fall outside the font box. In michael@0: * general this is insufficient, because that assumption is not always true. michael@0: */ michael@0: virtual RunMetrics Measure(gfxTextRun *aTextRun, michael@0: uint32_t aStart, uint32_t aEnd, michael@0: BoundingBoxType aBoundingBoxType, michael@0: gfxContext *aContextForTightBoundingBox, michael@0: Spacing *aSpacing); michael@0: /** michael@0: * Line breaks have been changed at the beginning and/or end of a substring michael@0: * of the text. Reshaping may be required; glyph updating is permitted. michael@0: * @return true if anything was changed, false otherwise michael@0: */ michael@0: bool NotifyLineBreaksChanged(gfxTextRun *aTextRun, michael@0: uint32_t aStart, uint32_t aLength) michael@0: { return false; } michael@0: michael@0: // Expiration tracking michael@0: nsExpirationState *GetExpirationState() { return &mExpirationState; } michael@0: michael@0: // Get the glyphID of a space michael@0: virtual uint32_t GetSpaceGlyph() = 0; michael@0: michael@0: gfxGlyphExtents *GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit); michael@0: michael@0: // You need to call SetupCairoFont on the aCR just before calling this michael@0: virtual void SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, michael@0: bool aNeedTight, gfxGlyphExtents *aExtents); michael@0: michael@0: // This is called by the default Draw() implementation above. michael@0: virtual bool SetupCairoFont(gfxContext *aContext) = 0; michael@0: michael@0: virtual bool AllowSubpixelAA() { return true; } michael@0: michael@0: bool IsSyntheticBold() { return mApplySyntheticBold; } michael@0: michael@0: // Amount by which synthetic bold "fattens" the glyphs: michael@0: // For size S up to a threshold size T, we use (0.25 + 3S / 4T), michael@0: // so that the result ranges from 0.25 to 1.0; thereafter, michael@0: // simply use (S / T). michael@0: gfxFloat GetSyntheticBoldOffset() { michael@0: gfxFloat size = GetAdjustedSize(); michael@0: const gfxFloat threshold = 48.0; michael@0: return size < threshold ? (0.25 + 0.75 * size / threshold) : michael@0: (size / threshold); michael@0: } michael@0: michael@0: gfxFontEntry *GetFontEntry() { return mFontEntry.get(); } michael@0: bool HasCharacter(uint32_t ch) { michael@0: if (!mIsValid) michael@0: return false; michael@0: return mFontEntry->HasCharacter(ch); michael@0: } michael@0: michael@0: uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS) { michael@0: if (!mIsValid) { michael@0: return 0; michael@0: } michael@0: return mFontEntry->GetUVSGlyph(aCh, aVS); michael@0: } michael@0: michael@0: // call the (virtual) InitTextRun method to do glyph generation/shaping, michael@0: // limiting the length of text passed by processing the run in multiple michael@0: // segments if necessary michael@0: template michael@0: bool SplitAndInitTextRun(gfxContext *aContext, michael@0: gfxTextRun *aTextRun, michael@0: const T *aString, michael@0: uint32_t aRunStart, michael@0: uint32_t aRunLength, michael@0: int32_t aRunScript); michael@0: michael@0: // Get a ShapedWord representing the given text (either 8- or 16-bit) michael@0: // for use in setting up a gfxTextRun. michael@0: template michael@0: gfxShapedWord* GetShapedWord(gfxContext *aContext, michael@0: const T *aText, michael@0: uint32_t aLength, michael@0: uint32_t aHash, michael@0: int32_t aRunScript, michael@0: int32_t aAppUnitsPerDevUnit, michael@0: uint32_t aFlags, michael@0: gfxTextPerfMetrics *aTextPerf); michael@0: michael@0: // Ensure the ShapedWord cache is initialized. This MUST be called before michael@0: // any attempt to use GetShapedWord(). michael@0: void InitWordCache() { michael@0: if (!mWordCache) { michael@0: mWordCache = new nsTHashtable; michael@0: } michael@0: } michael@0: michael@0: // Called by the gfxFontCache timer to increment the age of all the words, michael@0: // so that they'll expire after a sufficient period of non-use michael@0: void AgeCachedWords() { michael@0: if (mWordCache) { michael@0: (void)mWordCache->EnumerateEntries(AgeCacheEntry, this); michael@0: } michael@0: } michael@0: michael@0: // Discard all cached word records; called on memory-pressure notification. michael@0: void ClearCachedWords() { michael@0: if (mWordCache) { michael@0: mWordCache->Clear(); michael@0: } michael@0: } michael@0: michael@0: // Glyph rendering/geometry has changed, so invalidate data as necessary. michael@0: void NotifyGlyphsChanged(); michael@0: michael@0: virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, michael@0: FontCacheSizes* aSizes) const; michael@0: virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, michael@0: FontCacheSizes* aSizes) const; michael@0: michael@0: typedef enum { michael@0: FONT_TYPE_DWRITE, michael@0: FONT_TYPE_GDI, michael@0: FONT_TYPE_FT2, michael@0: FONT_TYPE_MAC, michael@0: FONT_TYPE_OS2, michael@0: FONT_TYPE_CAIRO michael@0: } FontType; michael@0: michael@0: virtual FontType GetType() const = 0; michael@0: michael@0: virtual mozilla::TemporaryRef GetScaledFont(mozilla::gfx::DrawTarget *aTarget) michael@0: { return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this); } michael@0: michael@0: bool KerningDisabled() { michael@0: return mKerningSet && !mKerningEnabled; michael@0: } michael@0: michael@0: /** michael@0: * Subclass this object to be notified of glyph changes. Delete the object michael@0: * when no longer needed. michael@0: */ michael@0: class GlyphChangeObserver { michael@0: public: michael@0: virtual ~GlyphChangeObserver() michael@0: { michael@0: if (mFont) { michael@0: mFont->RemoveGlyphChangeObserver(this); michael@0: } michael@0: } michael@0: // This gets called when the gfxFont dies. michael@0: void ForgetFont() { mFont = nullptr; } michael@0: virtual void NotifyGlyphsChanged() = 0; michael@0: protected: michael@0: GlyphChangeObserver(gfxFont *aFont) : mFont(aFont) michael@0: { michael@0: mFont->AddGlyphChangeObserver(this); michael@0: } michael@0: gfxFont* mFont; michael@0: }; michael@0: friend class GlyphChangeObserver; michael@0: michael@0: bool GlyphsMayChange() michael@0: { michael@0: // Currently only fonts with SVG glyphs can have animated glyphs michael@0: return mFontEntry->TryGetSVGData(this); michael@0: } michael@0: michael@0: static void DestroySingletons() { michael@0: delete sScriptTagToCode; michael@0: delete sDefaultFeatures; michael@0: } michael@0: michael@0: protected: michael@0: // subclasses may provide (possibly hinted) glyph widths (in font units); michael@0: // if they do not override this, harfbuzz will use unhinted widths michael@0: // derived from the font tables michael@0: virtual bool ProvidesGlyphWidths() { michael@0: return false; michael@0: } michael@0: michael@0: // The return value is interpreted as a horizontal advance in 16.16 fixed michael@0: // point format. michael@0: virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID) { michael@0: return -1; michael@0: } michael@0: michael@0: void AddGlyphChangeObserver(GlyphChangeObserver *aObserver); michael@0: void RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver); michael@0: michael@0: // whether font contains substitution lookups containing spaces michael@0: bool HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript); michael@0: michael@0: // do spaces participate in shaping rules? if so, can't used word cache michael@0: bool SpaceMayParticipateInShaping(int32_t aRunScript); michael@0: michael@0: // For 8-bit text, expand to 16-bit and then call the following method. michael@0: bool ShapeText(gfxContext *aContext, michael@0: const uint8_t *aText, michael@0: uint32_t aOffset, // dest offset in gfxShapedText michael@0: uint32_t aLength, michael@0: int32_t aScript, michael@0: gfxShapedText *aShapedText, // where to store the result michael@0: bool aPreferPlatformShaping = false); michael@0: michael@0: // Call the appropriate shaper to generate glyphs for aText and store michael@0: // them into aShapedText. 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 = false); michael@0: michael@0: // Helper to adjust for synthetic bold and set character-type flags michael@0: // in the shaped text; implementations of ShapeText should call this michael@0: // after glyph shaping has been completed. michael@0: void PostShapingFixup(gfxContext *aContext, michael@0: const char16_t *aText, michael@0: uint32_t aOffset, // position within aShapedText michael@0: uint32_t aLength, michael@0: gfxShapedText *aShapedText); michael@0: michael@0: // Shape text directly into a range within a textrun, without using the michael@0: // font's word cache. Intended for use when the font has layout features michael@0: // that involve space, and therefore require shaping complete runs rather michael@0: // than isolated words, or for long strings that are inefficient to cache. michael@0: // This will split the text on "invalid" characters (tab/newline) that are michael@0: // not handled via normal shaping, but does not otherwise divide up the michael@0: // text. michael@0: template michael@0: bool ShapeTextWithoutWordCache(gfxContext *aContext, michael@0: const T *aText, michael@0: uint32_t aOffset, michael@0: uint32_t aLength, michael@0: int32_t aScript, michael@0: gfxTextRun *aTextRun); michael@0: michael@0: // Shape a fragment of text (a run that is known to contain only michael@0: // "valid" characters, no newlines/tabs/other control chars). michael@0: // All non-wordcache shaping goes through here; this is the function michael@0: // that will ensure we don't pass excessively long runs to the various michael@0: // platform shapers. michael@0: template michael@0: bool ShapeFragmentWithoutWordCache(gfxContext *aContext, michael@0: const T *aText, michael@0: uint32_t aOffset, michael@0: uint32_t aLength, michael@0: int32_t aScript, michael@0: gfxTextRun *aTextRun); michael@0: michael@0: void CheckForFeaturesInvolvingSpace(); michael@0: michael@0: // whether a given feature is included in feature settings from both the michael@0: // font and the style. aFeatureOn set if resolved feature value is non-zero michael@0: bool HasFeatureSet(uint32_t aFeature, bool& aFeatureOn); michael@0: michael@0: // used when analyzing whether a font has space contextual lookups michael@0: static nsDataHashtable *sScriptTagToCode; michael@0: static nsTHashtable *sDefaultFeatures; michael@0: michael@0: nsRefPtr mFontEntry; michael@0: michael@0: struct CacheHashKey { michael@0: union { michael@0: const uint8_t *mSingle; michael@0: const char16_t *mDouble; michael@0: } mText; michael@0: uint32_t mLength; michael@0: uint32_t mFlags; michael@0: int32_t mScript; michael@0: int32_t mAppUnitsPerDevUnit; michael@0: PLDHashNumber mHashKey; michael@0: bool mTextIs8Bit; michael@0: michael@0: CacheHashKey(const uint8_t *aText, uint32_t aLength, michael@0: uint32_t aStringHash, michael@0: int32_t aScriptCode, int32_t aAppUnitsPerDevUnit, michael@0: uint32_t aFlags) michael@0: : mLength(aLength), michael@0: mFlags(aFlags), michael@0: mScript(aScriptCode), michael@0: mAppUnitsPerDevUnit(aAppUnitsPerDevUnit), michael@0: mHashKey(aStringHash + aScriptCode + michael@0: aAppUnitsPerDevUnit * 0x100 + aFlags * 0x10000), michael@0: mTextIs8Bit(true) michael@0: { michael@0: NS_ASSERTION(aFlags & gfxTextRunFactory::TEXT_IS_8BIT, michael@0: "8-bit flag should have been set"); michael@0: mText.mSingle = aText; michael@0: } michael@0: michael@0: CacheHashKey(const char16_t *aText, uint32_t aLength, michael@0: uint32_t aStringHash, michael@0: int32_t aScriptCode, int32_t aAppUnitsPerDevUnit, michael@0: uint32_t aFlags) michael@0: : mLength(aLength), michael@0: mFlags(aFlags), michael@0: mScript(aScriptCode), michael@0: mAppUnitsPerDevUnit(aAppUnitsPerDevUnit), michael@0: mHashKey(aStringHash + aScriptCode + michael@0: aAppUnitsPerDevUnit * 0x100 + aFlags * 0x10000), michael@0: mTextIs8Bit(false) michael@0: { michael@0: // We can NOT assert that TEXT_IS_8BIT is false in aFlags here, michael@0: // because this might be an 8bit-only word from a 16-bit textrun, michael@0: // in which case the text we're passed is still in 16-bit form, michael@0: // and we'll have to use an 8-to-16bit comparison in KeyEquals. michael@0: mText.mDouble = aText; michael@0: } michael@0: }; michael@0: michael@0: class CacheHashEntry : public PLDHashEntryHdr { michael@0: public: michael@0: typedef const CacheHashKey &KeyType; michael@0: typedef const CacheHashKey *KeyTypePointer; michael@0: michael@0: // When constructing a new entry in the hashtable, the caller of Put() michael@0: // will fill us in. michael@0: CacheHashEntry(KeyTypePointer aKey) { } michael@0: CacheHashEntry(const CacheHashEntry& toCopy) { NS_ERROR("Should not be called"); } michael@0: ~CacheHashEntry() { } michael@0: michael@0: bool KeyEquals(const KeyTypePointer aKey) const; michael@0: michael@0: static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } michael@0: michael@0: static PLDHashNumber HashKey(const KeyTypePointer aKey) { michael@0: return aKey->mHashKey; michael@0: } michael@0: michael@0: enum { ALLOW_MEMMOVE = true }; michael@0: michael@0: nsAutoPtr mShapedWord; michael@0: }; michael@0: michael@0: static size_t michael@0: WordCacheEntrySizeOfExcludingThis(CacheHashEntry* aHashEntry, michael@0: mozilla::MallocSizeOf aMallocSizeOf, michael@0: void* aUserArg); michael@0: michael@0: nsAutoPtr > mWordCache; michael@0: michael@0: static PLDHashOperator AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData); michael@0: static const uint32_t kShapedWordCacheMaxAge = 3; michael@0: michael@0: bool mIsValid; michael@0: michael@0: // use synthetic bolding for environments where this is not supported michael@0: // by the platform michael@0: bool mApplySyntheticBold; michael@0: michael@0: bool mKerningSet; // kerning explicitly set? michael@0: bool mKerningEnabled; // if set, on or off? michael@0: michael@0: nsExpirationState mExpirationState; michael@0: gfxFontStyle mStyle; michael@0: nsAutoTArray mGlyphExtentsArray; michael@0: nsAutoPtr > > mGlyphChangeObservers; michael@0: michael@0: gfxFloat mAdjustedSize; michael@0: michael@0: float mFUnitsConvFactor; // conversion factor from font units to dev units michael@0: michael@0: // the AA setting requested for this font - may affect glyph bounds michael@0: AntialiasOption mAntialiasOption; michael@0: michael@0: // a copy of the font without antialiasing, if needed for separate michael@0: // measurement by mathml code michael@0: nsAutoPtr mNonAAFont; michael@0: michael@0: // we may switch between these shapers on the fly, based on the script michael@0: // of the text run being shaped michael@0: nsAutoPtr mPlatformShaper; michael@0: nsAutoPtr mHarfBuzzShaper; michael@0: nsAutoPtr mGraphiteShaper; michael@0: michael@0: mozilla::RefPtr mAzureScaledFont; michael@0: michael@0: // Create a default platform text shaper for this font. michael@0: // (TODO: This should become pure virtual once all font backends have michael@0: // been updated.) michael@0: virtual void CreatePlatformShaper() { } michael@0: michael@0: // Helper for subclasses that want to initialize standard metrics from the michael@0: // tables of sfnt (TrueType/OpenType) fonts. michael@0: // This will use mFUnitsConvFactor if it is already set, else compute it michael@0: // from mAdjustedSize and the unitsPerEm in the font's 'head' table. michael@0: // Returns TRUE and sets mIsValid=TRUE if successful; michael@0: // Returns TRUE but leaves mIsValid=FALSE if the font seems to be broken. michael@0: // Returns FALSE if the font does not appear to be an sfnt at all, michael@0: // and should be handled (if possible) using other APIs. michael@0: bool InitMetricsFromSfntTables(Metrics& aMetrics); michael@0: michael@0: // Helper to calculate various derived metrics from the results of michael@0: // InitMetricsFromSfntTables or equivalent platform code michael@0: void CalculateDerivedMetrics(Metrics& aMetrics); michael@0: michael@0: // some fonts have bad metrics, this method sanitize them. michael@0: // if this font has bad underline offset, aIsBadUnderlineFont should be true. michael@0: void SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont); michael@0: michael@0: bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, michael@0: uint32_t aGlyphId, gfxTextContextPaint *aContextPaint); michael@0: bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, michael@0: uint32_t aGlyphId, gfxTextContextPaint *aContextPaint, michael@0: gfxTextRunDrawCallbacks *aCallbacks, michael@0: bool& aEmittedGlyphs); michael@0: michael@0: // Bug 674909. When synthetic bolding text by drawing twice, need to michael@0: // render using a pixel offset in device pixels, otherwise text michael@0: // doesn't appear bolded, it appears as if a bad text shadow exists michael@0: // when a non-identity transform exists. Use an offset factor so that michael@0: // the second draw occurs at a constant offset in device pixels. michael@0: // This helper calculates the scale factor we need to apply to the michael@0: // synthetic-bold offset. michael@0: static double CalcXScale(gfxContext *aContext); michael@0: }; michael@0: michael@0: // proportion of ascent used for x-height, if unable to read value from font michael@0: #define DEFAULT_XHEIGHT_FACTOR 0.56f michael@0: michael@0: /* michael@0: * gfxShapedText is an abstract superclass for gfxShapedWord and gfxTextRun. michael@0: * These are objects that store a list of zero or more glyphs for each character. michael@0: * For each glyph we store the glyph ID, the advance, and possibly x/y-offsets. michael@0: * The idea is that a string is rendered by a loop that draws each glyph michael@0: * at its designated offset from the current point, then advances the current michael@0: * point by the glyph's advance in the direction of the textrun (LTR or RTL). michael@0: * Each glyph advance is always rounded to the nearest appunit; this ensures michael@0: * consistent results when dividing the text in a textrun into multiple text michael@0: * frames (frame boundaries are always aligned to appunits). We optimize michael@0: * for the case where a character has a single glyph and zero xoffset and yoffset, michael@0: * and the glyph ID and advance are in a reasonable range so we can pack all michael@0: * necessary data into 32 bits. michael@0: * michael@0: * gfxFontShaper can shape text into either a gfxShapedWord (cached by a gfxFont) michael@0: * or directly into a gfxTextRun (for cases where we want to shape textruns in michael@0: * their entirety rather than using cached words, because there may be layout michael@0: * features that depend on the inter-word spaces). michael@0: */ michael@0: class gfxShapedText michael@0: { michael@0: public: michael@0: gfxShapedText(uint32_t aLength, uint32_t aFlags, michael@0: int32_t aAppUnitsPerDevUnit) michael@0: : mLength(aLength) michael@0: , mFlags(aFlags) michael@0: , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit) michael@0: { } michael@0: michael@0: virtual ~gfxShapedText() { } michael@0: michael@0: /** michael@0: * This class records the information associated with a character in the michael@0: * input string. It's optimized for the case where there is one glyph michael@0: * representing that character alone. michael@0: * michael@0: * A character can have zero or more associated glyphs. Each glyph michael@0: * has an advance width and an x and y offset. michael@0: * A character may be the start of a cluster. michael@0: * A character may be the start of a ligature group. michael@0: * A character can be "missing", indicating that the system is unable michael@0: * to render the character. michael@0: * michael@0: * All characters in a ligature group conceptually share all the glyphs michael@0: * associated with the characters in a group. michael@0: */ michael@0: class CompressedGlyph { michael@0: public: michael@0: CompressedGlyph() { mValue = 0; } michael@0: michael@0: enum { michael@0: // Indicates that a cluster and ligature group starts at this michael@0: // character; this character has a single glyph with a reasonable michael@0: // advance and zero offsets. A "reasonable" advance michael@0: // is one that fits in the available bits (currently 12) (specified michael@0: // in appunits). michael@0: FLAG_IS_SIMPLE_GLYPH = 0x80000000U, michael@0: michael@0: // Indicates whether a linebreak is allowed before this character; michael@0: // this is a two-bit field that holds a FLAG_BREAK_TYPE_xxx value michael@0: // indicating the kind of linebreak (if any) allowed here. michael@0: FLAGS_CAN_BREAK_BEFORE = 0x60000000U, michael@0: michael@0: FLAGS_CAN_BREAK_SHIFT = 29, michael@0: FLAG_BREAK_TYPE_NONE = 0, michael@0: FLAG_BREAK_TYPE_NORMAL = 1, michael@0: FLAG_BREAK_TYPE_HYPHEN = 2, michael@0: michael@0: FLAG_CHAR_IS_SPACE = 0x10000000U, michael@0: michael@0: // The advance is stored in appunits michael@0: ADVANCE_MASK = 0x0FFF0000U, michael@0: ADVANCE_SHIFT = 16, michael@0: michael@0: GLYPH_MASK = 0x0000FFFFU, michael@0: michael@0: // Non-simple glyphs may or may not have glyph data in the michael@0: // corresponding mDetailedGlyphs entry. They have the following michael@0: // flag bits: michael@0: michael@0: // When NOT set, indicates that this character corresponds to a michael@0: // missing glyph and should be skipped (or possibly, render the character michael@0: // Unicode value in some special way). If there are glyphs, michael@0: // the mGlyphID is actually the UTF16 character code. The bit is michael@0: // inverted so we can memset the array to zero to indicate all missing. michael@0: FLAG_NOT_MISSING = 0x01, michael@0: FLAG_NOT_CLUSTER_START = 0x02, michael@0: FLAG_NOT_LIGATURE_GROUP_START = 0x04, michael@0: michael@0: FLAG_CHAR_IS_TAB = 0x08, michael@0: FLAG_CHAR_IS_NEWLINE = 0x10, michael@0: FLAG_CHAR_IS_LOW_SURROGATE = 0x20, michael@0: CHAR_IDENTITY_FLAGS_MASK = 0x38, michael@0: michael@0: GLYPH_COUNT_MASK = 0x00FFFF00U, michael@0: GLYPH_COUNT_SHIFT = 8 michael@0: }; michael@0: michael@0: // "Simple glyphs" have a simple glyph ID, simple advance and their michael@0: // x and y offsets are zero. Also the glyph extents do not overflow michael@0: // the font-box defined by the font ascent, descent and glyph advance width. michael@0: // These case is optimized to avoid storing DetailedGlyphs. michael@0: michael@0: // Returns true if the glyph ID aGlyph fits into the compressed representation michael@0: static bool IsSimpleGlyphID(uint32_t aGlyph) { michael@0: return (aGlyph & GLYPH_MASK) == aGlyph; michael@0: } michael@0: // Returns true if the advance aAdvance fits into the compressed representation. michael@0: // aAdvance is in appunits. michael@0: static bool IsSimpleAdvance(uint32_t aAdvance) { michael@0: return (aAdvance & (ADVANCE_MASK >> ADVANCE_SHIFT)) == aAdvance; michael@0: } michael@0: michael@0: bool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; } michael@0: uint32_t GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; } michael@0: uint32_t GetSimpleGlyph() const { return mValue & GLYPH_MASK; } michael@0: michael@0: bool IsMissing() const { return (mValue & (FLAG_NOT_MISSING|FLAG_IS_SIMPLE_GLYPH)) == 0; } michael@0: bool IsClusterStart() const { michael@0: return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_CLUSTER_START); michael@0: } michael@0: bool IsLigatureGroupStart() const { michael@0: return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_LIGATURE_GROUP_START); michael@0: } michael@0: bool IsLigatureContinuation() const { michael@0: return (mValue & FLAG_IS_SIMPLE_GLYPH) == 0 && michael@0: (mValue & (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING)) == michael@0: (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING); michael@0: } michael@0: michael@0: // Return true if the original character was a normal (breakable, michael@0: // trimmable) space (U+0020). Not true for other characters that michael@0: // may happen to map to the space glyph (U+00A0). michael@0: bool CharIsSpace() const { michael@0: return (mValue & FLAG_CHAR_IS_SPACE) != 0; michael@0: } michael@0: michael@0: bool CharIsTab() const { michael@0: return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_TAB) != 0; michael@0: } michael@0: bool CharIsNewline() const { michael@0: return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_NEWLINE) != 0; michael@0: } michael@0: bool CharIsLowSurrogate() const { michael@0: return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_LOW_SURROGATE) != 0; michael@0: } michael@0: michael@0: uint32_t CharIdentityFlags() const { michael@0: return IsSimpleGlyph() ? 0 : (mValue & CHAR_IDENTITY_FLAGS_MASK); michael@0: } michael@0: michael@0: void SetClusterStart(bool aIsClusterStart) { michael@0: NS_ASSERTION(!IsSimpleGlyph(), michael@0: "can't call SetClusterStart on simple glyphs"); michael@0: if (aIsClusterStart) { michael@0: mValue &= ~FLAG_NOT_CLUSTER_START; michael@0: } else { michael@0: mValue |= FLAG_NOT_CLUSTER_START; michael@0: } michael@0: } michael@0: michael@0: uint8_t CanBreakBefore() const { michael@0: return (mValue & FLAGS_CAN_BREAK_BEFORE) >> FLAGS_CAN_BREAK_SHIFT; michael@0: } michael@0: // Returns FLAGS_CAN_BREAK_BEFORE if the setting changed, 0 otherwise michael@0: uint32_t SetCanBreakBefore(uint8_t aCanBreakBefore) { michael@0: NS_ASSERTION(aCanBreakBefore <= 2, michael@0: "Bogus break-before value!"); michael@0: uint32_t breakMask = (uint32_t(aCanBreakBefore) << FLAGS_CAN_BREAK_SHIFT); michael@0: uint32_t toggle = breakMask ^ (mValue & FLAGS_CAN_BREAK_BEFORE); michael@0: mValue ^= toggle; michael@0: return toggle; michael@0: } michael@0: michael@0: CompressedGlyph& SetSimpleGlyph(uint32_t aAdvanceAppUnits, uint32_t aGlyph) { michael@0: NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow"); michael@0: NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow"); michael@0: NS_ASSERTION(!CharIdentityFlags(), "Char identity flags lost"); michael@0: mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) | michael@0: FLAG_IS_SIMPLE_GLYPH | michael@0: (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph; michael@0: return *this; michael@0: } michael@0: CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart, michael@0: uint32_t aGlyphCount) { michael@0: mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) | michael@0: FLAG_NOT_MISSING | michael@0: CharIdentityFlags() | michael@0: (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) | michael@0: (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) | michael@0: (aGlyphCount << GLYPH_COUNT_SHIFT); michael@0: return *this; michael@0: } michael@0: /** michael@0: * Missing glyphs are treated as ligature group starts; don't mess with michael@0: * the cluster-start flag (see bugs 618870 and 619286). michael@0: */ michael@0: CompressedGlyph& SetMissing(uint32_t aGlyphCount) { michael@0: mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_NOT_CLUSTER_START | michael@0: FLAG_CHAR_IS_SPACE)) | michael@0: CharIdentityFlags() | michael@0: (aGlyphCount << GLYPH_COUNT_SHIFT); michael@0: return *this; michael@0: } michael@0: uint32_t GetGlyphCount() const { michael@0: NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); michael@0: return (mValue & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT; michael@0: } michael@0: michael@0: void SetIsSpace() { michael@0: mValue |= FLAG_CHAR_IS_SPACE; michael@0: } michael@0: void SetIsTab() { michael@0: NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); michael@0: mValue |= FLAG_CHAR_IS_TAB; michael@0: } michael@0: void SetIsNewline() { michael@0: NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); michael@0: mValue |= FLAG_CHAR_IS_NEWLINE; michael@0: } michael@0: void SetIsLowSurrogate() { michael@0: NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); michael@0: mValue |= FLAG_CHAR_IS_LOW_SURROGATE; michael@0: } michael@0: michael@0: private: michael@0: uint32_t mValue; michael@0: }; michael@0: michael@0: // Accessor for the array of CompressedGlyph records, which will be in michael@0: // a different place in gfxShapedWord vs gfxTextRun michael@0: virtual CompressedGlyph *GetCharacterGlyphs() = 0; michael@0: michael@0: /** michael@0: * When the glyphs for a character don't fit into a CompressedGlyph record michael@0: * in SimpleGlyph format, we use an array of DetailedGlyphs instead. michael@0: */ michael@0: struct DetailedGlyph { michael@0: /** The glyphID, or the Unicode character michael@0: * if this is a missing glyph */ michael@0: uint32_t mGlyphID; michael@0: /** The advance, x-offset and y-offset of the glyph, in appunits michael@0: * mAdvance is in the text direction (RTL or LTR) michael@0: * mXOffset is always from left to right michael@0: * mYOffset is always from top to bottom */ michael@0: int32_t mAdvance; michael@0: float mXOffset, mYOffset; michael@0: }; michael@0: michael@0: void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph, michael@0: const DetailedGlyph *aGlyphs); michael@0: michael@0: void SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont); michael@0: michael@0: void SetIsSpace(uint32_t aIndex) { michael@0: GetCharacterGlyphs()[aIndex].SetIsSpace(); michael@0: } michael@0: michael@0: void SetIsLowSurrogate(uint32_t aIndex) { michael@0: SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr); michael@0: GetCharacterGlyphs()[aIndex].SetIsLowSurrogate(); michael@0: } michael@0: michael@0: bool HasDetailedGlyphs() const { michael@0: return mDetailedGlyphs != nullptr; michael@0: } michael@0: michael@0: bool IsClusterStart(uint32_t aPos) { michael@0: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); michael@0: return GetCharacterGlyphs()[aPos].IsClusterStart(); michael@0: } michael@0: michael@0: bool IsLigatureGroupStart(uint32_t aPos) { michael@0: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); michael@0: return GetCharacterGlyphs()[aPos].IsLigatureGroupStart(); michael@0: } michael@0: michael@0: // NOTE that this must not be called for a character offset that does michael@0: // not have any DetailedGlyph records; callers must have verified that michael@0: // GetCharacterGlyphs()[aCharIndex].GetGlyphCount() is greater than zero. michael@0: DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) { michael@0: NS_ASSERTION(GetCharacterGlyphs() && HasDetailedGlyphs() && michael@0: !GetCharacterGlyphs()[aCharIndex].IsSimpleGlyph() && michael@0: GetCharacterGlyphs()[aCharIndex].GetGlyphCount() > 0, michael@0: "invalid use of GetDetailedGlyphs; check the caller!"); michael@0: return mDetailedGlyphs->Get(aCharIndex); michael@0: } michael@0: michael@0: void AdjustAdvancesForSyntheticBold(float aSynBoldOffset, michael@0: uint32_t aOffset, uint32_t aLength); michael@0: michael@0: // Mark clusters in the CompressedGlyph records, starting at aOffset, michael@0: // based on the Unicode properties of the text in aString. michael@0: // This is also responsible to set the IsSpace flag for space characters. michael@0: void SetupClusterBoundaries(uint32_t aOffset, michael@0: const char16_t *aString, michael@0: uint32_t aLength); michael@0: // In 8-bit text, there won't actually be any clusters, but we still need michael@0: // the space-marking functionality. michael@0: void SetupClusterBoundaries(uint32_t aOffset, michael@0: const uint8_t *aString, michael@0: uint32_t aLength); michael@0: michael@0: uint32_t Flags() const { michael@0: return mFlags; michael@0: } michael@0: michael@0: bool IsRightToLeft() const { michael@0: return (Flags() & gfxTextRunFactory::TEXT_IS_RTL) != 0; michael@0: } michael@0: michael@0: float GetDirection() const { michael@0: return IsRightToLeft() ? -1.0f : 1.0f; michael@0: } michael@0: michael@0: bool DisableLigatures() const { michael@0: return (Flags() & gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0; michael@0: } michael@0: michael@0: bool TextIs8Bit() const { michael@0: return (Flags() & gfxTextRunFactory::TEXT_IS_8BIT) != 0; michael@0: } michael@0: michael@0: int32_t GetAppUnitsPerDevUnit() const { michael@0: return mAppUnitsPerDevUnit; michael@0: } michael@0: michael@0: uint32_t GetLength() const { michael@0: return mLength; michael@0: } michael@0: michael@0: bool FilterIfIgnorable(uint32_t aIndex, uint32_t aCh); michael@0: michael@0: protected: michael@0: // Allocate aCount DetailedGlyphs for the given index michael@0: DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex, michael@0: uint32_t aCount); michael@0: michael@0: // For characters whose glyph data does not fit the "simple" glyph criteria michael@0: // in CompressedGlyph, we use a sorted array to store the association michael@0: // between the source character offset and an index into an array michael@0: // DetailedGlyphs. The CompressedGlyph record includes a count of michael@0: // the number of DetailedGlyph records that belong to the character, michael@0: // starting at the given index. michael@0: class DetailedGlyphStore { michael@0: public: michael@0: DetailedGlyphStore() michael@0: : mLastUsed(0) michael@0: { } michael@0: michael@0: // This is optimized for the most common calling patterns: michael@0: // we rarely need random access to the records, access is most commonly michael@0: // sequential through the textRun, so we record the last-used index michael@0: // and check whether the caller wants the same record again, or the michael@0: // next; if not, it's most likely we're starting over from the start michael@0: // of the run, so we check the first entry before resorting to binary michael@0: // search as a last resort. michael@0: // NOTE that this must not be called for a character offset that does michael@0: // not have any DetailedGlyph records; callers must have verified that michael@0: // mCharacterGlyphs[aOffset].GetGlyphCount() is greater than zero michael@0: // before calling this, otherwise the assertions here will fire (in a michael@0: // debug build), and we'll probably crash. michael@0: DetailedGlyph* Get(uint32_t aOffset) { michael@0: NS_ASSERTION(mOffsetToIndex.Length() > 0, michael@0: "no detailed glyph records!"); michael@0: DetailedGlyph* details = mDetails.Elements(); michael@0: // check common cases (fwd iteration, initial entry, etc) first michael@0: if (mLastUsed < mOffsetToIndex.Length() - 1 && michael@0: aOffset == mOffsetToIndex[mLastUsed + 1].mOffset) { michael@0: ++mLastUsed; michael@0: } else if (aOffset == mOffsetToIndex[0].mOffset) { michael@0: mLastUsed = 0; michael@0: } else if (aOffset == mOffsetToIndex[mLastUsed].mOffset) { michael@0: // do nothing michael@0: } else if (mLastUsed > 0 && michael@0: aOffset == mOffsetToIndex[mLastUsed - 1].mOffset) { michael@0: --mLastUsed; michael@0: } else { michael@0: mLastUsed = michael@0: mOffsetToIndex.BinaryIndexOf(aOffset, CompareToOffset()); michael@0: } michael@0: NS_ASSERTION(mLastUsed != nsTArray::NoIndex, michael@0: "detailed glyph record missing!"); michael@0: return details + mOffsetToIndex[mLastUsed].mIndex; michael@0: } michael@0: michael@0: DetailedGlyph* Allocate(uint32_t aOffset, uint32_t aCount) { michael@0: uint32_t detailIndex = mDetails.Length(); michael@0: DetailedGlyph *details = mDetails.AppendElements(aCount); michael@0: if (!details) { michael@0: return nullptr; michael@0: } michael@0: // We normally set up glyph records sequentially, so the common case michael@0: // here is to append new records to the mOffsetToIndex array; michael@0: // test for that before falling back to the InsertElementSorted michael@0: // method. michael@0: if (mOffsetToIndex.Length() == 0 || michael@0: aOffset > mOffsetToIndex[mOffsetToIndex.Length() - 1].mOffset) { michael@0: if (!mOffsetToIndex.AppendElement(DGRec(aOffset, detailIndex))) { michael@0: return nullptr; michael@0: } michael@0: } else { michael@0: if (!mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex), michael@0: CompareRecordOffsets())) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: return details; michael@0: } michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) { michael@0: return aMallocSizeOf(this) + michael@0: mDetails.SizeOfExcludingThis(aMallocSizeOf) + michael@0: mOffsetToIndex.SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: private: michael@0: struct DGRec { michael@0: DGRec(const uint32_t& aOffset, const uint32_t& aIndex) michael@0: : mOffset(aOffset), mIndex(aIndex) { } michael@0: uint32_t mOffset; // source character offset in the textrun michael@0: uint32_t mIndex; // index where this char's DetailedGlyphs begin michael@0: }; michael@0: michael@0: struct CompareToOffset { michael@0: bool Equals(const DGRec& a, const uint32_t& b) const { michael@0: return a.mOffset == b; michael@0: } michael@0: bool LessThan(const DGRec& a, const uint32_t& b) const { michael@0: return a.mOffset < b; michael@0: } michael@0: }; michael@0: michael@0: struct CompareRecordOffsets { michael@0: bool Equals(const DGRec& a, const DGRec& b) const { michael@0: return a.mOffset == b.mOffset; michael@0: } michael@0: bool LessThan(const DGRec& a, const DGRec& b) const { michael@0: return a.mOffset < b.mOffset; michael@0: } michael@0: }; michael@0: michael@0: // Concatenated array of all the DetailedGlyph records needed for the michael@0: // textRun; individual character offsets are associated with indexes michael@0: // into this array via the mOffsetToIndex table. michael@0: nsTArray mDetails; michael@0: michael@0: // For each character offset that needs DetailedGlyphs, we record the michael@0: // index in mDetails where the list of glyphs begins. This array is michael@0: // sorted by mOffset. michael@0: nsTArray mOffsetToIndex; michael@0: michael@0: // Records the most recently used index into mOffsetToIndex, so that michael@0: // we can support sequential access more quickly than just doing michael@0: // a binary search each time. michael@0: nsTArray::index_type mLastUsed; michael@0: }; michael@0: michael@0: nsAutoPtr mDetailedGlyphs; michael@0: michael@0: // Number of char16_t characters and CompressedGlyph glyph records michael@0: uint32_t mLength; michael@0: michael@0: // Shaping flags (direction, ligature-suppression) michael@0: uint32_t mFlags; michael@0: michael@0: int32_t mAppUnitsPerDevUnit; michael@0: }; michael@0: michael@0: /* michael@0: * gfxShapedWord: an individual (space-delimited) run of text shaped with a michael@0: * particular font, without regard to external context. michael@0: * michael@0: * The glyph data is copied into gfxTextRuns as needed from the cache of michael@0: * ShapedWords associated with each gfxFont instance. michael@0: */ michael@0: class gfxShapedWord : public gfxShapedText michael@0: { michael@0: public: michael@0: // Create a ShapedWord that can hold glyphs for aLength characters, michael@0: // with mCharacterGlyphs sized appropriately. michael@0: // michael@0: // Returns null on allocation failure (does NOT use infallible alloc) michael@0: // so caller must check for success. michael@0: // michael@0: // This does NOT perform shaping, so the returned word contains no michael@0: // glyph data; the caller must call gfxFont::ShapeText() with appropriate michael@0: // parameters to set up the glyphs. michael@0: static gfxShapedWord* Create(const uint8_t *aText, uint32_t aLength, michael@0: int32_t aRunScript, michael@0: int32_t aAppUnitsPerDevUnit, michael@0: uint32_t aFlags) { michael@0: NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(), michael@0: "excessive length for gfxShapedWord!"); michael@0: michael@0: // Compute size needed including the mCharacterGlyphs array michael@0: // and a copy of the original text michael@0: uint32_t size = michael@0: offsetof(gfxShapedWord, mCharGlyphsStorage) + michael@0: aLength * (sizeof(CompressedGlyph) + sizeof(uint8_t)); michael@0: void *storage = moz_malloc(size); michael@0: if (!storage) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Construct in the pre-allocated storage, using placement new michael@0: return new (storage) gfxShapedWord(aText, aLength, aRunScript, michael@0: aAppUnitsPerDevUnit, aFlags); michael@0: } michael@0: michael@0: static gfxShapedWord* Create(const char16_t *aText, uint32_t aLength, michael@0: int32_t aRunScript, michael@0: int32_t aAppUnitsPerDevUnit, michael@0: uint32_t aFlags) { michael@0: NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(), michael@0: "excessive length for gfxShapedWord!"); michael@0: michael@0: // In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set, michael@0: // then we convert the text to an 8-bit version and call the 8-bit michael@0: // Create function instead. michael@0: if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) { michael@0: nsAutoCString narrowText; michael@0: LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength), michael@0: narrowText); michael@0: return Create((const uint8_t*)(narrowText.BeginReading()), michael@0: aLength, aRunScript, aAppUnitsPerDevUnit, aFlags); michael@0: } michael@0: michael@0: uint32_t size = michael@0: offsetof(gfxShapedWord, mCharGlyphsStorage) + michael@0: aLength * (sizeof(CompressedGlyph) + sizeof(char16_t)); michael@0: void *storage = moz_malloc(size); michael@0: if (!storage) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return new (storage) gfxShapedWord(aText, aLength, aRunScript, michael@0: aAppUnitsPerDevUnit, aFlags); michael@0: } michael@0: michael@0: // Override operator delete to properly free the object that was michael@0: // allocated via moz_malloc. michael@0: void operator delete(void* p) { michael@0: moz_free(p); michael@0: } michael@0: michael@0: CompressedGlyph *GetCharacterGlyphs() { michael@0: return &mCharGlyphsStorage[0]; michael@0: } michael@0: michael@0: const uint8_t* Text8Bit() const { michael@0: NS_ASSERTION(TextIs8Bit(), "invalid use of Text8Bit()"); michael@0: return reinterpret_cast(mCharGlyphsStorage + GetLength()); michael@0: } michael@0: michael@0: const char16_t* TextUnicode() const { michael@0: NS_ASSERTION(!TextIs8Bit(), "invalid use of TextUnicode()"); michael@0: return reinterpret_cast(mCharGlyphsStorage + GetLength()); michael@0: } michael@0: michael@0: char16_t GetCharAt(uint32_t aOffset) const { michael@0: NS_ASSERTION(aOffset < GetLength(), "aOffset out of range"); michael@0: return TextIs8Bit() ? michael@0: char16_t(Text8Bit()[aOffset]) : TextUnicode()[aOffset]; michael@0: } michael@0: michael@0: int32_t Script() const { michael@0: return mScript; michael@0: } michael@0: michael@0: void ResetAge() { michael@0: mAgeCounter = 0; michael@0: } michael@0: uint32_t IncrementAge() { michael@0: return ++mAgeCounter; michael@0: } michael@0: michael@0: private: michael@0: // so that gfxTextRun can share our DetailedGlyphStore class michael@0: friend class gfxTextRun; michael@0: michael@0: // Construct storage for a ShapedWord, ready to receive glyph data michael@0: gfxShapedWord(const uint8_t *aText, uint32_t aLength, michael@0: int32_t aRunScript, int32_t aAppUnitsPerDevUnit, michael@0: uint32_t aFlags) michael@0: : gfxShapedText(aLength, aFlags | gfxTextRunFactory::TEXT_IS_8BIT, michael@0: aAppUnitsPerDevUnit) michael@0: , mScript(aRunScript) michael@0: , mAgeCounter(0) michael@0: { michael@0: memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph)); michael@0: uint8_t *text = reinterpret_cast(&mCharGlyphsStorage[aLength]); michael@0: memcpy(text, aText, aLength * sizeof(uint8_t)); michael@0: } michael@0: michael@0: gfxShapedWord(const char16_t *aText, uint32_t aLength, michael@0: int32_t aRunScript, int32_t aAppUnitsPerDevUnit, michael@0: uint32_t aFlags) michael@0: : gfxShapedText(aLength, aFlags, aAppUnitsPerDevUnit) michael@0: , mScript(aRunScript) michael@0: , mAgeCounter(0) michael@0: { michael@0: memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph)); michael@0: char16_t *text = reinterpret_cast(&mCharGlyphsStorage[aLength]); michael@0: memcpy(text, aText, aLength * sizeof(char16_t)); michael@0: SetupClusterBoundaries(0, aText, aLength); michael@0: } michael@0: michael@0: int32_t mScript; michael@0: michael@0: uint32_t mAgeCounter; michael@0: michael@0: // The mCharGlyphsStorage array is actually a variable-size member; michael@0: // when the ShapedWord is created, its size will be increased as necessary michael@0: // to allow the proper number of glyphs to be stored. michael@0: // The original text, in either 8-bit or 16-bit form, will be stored michael@0: // immediately following the CompressedGlyphs. michael@0: CompressedGlyph mCharGlyphsStorage[1]; michael@0: }; michael@0: michael@0: /** michael@0: * Callback for Draw() to use when drawing text with mode michael@0: * DrawMode::GLYPH_PATH. michael@0: */ michael@0: struct gfxTextRunDrawCallbacks { michael@0: michael@0: /** michael@0: * Constructs a new DrawCallbacks object. michael@0: * michael@0: * @param aShouldPaintSVGGlyphs If true, SVG glyphs will be michael@0: * painted and the NotifyBeforeSVGGlyphPainted/NotifyAfterSVGGlyphPainted michael@0: * callbacks will be invoked for each SVG glyph. If false, SVG glyphs michael@0: * will not be painted; fallback plain glyphs are not emitted either. michael@0: */ michael@0: gfxTextRunDrawCallbacks(bool aShouldPaintSVGGlyphs = false) michael@0: : mShouldPaintSVGGlyphs(aShouldPaintSVGGlyphs) michael@0: { michael@0: } michael@0: michael@0: /** michael@0: * Called when a path has been emitted to the gfxContext when michael@0: * painting a text run. This can be called any number of times, michael@0: * due to partial ligatures and intervening SVG glyphs. michael@0: */ michael@0: virtual void NotifyGlyphPathEmitted() = 0; michael@0: michael@0: /** michael@0: * Called just before an SVG glyph has been painted to the gfxContext. michael@0: */ michael@0: virtual void NotifyBeforeSVGGlyphPainted() { } michael@0: michael@0: /** michael@0: * Called just after an SVG glyph has been painted to the gfxContext. michael@0: */ michael@0: virtual void NotifyAfterSVGGlyphPainted() { } michael@0: michael@0: bool mShouldPaintSVGGlyphs; michael@0: }; michael@0: michael@0: /** michael@0: * gfxTextRun is an abstraction for drawing and measuring substrings of a run michael@0: * of text. It stores runs of positioned glyph data, each run having a single michael@0: * gfxFont. The glyphs are associated with a string of source text, and the michael@0: * gfxTextRun APIs take parameters that are offsets into that source text. michael@0: * michael@0: * gfxTextRuns are not refcounted. They should be deleted when no longer required. michael@0: * michael@0: * gfxTextRuns are mostly immutable. The only things that can change are michael@0: * inter-cluster spacing and line break placement. Spacing is always obtained michael@0: * lazily by methods that need it, it is not cached. Line breaks are stored michael@0: * persistently (insofar as they affect the shaping of glyphs; gfxTextRun does michael@0: * not actually do anything to explicitly account for line breaks). Initially michael@0: * there are no line breaks. The textrun can record line breaks before or after michael@0: * any given cluster. (Line breaks specified inside clusters are ignored.) michael@0: * michael@0: * It is important that zero-length substrings are handled correctly. This will michael@0: * be on the test! michael@0: */ michael@0: class gfxTextRun : public gfxShapedText { michael@0: public: michael@0: michael@0: // Override operator delete to properly free the object that was michael@0: // allocated via moz_malloc. michael@0: void operator delete(void* p) { michael@0: moz_free(p); michael@0: } michael@0: michael@0: virtual ~gfxTextRun(); michael@0: michael@0: typedef gfxFont::RunMetrics Metrics; michael@0: michael@0: // Public textrun API for general use michael@0: michael@0: bool IsClusterStart(uint32_t aPos) { michael@0: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); michael@0: return mCharacterGlyphs[aPos].IsClusterStart(); michael@0: } michael@0: bool IsLigatureGroupStart(uint32_t aPos) { michael@0: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); michael@0: return mCharacterGlyphs[aPos].IsLigatureGroupStart(); michael@0: } michael@0: bool CanBreakLineBefore(uint32_t aPos) { michael@0: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); michael@0: return mCharacterGlyphs[aPos].CanBreakBefore() == michael@0: CompressedGlyph::FLAG_BREAK_TYPE_NORMAL; michael@0: } michael@0: bool CanHyphenateBefore(uint32_t aPos) { michael@0: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); michael@0: return mCharacterGlyphs[aPos].CanBreakBefore() == michael@0: CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN; michael@0: } michael@0: michael@0: bool CharIsSpace(uint32_t aPos) { michael@0: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); michael@0: return mCharacterGlyphs[aPos].CharIsSpace(); michael@0: } michael@0: bool CharIsTab(uint32_t aPos) { michael@0: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); michael@0: return mCharacterGlyphs[aPos].CharIsTab(); michael@0: } michael@0: bool CharIsNewline(uint32_t aPos) { michael@0: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); michael@0: return mCharacterGlyphs[aPos].CharIsNewline(); michael@0: } michael@0: bool CharIsLowSurrogate(uint32_t aPos) { michael@0: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); michael@0: return mCharacterGlyphs[aPos].CharIsLowSurrogate(); michael@0: } michael@0: michael@0: uint32_t GetLength() { return mLength; } michael@0: michael@0: // All uint32_t aStart, uint32_t aLength ranges below are restricted to michael@0: // grapheme cluster boundaries! All offsets are in terms of the string michael@0: // passed into MakeTextRun. michael@0: michael@0: // All coordinates are in layout/app units michael@0: michael@0: /** michael@0: * Set the potential linebreaks for a substring of the textrun. These are michael@0: * the "allow break before" points. Initially, there are no potential michael@0: * linebreaks. michael@0: * michael@0: * This can change glyphs and/or geometry! Some textruns' shapes michael@0: * depend on potential line breaks (e.g., title-case-converting textruns). michael@0: * This function is virtual so that those textruns can reshape themselves. michael@0: * michael@0: * @return true if this changed the linebreaks, false if the new line michael@0: * breaks are the same as the old michael@0: */ michael@0: virtual bool SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength, michael@0: uint8_t *aBreakBefore, michael@0: gfxContext *aRefContext); michael@0: michael@0: /** michael@0: * Layout provides PropertyProvider objects. These allow detection of michael@0: * potential line break points and computation of spacing. We pass the data michael@0: * this way to allow lazy data acquisition; for example BreakAndMeasureText michael@0: * will want to only ask for properties of text it's actually looking at. michael@0: * michael@0: * NOTE that requested spacing may not actually be applied, if the textrun michael@0: * is unable to apply it in some context. Exception: spacing around a michael@0: * whitespace character MUST always be applied. michael@0: */ michael@0: class PropertyProvider { michael@0: public: michael@0: // Detect hyphenation break opportunities in the given range; breaks michael@0: // not at cluster boundaries will be ignored. michael@0: virtual void GetHyphenationBreaks(uint32_t aStart, uint32_t aLength, michael@0: bool *aBreakBefore) = 0; michael@0: michael@0: // Returns the provider's hyphenation setting, so callers can decide michael@0: // whether it is necessary to call GetHyphenationBreaks. michael@0: // Result is an NS_STYLE_HYPHENS_* value. michael@0: virtual int8_t GetHyphensOption() = 0; michael@0: michael@0: // Returns the extra width that will be consumed by a hyphen. This should michael@0: // be constant for a given textrun. michael@0: virtual gfxFloat GetHyphenWidth() = 0; michael@0: michael@0: typedef gfxFont::Spacing Spacing; michael@0: michael@0: /** michael@0: * Get the spacing around the indicated characters. Spacing must be zero michael@0: * inside clusters. In other words, if character i is not michael@0: * CLUSTER_START, then character i-1 must have zero after-spacing and michael@0: * character i must have zero before-spacing. michael@0: */ michael@0: virtual void GetSpacing(uint32_t aStart, uint32_t aLength, michael@0: Spacing *aSpacing) = 0; michael@0: michael@0: // Returns a gfxContext that can be used to measure the hyphen glyph. michael@0: // Only called if the hyphen width is requested. michael@0: virtual already_AddRefed GetContext() = 0; michael@0: michael@0: // Return the appUnitsPerDevUnit value to be used when measuring. michael@0: // Only called if the hyphen width is requested. michael@0: virtual uint32_t GetAppUnitsPerDevUnit() = 0; michael@0: }; michael@0: michael@0: class ClusterIterator { michael@0: public: michael@0: ClusterIterator(gfxTextRun *aTextRun); michael@0: michael@0: void Reset(); michael@0: michael@0: bool NextCluster(); michael@0: michael@0: uint32_t Position() const { michael@0: return mCurrentChar; michael@0: } michael@0: michael@0: uint32_t ClusterLength() const; michael@0: michael@0: gfxFloat ClusterAdvance(PropertyProvider *aProvider) const; michael@0: michael@0: private: michael@0: gfxTextRun *mTextRun; michael@0: uint32_t mCurrentChar; michael@0: }; michael@0: michael@0: /** michael@0: * Draws a substring. Uses only GetSpacing from aBreakProvider. michael@0: * The provided point is the baseline origin on the left of the string michael@0: * for LTR, on the right of the string for RTL. michael@0: * @param aAdvanceWidth if non-null, the advance width of the substring michael@0: * is returned here. michael@0: * michael@0: * Drawing should respect advance widths in the sense that for LTR runs, michael@0: * Draw(ctx, pt, offset1, length1, dirty, &provider, &advance) followed by michael@0: * Draw(ctx, gfxPoint(pt.x + advance, pt.y), offset1 + length1, length2, michael@0: * dirty, &provider, nullptr) should have the same effect as michael@0: * Draw(ctx, pt, offset1, length1+length2, dirty, &provider, nullptr). michael@0: * For RTL runs the rule is: michael@0: * Draw(ctx, pt, offset1 + length1, length2, dirty, &provider, &advance) followed by michael@0: * Draw(ctx, gfxPoint(pt.x + advance, pt.y), offset1, length1, michael@0: * dirty, &provider, nullptr) should have the same effect as michael@0: * Draw(ctx, pt, offset1, length1+length2, dirty, &provider, nullptr). michael@0: * michael@0: * Glyphs should be drawn in logical content order, which can be significant michael@0: * if they overlap (perhaps due to negative spacing). michael@0: */ michael@0: void Draw(gfxContext *aContext, gfxPoint aPt, michael@0: DrawMode aDrawMode, michael@0: uint32_t aStart, uint32_t aLength, michael@0: PropertyProvider *aProvider, michael@0: gfxFloat *aAdvanceWidth, gfxTextContextPaint *aContextPaint, michael@0: gfxTextRunDrawCallbacks *aCallbacks = nullptr); michael@0: michael@0: /** michael@0: * Computes the ReflowMetrics for a substring. michael@0: * Uses GetSpacing from aBreakProvider. michael@0: * @param aBoundingBoxType which kind of bounding box (loose/tight) michael@0: */ michael@0: Metrics MeasureText(uint32_t aStart, uint32_t aLength, michael@0: gfxFont::BoundingBoxType aBoundingBoxType, michael@0: gfxContext *aRefContextForTightBoundingBox, michael@0: PropertyProvider *aProvider); michael@0: michael@0: /** michael@0: * Computes just the advance width for a substring. michael@0: * Uses GetSpacing from aBreakProvider. michael@0: */ michael@0: gfxFloat GetAdvanceWidth(uint32_t aStart, uint32_t aLength, michael@0: PropertyProvider *aProvider); michael@0: michael@0: /** michael@0: * Clear all stored line breaks for the given range (both before and after), michael@0: * and then set the line-break state before aStart to aBreakBefore and michael@0: * after the last cluster to aBreakAfter. michael@0: * michael@0: * We require that before and after line breaks be consistent. For clusters michael@0: * i and i+1, we require that if there is a break after cluster i, a break michael@0: * will be specified before cluster i+1. This may be temporarily violated michael@0: * (e.g. after reflowing line L and before reflowing line L+1); to handle michael@0: * these temporary violations, we say that there is a break betwen i and i+1 michael@0: * if a break is specified after i OR a break is specified before i+1. michael@0: * michael@0: * This can change textrun geometry! The existence of a linebreak can affect michael@0: * the advance width of the cluster before the break (when kerning) or the michael@0: * geometry of one cluster before the break or any number of clusters michael@0: * after the break. (The one-cluster-before-the-break limit is somewhat michael@0: * arbitrary; if some scripts require breaking it, then we need to michael@0: * alter nsTextFrame::TrimTrailingWhitespace, perhaps drastically becase michael@0: * it could affect the layout of frames before it...) michael@0: * michael@0: * We return true if glyphs or geometry changed, false otherwise. This michael@0: * function is virtual so that gfxTextRun subclasses can reshape michael@0: * properly. michael@0: * michael@0: * @param aAdvanceWidthDelta if non-null, returns the change in advance michael@0: * width of the given range. michael@0: */ michael@0: virtual bool SetLineBreaks(uint32_t aStart, uint32_t aLength, michael@0: bool aLineBreakBefore, bool aLineBreakAfter, michael@0: gfxFloat *aAdvanceWidthDelta, michael@0: gfxContext *aRefContext); michael@0: michael@0: /** michael@0: * Finds the longest substring that will fit into the given width. michael@0: * Uses GetHyphenationBreaks and GetSpacing from aBreakProvider. michael@0: * Guarantees the following: michael@0: * -- 0 <= result <= aMaxLength michael@0: * -- result is the maximal value of N such that either michael@0: * N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth michael@0: * OR N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth michael@0: * OR N == aMaxLength && GetAdvanceWidth(aStart, N) <= aWidth michael@0: * where GetAdvanceWidth assumes the effect of michael@0: * SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider) michael@0: * -- if no such N exists, then result is the smallest N such that michael@0: * N < aMaxLength && line break at N michael@0: * OR N < aMaxLength && hyphen break at N michael@0: * OR N == aMaxLength michael@0: * michael@0: * The call has the effect of michael@0: * SetLineBreaks(aStart, result, aLineBreakBefore, result < aMaxLength, aProvider) michael@0: * and the returned metrics and the invariants above reflect this. michael@0: * michael@0: * @param aMaxLength this can be UINT32_MAX, in which case the length used michael@0: * is up to the end of the string michael@0: * @param aLineBreakBefore set to true if and only if there is an actual michael@0: * line break at the start of this string. michael@0: * @param aSuppressInitialBreak if true, then we assume there is no possible michael@0: * linebreak before aStart. If false, then we will check the internal michael@0: * line break opportunity state before deciding whether to return 0 as the michael@0: * character to break before. michael@0: * @param aTrimWhitespace if non-null, then we allow a trailing run of michael@0: * spaces to be trimmed; the width of the space(s) will not be included in michael@0: * the measured string width for comparison with the limit aWidth, and michael@0: * trimmed spaces will not be included in returned metrics. The width michael@0: * of the trimmed spaces will be returned in aTrimWhitespace. michael@0: * Trimmed spaces are still counted in the "characters fit" result. michael@0: * @param aMetrics if non-null, we fill this in for the returned substring. michael@0: * If a hyphenation break was used, the hyphen is NOT included in the returned metrics. michael@0: * @param aBoundingBoxType whether to make the bounding box in aMetrics tight michael@0: * @param aRefContextForTightBoundingBox a reference context to get the michael@0: * tight bounding box, if requested michael@0: * @param aUsedHyphenation if non-null, records if we selected a hyphenation break michael@0: * @param aLastBreak if non-null and result is aMaxLength, we set this to michael@0: * the maximal N such that michael@0: * N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth michael@0: * OR N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth michael@0: * or UINT32_MAX if no such N exists, where GetAdvanceWidth assumes michael@0: * the effect of michael@0: * SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider) michael@0: * michael@0: * @param aCanWordWrap true if we can break between any two grapheme michael@0: * clusters. This is set by word-wrap: break-word michael@0: * michael@0: * @param aBreakPriority in/out the priority of the break opportunity michael@0: * saved in the line. If we are prioritizing break opportunities, we will michael@0: * not set a break with a lower priority. @see gfxBreakPriority. michael@0: * michael@0: * Note that negative advance widths are possible especially if negative michael@0: * spacing is provided. michael@0: */ michael@0: uint32_t BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength, michael@0: bool aLineBreakBefore, gfxFloat aWidth, michael@0: PropertyProvider *aProvider, michael@0: bool aSuppressInitialBreak, michael@0: gfxFloat *aTrimWhitespace, michael@0: Metrics *aMetrics, michael@0: gfxFont::BoundingBoxType aBoundingBoxType, michael@0: gfxContext *aRefContextForTightBoundingBox, michael@0: bool *aUsedHyphenation, michael@0: uint32_t *aLastBreak, michael@0: bool aCanWordWrap, michael@0: gfxBreakPriority *aBreakPriority); michael@0: michael@0: /** michael@0: * Update the reference context. michael@0: * XXX this is a hack. New text frame does not call this. Use only michael@0: * temporarily for old text frame. michael@0: */ michael@0: void SetContext(gfxContext *aContext) {} michael@0: michael@0: // Utility getters michael@0: michael@0: gfxFloat GetDirection() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) ? -1.0 : 1.0; } michael@0: void *GetUserData() const { return mUserData; } michael@0: void SetUserData(void *aUserData) { mUserData = aUserData; } michael@0: uint32_t GetFlags() const { return mFlags; } michael@0: void SetFlagBits(uint32_t aFlags) { michael@0: NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS), michael@0: "Only user flags should be mutable"); michael@0: mFlags |= aFlags; michael@0: } michael@0: void ClearFlagBits(uint32_t aFlags) { michael@0: NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS), michael@0: "Only user flags should be mutable"); michael@0: mFlags &= ~aFlags; michael@0: } michael@0: const gfxSkipChars& GetSkipChars() const { return mSkipChars; } michael@0: gfxFontGroup *GetFontGroup() const { return mFontGroup; } michael@0: michael@0: michael@0: // Call this, don't call "new gfxTextRun" directly. This does custom michael@0: // allocation and initialization michael@0: static gfxTextRun *Create(const gfxTextRunFactory::Parameters *aParams, michael@0: uint32_t aLength, gfxFontGroup *aFontGroup, michael@0: uint32_t aFlags); michael@0: michael@0: // The text is divided into GlyphRuns as necessary michael@0: struct GlyphRun { michael@0: nsRefPtr mFont; // never null michael@0: uint32_t mCharacterOffset; // into original UTF16 string michael@0: uint8_t mMatchType; michael@0: }; michael@0: michael@0: class GlyphRunIterator { michael@0: public: michael@0: GlyphRunIterator(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aLength) michael@0: : mTextRun(aTextRun), mStartOffset(aStart), mEndOffset(aStart + aLength) { michael@0: mNextIndex = mTextRun->FindFirstGlyphRunContaining(aStart); michael@0: } michael@0: bool NextRun(); michael@0: GlyphRun *GetGlyphRun() { return mGlyphRun; } michael@0: uint32_t GetStringStart() { return mStringStart; } michael@0: uint32_t GetStringEnd() { return mStringEnd; } michael@0: private: michael@0: gfxTextRun *mTextRun; michael@0: GlyphRun *mGlyphRun; michael@0: uint32_t mStringStart; michael@0: uint32_t mStringEnd; michael@0: uint32_t mNextIndex; michael@0: uint32_t mStartOffset; michael@0: uint32_t mEndOffset; michael@0: }; michael@0: michael@0: class GlyphRunOffsetComparator { michael@0: public: michael@0: bool Equals(const GlyphRun& a, michael@0: const GlyphRun& b) const michael@0: { michael@0: return a.mCharacterOffset == b.mCharacterOffset; michael@0: } michael@0: michael@0: bool LessThan(const GlyphRun& a, michael@0: const GlyphRun& b) const michael@0: { michael@0: return a.mCharacterOffset < b.mCharacterOffset; michael@0: } michael@0: }; michael@0: michael@0: friend class GlyphRunIterator; michael@0: friend class FontSelector; michael@0: michael@0: // API for setting up the textrun glyphs. Should only be called by michael@0: // things that construct textruns. michael@0: /** michael@0: * We've found a run of text that should use a particular font. Call this michael@0: * only during initialization when font substitution has been computed. michael@0: * Call it before setting up the glyphs for the characters in this run; michael@0: * SetMissingGlyph requires that the correct glyphrun be installed. michael@0: * michael@0: * If aForceNewRun, a new glyph run will be added, even if the michael@0: * previously added run uses the same font. If glyph runs are michael@0: * added out of strictly increasing aStartCharIndex order (via michael@0: * force), then SortGlyphRuns must be called after all glyph runs michael@0: * are added before any further operations are performed with this michael@0: * TextRun. michael@0: */ michael@0: nsresult AddGlyphRun(gfxFont *aFont, uint8_t aMatchType, michael@0: uint32_t aStartCharIndex, bool aForceNewRun); michael@0: void ResetGlyphRuns() { mGlyphRuns.Clear(); } michael@0: void SortGlyphRuns(); michael@0: void SanitizeGlyphRuns(); michael@0: michael@0: CompressedGlyph* GetCharacterGlyphs() { michael@0: NS_ASSERTION(mCharacterGlyphs, "failed to initialize mCharacterGlyphs"); michael@0: return mCharacterGlyphs; michael@0: } michael@0: michael@0: void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, uint32_t aCharIndex); michael@0: michael@0: // Set the glyph data for the given character index to the font's michael@0: // space glyph, IF this can be done as a "simple" glyph record michael@0: // (not requiring a DetailedGlyph entry). This avoids the need to call michael@0: // the font shaper and go through the shaped-word cache for most spaces. michael@0: // michael@0: // The parameter aSpaceChar is the original character code for which michael@0: // this space glyph is being used; if this is U+0020, we need to record michael@0: // that it could be trimmed at a run edge, whereas other kinds of space michael@0: // (currently just U+00A0) would not be trimmable/breakable. michael@0: // michael@0: // Returns true if it was able to set simple glyph data for the space; michael@0: // if it returns false, the caller needs to fall back to some other michael@0: // means to create the necessary (detailed) glyph data. michael@0: bool SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext, michael@0: uint32_t aCharIndex, char16_t aSpaceChar); michael@0: michael@0: // Record the positions of specific characters that layout may need to michael@0: // detect in the textrun, even though it doesn't have an explicit copy michael@0: // of the original text. These are recorded using flag bits in the michael@0: // CompressedGlyph record; if necessary, we convert "simple" glyph records michael@0: // to "complex" ones as the Tab and Newline flags are not present in michael@0: // simple CompressedGlyph records. michael@0: void SetIsTab(uint32_t aIndex) { michael@0: CompressedGlyph *g = &mCharacterGlyphs[aIndex]; michael@0: if (g->IsSimpleGlyph()) { michael@0: DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1); michael@0: details->mGlyphID = g->GetSimpleGlyph(); michael@0: details->mAdvance = g->GetSimpleAdvance(); michael@0: details->mXOffset = details->mYOffset = 0; michael@0: SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1), details); michael@0: } michael@0: g->SetIsTab(); michael@0: } michael@0: void SetIsNewline(uint32_t aIndex) { michael@0: CompressedGlyph *g = &mCharacterGlyphs[aIndex]; michael@0: if (g->IsSimpleGlyph()) { michael@0: DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1); michael@0: details->mGlyphID = g->GetSimpleGlyph(); michael@0: details->mAdvance = g->GetSimpleAdvance(); michael@0: details->mXOffset = details->mYOffset = 0; michael@0: SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1), details); michael@0: } michael@0: g->SetIsNewline(); michael@0: } michael@0: void SetIsLowSurrogate(uint32_t aIndex) { michael@0: SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr); michael@0: mCharacterGlyphs[aIndex].SetIsLowSurrogate(); michael@0: } michael@0: michael@0: /** michael@0: * Prefetch all the glyph extents needed to ensure that Measure calls michael@0: * on this textrun not requesting tight boundingBoxes will succeed. Note michael@0: * that some glyph extents might not be fetched due to OOM or other michael@0: * errors. michael@0: */ michael@0: void FetchGlyphExtents(gfxContext *aRefContext); michael@0: michael@0: uint32_t CountMissingGlyphs(); michael@0: const GlyphRun *GetGlyphRuns(uint32_t *aNumGlyphRuns) { michael@0: *aNumGlyphRuns = mGlyphRuns.Length(); michael@0: return mGlyphRuns.Elements(); michael@0: } michael@0: // Returns the index of the GlyphRun containing the given offset. michael@0: // Returns mGlyphRuns.Length() when aOffset is mCharacterCount. michael@0: uint32_t FindFirstGlyphRunContaining(uint32_t aOffset); michael@0: michael@0: // Copy glyph data from a ShapedWord into this textrun. michael@0: void CopyGlyphDataFrom(gfxShapedWord *aSource, uint32_t aStart); michael@0: michael@0: // Copy glyph data for a range of characters from aSource to this michael@0: // textrun. michael@0: void CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart, michael@0: uint32_t aLength, uint32_t aDest); michael@0: michael@0: nsExpirationState *GetExpirationState() { return &mExpirationState; } michael@0: michael@0: // Tell the textrun to release its reference to its creating gfxFontGroup michael@0: // immediately, rather than on destruction. This is used for textruns michael@0: // that are actually owned by a gfxFontGroup, so that they don't keep it michael@0: // permanently alive due to a circular reference. (The caller of this is michael@0: // taking responsibility for ensuring the textrun will not outlive its michael@0: // mFontGroup.) michael@0: void ReleaseFontGroup(); michael@0: michael@0: struct LigatureData { michael@0: // textrun offsets of the start and end of the containing ligature michael@0: uint32_t mLigatureStart; michael@0: uint32_t mLigatureEnd; michael@0: // appunits advance to the start of the ligature part within the ligature; michael@0: // never includes any spacing michael@0: gfxFloat mPartAdvance; michael@0: // appunits width of the ligature part; includes before-spacing michael@0: // when the part is at the start of the ligature, and after-spacing michael@0: // when the part is as the end of the ligature michael@0: gfxFloat mPartWidth; michael@0: michael@0: bool mClipBeforePart; michael@0: bool mClipAfterPart; michael@0: }; michael@0: michael@0: // return storage used by this run, for memory reporter; michael@0: // nsTransformedTextRun needs to override this as it holds additional data michael@0: virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) michael@0: MOZ_MUST_OVERRIDE; michael@0: virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) michael@0: MOZ_MUST_OVERRIDE; michael@0: michael@0: // Get the size, if it hasn't already been gotten, marking as it goes. michael@0: size_t MaybeSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) { michael@0: if (mFlags & gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED) { michael@0: return 0; michael@0: } michael@0: mFlags |= gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED; michael@0: return SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: void ResetSizeOfAccountingFlags() { michael@0: mFlags &= ~gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void Dump(FILE* aOutput); michael@0: #endif michael@0: michael@0: protected: michael@0: /** michael@0: * Create a textrun, and set its mCharacterGlyphs to point immediately michael@0: * after the base object; this is ONLY used in conjunction with placement michael@0: * new, after allocating a block large enough for the glyph records to michael@0: * follow the base textrun object. michael@0: */ michael@0: gfxTextRun(const gfxTextRunFactory::Parameters *aParams, michael@0: uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags); michael@0: michael@0: /** michael@0: * Helper for the Create() factory method to allocate the required michael@0: * glyph storage for a textrun object with the basic size aSize, michael@0: * plus room for aLength glyph records. michael@0: */ michael@0: static void* AllocateStorageForTextRun(size_t aSize, uint32_t aLength); michael@0: michael@0: // Pointer to the array of CompressedGlyph records; must be initialized michael@0: // when the object is constructed. michael@0: CompressedGlyph *mCharacterGlyphs; michael@0: michael@0: private: michael@0: // **** general helpers **** michael@0: michael@0: // Allocate aCount DetailedGlyphs for the given index michael@0: DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex, uint32_t aCount); michael@0: michael@0: // Get the total advance for a range of glyphs. michael@0: int32_t GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd); michael@0: michael@0: // Spacing for characters outside the range aSpacingStart/aSpacingEnd michael@0: // is assumed to be zero; such characters are not passed to aProvider. michael@0: // This is useful to protect aProvider from being passed character indices michael@0: // it is not currently able to handle. michael@0: bool GetAdjustedSpacingArray(uint32_t aStart, uint32_t aEnd, michael@0: PropertyProvider *aProvider, michael@0: uint32_t aSpacingStart, uint32_t aSpacingEnd, michael@0: nsTArray *aSpacing); michael@0: michael@0: // **** ligature helpers **** michael@0: // (Platforms do the actual ligaturization, but we need to do a bunch of stuff michael@0: // to handle requests that begin or end inside a ligature) michael@0: michael@0: // if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero michael@0: LigatureData ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd, michael@0: PropertyProvider *aProvider); michael@0: gfxFloat ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd, michael@0: PropertyProvider *aProvider); michael@0: void DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, michael@0: uint32_t aStart, uint32_t aEnd, gfxPoint *aPt, michael@0: PropertyProvider *aProvider, michael@0: gfxTextRunDrawCallbacks *aCallbacks); michael@0: // Advance aStart to the start of the nearest ligature; back up aEnd michael@0: // to the nearest ligature end; may result in *aStart == *aEnd michael@0: void ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd); michael@0: // result in appunits michael@0: gfxFloat GetPartialLigatureWidth(uint32_t aStart, uint32_t aEnd, PropertyProvider *aProvider); michael@0: void AccumulatePartialLigatureMetrics(gfxFont *aFont, michael@0: uint32_t aStart, uint32_t aEnd, michael@0: gfxFont::BoundingBoxType aBoundingBoxType, michael@0: gfxContext *aRefContext, michael@0: PropertyProvider *aProvider, michael@0: Metrics *aMetrics); michael@0: michael@0: // **** measurement helper **** michael@0: void AccumulateMetricsForRun(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, michael@0: gfxFont::BoundingBoxType aBoundingBoxType, michael@0: gfxContext *aRefContext, michael@0: PropertyProvider *aProvider, michael@0: uint32_t aSpacingStart, uint32_t aSpacingEnd, michael@0: Metrics *aMetrics); michael@0: michael@0: // **** drawing helper **** michael@0: void DrawGlyphs(gfxFont *aFont, gfxContext *aContext, michael@0: DrawMode aDrawMode, gfxPoint *aPt, michael@0: gfxTextContextPaint *aContextPaint, uint32_t aStart, michael@0: uint32_t aEnd, PropertyProvider *aProvider, michael@0: uint32_t aSpacingStart, uint32_t aSpacingEnd, michael@0: gfxTextRunDrawCallbacks *aCallbacks); michael@0: michael@0: // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*, michael@0: // for smaller size especially in the super-common one-glyphrun case michael@0: nsAutoTArray mGlyphRuns; michael@0: michael@0: void *mUserData; michael@0: gfxFontGroup *mFontGroup; // addrefed on creation, but our reference michael@0: // may be released by ReleaseFontGroup() michael@0: gfxSkipChars mSkipChars; michael@0: nsExpirationState mExpirationState; michael@0: michael@0: bool mSkipDrawing; // true if the font group we used had a user font michael@0: // download that's in progress, so we should hide text michael@0: // until the download completes (or timeout fires) michael@0: bool mReleasedFontGroup; // we already called NS_RELEASE on michael@0: // mFontGroup, so don't do it again michael@0: }; michael@0: michael@0: class gfxFontGroup : public gfxTextRunFactory { michael@0: public: michael@0: class FamilyFace { michael@0: public: michael@0: FamilyFace() { } michael@0: michael@0: FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont) michael@0: : mFamily(aFamily), mFont(aFont) michael@0: { michael@0: NS_ASSERTION(aFont, "font pointer must not be null"); michael@0: NS_ASSERTION(!aFamily || michael@0: aFamily->ContainsFace(aFont->GetFontEntry()), michael@0: "font is not a member of the given family"); michael@0: } michael@0: michael@0: gfxFontFamily* Family() const { return mFamily.get(); } michael@0: gfxFont* Font() const { return mFont.get(); } michael@0: michael@0: private: michael@0: nsRefPtr mFamily; michael@0: nsRefPtr mFont; michael@0: }; michael@0: michael@0: static void Shutdown(); // platform must call this to release the languageAtomService michael@0: michael@0: gfxFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle, gfxUserFontSet *aUserFontSet = nullptr); michael@0: michael@0: virtual ~gfxFontGroup(); michael@0: michael@0: virtual gfxFont *GetFontAt(int32_t i) { 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: NS_ASSERTION(mFonts.Length() > uint32_t(i) && mFonts[i].Font(), michael@0: "Requesting a font index that doesn't exist"); michael@0: michael@0: return mFonts[i].Font(); michael@0: } michael@0: michael@0: uint32_t FontListLength() const { michael@0: return mFonts.Length(); michael@0: } michael@0: michael@0: bool Equals(const gfxFontGroup& other) const { michael@0: return mFamilies.Equals(other.mFamilies) && michael@0: mStyle.Equals(other.mStyle); michael@0: } michael@0: michael@0: const gfxFontStyle *GetStyle() const { return &mStyle; } michael@0: michael@0: virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle); michael@0: michael@0: /** michael@0: * The listed characters should be treated as invisible and zero-width michael@0: * when creating textruns. michael@0: */ michael@0: static bool IsInvalidChar(uint8_t ch); michael@0: static bool IsInvalidChar(char16_t ch); michael@0: michael@0: /** michael@0: * Make a textrun for a given string. michael@0: * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the michael@0: * textrun will copy it. michael@0: * This calls FetchGlyphExtents on the textrun. michael@0: */ michael@0: virtual gfxTextRun *MakeTextRun(const char16_t *aString, uint32_t aLength, michael@0: const Parameters *aParams, uint32_t aFlags); michael@0: /** michael@0: * Make a textrun for a given string. michael@0: * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the michael@0: * textrun will copy it. michael@0: * This calls FetchGlyphExtents on the textrun. michael@0: */ michael@0: virtual gfxTextRun *MakeTextRun(const uint8_t *aString, uint32_t aLength, michael@0: const Parameters *aParams, uint32_t aFlags); michael@0: michael@0: /** michael@0: * Textrun creation helper for clients that don't want to pass michael@0: * a full Parameters record. michael@0: */ michael@0: template michael@0: gfxTextRun *MakeTextRun(const T *aString, uint32_t aLength, michael@0: gfxContext *aRefContext, michael@0: int32_t aAppUnitsPerDevUnit, michael@0: uint32_t aFlags) michael@0: { michael@0: gfxTextRunFactory::Parameters params = { michael@0: aRefContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit michael@0: }; michael@0: return MakeTextRun(aString, aLength, ¶ms, aFlags); michael@0: } michael@0: michael@0: /** michael@0: * Get the (possibly-cached) width of the hyphen character. michael@0: * The aCtx and aAppUnitsPerDevUnit parameters will be used only if michael@0: * needed to initialize the cached hyphen width; otherwise they are michael@0: * ignored. michael@0: */ michael@0: gfxFloat GetHyphenWidth(gfxTextRun::PropertyProvider* aProvider); michael@0: michael@0: /** michael@0: * Make a text run representing a single hyphen character. michael@0: * This will use U+2010 HYPHEN if available in the first font, michael@0: * otherwise fall back to U+002D HYPHEN-MINUS. michael@0: * The caller is responsible for deleting the returned text run michael@0: * when no longer required. michael@0: */ michael@0: gfxTextRun *MakeHyphenTextRun(gfxContext *aCtx, michael@0: uint32_t aAppUnitsPerDevUnit); michael@0: michael@0: /* helper function for splitting font families on commas and michael@0: * calling a function for each family to fill the mFonts array michael@0: */ michael@0: typedef bool (*FontCreationCallback) (const nsAString& aName, michael@0: const nsACString& aGenericName, michael@0: bool aUseFontSet, michael@0: void *closure); michael@0: bool ForEachFont(const nsAString& aFamilies, michael@0: nsIAtom *aLanguage, michael@0: FontCreationCallback fc, michael@0: void *closure); michael@0: bool ForEachFont(FontCreationCallback fc, void *closure); michael@0: michael@0: /** michael@0: * Check whether a given font (specified by its gfxFontEntry) michael@0: * is already in the fontgroup's list of actual fonts michael@0: */ michael@0: bool HasFont(const gfxFontEntry *aFontEntry); michael@0: michael@0: const nsString& GetFamilies() { return mFamilies; } michael@0: michael@0: // This returns the preferred underline for this font group. michael@0: // Some CJK fonts have wrong underline offset in its metrics. michael@0: // If this group has such "bad" font, each platform's gfxFontGroup initialized mUnderlineOffset. michael@0: // The value should be lower value of first font's metrics and the bad font's metrics. michael@0: // Otherwise, this returns from first font's metrics. michael@0: enum { UNDERLINE_OFFSET_NOT_SET = INT16_MAX }; michael@0: virtual gfxFloat GetUnderlineOffset() { michael@0: if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET) michael@0: mUnderlineOffset = GetFontAt(0)->GetMetrics().underlineOffset; michael@0: return mUnderlineOffset; michael@0: } michael@0: michael@0: virtual already_AddRefed michael@0: FindFontForChar(uint32_t ch, uint32_t prevCh, int32_t aRunScript, michael@0: gfxFont *aPrevMatchedFont, michael@0: uint8_t *aMatchType); michael@0: michael@0: // search through pref fonts for a character, return nullptr if no matching pref font michael@0: virtual already_AddRefed WhichPrefFontSupportsChar(uint32_t aCh); michael@0: michael@0: virtual already_AddRefed michael@0: WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript); michael@0: michael@0: template michael@0: void ComputeRanges(nsTArray& mRanges, michael@0: const T *aString, uint32_t aLength, michael@0: int32_t aRunScript); michael@0: michael@0: gfxUserFontSet* GetUserFontSet(); michael@0: michael@0: // With downloadable fonts, the composition of the font group can change as fonts are downloaded michael@0: // for each change in state of the user font set, the generation value is bumped to avoid picking up michael@0: // previously created text runs in the text run word cache. For font groups based on stylesheets michael@0: // with no @font-face rule, this always returns 0. michael@0: uint64_t GetGeneration(); michael@0: michael@0: // used when logging text performance michael@0: gfxTextPerfMetrics *GetTextPerfMetrics() { return mTextPerf; } michael@0: void SetTextPerfMetrics(gfxTextPerfMetrics *aTextPerf) { mTextPerf = aTextPerf; } michael@0: michael@0: // This will call UpdateFontList() if the user font set is changed. michael@0: void SetUserFontSet(gfxUserFontSet *aUserFontSet); michael@0: michael@0: // If there is a user font set, check to see whether the font list or any michael@0: // caches need updating. michael@0: virtual void UpdateFontList(); michael@0: michael@0: bool ShouldSkipDrawing() const { michael@0: return mSkipDrawing; michael@0: } michael@0: michael@0: class LazyReferenceContextGetter { michael@0: public: michael@0: virtual already_AddRefed GetRefContext() = 0; michael@0: }; michael@0: // The gfxFontGroup keeps ownership of this textrun. michael@0: // It is only guaranteed to exist until the next call to GetEllipsisTextRun michael@0: // (which might use a different appUnitsPerDev value) for the font group, michael@0: // or until UpdateFontList is called, or the fontgroup is destroyed. michael@0: // Get it/use it/forget it :) - don't keep a reference that might go stale. michael@0: gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, michael@0: LazyReferenceContextGetter& aRefContextGetter); michael@0: michael@0: protected: michael@0: nsString mFamilies; michael@0: gfxFontStyle mStyle; michael@0: nsTArray mFonts; michael@0: gfxFloat mUnderlineOffset; michael@0: gfxFloat mHyphenWidth; michael@0: michael@0: nsRefPtr mUserFontSet; michael@0: uint64_t mCurrGeneration; // track the current user font set generation, rebuild font list if needed michael@0: michael@0: gfxTextPerfMetrics *mTextPerf; michael@0: michael@0: // Cache a textrun representing an ellipsis (useful for CSS text-overflow) michael@0: // at a specific appUnitsPerDevPixel size michael@0: nsAutoPtr mCachedEllipsisTextRun; michael@0: michael@0: // cache the most recent pref font to avoid general pref font lookup michael@0: nsRefPtr mLastPrefFamily; michael@0: nsRefPtr mLastPrefFont; michael@0: eFontPrefLang mLastPrefLang; // lang group for last pref font michael@0: eFontPrefLang mPageLang; michael@0: bool mLastPrefFirstFont; // is this the first font in the list of pref fonts for this lang group? michael@0: michael@0: bool mSkipDrawing; // hide text while waiting for a font michael@0: // download to complete (or fallback michael@0: // timer to fire) michael@0: michael@0: /** michael@0: * Textrun creation short-cuts for special cases where we don't need to michael@0: * call a font shaper to generate glyphs. michael@0: */ michael@0: gfxTextRun *MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags); michael@0: gfxTextRun *MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags); michael@0: gfxTextRun *MakeBlankTextRun(uint32_t aLength, michael@0: const Parameters *aParams, uint32_t aFlags); michael@0: michael@0: // Initialize the list of fonts michael@0: void BuildFontList(); michael@0: michael@0: // Init this font group's font metrics. If there no bad fonts, you don't need to call this. michael@0: // But if there are one or more bad fonts which have bad underline offset, michael@0: // you should call this with the *first* bad font. michael@0: void InitMetricsForBadFont(gfxFont* aBadFont); michael@0: michael@0: // Set up the textrun glyphs for an entire text run: michael@0: // find script runs, and then call InitScriptRun for each michael@0: template michael@0: void InitTextRun(gfxContext *aContext, michael@0: gfxTextRun *aTextRun, michael@0: const T *aString, michael@0: uint32_t aLength); michael@0: michael@0: // InitTextRun helper to handle a single script run, by finding font ranges michael@0: // and calling each font's InitTextRun() as appropriate michael@0: template michael@0: void InitScriptRun(gfxContext *aContext, michael@0: gfxTextRun *aTextRun, michael@0: const T *aString, michael@0: uint32_t aScriptRunStart, michael@0: uint32_t aScriptRunEnd, michael@0: int32_t aRunScript); michael@0: michael@0: /* If aResolveGeneric is true, then CSS/Gecko generic family names are michael@0: * replaced with preferred fonts. michael@0: * michael@0: * If aResolveFontName is true then fc() is called only for existing fonts michael@0: * and with actual font names. If false then fc() is called with each michael@0: * family name in aFamilies (after resolving CSS/Gecko generic family names michael@0: * if aResolveGeneric). michael@0: * If aUseFontSet is true, the fontgroup's user font set is checked; michael@0: * if false then it is skipped. michael@0: */ michael@0: bool ForEachFontInternal(const nsAString& aFamilies, michael@0: nsIAtom *aLanguage, michael@0: bool aResolveGeneric, michael@0: bool aResolveFontName, michael@0: bool aUseFontSet, michael@0: FontCreationCallback fc, michael@0: void *closure); michael@0: michael@0: // Helper for font-matching: michael@0: // see if aCh is supported in any of the faces from aFamily; michael@0: // if so return the best style match, else return null. michael@0: already_AddRefed TryAllFamilyMembers(gfxFontFamily* aFamily, michael@0: uint32_t aCh); michael@0: michael@0: static bool FontResolverProc(const nsAString& aName, void *aClosure); michael@0: michael@0: static bool FindPlatformFont(const nsAString& aName, michael@0: const nsACString& aGenericName, michael@0: bool aUseFontSet, michael@0: void *closure); michael@0: michael@0: static NS_HIDDEN_(nsILanguageAtomService*) gLangService; michael@0: }; michael@0: #endif