summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxTextRun.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/thebes/gfxTextRun.h
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/thebes/gfxTextRun.h')
-rw-r--r--gfx/thebes/gfxTextRun.h1551
1 files changed, 1551 insertions, 0 deletions
diff --git a/gfx/thebes/gfxTextRun.h b/gfx/thebes/gfxTextRun.h
new file mode 100644
index 0000000000..e5720b21e6
--- /dev/null
+++ b/gfx/thebes/gfxTextRun.h
@@ -0,0 +1,1551 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=4 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TEXTRUN_H
+#define GFX_TEXTRUN_H
+
+#include <stdint.h>
+
+#include "gfxTypes.h"
+#include "gfxPoint.h"
+#include "gfxFont.h"
+#include "gfxFontConstants.h"
+#include "gfxSkipChars.h"
+#include "gfxPlatform.h"
+#include "gfxPlatformFontList.h"
+#include "gfxUserFontSet.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/RefPtr.h"
+#include "nsPoint.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsTextFrameUtils.h"
+#include "DrawMode.h"
+#include "harfbuzz/hb.h"
+#include "nsUnicodeScriptCodes.h"
+#include "nsColor.h"
+#include "nsFrameList.h"
+#include "X11UndefineNone.h"
+
+#ifdef DEBUG_FRAME_DUMP
+# include <stdio.h>
+#endif
+
+class gfxContext;
+class gfxFontGroup;
+class nsAtom;
+class nsLanguageAtomService;
+class gfxMissingFontRecorder;
+
+namespace mozilla {
+class PostTraversalTask;
+class SVGContextPaint;
+enum class StyleHyphens : uint8_t;
+}; // namespace mozilla
+
+/**
+ * Callback for Draw() to use when drawing text with mode
+ * DrawMode::GLYPH_PATH.
+ */
+struct gfxTextRunDrawCallbacks {
+ /**
+ * Constructs a new DrawCallbacks object.
+ *
+ * @param aShouldPaintSVGGlyphs If true, SVG glyphs will be painted. If
+ * false, SVG glyphs will not be painted; fallback plain glyphs are not
+ * emitted either.
+ */
+ explicit gfxTextRunDrawCallbacks(bool aShouldPaintSVGGlyphs = false)
+ : mShouldPaintSVGGlyphs(aShouldPaintSVGGlyphs) {}
+
+ /**
+ * Called when a path has been emitted to the gfxContext when
+ * painting a text run. This can be called any number of times,
+ * due to partial ligatures and intervening SVG glyphs.
+ */
+ virtual void NotifyGlyphPathEmitted() = 0;
+
+ bool mShouldPaintSVGGlyphs;
+};
+
+/**
+ * gfxTextRun is an abstraction for drawing and measuring substrings of a run
+ * of text. It stores runs of positioned glyph data, each run having a single
+ * gfxFont. The glyphs are associated with a string of source text, and the
+ * gfxTextRun APIs take parameters that are offsets into that source text.
+ *
+ * gfxTextRuns are mostly immutable. The only things that can change are
+ * inter-cluster spacing and line break placement. Spacing is always obtained
+ * lazily by methods that need it, it is not cached. Line breaks are stored
+ * persistently (insofar as they affect the shaping of glyphs; gfxTextRun does
+ * not actually do anything to explicitly account for line breaks). Initially
+ * there are no line breaks. The textrun can record line breaks before or after
+ * any given cluster. (Line breaks specified inside clusters are ignored.)
+ *
+ * It is important that zero-length substrings are handled correctly. This will
+ * be on the test!
+ */
+class gfxTextRun : public gfxShapedText {
+ NS_INLINE_DECL_REFCOUNTING(gfxTextRun);
+
+ protected:
+ // Override operator delete to properly free the object that was
+ // allocated via malloc.
+ void operator delete(void* p) { free(p); }
+
+ virtual ~gfxTextRun();
+
+ public:
+ typedef gfxFont::RunMetrics Metrics;
+ typedef mozilla::gfx::DrawTarget DrawTarget;
+
+ // Public textrun API for general use
+
+ bool IsClusterStart(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].IsClusterStart();
+ }
+ bool IsLigatureGroupStart(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].IsLigatureGroupStart();
+ }
+ bool CanBreakLineBefore(uint32_t aPos) const {
+ return CanBreakBefore(aPos) == CompressedGlyph::FLAG_BREAK_TYPE_NORMAL;
+ }
+ bool CanHyphenateBefore(uint32_t aPos) const {
+ return CanBreakBefore(aPos) == CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN;
+ }
+
+ // Returns a gfxShapedText::CompressedGlyph::FLAG_BREAK_TYPE_* value
+ // as defined in gfxFont.h (may be NONE, NORMAL or HYPHEN).
+ uint8_t CanBreakBefore(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CanBreakBefore();
+ }
+
+ bool CharIsSpace(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CharIsSpace();
+ }
+ bool CharIsTab(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CharIsTab();
+ }
+ bool CharIsNewline(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CharIsNewline();
+ }
+ bool CharMayHaveEmphasisMark(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CharMayHaveEmphasisMark();
+ }
+ bool CharIsFormattingControl(uint32_t aPos) const {
+ MOZ_ASSERT(aPos < GetLength());
+ return mCharacterGlyphs[aPos].CharIsFormattingControl();
+ }
+
+ // All offsets are in terms of the string passed into MakeTextRun.
+
+ // Describe range [start, end) of a text run. The range is
+ // restricted to grapheme cluster boundaries.
+ struct Range {
+ uint32_t start;
+ uint32_t end;
+ uint32_t Length() const { return end - start; }
+
+ Range() : start(0), end(0) {}
+ Range(uint32_t aStart, uint32_t aEnd) : start(aStart), end(aEnd) {}
+ explicit Range(const gfxTextRun* aTextRun)
+ : start(0), end(aTextRun->GetLength()) {}
+ };
+
+ // All coordinates are in layout/app units
+
+ /**
+ * Set the potential linebreaks for a substring of the textrun. These are
+ * the "allow break before" points. Initially, there are no potential
+ * linebreaks.
+ *
+ * This can change glyphs and/or geometry! Some textruns' shapes
+ * depend on potential line breaks (e.g., title-case-converting textruns).
+ * This function is virtual so that those textruns can reshape themselves.
+ *
+ * @return true if this changed the linebreaks, false if the new line
+ * breaks are the same as the old
+ */
+ virtual bool SetPotentialLineBreaks(Range aRange,
+ const uint8_t* aBreakBefore);
+
+ enum class HyphenType : uint8_t {
+ // Code in BreakAndMeasureText depends on the ordering of these values!
+ None,
+ Explicit,
+ Soft,
+ AutoWithManualInSameWord,
+ AutoWithoutManualInSameWord
+ };
+
+ struct HyphenationState {
+ uint32_t mostRecentBoundary = 0;
+ bool hasManualHyphen = false;
+ bool hasExplicitHyphen = false;
+ bool hasAutoHyphen = false;
+ };
+
+ /**
+ * Layout provides PropertyProvider objects. These allow detection of
+ * potential line break points and computation of spacing. We pass the data
+ * this way to allow lazy data acquisition; for example BreakAndMeasureText
+ * will want to only ask for properties of text it's actually looking at.
+ *
+ * NOTE that requested spacing may not actually be applied, if the textrun
+ * is unable to apply it in some context. Exception: spacing around a
+ * whitespace character MUST always be applied.
+ */
+ class PropertyProvider {
+ public:
+ // Detect hyphenation break opportunities in the given range; breaks
+ // not at cluster boundaries will be ignored.
+ virtual void GetHyphenationBreaks(Range aRange,
+ HyphenType* aBreakBefore) const = 0;
+
+ // Returns the provider's hyphenation setting, so callers can decide
+ // whether it is necessary to call GetHyphenationBreaks.
+ // Result is an StyleHyphens value.
+ virtual mozilla::StyleHyphens GetHyphensOption() const = 0;
+
+ // Returns the extra width that will be consumed by a hyphen. This should
+ // be constant for a given textrun.
+ virtual gfxFloat GetHyphenWidth() const = 0;
+
+ typedef gfxFont::Spacing Spacing;
+
+ /**
+ * Get the spacing around the indicated characters. Spacing must be zero
+ * inside clusters. In other words, if character i is not
+ * CLUSTER_START, then character i-1 must have zero after-spacing and
+ * character i must have zero before-spacing.
+ */
+ virtual void GetSpacing(Range aRange, Spacing* aSpacing) const = 0;
+
+ // Returns a gfxContext that can be used to measure the hyphen glyph.
+ // Only called if the hyphen width is requested.
+ virtual already_AddRefed<DrawTarget> GetDrawTarget() const = 0;
+
+ // Return the appUnitsPerDevUnit value to be used when measuring.
+ // Only called if the hyphen width is requested.
+ virtual uint32_t GetAppUnitsPerDevUnit() const = 0;
+ };
+
+ struct MOZ_STACK_CLASS DrawParams {
+ gfxContext* context;
+ DrawMode drawMode = DrawMode::GLYPH_FILL;
+ nscolor textStrokeColor = 0;
+ gfxPattern* textStrokePattern = nullptr;
+ const mozilla::gfx::StrokeOptions* strokeOpts = nullptr;
+ const mozilla::gfx::DrawOptions* drawOpts = nullptr;
+ PropertyProvider* provider = nullptr;
+ // If non-null, the advance width of the substring is set.
+ gfxFloat* advanceWidth = nullptr;
+ mozilla::SVGContextPaint* contextPaint = nullptr;
+ gfxTextRunDrawCallbacks* callbacks = nullptr;
+ explicit DrawParams(gfxContext* aContext) : context(aContext) {}
+ };
+
+ /**
+ * Draws a substring. Uses only GetSpacing from aBreakProvider.
+ * The provided point is the baseline origin on the left of the string
+ * for LTR, on the right of the string for RTL.
+ *
+ * Drawing should respect advance widths in the sense that for LTR runs,
+ * Draw(Range(start, middle), pt, ...) followed by
+ * Draw(Range(middle, end), gfxPoint(pt.x + advance, pt.y), ...)
+ * should have the same effect as
+ * Draw(Range(start, end), pt, ...)
+ *
+ * For RTL runs the rule is:
+ * Draw(Range(middle, end), pt, ...) followed by
+ * Draw(Range(start, middle), gfxPoint(pt.x + advance, pt.y), ...)
+ * should have the same effect as
+ * Draw(Range(start, end), pt, ...)
+ *
+ * Glyphs should be drawn in logical content order, which can be significant
+ * if they overlap (perhaps due to negative spacing).
+ */
+ void Draw(const Range aRange, const mozilla::gfx::Point aPt,
+ const DrawParams& aParams) const;
+
+ /**
+ * Draws the emphasis marks for this text run. Uses only GetSpacing
+ * from aProvider. The provided point is the baseline origin of the
+ * line of emphasis marks.
+ */
+ void DrawEmphasisMarks(gfxContext* aContext, gfxTextRun* aMark,
+ gfxFloat aMarkAdvance, mozilla::gfx::Point aPt,
+ Range aRange, PropertyProvider* aProvider) const;
+
+ /**
+ * Computes the ReflowMetrics for a substring.
+ * Uses GetSpacing from aBreakProvider.
+ * @param aBoundingBoxType which kind of bounding box (loose/tight)
+ */
+ Metrics MeasureText(Range aRange, gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aDrawTargetForTightBoundingBox,
+ PropertyProvider* aProvider) const;
+
+ Metrics MeasureText(gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aDrawTargetForTightBoundingBox,
+ PropertyProvider* aProvider = nullptr) const {
+ return MeasureText(Range(this), aBoundingBoxType,
+ aDrawTargetForTightBoundingBox, aProvider);
+ }
+
+ /**
+ * Computes just the advance width for a substring.
+ * Uses GetSpacing from aBreakProvider.
+ * If aSpacing is not null, the spacing attached before and after
+ * the substring would be returned in it. NOTE: the spacing is
+ * included in the advance width.
+ */
+ gfxFloat GetAdvanceWidth(Range aRange, PropertyProvider* aProvider,
+ PropertyProvider::Spacing* aSpacing = nullptr) const;
+
+ gfxFloat GetAdvanceWidth() const {
+ return GetAdvanceWidth(Range(this), nullptr);
+ }
+
+ /**
+ * Computes the minimum advance width for a substring assuming line
+ * breaking is allowed everywhere.
+ */
+ gfxFloat GetMinAdvanceWidth(Range aRange);
+
+ /**
+ * Clear all stored line breaks for the given range (both before and after),
+ * and then set the line-break state before aRange.start to aBreakBefore and
+ * after the last cluster to aBreakAfter.
+ *
+ * We require that before and after line breaks be consistent. For clusters
+ * i and i+1, we require that if there is a break after cluster i, a break
+ * will be specified before cluster i+1. This may be temporarily violated
+ * (e.g. after reflowing line L and before reflowing line L+1); to handle
+ * these temporary violations, we say that there is a break betwen i and i+1
+ * if a break is specified after i OR a break is specified before i+1.
+ *
+ * This can change textrun geometry! The existence of a linebreak can affect
+ * the advance width of the cluster before the break (when kerning) or the
+ * geometry of one cluster before the break or any number of clusters
+ * after the break. (The one-cluster-before-the-break limit is somewhat
+ * arbitrary; if some scripts require breaking it, then we need to
+ * alter nsTextFrame::TrimTrailingWhitespace, perhaps drastically becase
+ * it could affect the layout of frames before it...)
+ *
+ * We return true if glyphs or geometry changed, false otherwise. This
+ * function is virtual so that gfxTextRun subclasses can reshape
+ * properly.
+ *
+ * @param aAdvanceWidthDelta if non-null, returns the change in advance
+ * width of the given range.
+ */
+ virtual bool SetLineBreaks(Range aRange, bool aLineBreakBefore,
+ bool aLineBreakAfter,
+ gfxFloat* aAdvanceWidthDelta);
+
+ enum SuppressBreak {
+ eNoSuppressBreak,
+ // Measure the range of text as if there is no break before it.
+ eSuppressInitialBreak,
+ // Measure the range of text as if it contains no break
+ eSuppressAllBreaks
+ };
+
+ void ClassifyAutoHyphenations(uint32_t aStart, Range aRange,
+ nsTArray<HyphenType>& aHyphenBuffer,
+ HyphenationState* aWordState);
+
+ /**
+ * Finds the longest substring that will fit into the given width.
+ * Uses GetHyphenationBreaks and GetSpacing from aProvider.
+ * Guarantees the following:
+ * -- 0 <= result <= aMaxLength
+ * -- result is the maximal value of N such that either
+ * N < aMaxLength && line break at N &&
+ * GetAdvanceWidth(Range(aStart, N), aProvider) <= aWidth
+ * OR N < aMaxLength && hyphen break at N &&
+ * GetAdvanceWidth(Range(aStart, N), aProvider) +
+ * GetHyphenWidth() <= aWidth
+ * OR N == aMaxLength &&
+ * GetAdvanceWidth(Range(aStart, N), aProvider) <= aWidth
+ * where GetAdvanceWidth assumes the effect of
+ * SetLineBreaks(Range(aStart, N),
+ * aLineBreakBefore, N < aMaxLength, aProvider)
+ * -- if no such N exists, then result is the smallest N such that
+ * N < aMaxLength && line break at N
+ * OR N < aMaxLength && hyphen break at N
+ * OR N == aMaxLength
+ *
+ * The call has the effect of
+ * SetLineBreaks(Range(aStart, result), aLineBreakBefore,
+ * result < aMaxLength, aProvider)
+ * and the returned metrics and the invariants above reflect this.
+ *
+ * @param aMaxLength this can be UINT32_MAX, in which case the length used
+ * is up to the end of the string
+ * @param aLineBreakBefore set to true if and only if there is an actual
+ * line break at the start of this string.
+ * @param aSuppressBreak what break should be suppressed.
+ * @param aTrimWhitespace if non-null, then we allow a trailing run of
+ * spaces to be trimmed; the width of the space(s) will not be included in
+ * the measured string width for comparison with the limit aWidth, and
+ * trimmed spaces will not be included in returned metrics. The width
+ * of the trimmed spaces will be returned in aTrimWhitespace.
+ * Trimmed spaces are still counted in the "characters fit" result.
+ * @param aHangWhitespace true if we allow whitespace to overflow the
+ * container at a soft-wrap
+ * @param aMetrics if non-null, we fill this in for the returned substring.
+ * If a hyphenation break was used, the hyphen is NOT included in the returned
+ * metrics.
+ * @param aBoundingBoxType whether to make the bounding box in aMetrics tight
+ * @param aDrawTargetForTightBoundingbox a reference DrawTarget to get the
+ * tight bounding box, if requested
+ * @param aUsedHyphenation if non-null, records if we selected a hyphenation
+ * break
+ * @param aLastBreak if non-null and result is aMaxLength, we set this to
+ * the maximal N such that
+ * N < aMaxLength && line break at N &&
+ * GetAdvanceWidth(Range(aStart, N), aProvider) <= aWidth
+ * OR N < aMaxLength && hyphen break at N &&
+ * GetAdvanceWidth(Range(aStart, N), aProvider) +
+ * GetHyphenWidth() <= aWidth
+ * or UINT32_MAX if no such N exists, where GetAdvanceWidth assumes
+ * the effect of
+ * SetLineBreaks(Range(aStart, N), aLineBreakBefore,
+ * N < aMaxLength, aProvider)
+ *
+ * @param aCanWordWrap true if we can break between any two grapheme
+ * clusters. This is set by overflow-wrap|word-wrap: break-word
+ *
+ * @param aBreakPriority in/out the priority of the break opportunity
+ * saved in the line. If we are prioritizing break opportunities, we will
+ * not set a break with a lower priority. @see gfxBreakPriority.
+ *
+ * Note that negative advance widths are possible especially if negative
+ * spacing is provided.
+ */
+ uint32_t BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
+ bool aLineBreakBefore, gfxFloat aWidth,
+ PropertyProvider* aProvider,
+ SuppressBreak aSuppressBreak,
+ gfxFloat* aTrimWhitespace, bool aHangWhitespace,
+ Metrics* aMetrics,
+ gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aDrawTargetForTightBoundingBox,
+ bool* aUsedHyphenation, uint32_t* aLastBreak,
+ bool aCanWordWrap, bool aCanWhitespaceWrap,
+ gfxBreakPriority* aBreakPriority);
+
+ // Utility getters
+
+ void* GetUserData() const { return mUserData; }
+ void SetUserData(void* aUserData) { mUserData = aUserData; }
+
+ void SetFlagBits(nsTextFrameUtils::Flags aFlags) { mFlags2 |= aFlags; }
+ void ClearFlagBits(nsTextFrameUtils::Flags aFlags) { mFlags2 &= ~aFlags; }
+ const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
+ gfxFontGroup* GetFontGroup() const { return mFontGroup; }
+
+ // Call this, don't call "new gfxTextRun" directly. This does custom
+ // allocation and initialization
+ static already_AddRefed<gfxTextRun> Create(
+ const gfxTextRunFactory::Parameters* aParams, uint32_t aLength,
+ gfxFontGroup* aFontGroup, mozilla::gfx::ShapedTextFlags aFlags,
+ nsTextFrameUtils::Flags aFlags2);
+
+ // The text is divided into GlyphRuns as necessary. (In the vast majority
+ // of cases, a gfxTextRun contains just a single GlyphRun.)
+ struct GlyphRun {
+ RefPtr<gfxFont> mFont; // never null in a valid GlyphRun
+ uint32_t mCharacterOffset; // into original UTF16 string
+ mozilla::gfx::ShapedTextFlags
+ mOrientation; // gfxTextRunFactory::TEXT_ORIENT_* value
+ FontMatchType mMatchType;
+ bool mIsCJK; // Whether the text was a CJK script run (used to decide if
+ // text-decoration-skip-ink should not be applied)
+
+ // Set up the properties (but NOT offset) of the GlyphRun.
+ void SetProperties(gfxFont* aFont,
+ mozilla::gfx::ShapedTextFlags aOrientation, bool aIsCJK,
+ FontMatchType aMatchType) {
+ mFont = aFont;
+ mOrientation = aOrientation;
+ mIsCJK = aIsCJK;
+ mMatchType = aMatchType;
+ }
+
+ // Return whether the GlyphRun matches the given properties;
+ // the given FontMatchType will be added to the run if not present.
+ bool Matches(gfxFont* aFont, mozilla::gfx::ShapedTextFlags aOrientation,
+ bool aIsCJK, FontMatchType aMatchType) {
+ if (mFont == aFont && mOrientation == aOrientation && mIsCJK == aIsCJK) {
+ mMatchType.kind |= aMatchType.kind;
+ if (mMatchType.generic == mozilla::StyleGenericFontFamily::None) {
+ mMatchType.generic = aMatchType.generic;
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+
+ // Script run codes that we will mark as CJK to suppress skip-ink behavior.
+ static inline bool IsCJKScript(Script aScript) {
+ switch (aScript) {
+ case Script::BOPOMOFO:
+ case Script::HAN:
+ case Script::HANGUL:
+ case Script::HIRAGANA:
+ case Script::KATAKANA:
+ case Script::KATAKANA_OR_HIRAGANA:
+ case Script::SIMPLIFIED_HAN:
+ case Script::TRADITIONAL_HAN:
+ case Script::JAPANESE:
+ case Script::KOREAN:
+ case Script::HAN_WITH_BOPOMOFO:
+ case Script::JAMO:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ class MOZ_STACK_CLASS GlyphRunIterator {
+ public:
+ GlyphRunIterator(const gfxTextRun* aTextRun, Range aRange,
+ bool aReverse = false)
+ : mTextRun(aTextRun),
+ mDirection(aReverse ? -1 : 1),
+ mStartOffset(aRange.start),
+ mEndOffset(aRange.end) {
+ mNextIndex = mTextRun->FindFirstGlyphRunContaining(
+ aReverse ? aRange.end - 1 : aRange.start);
+ }
+ bool NextRun();
+ const GlyphRun* GetGlyphRun() const { return mGlyphRun; }
+ uint32_t GetStringStart() const { return mStringStart; }
+ uint32_t GetStringEnd() const { return mStringEnd; }
+
+ private:
+ const gfxTextRun* mTextRun;
+ MOZ_INIT_OUTSIDE_CTOR const GlyphRun* mGlyphRun;
+ MOZ_INIT_OUTSIDE_CTOR uint32_t mStringStart;
+ MOZ_INIT_OUTSIDE_CTOR uint32_t mStringEnd;
+ const int32_t mDirection;
+ int32_t mNextIndex;
+ uint32_t mStartOffset;
+ uint32_t mEndOffset;
+ };
+
+ class GlyphRunOffsetComparator {
+ public:
+ bool Equals(const GlyphRun& a, const GlyphRun& b) const {
+ return a.mCharacterOffset == b.mCharacterOffset;
+ }
+
+ bool LessThan(const GlyphRun& a, const GlyphRun& b) const {
+ return a.mCharacterOffset < b.mCharacterOffset;
+ }
+ };
+
+ friend class GlyphRunIterator;
+ friend class FontSelector;
+
+ // API for setting up the textrun glyphs. Should only be called by
+ // things that construct textruns.
+ /**
+ * We've found a run of text that should use a particular font. Call this
+ * only during initialization when font substitution has been computed.
+ * Call it before setting up the glyphs for the characters in this run;
+ * SetMissingGlyph requires that the correct glyphrun be installed.
+ *
+ * If aForceNewRun, a new glyph run will be added, even if the
+ * previously added run uses the same font. If glyph runs are
+ * added out of strictly increasing aStartCharIndex order (via
+ * force), then SortGlyphRuns must be called after all glyph runs
+ * are added before any further operations are performed with this
+ * TextRun.
+ */
+ void AddGlyphRun(gfxFont* aFont, FontMatchType aMatchType,
+ uint32_t aUTF16Offset, bool aForceNewRun,
+ mozilla::gfx::ShapedTextFlags aOrientation, bool aIsCJK);
+ void ResetGlyphRuns() {
+ if (mHasGlyphRunArray) {
+ MOZ_ASSERT(mGlyphRunArray.Length() > 1);
+ // Discard all but the first GlyphRun...
+ mGlyphRunArray.TruncateLength(1);
+ // ...and then convert to the single-run representation.
+ ConvertFromGlyphRunArray();
+ }
+ // Clear out the one remaining GlyphRun.
+ mSingleGlyphRun.mFont = nullptr;
+ }
+ void SortGlyphRuns();
+ void SanitizeGlyphRuns();
+
+ const CompressedGlyph* GetCharacterGlyphs() const final {
+ MOZ_ASSERT(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
+ return mCharacterGlyphs;
+ }
+ CompressedGlyph* GetCharacterGlyphs() final {
+ MOZ_ASSERT(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
+ return mCharacterGlyphs;
+ }
+
+ // clean out results from shaping in progress, used for fallback scenarios
+ void ClearGlyphsAndCharacters();
+
+ void SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
+ uint32_t aCharIndex,
+ mozilla::gfx::ShapedTextFlags aOrientation);
+
+ // Set the glyph data for the given character index to the font's
+ // space glyph, IF this can be done as a "simple" glyph record
+ // (not requiring a DetailedGlyph entry). This avoids the need to call
+ // the font shaper and go through the shaped-word cache for most spaces.
+ //
+ // The parameter aSpaceChar is the original character code for which
+ // this space glyph is being used; if this is U+0020, we need to record
+ // that it could be trimmed at a run edge, whereas other kinds of space
+ // (currently just U+00A0) would not be trimmable/breakable.
+ //
+ // Returns true if it was able to set simple glyph data for the space;
+ // if it returns false, the caller needs to fall back to some other
+ // means to create the necessary (detailed) glyph data.
+ bool SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
+ char16_t aSpaceChar,
+ mozilla::gfx::ShapedTextFlags aOrientation);
+
+ // Record the positions of specific characters that layout may need to
+ // detect in the textrun, even though it doesn't have an explicit copy
+ // of the original text. These are recorded using flag bits in the
+ // CompressedGlyph record; if necessary, we convert "simple" glyph records
+ // to "complex" ones as the Tab and Newline flags are not present in
+ // simple CompressedGlyph records.
+ void SetIsTab(uint32_t aIndex) { EnsureComplexGlyph(aIndex).SetIsTab(); }
+ void SetIsNewline(uint32_t aIndex) {
+ EnsureComplexGlyph(aIndex).SetIsNewline();
+ }
+ void SetNoEmphasisMark(uint32_t aIndex) {
+ EnsureComplexGlyph(aIndex).SetNoEmphasisMark();
+ }
+ void SetIsFormattingControl(uint32_t aIndex) {
+ EnsureComplexGlyph(aIndex).SetIsFormattingControl();
+ }
+
+ /**
+ * Prefetch all the glyph extents needed to ensure that Measure calls
+ * on this textrun not requesting tight boundingBoxes will succeed. Note
+ * that some glyph extents might not be fetched due to OOM or other
+ * errors.
+ */
+ void FetchGlyphExtents(DrawTarget* aRefDrawTarget);
+
+ const GlyphRun* GetGlyphRuns(uint32_t* aNumGlyphRuns) const {
+ if (mHasGlyphRunArray) {
+ *aNumGlyphRuns = mGlyphRunArray.Length();
+ return mGlyphRunArray.Elements();
+ } else {
+ *aNumGlyphRuns = mSingleGlyphRun.mFont ? 1 : 0;
+ return &mSingleGlyphRun;
+ }
+ }
+
+ const GlyphRun* TrailingGlyphRun() const {
+ uint32_t count;
+ const GlyphRun* runs = GetGlyphRuns(&count);
+ return count ? runs + count - 1 : nullptr;
+ }
+
+ // Returns the index of the GlyphRun containing the given offset.
+ // Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
+ uint32_t FindFirstGlyphRunContaining(uint32_t aOffset) const;
+
+ // Copy glyph data from a ShapedWord into this textrun.
+ void CopyGlyphDataFrom(gfxShapedWord* aSource, uint32_t aStart);
+
+ // Copy glyph data for a range of characters from aSource to this
+ // textrun.
+ void CopyGlyphDataFrom(gfxTextRun* aSource, Range aRange, uint32_t aDest);
+
+ // Tell the textrun to release its reference to its creating gfxFontGroup
+ // immediately, rather than on destruction. This is used for textruns
+ // that are actually owned by a gfxFontGroup, so that they don't keep it
+ // permanently alive due to a circular reference. (The caller of this is
+ // taking responsibility for ensuring the textrun will not outlive its
+ // mFontGroup.)
+ void ReleaseFontGroup();
+
+ struct LigatureData {
+ // textrun range of the containing ligature
+ Range mRange;
+ // appunits advance to the start of the ligature part within the ligature;
+ // never includes any spacing
+ gfxFloat mPartAdvance;
+ // appunits width of the ligature part; includes before-spacing
+ // when the part is at the start of the ligature, and after-spacing
+ // when the part is as the end of the ligature
+ gfxFloat mPartWidth;
+
+ bool mClipBeforePart;
+ bool mClipAfterPart;
+ };
+
+ // return storage used by this run, for memory reporter;
+ // nsTransformedTextRun needs to override this as it holds additional data
+ virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+ MOZ_MUST_OVERRIDE;
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+ MOZ_MUST_OVERRIDE;
+
+ nsTextFrameUtils::Flags GetFlags2() const { return mFlags2; }
+
+ // Get the size, if it hasn't already been gotten, marking as it goes.
+ size_t MaybeSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
+ if (mFlags2 & nsTextFrameUtils::Flags::RunSizeAccounted) {
+ return 0;
+ }
+ mFlags2 |= nsTextFrameUtils::Flags::RunSizeAccounted;
+ return SizeOfIncludingThis(aMallocSizeOf);
+ }
+ void ResetSizeOfAccountingFlags() {
+ mFlags2 &= ~nsTextFrameUtils::Flags::RunSizeAccounted;
+ }
+
+ // shaping state - for some font features, fallback is required that
+ // affects the entire run. for example, fallback for one script/font
+ // portion of a textrun requires fallback to be applied to the entire run
+
+ enum ShapingState : uint8_t {
+ eShapingState_Normal, // default state
+ eShapingState_ShapingWithFeature, // have shaped with feature
+ eShapingState_ShapingWithFallback, // have shaped with fallback
+ eShapingState_Aborted, // abort initial iteration
+ eShapingState_ForceFallbackFeature // redo with fallback forced on
+ };
+
+ ShapingState GetShapingState() const { return mShapingState; }
+ void SetShapingState(ShapingState aShapingState) {
+ mShapingState = aShapingState;
+ }
+
+ int32_t GetAdvanceForGlyph(uint32_t aIndex) const {
+ const CompressedGlyph& glyphData = mCharacterGlyphs[aIndex];
+ if (glyphData.IsSimpleGlyph()) {
+ return glyphData.GetSimpleAdvance();
+ }
+ uint32_t glyphCount = glyphData.GetGlyphCount();
+ if (!glyphCount) {
+ return 0;
+ }
+ const DetailedGlyph* details = GetDetailedGlyphs(aIndex);
+ int32_t advance = 0;
+ for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
+ advance += details->mAdvance;
+ }
+ return advance;
+ }
+
+#ifdef DEBUG_FRAME_DUMP
+ void Dump(FILE* aOutput = stderr);
+#endif
+
+ protected:
+ /**
+ * Create a textrun, and set its mCharacterGlyphs to point immediately
+ * after the base object; this is ONLY used in conjunction with placement
+ * new, after allocating a block large enough for the glyph records to
+ * follow the base textrun object.
+ */
+ gfxTextRun(const gfxTextRunFactory::Parameters* aParams, uint32_t aLength,
+ gfxFontGroup* aFontGroup, mozilla::gfx::ShapedTextFlags aFlags,
+ nsTextFrameUtils::Flags aFlags2);
+
+ /**
+ * Helper for the Create() factory method to allocate the required
+ * glyph storage for a textrun object with the basic size aSize,
+ * plus room for aLength glyph records.
+ */
+ static void* AllocateStorageForTextRun(size_t aSize, uint32_t aLength);
+
+ // Pointer to the array of CompressedGlyph records; must be initialized
+ // when the object is constructed.
+ CompressedGlyph* mCharacterGlyphs;
+
+ private:
+ // **** general helpers ****
+
+ // Get the total advance for a range of glyphs.
+ int32_t GetAdvanceForGlyphs(Range aRange) const;
+
+ // Spacing for characters outside the range aSpacingStart/aSpacingEnd
+ // is assumed to be zero; such characters are not passed to aProvider.
+ // This is useful to protect aProvider from being passed character indices
+ // it is not currently able to handle.
+ bool GetAdjustedSpacingArray(
+ Range aRange, PropertyProvider* aProvider, Range aSpacingRange,
+ nsTArray<PropertyProvider::Spacing>* aSpacing) const;
+
+ CompressedGlyph& EnsureComplexGlyph(uint32_t aIndex) {
+ gfxShapedText::EnsureComplexGlyph(aIndex, mCharacterGlyphs[aIndex]);
+ return mCharacterGlyphs[aIndex];
+ }
+
+ // **** ligature helpers ****
+ // (Platforms do the actual ligaturization, but we need to do a bunch of stuff
+ // to handle requests that begin or end inside a ligature)
+
+ // if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero
+ LigatureData ComputeLigatureData(Range aPartRange,
+ PropertyProvider* aProvider) const;
+ gfxFloat ComputePartialLigatureWidth(Range aPartRange,
+ PropertyProvider* aProvider) const;
+ void DrawPartialLigature(gfxFont* aFont, Range aRange,
+ mozilla::gfx::Point* aPt,
+ PropertyProvider* aProvider,
+ TextRunDrawParams& aParams,
+ mozilla::gfx::ShapedTextFlags aOrientation) const;
+ // Advance aRange.start to the start of the nearest ligature, back
+ // up aRange.end to the nearest ligature end; may result in
+ // aRange->start == aRange->end.
+ void ShrinkToLigatureBoundaries(Range* aRange) const;
+ // result in appunits
+ gfxFloat GetPartialLigatureWidth(Range aRange,
+ PropertyProvider* aProvider) const;
+ void AccumulatePartialLigatureMetrics(
+ gfxFont* aFont, Range aRange, gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aRefDrawTarget, PropertyProvider* aProvider,
+ mozilla::gfx::ShapedTextFlags aOrientation, Metrics* aMetrics) const;
+
+ // **** measurement helper ****
+ void AccumulateMetricsForRun(gfxFont* aFont, Range aRange,
+ gfxFont::BoundingBoxType aBoundingBoxType,
+ DrawTarget* aRefDrawTarget,
+ PropertyProvider* aProvider, Range aSpacingRange,
+ mozilla::gfx::ShapedTextFlags aOrientation,
+ Metrics* aMetrics) const;
+
+ // **** drawing helper ****
+ void DrawGlyphs(gfxFont* aFont, Range aRange, mozilla::gfx::Point* aPt,
+ PropertyProvider* aProvider, Range aSpacingRange,
+ TextRunDrawParams& aParams,
+ mozilla::gfx::ShapedTextFlags aOrientation) const;
+
+ // The textrun holds either a single GlyphRun -or- an array;
+ // the flag mHasGlyphRunArray tells us which is present.
+ union {
+ GlyphRun mSingleGlyphRun;
+ nsTArray<GlyphRun> mGlyphRunArray;
+ };
+
+ void ConvertToGlyphRunArray() {
+ MOZ_ASSERT(!mHasGlyphRunArray && mSingleGlyphRun.mFont);
+ GlyphRun tmp = std::move(mSingleGlyphRun);
+ mSingleGlyphRun.~GlyphRun();
+ new (&mGlyphRunArray) nsTArray<GlyphRun>(2);
+ mGlyphRunArray.AppendElement(std::move(tmp));
+ mHasGlyphRunArray = true;
+ }
+
+ void ConvertFromGlyphRunArray() {
+ MOZ_ASSERT(mHasGlyphRunArray && mGlyphRunArray.Length() == 1);
+ GlyphRun tmp = std::move(mGlyphRunArray[0]);
+ mGlyphRunArray.~nsTArray<GlyphRun>();
+ new (&mSingleGlyphRun) GlyphRun(std::move(tmp));
+ mHasGlyphRunArray = false;
+ }
+
+ void* mUserData;
+
+ // mFontGroup is usually a strong reference, but refcounting is managed
+ // manually because it may be explicitly released by ReleaseFontGroup()
+ // in the case where the font group actually owns the textrun.
+ gfxFontGroup* MOZ_OWNING_REF mFontGroup;
+
+ gfxSkipChars mSkipChars;
+
+ nsTextFrameUtils::Flags
+ mFlags2; // additional flags (see also gfxShapedText::mFlags)
+
+ bool mDontSkipDrawing; // true if the text run must not skip drawing, even if
+ // waiting for a user font download, e.g. because we
+ // are using it to draw canvas text
+ bool mReleasedFontGroup; // we already called NS_RELEASE on
+ // mFontGroup, so don't do it again
+ bool mReleasedFontGroupSkippedDrawing; // whether our old mFontGroup value
+ // was set to skip drawing
+ bool mHasGlyphRunArray; // whether we're using an array or
+ // just storing a single glyphrun
+
+ // shaping state for handling variant fallback features
+ // such as subscript/superscript variant glyphs
+ ShapingState mShapingState;
+};
+
+enum class FallbackTypes : uint8_t {
+ // Font fallback used a font configured in Preferences
+ FallbackToPrefsFont = 1 << 0,
+ // Font fallback used a font with FontVisibility::Base
+ FallbackToBaseFont = 1 << 1,
+ // Font fallback used a font with FontVisibility::LangPack
+ FallbackToLangPackFont = 1 << 2,
+ // Font fallback used a font with FontVisibility::User
+ FallbackToUserFont = 1 << 3,
+ // Rendered missing-glyph because no font available for the character
+ MissingFont = 1 << 4,
+ // Rendered missing-glyph but a LangPack font could have been used
+ MissingFontLangPack = 1 << 5,
+ // Rendered missing-glyph but a User font could have been used
+ MissingFontUser = 1 << 6,
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(FallbackTypes)
+
+struct FontMatchingStats {
+ // Set of names that have been looked up (whether successfully or not).
+ nsTHashtable<nsCStringHashKey> mFamilyNames;
+ // Number of font-family names resolved at each level of visibility.
+ uint32_t mBaseFonts = 0;
+ uint32_t mLangPackFonts = 0;
+ uint32_t mUserFonts = 0;
+ uint32_t mWebFonts = 0;
+ FallbackTypes mFallbacks = FallbackTypes(0);
+};
+
+class gfxFontGroup final : public gfxTextRunFactory {
+ public:
+ typedef mozilla::unicode::Script Script;
+ typedef gfxShapedText::CompressedGlyph CompressedGlyph;
+
+ static void
+ Shutdown(); // platform must call this to release the languageAtomService
+
+ gfxFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
+ const gfxFontStyle* aStyle, nsAtom* aLanguage,
+ bool aExplicitLanguage, gfxTextPerfMetrics* aTextPerf,
+ FontMatchingStats* aFontMatchingStats,
+ gfxUserFontSet* aUserFontSet, gfxFloat aDevToCssSize);
+
+ virtual ~gfxFontGroup();
+
+ gfxFontGroup(const gfxFontGroup& aOther) = delete;
+
+ // Returns first valid font in the fontlist or default font.
+ // Initiates userfont loads if userfont not loaded.
+ // aGeneric: if non-null, returns the CSS generic type that was mapped to
+ // this font
+ gfxFont* GetFirstValidFont(
+ uint32_t aCh = 0x20, mozilla::StyleGenericFontFamily* aGeneric = nullptr);
+
+ // Returns the first font in the font-group that has an OpenType MATH table,
+ // or null if no such font is available. The GetMathConstant methods may be
+ // called on the returned font.
+ gfxFont* GetFirstMathFont();
+
+ const gfxFontStyle* GetStyle() const { return &mStyle; }
+
+ /**
+ * The listed characters should be treated as invisible and zero-width
+ * when creating textruns.
+ */
+ static bool IsInvalidChar(uint8_t ch);
+ static bool IsInvalidChar(char16_t ch);
+
+ /**
+ * Make a textrun for a given string.
+ * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
+ * textrun will copy it.
+ * This calls FetchGlyphExtents on the textrun.
+ */
+ already_AddRefed<gfxTextRun> MakeTextRun(const char16_t* aString,
+ uint32_t aLength,
+ const Parameters* aParams,
+ mozilla::gfx::ShapedTextFlags aFlags,
+ nsTextFrameUtils::Flags aFlags2,
+ gfxMissingFontRecorder* aMFR);
+ /**
+ * Make a textrun for a given string.
+ * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
+ * textrun will copy it.
+ * This calls FetchGlyphExtents on the textrun.
+ */
+ already_AddRefed<gfxTextRun> MakeTextRun(const uint8_t* aString,
+ uint32_t aLength,
+ const Parameters* aParams,
+ mozilla::gfx::ShapedTextFlags aFlags,
+ nsTextFrameUtils::Flags aFlags2,
+ gfxMissingFontRecorder* aMFR);
+
+ /**
+ * Textrun creation helper for clients that don't want to pass
+ * a full Parameters record.
+ */
+ template <typename T>
+ already_AddRefed<gfxTextRun> MakeTextRun(const T* aString, uint32_t aLength,
+ DrawTarget* aRefDrawTarget,
+ int32_t aAppUnitsPerDevUnit,
+ mozilla::gfx::ShapedTextFlags aFlags,
+ nsTextFrameUtils::Flags aFlags2,
+ gfxMissingFontRecorder* aMFR) {
+ gfxTextRunFactory::Parameters params = {
+ aRefDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit};
+ return MakeTextRun(aString, aLength, &params, aFlags, aFlags2, aMFR);
+ }
+
+ // Get the (possibly-cached) width of the hyphen character.
+ gfxFloat GetHyphenWidth(const gfxTextRun::PropertyProvider* aProvider);
+
+ /**
+ * Make a text run representing a single hyphen character.
+ * This will use U+2010 HYPHEN if available in the first font,
+ * otherwise fall back to U+002D HYPHEN-MINUS.
+ * The caller is responsible for deleting the returned text run
+ * when no longer required.
+ */
+ already_AddRefed<gfxTextRun> MakeHyphenTextRun(DrawTarget* aDrawTarget,
+ uint32_t aAppUnitsPerDevUnit);
+
+ /**
+ * Check whether a given font (specified by its gfxFontEntry)
+ * is already in the fontgroup's list of actual fonts
+ */
+ bool HasFont(const gfxFontEntry* aFontEntry);
+
+ // This returns the preferred underline for this font group.
+ // Some CJK fonts have wrong underline offset in its metrics.
+ // If this group has such "bad" font, each platform's gfxFontGroup
+ // initialized mUnderlineOffset. The value should be lower value of
+ // first font's metrics and the bad font's metrics. Otherwise, this
+ // returns from first font's metrics.
+ enum { UNDERLINE_OFFSET_NOT_SET = INT16_MAX };
+ gfxFloat GetUnderlineOffset();
+
+ gfxFont* FindFontForChar(uint32_t ch, uint32_t prevCh, uint32_t aNextCh,
+ Script aRunScript, gfxFont* aPrevMatchedFont,
+ FontMatchType* aMatchType);
+
+ gfxUserFontSet* GetUserFontSet();
+
+ // With downloadable fonts, the composition of the font group can change as
+ // fonts are downloaded for each change in state of the user font set, the
+ // generation value is bumped to avoid picking up previously created text runs
+ // in the text run word cache. For font groups based on stylesheets with no
+ // @font-face rule, this always returns 0.
+ uint64_t GetGeneration();
+
+ // generation of the latest fontset rebuild, 0 when no fontset present
+ uint64_t GetRebuildGeneration();
+
+ // used when logging text performance
+ gfxTextPerfMetrics* GetTextPerfMetrics() { return mTextPerf; }
+
+ // This will call UpdateUserFonts() if the user font set is changed.
+ void SetUserFontSet(gfxUserFontSet* aUserFontSet);
+
+ void ClearCachedData() {
+ mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
+ mSkipDrawing = false;
+ mHyphenWidth = -1;
+ mCachedEllipsisTextRun = nullptr;
+ }
+
+ // If there is a user font set, check to see whether the font list or any
+ // caches need updating.
+ void UpdateUserFonts();
+
+ // search for a specific userfont in the list of fonts
+ bool ContainsUserFont(const gfxUserFontEntry* aUserFont);
+
+ bool ShouldSkipDrawing() const { return mSkipDrawing; }
+
+ class LazyReferenceDrawTargetGetter {
+ public:
+ virtual already_AddRefed<DrawTarget> GetRefDrawTarget() = 0;
+ };
+ // The gfxFontGroup keeps ownership of this textrun.
+ // It is only guaranteed to exist until the next call to GetEllipsisTextRun
+ // (which might use a different appUnitsPerDev value or flags) for the font
+ // group, or until UpdateUserFonts is called, or the fontgroup is destroyed.
+ // Get it/use it/forget it :) - don't keep a reference that might go stale.
+ gfxTextRun* GetEllipsisTextRun(
+ int32_t aAppUnitsPerDevPixel, mozilla::gfx::ShapedTextFlags aFlags,
+ LazyReferenceDrawTargetGetter& aRefDrawTargetGetter);
+
+ void CheckForUpdatedPlatformList() {
+ auto* pfl = gfxPlatformFontList::PlatformFontList();
+ if (mFontListGeneration != pfl->GetGeneration()) {
+ // Forget cached fonts that may no longer be valid.
+ mLastPrefFamily = FontFamily();
+ mLastPrefFont = nullptr;
+ mFonts.Clear();
+ BuildFontList();
+ }
+ }
+
+ nsAtom* Language() const { return mLanguage.get(); }
+
+ protected:
+ friend class mozilla::PostTraversalTask;
+
+ struct TextRange {
+ TextRange(uint32_t aStart, uint32_t aEnd, gfxFont* aFont,
+ FontMatchType aMatchType,
+ mozilla::gfx::ShapedTextFlags aOrientation)
+ : start(aStart),
+ end(aEnd),
+ font(aFont),
+ matchType(aMatchType),
+ orientation(aOrientation) {}
+ uint32_t Length() const { return end - start; }
+ uint32_t start, end;
+ RefPtr<gfxFont> font;
+ FontMatchType matchType;
+ mozilla::gfx::ShapedTextFlags orientation;
+ };
+
+ // search through pref fonts for a character, return nullptr if no matching
+ // pref font
+ gfxFont* WhichPrefFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
+ eFontPresentation aPresentation);
+
+ gfxFont* WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
+ Script aRunScript,
+ eFontPresentation aPresentation);
+
+ template <typename T>
+ void ComputeRanges(nsTArray<TextRange>& aRanges, const T* aString,
+ uint32_t aLength, Script aRunScript,
+ mozilla::gfx::ShapedTextFlags aOrientation);
+
+ class FamilyFace {
+ public:
+ FamilyFace()
+ : mOwnedFamily(nullptr),
+ mFontEntry(nullptr),
+ mGeneric(mozilla::StyleGenericFontFamily::None),
+ mFontCreated(false),
+ mLoading(false),
+ mInvalid(false),
+ mCheckForFallbackFaces(false),
+ mIsSharedFamily(false),
+ mHasFontEntry(false) {}
+
+ FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont,
+ mozilla::StyleGenericFontFamily aGeneric)
+ : mOwnedFamily(aFamily),
+ mGeneric(aGeneric),
+ mFontCreated(true),
+ mLoading(false),
+ mInvalid(false),
+ mCheckForFallbackFaces(false),
+ mIsSharedFamily(false),
+ mHasFontEntry(false) {
+ NS_ASSERTION(aFont, "font pointer must not be null");
+ NS_ASSERTION(!aFamily || aFamily->ContainsFace(aFont->GetFontEntry()),
+ "font is not a member of the given family");
+ NS_IF_ADDREF(aFamily);
+ mFont = aFont;
+ NS_ADDREF(aFont);
+ }
+
+ FamilyFace(gfxFontFamily* aFamily, gfxFontEntry* aFontEntry,
+ mozilla::StyleGenericFontFamily aGeneric)
+ : mOwnedFamily(aFamily),
+ mGeneric(aGeneric),
+ mFontCreated(false),
+ mLoading(false),
+ mInvalid(false),
+ mCheckForFallbackFaces(false),
+ mIsSharedFamily(false),
+ mHasFontEntry(true) {
+ NS_ASSERTION(aFontEntry, "font entry pointer must not be null");
+ NS_ASSERTION(!aFamily || aFamily->ContainsFace(aFontEntry),
+ "font is not a member of the given family");
+ NS_IF_ADDREF(aFamily);
+ mFontEntry = aFontEntry;
+ NS_ADDREF(aFontEntry);
+ }
+
+ FamilyFace(mozilla::fontlist::Family* aFamily, gfxFontEntry* aFontEntry,
+ mozilla::StyleGenericFontFamily aGeneric)
+ : mSharedFamily(aFamily),
+ mGeneric(aGeneric),
+ mFontCreated(false),
+ mLoading(false),
+ mInvalid(false),
+ mCheckForFallbackFaces(false),
+ mIsSharedFamily(true),
+ mHasFontEntry(true) {
+ MOZ_ASSERT(aFamily && aFontEntry && aFontEntry->mShmemFace);
+ mFontEntry = aFontEntry;
+ NS_ADDREF(aFontEntry);
+ }
+
+ FamilyFace(const FamilyFace& aOtherFamilyFace)
+ : mGeneric(aOtherFamilyFace.mGeneric),
+ mFontCreated(aOtherFamilyFace.mFontCreated),
+ mLoading(aOtherFamilyFace.mLoading),
+ mInvalid(aOtherFamilyFace.mInvalid),
+ mCheckForFallbackFaces(aOtherFamilyFace.mCheckForFallbackFaces),
+ mIsSharedFamily(aOtherFamilyFace.mIsSharedFamily),
+ mHasFontEntry(aOtherFamilyFace.mHasFontEntry) {
+ if (mIsSharedFamily) {
+ mSharedFamily = aOtherFamilyFace.mSharedFamily;
+ if (mFontCreated) {
+ mFont = aOtherFamilyFace.mFont;
+ NS_ADDREF(mFont);
+ } else if (mHasFontEntry) {
+ mFontEntry = aOtherFamilyFace.mFontEntry;
+ NS_ADDREF(mFontEntry);
+ } else {
+ mSharedFace = aOtherFamilyFace.mSharedFace;
+ }
+ } else {
+ mOwnedFamily = aOtherFamilyFace.mOwnedFamily;
+ NS_IF_ADDREF(mOwnedFamily);
+ if (mFontCreated) {
+ mFont = aOtherFamilyFace.mFont;
+ NS_ADDREF(mFont);
+ } else {
+ mFontEntry = aOtherFamilyFace.mFontEntry;
+ NS_IF_ADDREF(mFontEntry);
+ }
+ }
+ }
+
+ ~FamilyFace() {
+ if (mFontCreated) {
+ NS_RELEASE(mFont);
+ }
+ if (!mIsSharedFamily) {
+ NS_IF_RELEASE(mOwnedFamily);
+ }
+ if (mHasFontEntry) {
+ NS_RELEASE(mFontEntry);
+ }
+ }
+
+ FamilyFace& operator=(const FamilyFace& aOther) {
+ if (mFontCreated) {
+ NS_RELEASE(mFont);
+ }
+ if (!mIsSharedFamily) {
+ NS_IF_RELEASE(mOwnedFamily);
+ }
+ if (mHasFontEntry) {
+ NS_RELEASE(mFontEntry);
+ }
+
+ mGeneric = aOther.mGeneric;
+ mFontCreated = aOther.mFontCreated;
+ mLoading = aOther.mLoading;
+ mInvalid = aOther.mInvalid;
+ mIsSharedFamily = aOther.mIsSharedFamily;
+ mHasFontEntry = aOther.mHasFontEntry;
+
+ if (mIsSharedFamily) {
+ mSharedFamily = aOther.mSharedFamily;
+ if (mFontCreated) {
+ mFont = aOther.mFont;
+ NS_ADDREF(mFont);
+ } else if (mHasFontEntry) {
+ mFontEntry = aOther.mFontEntry;
+ NS_ADDREF(mFontEntry);
+ } else {
+ mSharedFace = aOther.mSharedFace;
+ }
+ } else {
+ mOwnedFamily = aOther.mOwnedFamily;
+ NS_IF_ADDREF(mOwnedFamily);
+ if (mFontCreated) {
+ mFont = aOther.mFont;
+ NS_ADDREF(mFont);
+ } else {
+ mFontEntry = aOther.mFontEntry;
+ NS_IF_ADDREF(mFontEntry);
+ }
+ }
+
+ return *this;
+ }
+
+ gfxFontFamily* OwnedFamily() const {
+ MOZ_ASSERT(!mIsSharedFamily);
+ return mOwnedFamily;
+ }
+ mozilla::fontlist::Family* SharedFamily() const {
+ MOZ_ASSERT(mIsSharedFamily);
+ return mSharedFamily;
+ }
+ gfxFont* Font() const { return mFontCreated ? mFont : nullptr; }
+
+ gfxFontEntry* FontEntry() const {
+ if (mFontCreated) {
+ return mFont->GetFontEntry();
+ }
+ if (mHasFontEntry) {
+ return mFontEntry;
+ }
+ if (mIsSharedFamily) {
+ return gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(
+ mSharedFace, SharedFamily());
+ }
+ return nullptr;
+ }
+
+ mozilla::StyleGenericFontFamily Generic() const { return mGeneric; }
+
+ bool IsSharedFamily() const { return mIsSharedFamily; }
+ bool IsUserFontContainer() const {
+ gfxFontEntry* fe = FontEntry();
+ return fe && fe->mIsUserFontContainer;
+ }
+ bool IsLoading() const { return mLoading; }
+ bool IsInvalid() const { return mInvalid; }
+ void CheckState(bool& aSkipDrawing);
+ void SetLoading(bool aIsLoading) { mLoading = aIsLoading; }
+ void SetInvalid() { mInvalid = true; }
+ bool CheckForFallbackFaces() const { return mCheckForFallbackFaces; }
+ void SetCheckForFallbackFaces() { mCheckForFallbackFaces = true; }
+
+ // Return true if we're currently loading (or waiting for) a resource that
+ // may support the given character.
+ bool IsLoadingFor(uint32_t aCh) {
+ if (!IsLoading()) {
+ return false;
+ }
+ MOZ_ASSERT(IsUserFontContainer());
+ auto* ufe = static_cast<gfxUserFontEntry*>(FontEntry());
+ return ufe && ufe->CharacterInUnicodeRange(aCh);
+ }
+
+ void SetFont(gfxFont* aFont) {
+ NS_ASSERTION(aFont, "font pointer must not be null");
+ NS_ADDREF(aFont);
+ if (mFontCreated) {
+ NS_RELEASE(mFont);
+ } else if (mHasFontEntry) {
+ NS_RELEASE(mFontEntry);
+ mHasFontEntry = false;
+ }
+ mFont = aFont;
+ mFontCreated = true;
+ mLoading = false;
+ }
+
+ bool EqualsUserFont(const gfxUserFontEntry* aUserFont) const;
+
+ private:
+ union {
+ gfxFontFamily* MOZ_OWNING_REF mOwnedFamily;
+ mozilla::fontlist::Family* MOZ_NON_OWNING_REF mSharedFamily;
+ };
+ // either a font or a font entry exists
+ union {
+ // Whichever of these fields is actually present will be a strong
+ // reference, with refcounting handled manually.
+ gfxFont* MOZ_OWNING_REF mFont;
+ gfxFontEntry* MOZ_OWNING_REF mFontEntry;
+ mozilla::fontlist::Face* MOZ_NON_OWNING_REF mSharedFace;
+ };
+ mozilla::StyleGenericFontFamily mGeneric;
+ bool mFontCreated : 1;
+ bool mLoading : 1;
+ bool mInvalid : 1;
+ bool mCheckForFallbackFaces : 1;
+ bool mIsSharedFamily : 1;
+ bool mHasFontEntry : 1;
+ };
+
+ // List of font families, either named or generic.
+ // Generic names map to system pref fonts based on language.
+ mozilla::FontFamilyList mFamilyList;
+
+ // Fontlist containing a font entry for each family found. gfxFont objects
+ // are created as needed and userfont loads are initiated when needed.
+ // Code should be careful about addressing this array directly.
+ nsTArray<FamilyFace> mFonts;
+
+ RefPtr<gfxFont> mDefaultFont;
+ gfxFontStyle mStyle;
+
+ RefPtr<nsAtom> mLanguage;
+
+ gfxFloat mUnderlineOffset;
+ gfxFloat mHyphenWidth;
+ gfxFloat mDevToCssSize;
+
+ RefPtr<gfxUserFontSet> mUserFontSet;
+ uint64_t mCurrGeneration; // track the current user font set generation,
+ // rebuild font list if needed
+
+ gfxTextPerfMetrics* mTextPerf;
+
+ FontMatchingStats* mFontMatchingStats;
+
+ // Cache a textrun representing an ellipsis (useful for CSS text-overflow)
+ // at a specific appUnitsPerDevPixel size and orientation
+ RefPtr<gfxTextRun> mCachedEllipsisTextRun;
+
+ // cache the most recent pref font to avoid general pref font lookup
+ FontFamily mLastPrefFamily;
+ RefPtr<gfxFont> mLastPrefFont;
+ eFontPrefLang mLastPrefLang; // lang group for last pref font
+ eFontPrefLang mPageLang;
+ bool mLastPrefFirstFont; // is this the first font in the list of pref fonts
+ // for this lang group?
+
+ bool mSkipDrawing; // hide text while waiting for a font
+ // download to complete (or fallback
+ // timer to fire)
+
+ bool mExplicitLanguage; // Does mLanguage come from an explicit attribute?
+
+ uint32_t mFontListGeneration = 0; // platform font list generation for this
+ // fontgroup
+
+ /**
+ * Textrun creation short-cuts for special cases where we don't need to
+ * call a font shaper to generate glyphs.
+ */
+ already_AddRefed<gfxTextRun> MakeEmptyTextRun(
+ const Parameters* aParams, mozilla::gfx::ShapedTextFlags aFlags,
+ nsTextFrameUtils::Flags aFlags2);
+
+ already_AddRefed<gfxTextRun> MakeSpaceTextRun(
+ const Parameters* aParams, mozilla::gfx::ShapedTextFlags aFlags,
+ nsTextFrameUtils::Flags aFlags2);
+
+ template <typename T>
+ already_AddRefed<gfxTextRun> MakeBlankTextRun(
+ const T* aString, uint32_t aLength, const Parameters* aParams,
+ mozilla::gfx::ShapedTextFlags aFlags, nsTextFrameUtils::Flags aFlags2);
+
+ // Initialize the list of fonts
+ void BuildFontList();
+
+ // Get the font at index i within the fontlist, for character aCh (in case
+ // of fonts with multiple resources and unicode-range partitioning).
+ // Will initiate userfont load if not already loaded.
+ // May return null if userfont not loaded or if font invalid.
+ // If *aLoading is true, a relevant resource is already being loaded so no
+ // new download will be initiated; if a download is started, *aLoading will
+ // be set to true on return.
+ gfxFont* GetFontAt(int32_t i, uint32_t aCh, bool* aLoading);
+
+ // Simplified version of GetFontAt() for use where we just need a font for
+ // metrics, math layout tables, etc.
+ gfxFont* GetFontAt(int32_t i, uint32_t aCh = 0x20) {
+ bool loading = false;
+ return GetFontAt(i, aCh, &loading);
+ }
+
+ // will always return a font or force a shutdown
+ gfxFont* GetDefaultFont();
+
+ // Init this font group's font metrics. If there no bad fonts, you don't need
+ // to call this. But if there are one or more bad fonts which have bad
+ // underline offset, you should call this with the *first* bad font.
+ void InitMetricsForBadFont(gfxFont* aBadFont);
+
+ // Set up the textrun glyphs for an entire text run:
+ // find script runs, and then call InitScriptRun for each
+ template <typename T>
+ void InitTextRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun,
+ const T* aString, uint32_t aLength,
+ gfxMissingFontRecorder* aMFR);
+
+ // InitTextRun helper to handle a single script run, by finding font ranges
+ // and calling each font's InitTextRun() as appropriate
+ template <typename T>
+ void InitScriptRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun,
+ const T* aString, uint32_t aScriptRunStart,
+ uint32_t aScriptRunEnd, Script aRunScript,
+ gfxMissingFontRecorder* aMFR);
+
+ // Helper for font-matching:
+ // search all faces in a family for a fallback in cases where it's unclear
+ // whether the family might have a font for a given character
+ gfxFont* FindFallbackFaceForChar(const FamilyFace& aFamily, uint32_t aCh,
+ uint32_t aNextCh,
+ eFontPresentation aPresentation);
+
+ gfxFont* FindFallbackFaceForChar(mozilla::fontlist::Family* aFamily,
+ uint32_t aCh, uint32_t aNextCh,
+ eFontPresentation aPresentation);
+
+ gfxFont* FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh,
+ uint32_t aNextCh,
+ eFontPresentation aPresentation);
+
+ // helper methods for looking up fonts
+
+ // lookup and add a font with a given name (i.e. *not* a generic!)
+ void AddPlatformFont(const nsACString& aName, bool aQuotedName,
+ nsTArray<FamilyAndGeneric>& aFamilyList);
+
+ // do style selection and add entries to list
+ void AddFamilyToFontList(gfxFontFamily* aFamily,
+ mozilla::StyleGenericFontFamily aGeneric);
+ void AddFamilyToFontList(mozilla::fontlist::Family* aFamily,
+ mozilla::StyleGenericFontFamily aGeneric);
+};
+
+// A "missing font recorder" is to be used during text-run creation to keep
+// a record of any scripts encountered for which font coverage was lacking;
+// when Flush() is called, it sends a notification that front-end code can use
+// to download fonts on demand (or whatever else it wants to do).
+
+#define GFX_MISSING_FONTS_NOTIFY_PREF "gfx.missing_fonts.notify"
+
+class gfxMissingFontRecorder {
+ public:
+ gfxMissingFontRecorder() {
+ MOZ_COUNT_CTOR(gfxMissingFontRecorder);
+ memset(&mMissingFonts, 0, sizeof(mMissingFonts));
+ }
+
+ ~gfxMissingFontRecorder() {
+#ifdef DEBUG
+ for (uint32_t i = 0; i < kNumScriptBitsWords; i++) {
+ NS_ASSERTION(mMissingFonts[i] == 0,
+ "failed to flush the missing-font recorder");
+ }
+#endif
+ MOZ_COUNT_DTOR(gfxMissingFontRecorder);
+ }
+
+ // record this script code in our mMissingFonts bitset
+ void RecordScript(mozilla::unicode::Script aScriptCode) {
+ mMissingFonts[static_cast<uint32_t>(aScriptCode) >> 5] |=
+ (1 << (static_cast<uint32_t>(aScriptCode) & 0x1f));
+ }
+
+ // send a notification of any missing-scripts that have been
+ // recorded, and clear the mMissingFonts set for re-use
+ void Flush();
+
+ // forget any missing-scripts that have been recorded up to now;
+ // called before discarding a recorder we no longer care about
+ void Clear() { memset(&mMissingFonts, 0, sizeof(mMissingFonts)); }
+
+ private:
+ // Number of 32-bit words needed for the missing-script flags
+ static const uint32_t kNumScriptBitsWords =
+ ((static_cast<int>(mozilla::unicode::Script::NUM_SCRIPT_CODES) + 31) /
+ 32);
+ uint32_t mMissingFonts[kNumScriptBitsWords];
+};
+
+#endif