зеркало из https://github.com/mozilla/gecko-dev.git
1561 строка
60 KiB
C++
1561 строка
60 KiB
C++
/* -*- 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 "nsTHashSet.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).
|
|
nsTHashSet<nsCString> 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::StyleFontFamilyList& 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, ¶ms, 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() const { return mTextPerf; }
|
|
|
|
FontMatchingStats* GetFontMatchingStats() const { return mFontMatchingStats; }
|
|
|
|
// 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;
|
|
mDefaultFont = 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::StyleFontFamilyList 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?
|
|
|
|
// First CSS generic in the list, if any (used to select among font prefs
|
|
// during fallback)
|
|
mozilla::StyleGenericFontFamily mFirstGeneric =
|
|
mozilla::StyleGenericFontFamily::None;
|
|
|
|
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
|