mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-05 15:59:45 +00:00
Bug 370588. Make gfxTextRun be a single shared class for all platforms. Move all platform-specific code to gfxFontGroup and gfxFont implementations. Actually implement textruns properly on Mac and Windows. r=vlad,pavlov
This commit is contained in:
parent
1723cb6673
commit
807c3d9076
@ -424,11 +424,9 @@ nsThebesFontMetrics::DrawString(const char *aString, PRUint32 aLength,
|
||||
if (!textRun.get())
|
||||
return NS_ERROR_FAILURE;
|
||||
gfxPoint pt(aX, aY);
|
||||
#ifdef MOZ_X11
|
||||
if (mTextRunRTL) {
|
||||
pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
|
||||
}
|
||||
#endif
|
||||
textRun->Draw(aContext->Thebes(), pt, 0, aLength,
|
||||
nsnull, &provider, nsnull);
|
||||
return NS_OK;
|
||||
@ -450,11 +448,9 @@ nsThebesFontMetrics::DrawString(const PRUnichar* aString, PRUint32 aLength,
|
||||
if (!textRun.get())
|
||||
return NS_ERROR_FAILURE;
|
||||
gfxPoint pt(aX, aY);
|
||||
#ifdef MOZ_X11
|
||||
if (mTextRunRTL) {
|
||||
pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
|
||||
}
|
||||
#endif
|
||||
textRun->Draw(aContext->Thebes(), pt, 0, aLength,
|
||||
nsnull, &provider, nsnull);
|
||||
return NS_OK;
|
||||
|
@ -148,11 +148,7 @@ public:
|
||||
virtual gfxFontGroup* GetThebesFontGroup() { return mFontGroup; }
|
||||
|
||||
PRBool GetRightToLeftTextRunMode() {
|
||||
#ifdef MOZ_X11
|
||||
return mTextRunRTL;
|
||||
#else
|
||||
return mIsRightToLeft;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -247,11 +247,8 @@ nsThebesRenderingContext::GetHints(PRUint32& aResult)
|
||||
|
||||
aResult |= (NS_RENDERING_HINT_BIDI_REORDERING |
|
||||
NS_RENDERING_HINT_ARABIC_SHAPING |
|
||||
NS_RENDERING_HINT_REORDER_SPACED_TEXT);
|
||||
|
||||
#ifdef MOZ_X11
|
||||
aResult |= NS_RENDERING_HINT_NEW_TEXT_RUNS;
|
||||
#endif
|
||||
NS_RENDERING_HINT_REORDER_SPACED_TEXT |
|
||||
NS_RENDERING_HINT_NEW_TEXT_RUNS);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -82,6 +82,11 @@ protected:
|
||||
|
||||
gfxFloat mAdjustedSize;
|
||||
void InitMetrics(ATSUFontID aFontID, ATSFontRef aFontRef);
|
||||
|
||||
virtual void SetupCairoFont(cairo_t *aCR)
|
||||
{
|
||||
cairo_set_scaled_font (aCR, CairoScaledFont());
|
||||
}
|
||||
};
|
||||
|
||||
class THEBES_API gfxAtsuiFontGroup : public gfxFontGroup {
|
||||
@ -98,6 +103,11 @@ public:
|
||||
Parameters* aParams);
|
||||
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
|
||||
Parameters* aParams);
|
||||
// Here, aString is actually aLength + 1 chars long; the first char
|
||||
// is an LRO or RLO bidi control character to force setting the direction
|
||||
// for all characters
|
||||
gfxTextRun *MakeTextRunInternal(const PRUnichar *aString, PRUint32 aLength,
|
||||
Parameters *aParams);
|
||||
|
||||
ATSUFontFallbacks *GetATSUFontFallbacksPtr() { return &mFallbacks; }
|
||||
|
||||
@ -112,32 +122,8 @@ protected:
|
||||
const nsACString& aGenericName,
|
||||
void *closure);
|
||||
|
||||
void InitTextRun(gfxTextRun *aRun, const PRUnichar *aString, PRUint32 aLength);
|
||||
|
||||
ATSUFontFallbacks mFallbacks;
|
||||
};
|
||||
|
||||
class THEBES_API gfxAtsuiTextRun {
|
||||
public:
|
||||
gfxAtsuiTextRun(const nsAString& aString, gfxAtsuiFontGroup *aFontGroup);
|
||||
~gfxAtsuiTextRun();
|
||||
|
||||
virtual void Draw(gfxContext *aContext, gfxPoint pt);
|
||||
virtual gfxFloat Measure(gfxContext *aContext);
|
||||
|
||||
virtual void SetSpacing(const nsTArray<gfxFloat>& spacingArray);
|
||||
virtual const nsTArray<gfxFloat> *const GetSpacing() const;
|
||||
|
||||
void SetRightToLeft(PRBool aIsRTL) { mIsRTL = aIsRTL; }
|
||||
PRBool IsRightToLeft() { return mIsRTL; }
|
||||
|
||||
private:
|
||||
nsString mString;
|
||||
gfxAtsuiFontGroup *mGroup;
|
||||
|
||||
ATSUTextLayout mATSULayout;
|
||||
|
||||
nsTArray<ATSUStyle> mStylesToDispose;
|
||||
|
||||
PRPackedBool mIsRTL;
|
||||
};
|
||||
|
||||
#endif /* GFX_ATSUIFONTS_H */
|
||||
|
@ -50,6 +50,8 @@
|
||||
class gfxContext;
|
||||
class gfxTextRun;
|
||||
class nsIAtom;
|
||||
class gfxFontGroup;
|
||||
typedef struct _cairo cairo_t;
|
||||
|
||||
#define FONT_STYLE_NORMAL 0
|
||||
#define FONT_STYLE_ITALIC 1
|
||||
@ -66,8 +68,6 @@ class nsIAtom;
|
||||
#define FONT_WEIGHT_NORMAL 400
|
||||
#define FONT_WEIGHT_BOLD 700
|
||||
|
||||
class gfxFontGroup;
|
||||
|
||||
struct THEBES_API gfxFontStyle {
|
||||
gfxFontStyle(PRUint8 aStyle, PRUint8 aVariant,
|
||||
PRUint16 aWeight, PRUint8 aDecoration, gfxFloat aSize,
|
||||
@ -144,6 +144,7 @@ public:
|
||||
|
||||
virtual nsString GetUniqueName() { return GetName(); }
|
||||
|
||||
// Font metrics
|
||||
struct Metrics {
|
||||
gfxFloat xHeight;
|
||||
gfxFloat superscriptOffset;
|
||||
@ -170,10 +171,121 @@ public:
|
||||
};
|
||||
virtual const gfxFont::Metrics& GetMetrics() = 0;
|
||||
|
||||
/**
|
||||
* We let layout specify spacing on either side of any
|
||||
* character. We need to specify both before and after
|
||||
* spacing so that substring measurement can do the right things.
|
||||
*/
|
||||
struct Spacing {
|
||||
gfxFloat mBefore;
|
||||
gfxFloat mAfter;
|
||||
};
|
||||
/**
|
||||
* Metrics for a particular string
|
||||
*/
|
||||
struct RunMetrics {
|
||||
RunMetrics() {
|
||||
mAdvanceWidth = mAscent = mDescent = 0.0;
|
||||
mBoundingBox = gfxRect(0,0,0,0);
|
||||
mClusterCount = 0;
|
||||
}
|
||||
|
||||
void CombineWith(const RunMetrics& aOtherOnRight) {
|
||||
mAscent = PR_MAX(mAscent, aOtherOnRight.mAscent);
|
||||
mDescent = PR_MAX(mDescent, aOtherOnRight.mDescent);
|
||||
mBoundingBox =
|
||||
mBoundingBox.Union(aOtherOnRight.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
|
||||
mAdvanceWidth += aOtherOnRight.mAdvanceWidth;
|
||||
mClusterCount += aOtherOnRight.mClusterCount;
|
||||
}
|
||||
|
||||
// can be negative (partly due to negative spacing).
|
||||
// Advance widths should be additive: the advance width of the
|
||||
// (offset1, length1) plus the advance width of (offset1 + length1,
|
||||
// length2) should be the advance width of (offset1, length1 + length2)
|
||||
gfxFloat mAdvanceWidth;
|
||||
|
||||
// For zero-width substrings, these must be zero!
|
||||
gfxFloat mAscent; // always non-negative
|
||||
gfxFloat mDescent; // always non-negative
|
||||
|
||||
// Bounding box that is guaranteed to include everything drawn.
|
||||
// If aTightBoundingBox was set to true when these metrics were
|
||||
// generated, this will tightly wrap the glyphs, otherwise it is
|
||||
// "loose" and may be larger than the true bounding box.
|
||||
// Coordinates are relative to the baseline left origin, so typically
|
||||
// mBoundingBox.y == -mAscent
|
||||
gfxRect mBoundingBox;
|
||||
|
||||
// Count of the number of grapheme clusters. Layout needs
|
||||
// this to compute tab offsets. For SpecialStrings, this is always 1.
|
||||
PRInt32 mClusterCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw a series of glyphs to aContext. The direction of aTextRun must
|
||||
* be honoured.
|
||||
* @param aStart the first character to draw
|
||||
* @param aEnd draw characters up to here
|
||||
* @param aBaselineOrigin the baseline origin; the left end of the baseline
|
||||
* for LTR textruns, the right end of the baseline for RTL textruns. On return,
|
||||
* this should be updated to the other end of the baseline. In application units.
|
||||
* @param aSpacing spacing to insert before and after characters (for RTL
|
||||
* glyphs, before-spacing is inserted to the right of characters). There
|
||||
* are aEnd - aStart elements in this array, unless it's null to indicate
|
||||
* that there is no spacing.
|
||||
* @param aDrawToPath when true, add the glyph outlines to the current path
|
||||
* instead of drawing the glyphs
|
||||
*
|
||||
* Callers guarantee:
|
||||
* -- aStart and aEnd are aligned to cluster and ligature boundaries
|
||||
* -- all glyphs use this font
|
||||
*
|
||||
* The default implementation builds a cairo glyph array and
|
||||
* calls cairo_show_glyphs or cairo_glyph_path.
|
||||
*/
|
||||
virtual void Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
|
||||
gfxContext *aContext, PRBool aDrawToPath, gfxPoint *aBaselineOrigin,
|
||||
Spacing *aSpacing);
|
||||
/**
|
||||
* Measure a run of characters. See gfxTextRun::Metrics.
|
||||
* @param aTight if false, then return the union of the glyph extents
|
||||
* with the font-box for the characters (the rectangle with x=0,width=
|
||||
* the advance width for the character run,y=-(font ascent), and height=
|
||||
* font ascent + font descent). Otherwise, we must return as tight as possible
|
||||
* an approximation to the area actually painted by glyphs.
|
||||
* @param aSpacing spacing to insert before and after glyphs. The bounding box
|
||||
* need not include the spacing itself, but the spacing affects the glyph
|
||||
* positions. null if there is no spacing.
|
||||
*
|
||||
* Callers guarantee:
|
||||
* -- aStart and aEnd are aligned to cluster and ligature boundaries
|
||||
* -- all glyphs use this font
|
||||
*
|
||||
* The default implementation just uses font metrics and aTextRun's
|
||||
* advances, and assumes no characters fall outside the font box. In
|
||||
* general this is insufficient, because that assumption is not always true.
|
||||
*/
|
||||
virtual RunMetrics Measure(gfxTextRun *aTextRun,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
PRBool aTightBoundingBox,
|
||||
Spacing *aSpacing);
|
||||
/**
|
||||
* Line breaks have been changed at the beginning and/or end of a substring
|
||||
* of the text. Reshaping may be required; glyph updating is permitted.
|
||||
* @return true if anything was changed, false otherwise
|
||||
*/
|
||||
PRBool NotifyLineBreaksChanged(gfxTextRun *aTextRun,
|
||||
PRUint32 aStart, PRUint32 aLength)
|
||||
{ return PR_FALSE; }
|
||||
|
||||
protected:
|
||||
// The family name of the font
|
||||
nsString mName;
|
||||
|
||||
// This is called by the default Draw() implementation above.
|
||||
virtual void SetupCairoFont(cairo_t *aCR) = 0;
|
||||
|
||||
const gfxFontStyle *mStyle;
|
||||
};
|
||||
|
||||
@ -225,7 +337,15 @@ public:
|
||||
* When set, the text may have UTF16 surrogate pairs, otherwise it
|
||||
* doesn't.
|
||||
*/
|
||||
TEXT_HAS_SURROGATES = 0x0100
|
||||
TEXT_HAS_SURROGATES = 0x0100,
|
||||
/**
|
||||
* When set, the RunMetrics::mBoundingBox field will be initialized
|
||||
* properly based on glyph extents, in particular, glyph extents that
|
||||
* overflow the standard font-box (the box defined by the ascent, descent
|
||||
* and advance width of the glyph). When not set, it may just be the
|
||||
* standard font-box even if glyphs overflow.
|
||||
*/
|
||||
TEXT_NEED_BOUNDING_BOX = 0x0200
|
||||
};
|
||||
|
||||
/**
|
||||
@ -268,92 +388,11 @@ public:
|
||||
Parameters *aParams) = 0;
|
||||
};
|
||||
|
||||
class THEBES_API gfxFontGroup : public gfxTextRunFactory {
|
||||
public:
|
||||
gfxFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle);
|
||||
|
||||
virtual ~gfxFontGroup() {
|
||||
mFonts.Clear();
|
||||
}
|
||||
|
||||
gfxFont *GetFontAt(PRInt32 i) {
|
||||
return NS_STATIC_CAST(gfxFont*, mFonts[i]);
|
||||
}
|
||||
PRUint32 FontListLength() const {
|
||||
return mFonts.Length();
|
||||
}
|
||||
|
||||
PRBool Equals(const gfxFontGroup& other) const {
|
||||
return mFamilies.Equals(other.mFamilies) &&
|
||||
mStyle.Equals(other.mStyle);
|
||||
}
|
||||
|
||||
const gfxFontStyle *GetStyle() const { return &mStyle; }
|
||||
|
||||
virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle) = 0;
|
||||
|
||||
// These need to be repeated from gfxTextRunFactory because of C++'s
|
||||
// hiding rules!
|
||||
virtual gfxTextRun *MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
|
||||
Parameters* aParams) = 0;
|
||||
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
|
||||
Parameters* aParams) = 0;
|
||||
|
||||
/* helper function for splitting font families on commas and
|
||||
* calling a function for each family to fill the mFonts array
|
||||
*/
|
||||
typedef PRBool (*FontCreationCallback) (const nsAString& aName,
|
||||
const nsACString& aGenericName,
|
||||
void *closure);
|
||||
static PRBool ForEachFont(const nsAString& aFamilies,
|
||||
const nsACString& aLangGroup,
|
||||
FontCreationCallback fc,
|
||||
void *closure);
|
||||
PRBool ForEachFont(FontCreationCallback fc, void *closure);
|
||||
|
||||
/* this will call back fc with the a generic font based on the style's langgroup */
|
||||
void FindGenericFontFromStyle(FontCreationCallback fc, void *closure);
|
||||
|
||||
const nsString& GetFamilies() { return mFamilies; }
|
||||
|
||||
/**
|
||||
* Special strings are strings that we might need to draw/measure but aren't
|
||||
* actually substrings of DOM text. They never have extra spacing and
|
||||
* aren't involved in breaking.
|
||||
*/
|
||||
enum SpecialString {
|
||||
STRING_ELLIPSIS,
|
||||
STRING_HYPHEN,
|
||||
STRING_SPACE,
|
||||
STRING_MAX = STRING_SPACE
|
||||
};
|
||||
|
||||
// Get the textrun for the given special string. Ownership remains
|
||||
// with this gfxFontGroup. Copy relevant parameters from the template textrun.
|
||||
// SpecialString textruns do not use spacing and it's fine to pass null
|
||||
// as the PropertyProvider* in their methods.
|
||||
// This is stubbed out until we have full textrun support on all platforms.
|
||||
virtual gfxTextRun *GetSpecialStringTextRun(SpecialString aString,
|
||||
gfxTextRun *aTemplate)
|
||||
{ return nsnull; }
|
||||
|
||||
protected:
|
||||
nsString mFamilies;
|
||||
gfxFontStyle mStyle;
|
||||
nsTArray< nsRefPtr<gfxFont> > mFonts;
|
||||
|
||||
static PRBool ForEachFontInternal(const nsAString& aFamilies,
|
||||
const nsACString& aLangGroup,
|
||||
PRBool aResolveGeneric,
|
||||
FontCreationCallback fc,
|
||||
void *closure);
|
||||
|
||||
static PRBool FontResolverProc(const nsAString& aName, void *aClosure);
|
||||
};
|
||||
|
||||
/**
|
||||
* gfxTextRun is an abstraction for drawing and measuring substrings of a run
|
||||
* of text.
|
||||
* 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.
|
||||
*
|
||||
* \r and \n characters must be ignored completely. Substring operations
|
||||
* will not normally include these characters.
|
||||
@ -379,25 +418,31 @@ protected:
|
||||
*
|
||||
* It is important that zero-length substrings are handled correctly. This will
|
||||
* be on the test!
|
||||
*
|
||||
* This class should not be subclassed.
|
||||
*/
|
||||
class THEBES_API gfxTextRun {
|
||||
public:
|
||||
virtual ~gfxTextRun() {}
|
||||
~gfxTextRun() {}
|
||||
|
||||
enum {
|
||||
// character is the start of a grapheme cluster
|
||||
CLUSTER_START = 0x01,
|
||||
// character is a cluster start but is part of a ligature started
|
||||
// in a previous cluster.
|
||||
CONTINUES_LIGATURE = 0x02,
|
||||
// line break opportunity before this character
|
||||
LINE_BREAK_BEFORE = 0x04
|
||||
};
|
||||
virtual void GetCharFlags(PRUint32 aStart, PRUint32 aLength,
|
||||
PRUint8 *aFlags) = 0;
|
||||
virtual PRUint8 GetCharFlags(PRUint32 aStart) = 0;
|
||||
typedef gfxFont::RunMetrics Metrics;
|
||||
|
||||
virtual PRUint32 GetLength() = 0;
|
||||
// Public textrun API for general use
|
||||
|
||||
PRBool IsClusterStart(PRUint32 aPos) {
|
||||
NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
|
||||
return mCharacterGlyphs[aPos].IsClusterStart();
|
||||
}
|
||||
PRBool IsLigatureContinuation(PRUint32 aPos) {
|
||||
NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
|
||||
return mCharacterGlyphs[aPos].IsLigatureContinuation();
|
||||
}
|
||||
PRBool CanBreakLineBefore(PRUint32 aPos) {
|
||||
NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
|
||||
return mCharacterGlyphs[aPos].CanBreakBefore();
|
||||
}
|
||||
|
||||
PRUint32 GetLength() { return mCharacterCount; }
|
||||
|
||||
// All PRUint32 aStart, PRUint32 aLength ranges below are restricted to
|
||||
// grapheme cluster boundaries! All offsets are in terms of the string
|
||||
@ -409,12 +454,10 @@ public:
|
||||
* This can be called to force gfxTextRun to remember the text used
|
||||
* to create it and *never* call TextProvider::GetText again.
|
||||
*
|
||||
* The default implementation is to not remember anything. If you want
|
||||
* to be able to recover text from the gfxTextRun user you need to override
|
||||
* these.
|
||||
* Right now we don't implement these.
|
||||
*/
|
||||
virtual void RememberText(const PRUnichar *aText, PRUint32 aLength) {}
|
||||
virtual void RememberText(const PRUint8 *aText, PRUint32 aLength) {}
|
||||
void RememberText(const PRUnichar *aText, PRUint32 aLength) {}
|
||||
void RememberText(const PRUint8 *aText, PRUint32 aLength) {}
|
||||
|
||||
/**
|
||||
* Set the potential linebreaks for a substring of the textrun. These are
|
||||
@ -424,8 +467,8 @@ public:
|
||||
* @return true if this changed the linebreaks, false if the new line
|
||||
* breaks are the same as the old
|
||||
*/
|
||||
virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
|
||||
PRPackedBool *aBreakBefore) = 0;
|
||||
PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
|
||||
PRPackedBool *aBreakBefore);
|
||||
|
||||
/**
|
||||
* This is provided so a textrun can (re)obtain the original text used to
|
||||
@ -465,15 +508,8 @@ public:
|
||||
// be constant for a given textrun.
|
||||
virtual gfxFloat GetHyphenWidth() = 0;
|
||||
|
||||
/**
|
||||
* We let the property provider specify spacing on either side of any
|
||||
* character. We need to specify both before and after
|
||||
* spacing so that substring measurement can do the right things.
|
||||
*/
|
||||
struct Spacing {
|
||||
gfxFloat mBefore;
|
||||
gfxFloat mAfter;
|
||||
};
|
||||
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
|
||||
@ -509,11 +545,11 @@ public:
|
||||
* Glyphs should be drawn in logical content order, which can be significant
|
||||
* if they overlap (perhaps due to negative spacing).
|
||||
*/
|
||||
virtual void Draw(gfxContext *aContext, gfxPoint aPt,
|
||||
PRUint32 aStart, PRUint32 aLength,
|
||||
const gfxRect *aDirtyRect,
|
||||
PropertyProvider *aProvider,
|
||||
gfxFloat *aAdvanceWidth) = 0;
|
||||
void Draw(gfxContext *aContext, gfxPoint aPt,
|
||||
PRUint32 aStart, PRUint32 aLength,
|
||||
const gfxRect *aDirtyRect,
|
||||
PropertyProvider *aProvider,
|
||||
gfxFloat *aAdvanceWidth);
|
||||
|
||||
/**
|
||||
* Renders a substring to a path. Uses only GetSpacing from aBreakProvider.
|
||||
@ -529,66 +565,26 @@ public:
|
||||
* UNLIKE Draw above, this cannot be used to render substrings that start or
|
||||
* end inside a ligature.
|
||||
*/
|
||||
virtual void DrawToPath(gfxContext *aContext, gfxPoint aPt,
|
||||
PRUint32 aStart, PRUint32 aLength,
|
||||
PropertyProvider *aBreakProvider,
|
||||
gfxFloat *aAdvanceWidth) = 0;
|
||||
|
||||
// Metrics needed by reflow
|
||||
struct Metrics {
|
||||
Metrics() {
|
||||
mAdvanceWidth = mAscent = mDescent = 0.0;
|
||||
mBoundingBox = gfxRect(0,0,0,0);
|
||||
mClusterCount = 0;
|
||||
}
|
||||
|
||||
void CombineWith(const Metrics& aOtherOnRight) {
|
||||
mAscent = PR_MAX(mAscent, aOtherOnRight.mAscent);
|
||||
mDescent = PR_MAX(mDescent, aOtherOnRight.mDescent);
|
||||
mBoundingBox =
|
||||
mBoundingBox.Union(aOtherOnRight.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
|
||||
mAdvanceWidth += aOtherOnRight.mAdvanceWidth;
|
||||
mClusterCount += aOtherOnRight.mClusterCount;
|
||||
}
|
||||
|
||||
// can be negative (partly due to negative spacing).
|
||||
// Advance widths should be additive: the advance width of the
|
||||
// (offset1, length1) plus the advance width of (offset1 + length1,
|
||||
// length2) should be the advance width of (offset1, length1 + length2)
|
||||
gfxFloat mAdvanceWidth;
|
||||
|
||||
// For zero-width substrings, these must be zero!
|
||||
gfxFloat mAscent; // always non-negative
|
||||
gfxFloat mDescent; // always non-negative
|
||||
|
||||
// Bounding box that is guaranteed to include everything drawn.
|
||||
// If aTightBoundingBox was set to true when these metrics were
|
||||
// generated, this will tightly wrap the glyphs, otherwise it is
|
||||
// "loose" and may be larger than the true bounding box.
|
||||
// Coordinates are relative to the baseline left origin, so typically
|
||||
// mBoundingBox.y == -mAscent
|
||||
gfxRect mBoundingBox;
|
||||
|
||||
// Count of the number of grapheme clusters. Layout needs
|
||||
// this to compute tab offsets. For SpecialStrings, this is always 1.
|
||||
PRInt32 mClusterCount;
|
||||
};
|
||||
void DrawToPath(gfxContext *aContext, gfxPoint aPt,
|
||||
PRUint32 aStart, PRUint32 aLength,
|
||||
PropertyProvider *aBreakProvider,
|
||||
gfxFloat *aAdvanceWidth);
|
||||
|
||||
/**
|
||||
* Computes the ReflowMetrics for a substring.
|
||||
* Uses GetSpacing from aBreakProvider.
|
||||
* @param aTightBoundingBox if true, we make the bounding box tight
|
||||
*/
|
||||
virtual Metrics MeasureText(PRUint32 aStart, PRUint32 aLength,
|
||||
PRBool aTightBoundingBox,
|
||||
PropertyProvider *aProvider) = 0;
|
||||
Metrics MeasureText(PRUint32 aStart, PRUint32 aLength,
|
||||
PRBool aTightBoundingBox,
|
||||
PropertyProvider *aProvider);
|
||||
|
||||
/**
|
||||
* Computes just the advance width for a substring.
|
||||
* Uses GetSpacing from aBreakProvider.
|
||||
*/
|
||||
virtual gfxFloat GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
|
||||
PropertyProvider *aProvider) = 0;
|
||||
gfxFloat GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
|
||||
PropertyProvider *aProvider);
|
||||
|
||||
/**
|
||||
* Clear all stored line breaks for the given range (both before and after),
|
||||
@ -613,10 +609,10 @@ public:
|
||||
* @param aAdvanceWidthDelta if non-null, returns the change in advance
|
||||
* width of the given range.
|
||||
*/
|
||||
virtual void SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
|
||||
PRBool aLineBreakBefore, PRBool aLineBreakAfter,
|
||||
TextProvider *aProvider,
|
||||
gfxFloat *aAdvanceWidthDelta) = 0;
|
||||
void SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
|
||||
PRBool aLineBreakBefore, PRBool aLineBreakAfter,
|
||||
TextProvider *aProvider,
|
||||
gfxFloat *aAdvanceWidthDelta);
|
||||
|
||||
/**
|
||||
* Finds the longest substring that will fit into the given width.
|
||||
@ -661,20 +657,20 @@ public:
|
||||
* Note that negative advance widths are possible especially if negative
|
||||
* spacing is provided.
|
||||
*/
|
||||
virtual PRUint32 BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
|
||||
PRBool aLineBreakBefore, gfxFloat aWidth,
|
||||
PropertyProvider *aProvider,
|
||||
PRBool aSuppressInitialBreak,
|
||||
Metrics *aMetrics, PRBool aTightBoundingBox,
|
||||
PRBool *aUsedHyphenation,
|
||||
PRUint32 *aLastBreak) = 0;
|
||||
PRUint32 BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
|
||||
PRBool aLineBreakBefore, gfxFloat aWidth,
|
||||
PropertyProvider *aProvider,
|
||||
PRBool aSuppressInitialBreak,
|
||||
Metrics *aMetrics, PRBool aTightBoundingBox,
|
||||
PRBool *aUsedHyphenation,
|
||||
PRUint32 *aLastBreak);
|
||||
|
||||
/**
|
||||
* Update the reference context.
|
||||
* XXX this is a hack. New text frame does not call this. Use only
|
||||
* temporarily for old text frame.
|
||||
*/
|
||||
virtual void SetContext(gfxContext *aContext) {}
|
||||
void SetContext(gfxContext *aContext) {}
|
||||
|
||||
// Utility getters
|
||||
|
||||
@ -685,22 +681,360 @@ public:
|
||||
const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
|
||||
float GetAppUnitsPerDevUnit() { return mAppUnitsPerDevUnit; }
|
||||
|
||||
protected:
|
||||
gfxTextRun(gfxTextRunFactory::Parameters *aParams)
|
||||
: mUserData(aParams->mUserData),
|
||||
mAppUnitsPerDevUnit(aParams->mAppUnitsPerDevUnit),
|
||||
mFlags(aParams->mFlags)
|
||||
{
|
||||
if (aParams->mSkipChars) {
|
||||
mSkipChars.TakeFrom(aParams->mSkipChars);
|
||||
// The caller is responsible for initializing our glyphs after construction.
|
||||
// Initially all glyphs are such that GetCharacterGlyphs()[i].IsMissing() is true.
|
||||
gfxTextRun(gfxTextRunFactory::Parameters *aParams, PRUint32 aLength);
|
||||
|
||||
/**
|
||||
* This class records the information associated with a character in the
|
||||
* input string. It's optimized for the case where there is one glyph
|
||||
* representing that character alone.
|
||||
*/
|
||||
class CompressedGlyph {
|
||||
public:
|
||||
CompressedGlyph() { mValue = 0; }
|
||||
|
||||
enum {
|
||||
// Indicates that a cluster starts at this character and can be
|
||||
// rendered using a single glyph with a reasonable advance offset
|
||||
// and no special glyph offset. A "reasonable" advance offset is
|
||||
// one that is a) a multiple of a pixel and b) fits in the available
|
||||
// bits (currently 14). We should revisit this, especially a),
|
||||
// if we want to support subpixel-aligned text.
|
||||
FLAG_IS_SIMPLE_GLYPH = 0x80000000U,
|
||||
// Indicates that a linebreak is allowed before this character
|
||||
FLAG_CAN_BREAK_BEFORE = 0x40000000U,
|
||||
|
||||
ADVANCE_MASK = 0x3FFF0000U,
|
||||
ADVANCE_SHIFT = 16,
|
||||
|
||||
GLYPH_MASK = 0x0000FFFFU,
|
||||
|
||||
// Non-simple glyphs have the following tags
|
||||
|
||||
TAG_MASK = 0x000000FFU,
|
||||
// Indicates that this character corresponds to a missing glyph
|
||||
// and should be skipped (or possibly, render the character
|
||||
// Unicode value in some special way)
|
||||
TAG_MISSING = 0x00U,
|
||||
// Indicates that a cluster starts at this character and is rendered
|
||||
// using one or more glyphs which cannot be represented here.
|
||||
// Look up the DetailedGlyph table instead.
|
||||
TAG_COMPLEX_CLUSTER = 0x01U,
|
||||
// Indicates that a cluster starts at this character but is rendered
|
||||
// as part of a ligature starting in a previous cluster.
|
||||
// NOTE: we divide up the ligature's width by the number of clusters
|
||||
// to get the width assigned to each cluster.
|
||||
TAG_LIGATURE_CONTINUATION = 0x21U,
|
||||
|
||||
// Values where the upper 28 bits equal 0x80 are reserved for
|
||||
// non-cluster-start characters (see IsClusterStart below)
|
||||
|
||||
// Indicates that a cluster does not start at this character, this is
|
||||
// a low UTF16 surrogate
|
||||
TAG_LOW_SURROGATE = 0x80U,
|
||||
// Indicates that a cluster does not start at this character, this is
|
||||
// part of a cluster starting with an earlier character (but not
|
||||
// a low surrogate).
|
||||
TAG_CLUSTER_CONTINUATION = 0x81U
|
||||
};
|
||||
|
||||
// "Simple glyphs" have a simple glyph ID, simple advance and their
|
||||
// x and y offsets are zero. Also the glyph extents do not overflow
|
||||
// the font-box defined by the font ascent, descent and glyph advance width.
|
||||
// These case is optimized to avoid storing DetailedGlyphs.
|
||||
|
||||
// Returns true if the glyph ID aGlyph fits into the compressed representation
|
||||
static PRBool IsSimpleGlyphID(PRUint32 aGlyph) {
|
||||
return (aGlyph & GLYPH_MASK) == aGlyph;
|
||||
}
|
||||
// Returns true if the advance aAdvance fits into the compressed representation.
|
||||
// aAdvance is in pixels.
|
||||
static PRBool IsSimpleAdvancePixels(PRUint32 aAdvance) {
|
||||
return (aAdvance & (ADVANCE_MASK >> ADVANCE_SHIFT)) == aAdvance;
|
||||
}
|
||||
|
||||
PRBool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; }
|
||||
PRBool IsComplex(PRUint32 aTag) const { return (mValue & (FLAG_IS_SIMPLE_GLYPH|TAG_MASK)) == aTag; }
|
||||
PRBool IsMissing() const { return IsComplex(TAG_MISSING); }
|
||||
PRBool IsComplexCluster() const { return IsComplex(TAG_COMPLEX_CLUSTER); }
|
||||
PRBool IsLigatureContinuation() const { return IsComplex(TAG_LIGATURE_CONTINUATION); }
|
||||
PRBool IsClusterContinuation() const { return IsComplex(TAG_CLUSTER_CONTINUATION); }
|
||||
PRBool IsLowSurrogate() const { return IsComplex(TAG_LOW_SURROGATE); }
|
||||
PRBool IsClusterStart() const { return (mValue & (FLAG_IS_SIMPLE_GLYPH|0x80U)) != 0x80U; }
|
||||
|
||||
PRUint32 GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; }
|
||||
PRUint32 GetSimpleGlyph() const { return mValue & GLYPH_MASK; }
|
||||
|
||||
PRUint32 GetComplexTag() const { return mValue & TAG_MASK; }
|
||||
|
||||
PRBool CanBreakBefore() const { return (mValue & FLAG_CAN_BREAK_BEFORE) != 0; }
|
||||
// Returns FLAG_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
|
||||
PRUint32 SetCanBreakBefore(PRBool aCanBreakBefore) {
|
||||
PRUint32 breakMask = aCanBreakBefore*FLAG_CAN_BREAK_BEFORE;
|
||||
PRUint32 toggle = breakMask ^ (mValue & FLAG_CAN_BREAK_BEFORE);
|
||||
mValue ^= toggle;
|
||||
return toggle;
|
||||
}
|
||||
|
||||
CompressedGlyph& SetSimpleGlyph(PRUint32 aAdvancePixels, PRUint32 aGlyph) {
|
||||
NS_ASSERTION(IsSimpleAdvancePixels(aAdvancePixels), "Advance overflow");
|
||||
NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
|
||||
mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | FLAG_IS_SIMPLE_GLYPH |
|
||||
(aAdvancePixels << ADVANCE_SHIFT) | aGlyph;
|
||||
return *this;
|
||||
}
|
||||
CompressedGlyph& SetComplex(PRUint32 aTag) {
|
||||
mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | aTag;
|
||||
return *this;
|
||||
}
|
||||
CompressedGlyph& SetMissing() { return SetComplex(TAG_MISSING); }
|
||||
CompressedGlyph& SetComplexCluster() { return SetComplex(TAG_COMPLEX_CLUSTER); }
|
||||
CompressedGlyph& SetLowSurrogate() { return SetComplex(TAG_LOW_SURROGATE); }
|
||||
CompressedGlyph& SetLigatureContinuation() { return SetComplex(TAG_LIGATURE_CONTINUATION); }
|
||||
CompressedGlyph& SetClusterContinuation() { return SetComplex(TAG_CLUSTER_CONTINUATION); }
|
||||
private:
|
||||
PRUint32 mValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* When the glyphs for a character don't fit into a CompressedGlyph record
|
||||
* in SimpleGlyph format, we use an array of DetailedGlyphs instead.
|
||||
*/
|
||||
struct DetailedGlyph {
|
||||
/** This is true for the last DetailedGlyph in the array. This lets
|
||||
* us track the length of the array. */
|
||||
PRUint32 mIsLastGlyph:1;
|
||||
PRUint32 mGlyphID:31;
|
||||
// The advance, x-offset and y-offset of the glyph, in pixels
|
||||
float mAdvance, mXOffset, mYOffset;
|
||||
};
|
||||
|
||||
// The text is divided into GlyphRuns as necessary
|
||||
struct GlyphRun {
|
||||
nsRefPtr<gfxFont> mFont; // never null
|
||||
PRUint32 mCharacterOffset; // into original UTF16 string
|
||||
};
|
||||
|
||||
class GlyphRunIterator {
|
||||
public:
|
||||
GlyphRunIterator(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aLength)
|
||||
: mTextRun(aTextRun), mStartOffset(aStart), mEndOffset(aStart + aLength) {
|
||||
mNextIndex = mTextRun->FindFirstGlyphRunContaining(aStart);
|
||||
}
|
||||
PRBool NextRun();
|
||||
GlyphRun *GetGlyphRun() { return mGlyphRun; }
|
||||
PRUint32 GetStringStart() { return mStringStart; }
|
||||
PRUint32 GetStringEnd() { return mStringEnd; }
|
||||
private:
|
||||
gfxTextRun *mTextRun;
|
||||
GlyphRun *mGlyphRun;
|
||||
PRUint32 mStringStart;
|
||||
PRUint32 mStringEnd;
|
||||
PRUint32 mNextIndex;
|
||||
PRUint32 mStartOffset;
|
||||
PRUint32 mEndOffset;
|
||||
};
|
||||
|
||||
friend class GlyphRunIterator;
|
||||
friend class FontSelector;
|
||||
|
||||
// API for setting up the textrun glyphs. Should only be called by
|
||||
// things that construct textruns.
|
||||
/**
|
||||
* Record every character that is the second half of a surrogate pair.
|
||||
* This should be called after creating a Unicode textrun.
|
||||
*/
|
||||
void RecordSurrogates(const PRUnichar *aString);
|
||||
/**
|
||||
* We've found a run of text that should use a particular font. Call this
|
||||
* only during initialization when font substitution has been computed.
|
||||
*/
|
||||
nsresult AddGlyphRun(gfxFont *aFont, PRUint32 aStartCharIndex);
|
||||
// Call the following glyph-setters during initialization or during reshaping
|
||||
// only. It is OK to overwrite existing data for a character.
|
||||
/**
|
||||
* Set the glyph for a character. Also allows you to set low surrogates,
|
||||
* cluster and ligature continuations.
|
||||
*/
|
||||
void SetCharacterGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) {
|
||||
NS_ASSERTION(aCharIndex > 0 ||
|
||||
(aGlyph.IsClusterStart() && !aGlyph.IsLigatureContinuation()),
|
||||
"First character must be the start of a cluster and can't be a ligature continuation!");
|
||||
if (mCharacterGlyphs) {
|
||||
mCharacterGlyphs[aCharIndex] = aGlyph;
|
||||
}
|
||||
if (mDetailedGlyphs) {
|
||||
mDetailedGlyphs[aCharIndex] = nsnull;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set some detailed glyphs for a character. The data is copied from aGlyphs,
|
||||
* the caller retains ownership.
|
||||
*/
|
||||
void SetDetailedGlyphs(PRUint32 aCharIndex, DetailedGlyph *aGlyphs,
|
||||
PRUint32 aNumGlyphs);
|
||||
|
||||
void * mUserData;
|
||||
// API for access to the raw glyph data, needed by gfxFont::Draw
|
||||
// and gfxFont::GetBoundingBox
|
||||
const CompressedGlyph *GetCharacterGlyphs() { return mCharacterGlyphs; }
|
||||
const DetailedGlyph *GetDetailedGlyphs(PRUint32 aCharIndex) {
|
||||
NS_ASSERTION(mDetailedGlyphs && mDetailedGlyphs[aCharIndex],
|
||||
"Requested detailed glyphs when there aren't any, "
|
||||
"I think I'll go and have a lie down...");
|
||||
return mDetailedGlyphs[aCharIndex];
|
||||
}
|
||||
PRUint32 CountMissingGlyphs();
|
||||
|
||||
private:
|
||||
// **** general helpers ****
|
||||
|
||||
// Returns the index of the GlyphRun containing the given offset.
|
||||
// Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
|
||||
PRUint32 FindFirstGlyphRunContaining(PRUint32 aOffset);
|
||||
// Computes the x-advance for a given cluster starting at aClusterOffset. Does
|
||||
// not include any spacing. Result is in device pixels.
|
||||
gfxFloat ComputeClusterAdvance(PRUint32 aClusterOffset);
|
||||
|
||||
// **** 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)
|
||||
|
||||
struct LigatureData {
|
||||
PRUint32 mStartOffset;
|
||||
PRUint32 mEndOffset;
|
||||
PRUint32 mClusterCount;
|
||||
PRUint32 mPartClusterIndex;
|
||||
gfxFloat mLigatureWidth; // appunits
|
||||
gfxFloat mBeforeSpacing; // appunits
|
||||
gfxFloat mAfterSpacing; // appunits
|
||||
};
|
||||
// if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero
|
||||
LigatureData ComputeLigatureData(PRUint32 aPartOffset, PropertyProvider *aProvider);
|
||||
void GetAdjustedSpacing(PRUint32 aStart, PRUint32 aEnd,
|
||||
PropertyProvider *aProvider, PropertyProvider::Spacing *aSpacing);
|
||||
PRBool GetAdjustedSpacingArray(PRUint32 aStart, PRUint32 aEnd,
|
||||
PropertyProvider *aProvider,
|
||||
nsTArray<PropertyProvider::Spacing> *aSpacing);
|
||||
void DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, PRUint32 aOffset,
|
||||
const gfxRect *aDirtyRect, gfxPoint *aPt,
|
||||
PropertyProvider *aProvider);
|
||||
// result in appunits
|
||||
void ShrinkToLigatureBoundaries(PRUint32 *aStart, PRUint32 *aEnd);
|
||||
gfxFloat GetPartialLigatureWidth(PRUint32 aStart, PRUint32 aEnd, PropertyProvider *aProvider);
|
||||
void AccumulatePartialLigatureMetrics(gfxFont *aFont,
|
||||
PRUint32 aOffset, PRBool aTight,
|
||||
PropertyProvider *aProvider,
|
||||
Metrics *aMetrics);
|
||||
|
||||
// **** measurement helper ****
|
||||
void AccumulateMetricsForRun(gfxFont *aFont, PRUint32 aStart,
|
||||
PRUint32 aEnd, PRBool aTight,
|
||||
PropertyProvider *aProvider,
|
||||
Metrics *aMetrics);
|
||||
|
||||
// **** drawing helper ****
|
||||
void DrawGlyphs(gfxFont *aFont, gfxContext *aContext, PRBool aDrawToPath,
|
||||
gfxPoint *aPt, PRUint32 aStart, PRUint32 aEnd,
|
||||
PropertyProvider *aProvider);
|
||||
|
||||
// All our glyph data is in logical order, not visual
|
||||
nsAutoArrayPtr<CompressedGlyph> mCharacterGlyphs;
|
||||
nsAutoArrayPtr<nsAutoArrayPtr<DetailedGlyph> > mDetailedGlyphs; // only non-null if needed
|
||||
// XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
|
||||
// for smaller size especially in the super-common one-glyphrun case
|
||||
nsAutoTArray<GlyphRun,1> mGlyphRuns;
|
||||
|
||||
void *mUserData;
|
||||
gfxSkipChars mSkipChars;
|
||||
// This is actually an integer, but we keep it in float form to reduce
|
||||
// the conversions required
|
||||
float mAppUnitsPerDevUnit;
|
||||
PRUint32 mFlags;
|
||||
PRUint32 mCharacterCount;
|
||||
};
|
||||
|
||||
class THEBES_API gfxFontGroup : public gfxTextRunFactory {
|
||||
public:
|
||||
gfxFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle);
|
||||
|
||||
virtual ~gfxFontGroup() {
|
||||
mFonts.Clear();
|
||||
}
|
||||
|
||||
gfxFont *GetFontAt(PRInt32 i) {
|
||||
return NS_STATIC_CAST(gfxFont*, mFonts[i]);
|
||||
}
|
||||
PRUint32 FontListLength() const {
|
||||
return mFonts.Length();
|
||||
}
|
||||
|
||||
PRBool Equals(const gfxFontGroup& other) const {
|
||||
return mFamilies.Equals(other.mFamilies) &&
|
||||
mStyle.Equals(other.mStyle);
|
||||
}
|
||||
|
||||
const gfxFontStyle *GetStyle() const { return &mStyle; }
|
||||
|
||||
virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle) = 0;
|
||||
|
||||
// These need to be repeated from gfxTextRunFactory because of C++'s
|
||||
// hiding rules!
|
||||
virtual gfxTextRun *MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
|
||||
Parameters* aParams) = 0;
|
||||
// This function should set TEXT_IS_8BIT in aParams->mFlags.
|
||||
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
|
||||
Parameters* aParams) = 0;
|
||||
|
||||
/* helper function for splitting font families on commas and
|
||||
* calling a function for each family to fill the mFonts array
|
||||
*/
|
||||
typedef PRBool (*FontCreationCallback) (const nsAString& aName,
|
||||
const nsACString& aGenericName,
|
||||
void *closure);
|
||||
static PRBool ForEachFont(const nsAString& aFamilies,
|
||||
const nsACString& aLangGroup,
|
||||
FontCreationCallback fc,
|
||||
void *closure);
|
||||
PRBool ForEachFont(FontCreationCallback fc, void *closure);
|
||||
|
||||
/* this will call back fc with the a generic font based on the style's langgroup */
|
||||
void FindGenericFontFromStyle(FontCreationCallback fc, void *closure);
|
||||
|
||||
const nsString& GetFamilies() { return mFamilies; }
|
||||
|
||||
/**
|
||||
* Special strings are strings that we might need to draw/measure but aren't
|
||||
* actually substrings of DOM text. They never have extra spacing and
|
||||
* aren't involved in breaking.
|
||||
*/
|
||||
enum SpecialString {
|
||||
STRING_ELLIPSIS,
|
||||
STRING_HYPHEN,
|
||||
STRING_SPACE,
|
||||
STRING_MAX = STRING_SPACE
|
||||
};
|
||||
|
||||
// Get the textrun for the given special string. Ownership remains
|
||||
// with this gfxFontGroup. Copy relevant parameters from the template textrun.
|
||||
// SpecialString textruns do not use spacing and it's fine to pass null
|
||||
// as the PropertyProvider* in their methods.
|
||||
// This is stubbed out until we have full textrun support on all platforms.
|
||||
gfxTextRun *GetSpecialStringTextRun(SpecialString aString,
|
||||
gfxTextRun *aTemplate);
|
||||
|
||||
protected:
|
||||
nsString mFamilies;
|
||||
gfxFontStyle mStyle;
|
||||
nsTArray< nsRefPtr<gfxFont> > mFonts;
|
||||
nsAutoPtr<gfxTextRun> mSpecialStrings[STRING_MAX + 1];
|
||||
|
||||
static PRBool ForEachFontInternal(const nsAString& aFamilies,
|
||||
const nsACString& aLangGroup,
|
||||
PRBool aResolveGeneric,
|
||||
FontCreationCallback fc,
|
||||
void *closure);
|
||||
|
||||
static PRBool FontResolverProc(const nsAString& aName, void *aClosure);
|
||||
};
|
||||
#endif
|
||||
|
@ -77,57 +77,10 @@ public:
|
||||
XftFont *GetXftFont () { RealizeXftFont (); return mXftFont; }
|
||||
gfxFloat GetAdjustedSize() { RealizeFont(); return mAdjustedSize; }
|
||||
|
||||
// These APIs need to be moved up to gfxFont with gfxPangoTextRun switched
|
||||
// to gfxTextRun
|
||||
/**
|
||||
* Draw a series of glyphs to aContext. The direction of aTextRun must
|
||||
* be honoured.
|
||||
* @param aStart the first character to draw
|
||||
* @param aEnd draw characters up to here
|
||||
* @param aBaselineOrigin the baseline origin; the left end of the baseline
|
||||
* for LTR textruns, the right end of the baseline for RTL textruns. On return,
|
||||
* this should be updated to the other end of the baseline. In application units.
|
||||
* @param aSpacing spacing to insert before and after characters (for RTL
|
||||
* glyphs, before-spacing is inserted to the right of characters). There
|
||||
* are aEnd - aStart elements in this array, unless it's null to indicate
|
||||
* that there is no spacing.
|
||||
* @param aDrawToPath when true, add the glyph outlines to the current path
|
||||
* instead of drawing the glyphs
|
||||
*
|
||||
* Callers guarantee:
|
||||
* -- aStart and aEnd are aligned to cluster and ligature boundaries
|
||||
* -- all glyphs use this font
|
||||
*/
|
||||
void Draw(gfxPangoTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
|
||||
gfxContext *aContext, PRBool aDrawToPath, gfxPoint *aBaselineOrigin,
|
||||
gfxTextRun::PropertyProvider::Spacing *aSpacing);
|
||||
/**
|
||||
* Measure a run of characters. See gfxTextRun::Metrics.
|
||||
* @param aTight if false, then return the union of the glyph extents
|
||||
* with the font-box for the characters (the rectangle with x=0,width=
|
||||
* the advance width for the character run,y=-(font ascent), and height=
|
||||
* font ascent + font descent). Otherwise, we must return as tight as possible
|
||||
* an approximation to the area actually painted by glyphs.
|
||||
* @param aSpacing spacing to insert before and after glyphs. The bounding box
|
||||
* need not include the spacing itself, but the spacing affects the glyph
|
||||
* positions. null if there is no spacing.
|
||||
*
|
||||
* Callers guarantee:
|
||||
* -- aStart and aEnd are aligned to cluster and ligature boundaries
|
||||
* -- all glyphs use this font
|
||||
*/
|
||||
gfxTextRun::Metrics Measure(gfxPangoTextRun *aTextRun,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
PRBool aTightBoundingBox,
|
||||
gfxTextRun::PropertyProvider::Spacing *aSpacing);
|
||||
/**
|
||||
* Line breaks have been changed at the beginning and/or end of a substring
|
||||
* of the text. Reshaping may be required; glyph updating is permitted.
|
||||
* @return true if anything was changed, false otherwise
|
||||
*/
|
||||
PRBool NotifyLineBreaksChanged(gfxPangoTextRun *aTextRun,
|
||||
PRUint32 aStart, PRUint32 aLength)
|
||||
{ return PR_FALSE; }
|
||||
virtual gfxTextRun::Metrics Measure(gfxTextRun *aTextRun,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
PRBool aTightBoundingBox,
|
||||
Spacing *aSpacing);
|
||||
|
||||
protected:
|
||||
PangoFontDescription *mPangoFontDesc;
|
||||
@ -143,7 +96,8 @@ protected:
|
||||
void RealizeFont(PRBool force = PR_FALSE);
|
||||
void RealizeXftFont(PRBool force = PR_FALSE);
|
||||
void GetSize(const char *aString, PRUint32 aLength, gfxSize& inkSize, gfxSize& logSize);
|
||||
void SetupCairoFont(cairo_t *aCR);
|
||||
|
||||
virtual void SetupCairoFont(cairo_t *aCR);
|
||||
};
|
||||
|
||||
class FontSelector;
|
||||
@ -178,39 +132,33 @@ public:
|
||||
mFontCache.Put(aName, aFont);
|
||||
}
|
||||
|
||||
virtual gfxTextRun *GetSpecialStringTextRun(SpecialString aString,
|
||||
gfxTextRun *aTemplate);
|
||||
|
||||
protected:
|
||||
friend class FontSelector;
|
||||
|
||||
// ****** Textrun glyph conversion helpers ******
|
||||
|
||||
// If aUTF16Text is null, then the string contains no characters >= 0x100
|
||||
void InitTextRun(gfxPangoTextRun *aTextRun, const gchar *aUTF8Text,
|
||||
void InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
|
||||
PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength,
|
||||
const PRUnichar *aUTF16Text, PRUint32 aUTF16Length);
|
||||
// Returns NS_ERROR_FAILURE if there's a missing glyph
|
||||
nsresult SetGlyphs(gfxPangoTextRun *aTextRun, const gchar *aUTF8,
|
||||
nsresult SetGlyphs(gfxTextRun *aTextRun, const gchar *aUTF8,
|
||||
PRUint32 aUTF8Length,
|
||||
PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs,
|
||||
PangoGlyphUnit aOverrideSpaceWidth,
|
||||
PRBool aAbortOnMissingGlyph);
|
||||
// If aUTF16Text is null, then the string contains no characters >= 0x100.
|
||||
// Returns NS_ERROR_FAILURE if we require the itemizing path
|
||||
nsresult CreateGlyphRunsFast(gfxPangoTextRun *aTextRun,
|
||||
nsresult CreateGlyphRunsFast(gfxTextRun *aTextRun,
|
||||
const gchar *aUTF8, PRUint32 aUTF8Length,
|
||||
const PRUnichar *aUTF16Text, PRUint32 aUTF16Length);
|
||||
void CreateGlyphRunsItemizing(gfxPangoTextRun *aTextRun,
|
||||
void CreateGlyphRunsItemizing(gfxTextRun *aTextRun,
|
||||
const gchar *aUTF8, PRUint32 aUTF8Length,
|
||||
PRUint32 aUTF8HeaderLength);
|
||||
#if defined(ENABLE_XFT_FAST_PATH_8BIT) || defined(ENABLE_XFT_FAST_PATH_ALWAYS)
|
||||
void CreateGlyphRunsXft(gfxPangoTextRun *aTextRun,
|
||||
void CreateGlyphRunsXft(gfxTextRun *aTextRun,
|
||||
const gchar *aUTF8, PRUint32 aUTF8Length);
|
||||
#endif
|
||||
void SetupClusterBoundaries(gfxPangoTextRun *aTextRun,
|
||||
const gchar *aUTF8, PRUint32 aUTF8Length,
|
||||
PRUint32 aUTF16Offset, PangoAnalysis *aAnalysis);
|
||||
|
||||
static PRBool FontCallback (const nsAString& fontName,
|
||||
const nsACString& genericName,
|
||||
@ -219,294 +167,6 @@ protected:
|
||||
private:
|
||||
nsDataHashtable<nsStringHashKey, nsRefPtr<gfxPangoFont> > mFontCache;
|
||||
nsTArray<gfxFontStyle> mAdditionalStyles;
|
||||
nsAutoPtr<gfxTextRun> mSpecialStrings[STRING_MAX + 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* This is not really Pango-specific anymore. We'll merge this into gfxTextRun
|
||||
* soon. At that point uses of gfxPangoFontGroup, gfxPangoFont and gfxPangoTextRun
|
||||
* will be converted to use the cross-platform superclasses.
|
||||
*/
|
||||
class THEBES_API gfxPangoTextRun : public gfxTextRun {
|
||||
public:
|
||||
gfxPangoTextRun(gfxTextRunFactory::Parameters *aParams, PRUint32 aLength);
|
||||
// The caller is responsible for initializing our glyphs. Initially
|
||||
// all glyphs are such that GetCharacterGlyphs()[i].IsMissing() is true.
|
||||
|
||||
// gfxTextRun public API
|
||||
virtual void GetCharFlags(PRUint32 aStart, PRUint32 aLength,
|
||||
PRUint8 *aFlags);
|
||||
virtual PRUint8 GetCharFlags(PRUint32 aOffset);
|
||||
virtual PRUint32 GetLength();
|
||||
virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
|
||||
PRPackedBool *aBreakBefore);
|
||||
virtual void Draw(gfxContext *aContext, gfxPoint aPt,
|
||||
PRUint32 aStart, PRUint32 aLength,
|
||||
const gfxRect *aDirtyRect,
|
||||
PropertyProvider *aBreakProvider,
|
||||
gfxFloat *aAdvanceWidth);
|
||||
virtual void DrawToPath(gfxContext *aContext, gfxPoint aPt,
|
||||
PRUint32 aStart, PRUint32 aLength,
|
||||
PropertyProvider *aBreakProvider,
|
||||
gfxFloat *aAdvanceWidth);
|
||||
virtual Metrics MeasureText(PRUint32 aStart, PRUint32 aLength,
|
||||
PRBool aTightBoundingBox,
|
||||
PropertyProvider *aBreakProvider);
|
||||
virtual void SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
|
||||
PRBool aLineBreakBefore, PRBool aLineBreakAfter,
|
||||
TextProvider *aProvider,
|
||||
gfxFloat *aAdvanceWidthDelta);
|
||||
virtual gfxFloat GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
|
||||
PropertyProvider *aBreakProvider);
|
||||
virtual PRUint32 BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
|
||||
PRBool aLineBreakBefore, gfxFloat aWidth,
|
||||
PropertyProvider *aBreakProvider,
|
||||
PRBool aSuppressInitialBreak,
|
||||
Metrics *aMetrics, PRBool aTightBoundingBox,
|
||||
PRBool *aUsedHyphenation,
|
||||
PRUint32 *aLastBreak);
|
||||
|
||||
/**
|
||||
* This class records the information associated with a character in the
|
||||
* input string. It's optimized for the case where there is one glyph
|
||||
* representing that character alone.
|
||||
*/
|
||||
class CompressedGlyph {
|
||||
public:
|
||||
CompressedGlyph() { mValue = 0; }
|
||||
|
||||
enum {
|
||||
// Indicates that a cluster starts at this character and can be
|
||||
// rendered using a single glyph with a reasonable advance offset
|
||||
// and no special glyph offset. A "reasonable" advance offset is
|
||||
// one that is a) a multiple of a pixel and b) fits in the available
|
||||
// bits (currently 14). We should revisit this, especially a),
|
||||
// if we want to support subpixel-aligned text.
|
||||
FLAG_IS_SIMPLE_GLYPH = 0x80000000U,
|
||||
// Indicates that a linebreak is allowed before this character
|
||||
FLAG_CAN_BREAK_BEFORE = 0x40000000U,
|
||||
|
||||
ADVANCE_MASK = 0x3FFF0000U,
|
||||
ADVANCE_SHIFT = 16,
|
||||
|
||||
GLYPH_MASK = 0x0000FFFFU,
|
||||
|
||||
// Non-simple glyphs have the following tags
|
||||
|
||||
TAG_MASK = 0x000000FFU,
|
||||
// Indicates that this character corresponds to a missing glyph
|
||||
// and should be skipped (or possibly, render the character
|
||||
// Unicode value in some special way)
|
||||
TAG_MISSING = 0x00U,
|
||||
// Indicates that a cluster starts at this character and is rendered
|
||||
// using one or more glyphs which cannot be represented here.
|
||||
// Look up the DetailedGlyph table instead.
|
||||
TAG_COMPLEX_CLUSTER = 0x01U,
|
||||
// Indicates that a cluster starts at this character but is rendered
|
||||
// as part of a ligature starting in a previous cluster.
|
||||
// NOTE: we divide up the ligature's width by the number of clusters
|
||||
// to get the width assigned to each cluster.
|
||||
TAG_LIGATURE_CONTINUATION = 0x21U,
|
||||
|
||||
// Values where the upper 28 bits equal 0x80 are reserved for
|
||||
// non-cluster-start characters (see IsClusterStart below)
|
||||
|
||||
// Indicates that a cluster does not start at this character, this is
|
||||
// a low UTF16 surrogate
|
||||
TAG_LOW_SURROGATE = 0x80U,
|
||||
// Indicates that a cluster does not start at this character, this is
|
||||
// part of a cluster starting with an earlier character (but not
|
||||
// a low surrogate).
|
||||
TAG_CLUSTER_CONTINUATION = 0x81U
|
||||
};
|
||||
|
||||
// "Simple glyphs" have a simple glyph ID, simple advance and their
|
||||
// x and y offsets are zero. These cases are optimized to avoid storing
|
||||
// DetailedGlyphs.
|
||||
|
||||
// Returns true if the glyph ID aGlyph fits into the compressed representation
|
||||
static PRBool IsSimpleGlyphID(PRUint32 aGlyph) {
|
||||
return (aGlyph & GLYPH_MASK) == aGlyph;
|
||||
}
|
||||
// Returns true if the advance aAdvance fits into the compressed representation.
|
||||
// aAdvance is in pixels.
|
||||
static PRBool IsSimpleAdvancePixels(PRUint32 aAdvance) {
|
||||
return (aAdvance & (ADVANCE_MASK >> ADVANCE_SHIFT)) == aAdvance;
|
||||
}
|
||||
|
||||
PRBool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; }
|
||||
PRBool IsComplex(PRUint32 aTag) const { return (mValue & (FLAG_IS_SIMPLE_GLYPH|TAG_MASK)) == aTag; }
|
||||
PRBool IsMissing() const { return IsComplex(TAG_MISSING); }
|
||||
PRBool IsComplexCluster() const { return IsComplex(TAG_COMPLEX_CLUSTER); }
|
||||
PRBool IsLigatureContinuation() const { return IsComplex(TAG_LIGATURE_CONTINUATION); }
|
||||
PRBool IsLowSurrogate() const { return IsComplex(TAG_LOW_SURROGATE); }
|
||||
PRBool IsClusterStart() const { return (mValue & (FLAG_IS_SIMPLE_GLYPH|0x80U)) != 0x80U; }
|
||||
|
||||
PRUint32 GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; }
|
||||
PRUint32 GetSimpleGlyph() const { return mValue & GLYPH_MASK; }
|
||||
|
||||
PRUint32 GetComplexTag() const { return mValue & TAG_MASK; }
|
||||
|
||||
PRBool CanBreakBefore() const { return (mValue & FLAG_CAN_BREAK_BEFORE) != 0; }
|
||||
// Returns FLAG_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
|
||||
PRUint32 SetCanBreakBefore(PRBool aCanBreakBefore) {
|
||||
PRUint32 breakMask = aCanBreakBefore*FLAG_CAN_BREAK_BEFORE;
|
||||
PRUint32 toggle = breakMask ^ (mValue & FLAG_CAN_BREAK_BEFORE);
|
||||
mValue ^= toggle;
|
||||
return toggle;
|
||||
}
|
||||
|
||||
CompressedGlyph& SetSimpleGlyph(PRUint32 aAdvancePixels, PRUint32 aGlyph) {
|
||||
NS_ASSERTION(IsSimpleAdvancePixels(aAdvancePixels), "Advance overflow");
|
||||
NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
|
||||
mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | FLAG_IS_SIMPLE_GLYPH |
|
||||
(aAdvancePixels << ADVANCE_SHIFT) | aGlyph;
|
||||
return *this;
|
||||
}
|
||||
CompressedGlyph& SetComplex(PRUint32 aTag) {
|
||||
mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | aTag;
|
||||
return *this;
|
||||
}
|
||||
CompressedGlyph& SetMissing() { return SetComplex(TAG_MISSING); }
|
||||
CompressedGlyph& SetComplexCluster() { return SetComplex(TAG_COMPLEX_CLUSTER); }
|
||||
CompressedGlyph& SetLowSurrogate() { return SetComplex(TAG_LOW_SURROGATE); }
|
||||
CompressedGlyph& SetLigatureContinuation() { return SetComplex(TAG_LIGATURE_CONTINUATION); }
|
||||
CompressedGlyph& SetClusterContinuation() { return SetComplex(TAG_CLUSTER_CONTINUATION); }
|
||||
private:
|
||||
PRUint32 mValue;
|
||||
};
|
||||
struct DetailedGlyph {
|
||||
PRUint32 mIsLastGlyph:1;
|
||||
PRUint32 mGlyphID:31;
|
||||
float mAdvance, mXOffset, mYOffset;
|
||||
};
|
||||
// The text is divided into GlyphRuns at Pango item and text segment boundaries
|
||||
struct GlyphRun {
|
||||
nsRefPtr<gfxPangoFont> mFont; // never null
|
||||
PRUint32 mCharacterOffset; // into original UTF16 string
|
||||
};
|
||||
|
||||
class GlyphRunIterator {
|
||||
public:
|
||||
GlyphRunIterator(gfxPangoTextRun *aTextRun, PRUint32 aStart, PRUint32 aLength)
|
||||
: mTextRun(aTextRun), mStartOffset(aStart), mEndOffset(aStart + aLength) {
|
||||
mNextIndex = mTextRun->FindFirstGlyphRunContaining(aStart);
|
||||
}
|
||||
PRBool NextRun();
|
||||
GlyphRun *GetGlyphRun() { return mGlyphRun; }
|
||||
PRUint32 GetStringStart() { return mStringStart; }
|
||||
PRUint32 GetStringEnd() { return mStringEnd; }
|
||||
private:
|
||||
gfxPangoTextRun *mTextRun;
|
||||
GlyphRun *mGlyphRun;
|
||||
PRUint32 mStringStart;
|
||||
PRUint32 mStringEnd;
|
||||
PRUint32 mNextIndex;
|
||||
PRUint32 mStartOffset;
|
||||
PRUint32 mEndOffset;
|
||||
};
|
||||
|
||||
friend class GlyphRunIterator;
|
||||
friend class FontSelector;
|
||||
|
||||
// API for setting up the textrun glyphs
|
||||
|
||||
/**
|
||||
* We've found a run of text that should use a particular font. Call this
|
||||
* only during initialization when font substitution has been computed.
|
||||
*/
|
||||
nsresult AddGlyphRun(gfxPangoFont *aFont, PRUint32 aStartCharIndex);
|
||||
// Call the following glyph-setters during initialization or during reshaping
|
||||
// only. It is OK to overwrite existing data for a character.
|
||||
/**
|
||||
* Set the glyph for a character. Also allows you to set low surrogates,
|
||||
* cluster and ligature continuations.
|
||||
*/
|
||||
void SetCharacterGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) {
|
||||
if (mCharacterGlyphs) {
|
||||
mCharacterGlyphs[aCharIndex] = aGlyph;
|
||||
}
|
||||
if (mDetailedGlyphs) {
|
||||
mDetailedGlyphs[aCharIndex] = nsnull;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set some detailed glyphs for a character. The data is copied from aGlyphs,
|
||||
* the caller retains ownership.
|
||||
*/
|
||||
void SetDetailedGlyphs(PRUint32 aCharIndex, DetailedGlyph *aGlyphs,
|
||||
PRUint32 aNumGlyphs);
|
||||
|
||||
// API for access to the raw glyph data, needed by gfxPangoFont::Draw
|
||||
// and gfxPangoFont::GetBoundingBox
|
||||
const CompressedGlyph *GetCharacterGlyphs() { return mCharacterGlyphs; }
|
||||
const DetailedGlyph *GetDetailedGlyphs(PRUint32 aCharIndex) {
|
||||
NS_ASSERTION(mDetailedGlyphs && mDetailedGlyphs[aCharIndex],
|
||||
"Requested detailed glyphs when there aren't any, "
|
||||
"I think I'll go and have a lie down...");
|
||||
return mDetailedGlyphs[aCharIndex];
|
||||
}
|
||||
PRUint32 CountMissingGlyphs();
|
||||
|
||||
private:
|
||||
// **** general helpers ****
|
||||
|
||||
// Returns the index of the GlyphRun containing the given offset.
|
||||
// Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
|
||||
PRUint32 FindFirstGlyphRunContaining(PRUint32 aOffset);
|
||||
// Computes the x-advance for a given cluster starting at aClusterOffset. Does
|
||||
// not include any spacing. Result is in device pixels.
|
||||
gfxFloat ComputeClusterAdvance(PRUint32 aClusterOffset);
|
||||
|
||||
// **** 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)
|
||||
|
||||
struct LigatureData {
|
||||
PRUint32 mStartOffset;
|
||||
PRUint32 mEndOffset;
|
||||
PRUint32 mClusterCount;
|
||||
PRUint32 mPartClusterIndex;
|
||||
gfxFloat mLigatureWidth; // appunits
|
||||
gfxFloat mBeforeSpacing; // appunits
|
||||
gfxFloat mAfterSpacing; // appunits
|
||||
};
|
||||
// if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero
|
||||
LigatureData ComputeLigatureData(PRUint32 aPartOffset, PropertyProvider *aProvider);
|
||||
void GetAdjustedSpacing(PRUint32 aStart, PRUint32 aEnd,
|
||||
PropertyProvider *aProvider, PropertyProvider::Spacing *aSpacing);
|
||||
PRBool GetAdjustedSpacingArray(PRUint32 aStart, PRUint32 aEnd,
|
||||
PropertyProvider *aProvider,
|
||||
nsTArray<PropertyProvider::Spacing> *aSpacing);
|
||||
void DrawPartialLigature(gfxPangoFont *aFont, gfxContext *aCtx, PRUint32 aOffset,
|
||||
const gfxRect *aDirtyRect, gfxPoint *aPt,
|
||||
PropertyProvider *aProvider);
|
||||
// result in appunits
|
||||
void ShrinkToLigatureBoundaries(PRUint32 *aStart, PRUint32 *aEnd);
|
||||
gfxFloat GetPartialLigatureWidth(PRUint32 aStart, PRUint32 aEnd, PropertyProvider *aProvider);
|
||||
void AccumulatePartialLigatureMetrics(gfxPangoFont *aFont,
|
||||
PRUint32 aOffset, PRBool aTight,
|
||||
PropertyProvider *aProvider,
|
||||
Metrics *aMetrics);
|
||||
|
||||
// **** measurement helper ****
|
||||
void AccumulateMetricsForRun(gfxPangoFont *aFont, PRUint32 aStart,
|
||||
PRUint32 aEnd, PRBool aTight,
|
||||
PropertyProvider *aProvider,
|
||||
Metrics *aMetrics);
|
||||
|
||||
// **** drawing helper ****
|
||||
void DrawGlyphs(gfxPangoFont *aFont, gfxContext *aContext, PRBool aDrawToPath,
|
||||
gfxPoint *aPt, PRUint32 aStart, PRUint32 aEnd,
|
||||
PropertyProvider *aProvider);
|
||||
|
||||
// All our glyph data is in logical order, not visual
|
||||
nsAutoArrayPtr<CompressedGlyph> mCharacterGlyphs;
|
||||
nsAutoArrayPtr<nsAutoArrayPtr<DetailedGlyph> > mDetailedGlyphs; // only non-null if needed
|
||||
// XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
|
||||
// for smaller size especially in the super-common one-glyphrun case
|
||||
nsAutoTArray<GlyphRun,1> mGlyphRuns;
|
||||
PRUint32 mCharacterCount;
|
||||
};
|
||||
|
||||
#endif /* GFX_PANGOFONTS_H */
|
||||
|
@ -382,6 +382,10 @@ public:
|
||||
|
||||
virtual nsString GetUniqueName();
|
||||
|
||||
virtual void Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
|
||||
gfxContext *aContext, PRBool aDrawToPath, gfxPoint *aBaselineOrigin,
|
||||
Spacing *aSpacing);
|
||||
|
||||
protected:
|
||||
HFONT MakeHFONT();
|
||||
cairo_font_face_t *MakeCairoFontFace();
|
||||
@ -406,6 +410,8 @@ private:
|
||||
LOGFONTW mLogFont;
|
||||
|
||||
nsRefPtr<WeightTable> mWeightTable;
|
||||
|
||||
virtual void SetupCairoFont(cairo_t *aCR);
|
||||
};
|
||||
|
||||
/**********************************************************************
|
||||
@ -469,6 +475,11 @@ protected:
|
||||
const nsACString& genericName,
|
||||
void *closure);
|
||||
|
||||
void InitTextRunGDI(gfxContext *aContext, gfxTextRun *aRun, const char *aString, PRUint32 aLength);
|
||||
void InitTextRunGDI(gfxContext *aContext, gfxTextRun *aRun, const PRUnichar *aString, PRUint32 aLength);
|
||||
|
||||
void InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun, const PRUnichar *aString, PRUint32 aLength);
|
||||
|
||||
private:
|
||||
friend class gfxWindowsTextRun;
|
||||
|
||||
@ -477,47 +488,4 @@ private:
|
||||
nsDataHashtable<nsStringHashKey, nsRefPtr<gfxWindowsFont> > mFontCache;
|
||||
};
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
*
|
||||
* class gfxWindowsTextRun
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
class THEBES_API gfxWindowsTextRun {
|
||||
public:
|
||||
gfxWindowsTextRun(const nsAString& aString, gfxWindowsFontGroup *aFontGroup);
|
||||
gfxWindowsTextRun(const nsACString& aString, gfxWindowsFontGroup *aFontGroup);
|
||||
~gfxWindowsTextRun();
|
||||
|
||||
virtual void Draw(gfxContext *aContext, gfxPoint pt);
|
||||
virtual gfxFloat Measure(gfxContext *aContext);
|
||||
|
||||
virtual void SetSpacing(const nsTArray<gfxFloat>& spacingArray);
|
||||
virtual const nsTArray<gfxFloat> *const GetSpacing() const;
|
||||
|
||||
void SetRightToLeft(PRBool aIsRTL) { mIsRTL = aIsRTL; }
|
||||
PRBool IsRightToLeft() { return mIsRTL; }
|
||||
|
||||
private:
|
||||
double MeasureOrDrawFast(gfxContext *aContext, PRBool aDraw, gfxPoint pt);
|
||||
double MeasureOrDrawReallyFast(gfxContext *aContext, PRBool aDraw, gfxPoint pt, gfxRGBA color);
|
||||
double MeasureOrDrawUniscribe(gfxContext *aContext, PRBool aDraw, gfxPoint pt);
|
||||
|
||||
gfxWindowsFontGroup *mGroup;
|
||||
nsTArray<gfxFloat> mSpacing;
|
||||
|
||||
// These should probably be in a union
|
||||
nsString mString;
|
||||
nsCString mCString;
|
||||
|
||||
const PRPackedBool mIsASCII;
|
||||
PRPackedBool mIsRTL;
|
||||
|
||||
nsRefPtr<gfxWindowsFont> mFallbackFont;
|
||||
|
||||
/* cached values */
|
||||
double mLength;
|
||||
};
|
||||
|
||||
#endif /* GFX_WINDOWSFONTS_H */
|
||||
|
@ -339,145 +339,96 @@ gfxAtsuiFontGroup::~gfxAtsuiFontGroup()
|
||||
ATSUDisposeFontFallbacks(mFallbacks);
|
||||
}
|
||||
|
||||
class gfxWrapperTextRun : public gfxTextRun {
|
||||
public:
|
||||
gfxWrapperTextRun(gfxAtsuiFontGroup *aGroup,
|
||||
const PRUint8* aString, PRUint32 aLength,
|
||||
gfxTextRunFactory::Parameters* aParams)
|
||||
: gfxTextRun(aParams),
|
||||
mContext(aParams->mContext),
|
||||
mInner(NS_ConvertASCIItoUTF16(nsDependentCSubstring(reinterpret_cast<const char*>(aString),
|
||||
reinterpret_cast<const char*>(aString + aLength))),
|
||||
aGroup),
|
||||
mLength(aLength)
|
||||
{
|
||||
mInner.SetRightToLeft(IsRightToLeft());
|
||||
}
|
||||
gfxWrapperTextRun(gfxAtsuiFontGroup *aGroup,
|
||||
const PRUnichar* aString, PRUint32 aLength,
|
||||
gfxTextRunFactory::Parameters* aParams)
|
||||
: gfxTextRun(aParams), mContext(aParams->mContext),
|
||||
mInner(nsDependentSubstring(aString, aString + aLength), aGroup),
|
||||
mLength(aLength)
|
||||
{
|
||||
mInner.SetRightToLeft(IsRightToLeft());
|
||||
}
|
||||
~gfxWrapperTextRun() {}
|
||||
|
||||
virtual void GetCharFlags(PRUint32 aStart, PRUint32 aLength,
|
||||
PRUint8* aFlags)
|
||||
{ NS_ERROR("NOT IMPLEMENTED"); }
|
||||
virtual PRUint8 GetCharFlags(PRUint32 aOffset)
|
||||
{ NS_ERROR("NOT IMPLEMENTED"); return 0; }
|
||||
virtual PRUint32 GetLength()
|
||||
{ NS_ERROR("NOT IMPLEMENTED"); return 0; }
|
||||
virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
|
||||
PRPackedBool* aBreakBefore)
|
||||
{ NS_ERROR("NOT IMPLEMENTED"); return PR_FALSE; }
|
||||
virtual void DrawToPath(gfxContext *aContext, gfxPoint aPt,
|
||||
PRUint32 aStart, PRUint32 aLength,
|
||||
PropertyProvider* aBreakProvider,
|
||||
gfxFloat* aAdvanceWidth)
|
||||
{ NS_ERROR("NOT IMPLEMENTED"); }
|
||||
virtual Metrics MeasureText(PRUint32 aStart, PRUint32 aLength,
|
||||
PRBool aTightBoundingBox,
|
||||
PropertyProvider* aBreakProvider)
|
||||
{ NS_ERROR("NOT IMPLEMENTED"); return Metrics(); }
|
||||
virtual void SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
|
||||
PRBool aLineBreakBefore, PRBool aLineBreakAfter,
|
||||
TextProvider* aProvider,
|
||||
gfxFloat* aAdvanceWidthDelta)
|
||||
{ NS_ERROR("NOT IMPLEMENTED"); }
|
||||
virtual PRUint32 BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
|
||||
PRBool aLineBreakBefore, gfxFloat aWidth,
|
||||
PropertyProvider* aProvider,
|
||||
PRBool aSuppressInitialBreak,
|
||||
Metrics* aMetrics, PRBool aTightBoundingBox,
|
||||
PRBool* aUsedHyphenation,
|
||||
PRUint32* aLastBreak)
|
||||
{ NS_ERROR("NOT IMPLEMENTED"); return 0; }
|
||||
|
||||
virtual void Draw(gfxContext *aContext, gfxPoint aPt,
|
||||
PRUint32 aStart, PRUint32 aLength,
|
||||
const gfxRect* aDirtyRect,
|
||||
PropertyProvider* aBreakProvider,
|
||||
gfxFloat* aAdvanceWidth);
|
||||
virtual gfxFloat GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
|
||||
PropertyProvider* aBreakProvider);
|
||||
|
||||
virtual void SetContext(gfxContext* aContext) { mContext = aContext; }
|
||||
|
||||
private:
|
||||
gfxContext* mContext;
|
||||
gfxAtsuiTextRun mInner;
|
||||
PRUint32 mLength;
|
||||
|
||||
void SetupSpacingFromProvider(PropertyProvider* aProvider);
|
||||
};
|
||||
|
||||
#define ROUND(x) floor((x) + 0.5)
|
||||
|
||||
void
|
||||
gfxWrapperTextRun::SetupSpacingFromProvider(PropertyProvider* aProvider)
|
||||
static void
|
||||
SetupClusterBoundaries(gfxTextRun *aTextRun, const PRUnichar *aString, PRUint32 aLength)
|
||||
{
|
||||
if (!(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
|
||||
TextBreakLocatorRef locator;
|
||||
OSStatus status = UCCreateTextBreakLocator(NULL, 0, kUCTextBreakClusterMask,
|
||||
&locator);
|
||||
if (status != noErr)
|
||||
return;
|
||||
UniCharArrayOffset breakOffset;
|
||||
status = UCFindTextBreak(locator, kUCTextBreakClusterMask, 0, aString, aLength,
|
||||
0, &breakOffset);
|
||||
if (status != noErr) {
|
||||
UCDisposeTextBreakLocator(&locator);
|
||||
return;
|
||||
|
||||
NS_ASSERTION(mFlags & gfxTextRunFactory::TEXT_ABSOLUTE_SPACING,
|
||||
"Can't handle relative spacing");
|
||||
|
||||
nsAutoTArray<PropertyProvider::Spacing,200> spacing;
|
||||
spacing.AppendElements(mLength);
|
||||
aProvider->GetSpacing(0, mLength, spacing.Elements());
|
||||
|
||||
nsTArray<gfxFloat> spaceArray;
|
||||
PRUint32 i;
|
||||
gfxFloat offset = 0; // in device pixels
|
||||
for (i = 0; i < mLength; ++i) {
|
||||
NS_ASSERTION(spacing.Elements()[i].mBefore == 0,
|
||||
"Can't handle before-spacing!");
|
||||
gfxFloat nextOffset = offset + spacing.Elements()[i].mAfter/mAppUnitsPerDevUnit;
|
||||
spaceArray.AppendElement(ROUND(nextOffset) - ROUND(offset));
|
||||
offset = nextOffset;
|
||||
}
|
||||
mInner.SetSpacing(spaceArray);
|
||||
}
|
||||
|
||||
void
|
||||
gfxWrapperTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
|
||||
PRUint32 aStart, PRUint32 aLength,
|
||||
const gfxRect* aDirtyRect,
|
||||
PropertyProvider* aBreakProvider,
|
||||
gfxFloat* aAdvanceWidth)
|
||||
{
|
||||
NS_ASSERTION(aStart == 0 && aLength == mLength, "Can't handle substrings");
|
||||
SetupSpacingFromProvider(aBreakProvider);
|
||||
gfxPoint pt(aPt.x/mAppUnitsPerDevUnit, aPt.y/mAppUnitsPerDevUnit);
|
||||
return mInner.Draw(mContext, pt);
|
||||
}
|
||||
|
||||
gfxFloat
|
||||
gfxWrapperTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
|
||||
PropertyProvider* aBreakProvider)
|
||||
{
|
||||
NS_ASSERTION(aStart == 0 && aLength == mLength, "Can't handle substrings");
|
||||
SetupSpacingFromProvider(aBreakProvider);
|
||||
return mInner.Measure(mContext)*mAppUnitsPerDevUnit;
|
||||
NS_ASSERTION(breakOffset == 0, "Cluster should start at offset zero");
|
||||
gfxTextRun::CompressedGlyph g;
|
||||
while (breakOffset < aLength) {
|
||||
PRUint32 curOffset = breakOffset;
|
||||
status = UCFindTextBreak(locator, kUCTextBreakClusterMask,
|
||||
kUCTextBreakIterateMask,
|
||||
aString, aLength, curOffset, &breakOffset);
|
||||
if (status != noErr) {
|
||||
UCDisposeTextBreakLocator(&locator);
|
||||
return;
|
||||
}
|
||||
PRUint32 j;
|
||||
for (j = curOffset + 1; j < breakOffset; ++j) {
|
||||
aTextRun->SetCharacterGlyph(j, g.SetClusterContinuation());
|
||||
}
|
||||
}
|
||||
NS_ASSERTION(breakOffset == aLength, "Should have found a final break");
|
||||
UCDisposeTextBreakLocator(&locator);
|
||||
}
|
||||
|
||||
gfxTextRun *
|
||||
gfxAtsuiFontGroup::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
|
||||
Parameters* aParams)
|
||||
gfxAtsuiFontGroup::MakeTextRunInternal(const PRUnichar *aString, PRUint32 aLength,
|
||||
Parameters *aParams)
|
||||
{
|
||||
return new gfxWrapperTextRun(this, aString, aLength, aParams);
|
||||
// NS_ASSERTION(!(aParams->mFlags & TEXT_NEED_BOUNDING_BOX),
|
||||
// "Glyph extents not yet supported");
|
||||
|
||||
gfxTextRun *textRun = new gfxTextRun(aParams, aLength);
|
||||
if (!textRun)
|
||||
return nsnull;
|
||||
|
||||
// There's a one-char header in the string and a one-char trailer
|
||||
textRun->RecordSurrogates(aString + 1);
|
||||
if (!(aParams->mFlags & TEXT_IS_8BIT)) {
|
||||
SetupClusterBoundaries(textRun, aString + 1, aLength);
|
||||
}
|
||||
|
||||
InitTextRun(textRun, aString, aLength);
|
||||
return textRun;
|
||||
}
|
||||
|
||||
#define UNICODE_LRO 0x202d
|
||||
#define UNICODE_RLO 0x202e
|
||||
#define UNICODE_PDF 0x202c
|
||||
|
||||
static void
|
||||
AppendDirectionalIndicator(PRUint32 aFlags, nsAString& aString)
|
||||
{
|
||||
static const PRUnichar overrides[2] = { UNICODE_LRO, UNICODE_RLO };
|
||||
aString.Append(overrides[(aFlags & gfxTextRunFactory::TEXT_IS_RTL) != 0]);
|
||||
}
|
||||
|
||||
gfxTextRun *
|
||||
gfxAtsuiFontGroup::MakeTextRun(const PRUint8* aString, PRUint32 aLength,
|
||||
Parameters* aParams)
|
||||
gfxAtsuiFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
|
||||
Parameters *aParams)
|
||||
{
|
||||
nsAutoString utf16;
|
||||
AppendDirectionalIndicator(aParams->mFlags, utf16);
|
||||
utf16.Append(aString, aLength);
|
||||
utf16.Append(UNICODE_PDF);
|
||||
return MakeTextRunInternal(utf16.get(), aLength, aParams);
|
||||
}
|
||||
|
||||
gfxTextRun *
|
||||
gfxAtsuiFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
|
||||
Parameters *aParams)
|
||||
{
|
||||
aParams->mFlags |= TEXT_IS_8BIT;
|
||||
return new gfxWrapperTextRun(this, aString, aLength, aParams);
|
||||
nsDependentCSubstring cString(reinterpret_cast<const char*>(aString),
|
||||
reinterpret_cast<const char*>(aString + aLength));
|
||||
nsAutoString utf16;
|
||||
AppendDirectionalIndicator(aParams->mFlags, utf16);
|
||||
AppendASCIItoUTF16(cString, utf16);
|
||||
utf16.Append(UNICODE_PDF);
|
||||
return MakeTextRunInternal(utf16.get(), aLength, aParams);
|
||||
}
|
||||
|
||||
gfxAtsuiFont*
|
||||
@ -488,7 +439,7 @@ gfxAtsuiFontGroup::FindFontFor(ATSUFontID fid)
|
||||
// In most cases, this will just be 1 -- maybe a
|
||||
// small number, so no need for any more complex
|
||||
// lookup
|
||||
for (int i = 0; i < FontListLength(); i++) {
|
||||
for (PRUint32 i = 0; i < FontListLength(); i++) {
|
||||
font = GetFontAt(i);
|
||||
if (font->GetATSUFontID() == fid)
|
||||
return font;
|
||||
@ -501,43 +452,325 @@ gfxAtsuiFontGroup::FindFontFor(ATSUFontID fid)
|
||||
}
|
||||
|
||||
/**
|
||||
** gfxAtsuiTextRun
|
||||
**/
|
||||
* Simple wrapper for ATSU "direct data arrays"
|
||||
*/
|
||||
class AutoLayoutDataArrayPtr {
|
||||
public:
|
||||
AutoLayoutDataArrayPtr(ATSULineRef aLineRef,
|
||||
ATSUDirectDataSelector aSelector)
|
||||
: mLineRef(aLineRef), mSelector(aSelector)
|
||||
{
|
||||
OSStatus status =
|
||||
ATSUDirectGetLayoutDataArrayPtrFromLineRef(aLineRef,
|
||||
aSelector, PR_FALSE, &mArray, &mItemCount);
|
||||
if (status != noErr) {
|
||||
mArray = NULL;
|
||||
mItemCount = 0;
|
||||
}
|
||||
}
|
||||
~AutoLayoutDataArrayPtr() {
|
||||
if (mArray) {
|
||||
ATSUDirectReleaseLayoutDataArrayPtr(mLineRef, mSelector, &mArray);
|
||||
}
|
||||
}
|
||||
|
||||
gfxAtsuiTextRun::gfxAtsuiTextRun(const nsAString& aString, gfxAtsuiFontGroup *aGroup)
|
||||
: mString(aString), mGroup(aGroup)
|
||||
void *mArray;
|
||||
ItemCount mItemCount;
|
||||
|
||||
private:
|
||||
ATSULineRef mLineRef;
|
||||
ATSUDirectDataSelector mSelector;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstraction to iterate through an array in strange order. It just remaps
|
||||
* array indices. Tragically we mainly need this because of ATSUI bugs.
|
||||
* There are four cases we need to handle. Note that 0 <= i < L in all cases.
|
||||
* 1) f(i) = i (LTR, no bug, most common)
|
||||
* 2) f(i) = L - 1 - i (RTL, no bug, next most common)
|
||||
* 3) f(i) = i < N ? N - 1 - i : i; (compensate for ATSUI bug in RTL mode)
|
||||
* 4) f(i) = i < N ? i + L - N : L - 1 - i (compensate for ATSUI bug in LTR mode)
|
||||
* We collapse 2) into 3) by setting N=L.
|
||||
* We collapse 1) into 4) by setting N=L.
|
||||
*/
|
||||
class GlyphMapper {
|
||||
public:
|
||||
GlyphMapper(PRBool aIsRTL, PRUint32 aL, PRUint32 aN)
|
||||
: mL(aL), mN(aN), mRTL(aIsRTL) {}
|
||||
PRUint32 MapIndex(PRUint32 aIndex)
|
||||
{
|
||||
if (mRTL) {
|
||||
return aIndex < mN ? mN - 1 - aIndex : aIndex;
|
||||
} else {
|
||||
return aIndex < mN ? aIndex + mL - mN : mL - 1 - aIndex;
|
||||
}
|
||||
}
|
||||
private:
|
||||
PRUint32 mL, mN;
|
||||
PRBool mRTL;
|
||||
};
|
||||
|
||||
static void
|
||||
PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
|
||||
const PRUnichar *aString)
|
||||
{
|
||||
AutoLayoutDataArrayPtr advanceDeltasArray(aLine, kATSUDirectDataAdvanceDeltaFixedArray);
|
||||
AutoLayoutDataArrayPtr baselineDeltasArray(aLine, kATSUDirectDataBaselineDeltaFixedArray);
|
||||
AutoLayoutDataArrayPtr deviceDeltasArray(aLine, kATSUDirectDataDeviceDeltaSInt16Array);
|
||||
AutoLayoutDataArrayPtr glyphRecordsArray(aLine, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent);
|
||||
|
||||
PRUint32 numGlyphs = glyphRecordsArray.mItemCount;
|
||||
if (numGlyphs == 0 || !glyphRecordsArray.mArray) {
|
||||
NS_WARNING("Failed to retrieve key glyph data");
|
||||
return;
|
||||
}
|
||||
Fixed *advanceDeltas = NS_STATIC_CAST(Fixed *, advanceDeltasArray.mArray);
|
||||
Fixed *baselineDeltas = NS_STATIC_CAST(Fixed *, baselineDeltasArray.mArray);
|
||||
ATSLayoutRecord *glyphRecords = NS_STATIC_CAST(ATSLayoutRecord *, glyphRecordsArray.mArray);
|
||||
NS_ASSERTION((!advanceDeltas || advanceDeltasArray.mItemCount == numGlyphs) &&
|
||||
(!baselineDeltas || baselineDeltasArray.mItemCount == numGlyphs),
|
||||
"Mismatched glyph counts");
|
||||
NS_ASSERTION(glyphRecords[numGlyphs - 1].flags & kATSGlyphInfoTerminatorGlyph,
|
||||
"Last glyph should be a terminator glyph");
|
||||
--numGlyphs;
|
||||
if (numGlyphs == 0)
|
||||
return;
|
||||
|
||||
PRUint32 index = 0;
|
||||
ItemCount k = 0;
|
||||
PRUint32 length = aRun->GetLength();
|
||||
// ATSUI seems to have a bug where trailing whitespace in a run,
|
||||
// even after we've forced the direction with LRO/RLO/PDF, does not
|
||||
// necessarily get the required direction.
|
||||
// A specific testcase is "RLO space space PDF"; we get
|
||||
// glyphRecords[0] = { originalOffset:0, realPos:0; }
|
||||
// glyphRecords[1] = { originalOffset:2, realPos:>0 }
|
||||
// We count the number of glyphs that appear to be this sort of erroneous
|
||||
// whitespace.
|
||||
// In RTL situations, the bug manifests as the trailing whitespace characters
|
||||
// being rendered in LTR order at the end of the glyph array.
|
||||
// In LTR situations, the bug manifests as the trailing whitespace characters
|
||||
// being rendered in RTL order at the start of the glyph array.
|
||||
PRUint32 incorrectDirectionGlyphCount = 0;
|
||||
PRUint32 stringTailOffset = length - 1;
|
||||
while (glyphRecords[aRun->IsRightToLeft() ? numGlyphs - 1 - incorrectDirectionGlyphCount
|
||||
: incorrectDirectionGlyphCount].originalOffset == stringTailOffset*2 &&
|
||||
aString[stringTailOffset] == ' ') {
|
||||
++incorrectDirectionGlyphCount;
|
||||
stringTailOffset--;
|
||||
if (incorrectDirectionGlyphCount == numGlyphs)
|
||||
break;
|
||||
}
|
||||
GlyphMapper mapper(aRun->IsRightToLeft(), numGlyphs,
|
||||
numGlyphs - incorrectDirectionGlyphCount);
|
||||
gfxTextRun::CompressedGlyph g;
|
||||
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
|
||||
PRBool gotRealCharacter = PR_FALSE;
|
||||
while (index < length) {
|
||||
ATSLayoutRecord *glyph = nsnull;
|
||||
// ATSUI sometimes moves glyphs around in visual order to handle
|
||||
// situations such as DEVANAGARI VOWEL I appearing before its base
|
||||
// character (even though it follows the base character in the text).
|
||||
// To handle this we make the reordered glyph(s) and the characters
|
||||
// they're reordered around into a single cluster.
|
||||
PRUint32 forceClusterGlyphs = 0;
|
||||
PRUint32 nextIndex = index + 1;
|
||||
if (k < numGlyphs) {
|
||||
glyph = &glyphRecords[mapper.MapIndex(k)];
|
||||
// originalOffset is in bytes, so we need to adjust index by 2 for comparisons
|
||||
if (glyph->originalOffset > 2*index) {
|
||||
// Detect the situation above. We assume that at most
|
||||
// two glyphs have been moved from after a run of characters
|
||||
// to visually before the run of characters, so the following glyph
|
||||
// or the next glyph is a glyph for the current character.
|
||||
// We can tweak this up if necessary...
|
||||
const PRUint32 MAX_GLYPHS_REORDERED = 10;
|
||||
PRUint32 reorderedGlyphs = 0;
|
||||
PRUint32 i;
|
||||
PRUint32 maxOffset = glyph->originalOffset;
|
||||
for (i = 1; k + i < numGlyphs; ++i) {
|
||||
ATSLayoutRecord *nextGlyph = &glyphRecords[mapper.MapIndex(k + i)];
|
||||
if (i == MAX_GLYPHS_REORDERED || nextGlyph->originalOffset == 2*index) {
|
||||
reorderedGlyphs = i;
|
||||
break;
|
||||
}
|
||||
maxOffset = PR_MAX(maxOffset, nextGlyph->originalOffset);
|
||||
}
|
||||
if (reorderedGlyphs > 0) {
|
||||
forceClusterGlyphs = reorderedGlyphs;
|
||||
while (k + forceClusterGlyphs < numGlyphs) {
|
||||
ATSLayoutRecord *nextGlyph = &glyphRecords[mapper.MapIndex(k + forceClusterGlyphs)];
|
||||
if (nextGlyph->originalOffset > maxOffset)
|
||||
break;
|
||||
++forceClusterGlyphs;
|
||||
}
|
||||
nextIndex = maxOffset/2 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (k == numGlyphs ||
|
||||
(glyph->originalOffset > 2*index && !forceClusterGlyphs)) {
|
||||
NS_ASSERTION(index > 0, "Continuation at the start of a run??");
|
||||
if (!aRun->GetCharacterGlyphs()[index].IsClusterContinuation()) {
|
||||
if (gotRealCharacter) {
|
||||
// No glyphs for character 'index', it must be a ligature continuation
|
||||
aRun->SetCharacterGlyph(index, g.SetLigatureContinuation());
|
||||
} else {
|
||||
// Don't allow ligature continuations until we've actually
|
||||
// got something for them to be a continuation of
|
||||
aRun->SetCharacterGlyph(index, g.SetMissing());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NS_ASSERTION(glyph->originalOffset >= 2*index, "Lost some glyphs");
|
||||
PRUint32 glyphCount = 1;
|
||||
if (forceClusterGlyphs) {
|
||||
glyphCount = forceClusterGlyphs;
|
||||
PRUint32 i;
|
||||
// Mark all the characters that we forced to cluster, other
|
||||
// than this leading character, as cluster continuations.
|
||||
for (i = 0; i < forceClusterGlyphs; ++i) {
|
||||
PRUint32 offset = glyphRecords[mapper.MapIndex(k + i)].originalOffset;
|
||||
if (offset != 2*index) {
|
||||
aRun->SetCharacterGlyph(offset/2, g.SetClusterContinuation());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Find all the glyphs associated with this character
|
||||
while (k + glyphCount < numGlyphs &&
|
||||
glyphRecords[mapper.MapIndex(k + glyphCount)].originalOffset == 2*index) {
|
||||
++glyphCount;
|
||||
}
|
||||
}
|
||||
Fixed advance = glyph[1].realPos - glyph->realPos;
|
||||
PRUint32 advancePixels = advance >> 16;
|
||||
// "Fixed" values have their fraction in the low 16 bits
|
||||
if (glyphCount == 1 && advance >= 0 && (advance & 0xFFFF) == 0 &&
|
||||
(!baselineDeltas || baselineDeltas[mapper.MapIndex(k)] == 0) &&
|
||||
gfxTextRun::CompressedGlyph::IsSimpleAdvancePixels(advancePixels) &&
|
||||
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph->glyphID)) {
|
||||
aRun->SetCharacterGlyph(index, g.SetSimpleGlyph(advancePixels, glyph->glyphID));
|
||||
} else {
|
||||
if (detailedGlyphs.Length() < glyphCount) {
|
||||
if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
|
||||
return;
|
||||
}
|
||||
PRUint32 i;
|
||||
float appUnitsPerDevUnit = aRun->GetAppUnitsPerDevUnit();
|
||||
for (i = 0; i < glyphCount; ++i) {
|
||||
gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
|
||||
glyph = &glyphRecords[mapper.MapIndex(k + i)];
|
||||
details->mIsLastGlyph = i == glyphCount - 1;
|
||||
details->mGlyphID = glyph->glyphID;
|
||||
float advanceAppUnits =
|
||||
FixedToFloat(glyph[1].realPos - glyph->realPos)*appUnitsPerDevUnit;
|
||||
details->mAdvance = ceilf(advanceAppUnits)/appUnitsPerDevUnit;
|
||||
details->mXOffset = 0;
|
||||
details->mYOffset = !baselineDeltas ? 0.0f
|
||||
: FixedToFloat(baselineDeltas[mapper.MapIndex(k + i)]);
|
||||
}
|
||||
aRun->SetDetailedGlyphs(index, detailedGlyphs.Elements(), glyphCount);
|
||||
}
|
||||
gotRealCharacter = PR_TRUE;
|
||||
k += glyphCount;
|
||||
}
|
||||
|
||||
index = nextIndex;
|
||||
if (index + 1 < length && NS_IS_HIGH_SURROGATE(aString[index])) {
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PostLayoutCallbackClosure {
|
||||
gfxTextRun *mTextRun;
|
||||
const PRUnichar *mString;
|
||||
};
|
||||
|
||||
// This is really disgusting, but the ATSUI refCon thing is also disgusting
|
||||
static PostLayoutCallbackClosure *gCallbackClosure = nsnull;
|
||||
|
||||
static OSStatus
|
||||
PostLayoutOperationCallback(ATSULayoutOperationSelector iCurrentOperation,
|
||||
ATSULineRef iLineRef,
|
||||
UInt32 iRefCon,
|
||||
void *iOperationCallbackParameterPtr,
|
||||
ATSULayoutOperationCallbackStatus *oCallbackStatus)
|
||||
{
|
||||
PostLayoutCallback(iLineRef, gCallbackClosure->mTextRun,
|
||||
gCallbackClosure->mString);
|
||||
*oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
|
||||
return noErr;
|
||||
}
|
||||
|
||||
void
|
||||
gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
|
||||
const PRUnichar *aString, PRUint32 aLength)
|
||||
{
|
||||
OSStatus status;
|
||||
gfxAtsuiFont *atsuiFont = mGroup->GetFontAt(0);
|
||||
gfxAtsuiFont *atsuiFont = GetFontAt(0);
|
||||
ATSUStyle mainStyle = atsuiFont->GetATSUStyle();
|
||||
nsTArray<ATSUStyle> stylesToDispose;
|
||||
|
||||
UniCharCount runLengths = mString.Length();
|
||||
UniCharCount runLengths = aLength;
|
||||
ATSUTextLayout layout;
|
||||
// The string is actually aLength + 2 chars, with a header char to set
|
||||
// the direction and a trailer char to pop it. So create the text layout
|
||||
// giving the whole string as context, although we only want glyphs for
|
||||
// the inner substring.
|
||||
status = ATSUCreateTextLayoutWithTextPtr
|
||||
(nsPromiseFlatString(mString).get(),
|
||||
0,
|
||||
mString.Length(),
|
||||
mString.Length(),
|
||||
(aString,
|
||||
1,
|
||||
aLength,
|
||||
aLength + 2,
|
||||
1,
|
||||
&runLengths,
|
||||
&mainStyle,
|
||||
&mATSULayout);
|
||||
&layout);
|
||||
// XXX error check here?
|
||||
|
||||
PostLayoutCallbackClosure closure;
|
||||
closure.mTextRun = aRun;
|
||||
// Pass the real string to the closure, ignoring the header
|
||||
closure.mString = aString + 1;
|
||||
NS_ASSERTION(!gCallbackClosure, "Reentering InitTextRun? Expect disaster!");
|
||||
gCallbackClosure = &closure;
|
||||
|
||||
ATSULayoutOperationOverrideSpecifier override;
|
||||
override.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
|
||||
override.overrideUPP = PostLayoutOperationCallback;
|
||||
|
||||
// Set up our layout attributes
|
||||
ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers;
|
||||
ATSUAttributeTag layoutTags[] = { kATSULineLayoutOptionsTag, kATSULineFontFallbacksTag };
|
||||
ByteCount layoutArgSizes[] = { sizeof(ATSLineLayoutOptions), sizeof(ATSUFontFallbacks) };
|
||||
ATSUAttributeValuePtr layoutArgs[] = { &lineLayoutOptions, mGroup->GetATSUFontFallbacksPtr() };
|
||||
ATSUSetLayoutControls(mATSULayout,
|
||||
sizeof(layoutTags) / sizeof(ATSUAttributeTag),
|
||||
|
||||
static ATSUAttributeTag layoutTags[] = {
|
||||
kATSULineLayoutOptionsTag,
|
||||
kATSULineFontFallbacksTag,
|
||||
kATSULayoutOperationOverrideTag
|
||||
};
|
||||
static ByteCount layoutArgSizes[] = {
|
||||
sizeof(ATSLineLayoutOptions),
|
||||
sizeof(ATSUFontFallbacks),
|
||||
sizeof(ATSULayoutOperationOverrideSpecifier)
|
||||
};
|
||||
|
||||
ATSUAttributeValuePtr layoutArgs[] = {
|
||||
&lineLayoutOptions,
|
||||
GetATSUFontFallbacksPtr(),
|
||||
&override
|
||||
};
|
||||
ATSUSetLayoutControls(layout,
|
||||
NS_ARRAY_LENGTH(layoutTags),
|
||||
layoutTags,
|
||||
layoutArgSizes,
|
||||
layoutArgs);
|
||||
|
||||
/* Now go through and update the styles for the text, based on font matching. */
|
||||
|
||||
UniCharArrayOffset runStart = 0;
|
||||
UniCharCount totalLength = mString.Length();
|
||||
UniCharCount runLength = totalLength;
|
||||
UniCharArrayOffset runStart = 1;
|
||||
UniCharCount totalLength = aLength + 1;
|
||||
UniCharCount runLength = aLength;
|
||||
|
||||
//fprintf (stderr, "==== Starting font maching [string length: %d]\n", totalLength);
|
||||
while (runStart < totalLength) {
|
||||
@ -545,11 +778,12 @@ gfxAtsuiTextRun::gfxAtsuiTextRun(const nsAString& aString, gfxAtsuiFontGroup *aG
|
||||
UniCharArrayOffset changedOffset;
|
||||
UniCharCount changedLength;
|
||||
|
||||
OSStatus status = ATSUMatchFontsToText (mATSULayout, runStart, runLength,
|
||||
OSStatus status = ATSUMatchFontsToText (layout, runStart, runLength,
|
||||
&substituteFontID, &changedOffset, &changedLength);
|
||||
if (status == noErr) {
|
||||
//fprintf (stderr, "ATSUMatchFontsToText returned noErr\n");
|
||||
// everything's good, finish up
|
||||
aRun->AddGlyphRun(atsuiFont, runStart - 1);
|
||||
break;
|
||||
} else if (status == kATSUFontsMatched) {
|
||||
//fprintf (stderr, "ATSUMatchFontsToText returned kATSUFontsMatched: FID %d\n", substituteFontID);
|
||||
@ -564,188 +798,43 @@ gfxAtsuiTextRun::gfxAtsuiTextRun(const nsAString& aString, gfxAtsuiFontGroup *aG
|
||||
|
||||
ATSUSetAttributes (subStyle, 1, fontTags, fontArgSizes, fontArgs);
|
||||
|
||||
ATSUSetRunStyle (mATSULayout, subStyle, changedOffset, changedLength);
|
||||
if (changedOffset > runStart) {
|
||||
aRun->AddGlyphRun(atsuiFont, runStart - 1);
|
||||
}
|
||||
|
||||
mStylesToDispose.AppendElement(subStyle);
|
||||
ATSUSetRunStyle (layout, subStyle, changedOffset, changedLength);
|
||||
|
||||
gfxAtsuiFont *font = FindFontFor(substituteFontID);
|
||||
if (font) {
|
||||
aRun->AddGlyphRun(font, changedOffset - 1);
|
||||
}
|
||||
|
||||
stylesToDispose.AppendElement(subStyle);
|
||||
} else if (status == kATSUFontsNotMatched) {
|
||||
//fprintf (stderr, "ATSUMatchFontsToText returned kATSUFontsNotMatched\n");
|
||||
/* I need to select the last resort font; how the heck do I do that? */
|
||||
// Record which font is associated with these glyphs, anyway
|
||||
aRun->AddGlyphRun(atsuiFont, runStart - 1);
|
||||
}
|
||||
|
||||
//fprintf (stderr, "total length: %d changedOffset: %d changedLength: %d\n", runLength, changedOffset, changedLength);
|
||||
//fprintf (stderr, "total length: %d changedOffset: %d changedLength: %d\p=n", runLength, changedOffset, changedLength);
|
||||
|
||||
runStart = changedOffset + changedLength;
|
||||
runLength = totalLength - runStart;
|
||||
}
|
||||
|
||||
//fprintf (stderr, "==== End font matching\n");
|
||||
}
|
||||
|
||||
gfxAtsuiTextRun::~gfxAtsuiTextRun()
|
||||
{
|
||||
if (mATSULayout)
|
||||
ATSUDisposeTextLayout(mATSULayout);
|
||||
|
||||
for (PRUint32 i = 0; i < mStylesToDispose.Length(); i++) {
|
||||
ATSUStyle s = mStylesToDispose[i];
|
||||
ATSUDisposeStyle(s);
|
||||
}
|
||||
}
|
||||
|
||||
struct CairoGlyphBuffer {
|
||||
CairoGlyphBuffer() {
|
||||
size = 128;
|
||||
glyphs = staticGlyphBuf;
|
||||
}
|
||||
|
||||
~CairoGlyphBuffer() {
|
||||
if (glyphs != staticGlyphBuf)
|
||||
PR_Free(glyphs);
|
||||
}
|
||||
|
||||
void EnsureSize(PRUint32 numGlyphs) {
|
||||
if (size < numGlyphs) {
|
||||
if (glyphs != staticGlyphBuf)
|
||||
PR_Free(glyphs);
|
||||
glyphs = (cairo_glyph_t*) PR_Malloc(sizeof(cairo_glyph_t) * numGlyphs);
|
||||
size = numGlyphs;
|
||||
}
|
||||
}
|
||||
|
||||
PRUint32 size;
|
||||
cairo_glyph_t *glyphs;
|
||||
private:
|
||||
cairo_glyph_t staticGlyphBuf[128];
|
||||
};
|
||||
|
||||
void
|
||||
gfxAtsuiTextRun::Draw(gfxContext *aContext, gfxPoint pt)
|
||||
{
|
||||
gfxFloat offsetX, offsetY;
|
||||
nsRefPtr<gfxASurface> surf = aContext->CurrentSurface (&offsetX, &offsetY);
|
||||
|
||||
cairo_t *cr = aContext->GetCairo();
|
||||
double fontSize = mGroup->GetStyle()->size;
|
||||
|
||||
cairo_save (cr);
|
||||
cairo_translate (cr, pt.x, pt.y);
|
||||
|
||||
OSStatus status;
|
||||
|
||||
ByteCount bufferSize = 4096;
|
||||
ATSUGlyphInfoArray *glyphInfo = (ATSUGlyphInfoArray*) PR_Malloc (bufferSize);
|
||||
status = ATSUGetGlyphInfo (mATSULayout, kATSUFromTextBeginning, kATSUToTextEnd, &bufferSize, glyphInfo);
|
||||
if (status == buffersTooSmall) {
|
||||
ATSUGetGlyphInfo (mATSULayout, kATSUFromTextBeginning, kATSUToTextEnd, &bufferSize, NULL);
|
||||
PR_Free(glyphInfo);
|
||||
glyphInfo = (ATSUGlyphInfoArray*) PR_Malloc (bufferSize);
|
||||
|
||||
status = ATSUGetGlyphInfo (mATSULayout, kATSUFromTextBeginning, kATSUToTextEnd, &bufferSize, glyphInfo);
|
||||
}
|
||||
|
||||
if (status != noErr) {
|
||||
fprintf (stderr, "ATSUGetGlyphInfo returned error %d\n", (int) status);
|
||||
PR_Free (glyphInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
CairoGlyphBuffer cairoGlyphs;
|
||||
|
||||
ItemCount i = 0;
|
||||
while (i < glyphInfo->numGlyphs) {
|
||||
ATSUStyle runStyle = glyphInfo->glyphs[i].style;
|
||||
|
||||
ItemCount lastRunGlyph;
|
||||
for (lastRunGlyph = i+1; lastRunGlyph < glyphInfo->numGlyphs; lastRunGlyph++) {
|
||||
if (glyphInfo->glyphs[lastRunGlyph].style != runStyle)
|
||||
break;
|
||||
}
|
||||
|
||||
ItemCount numGlyphs = lastRunGlyph-i;
|
||||
ATSUFontID runFontID;
|
||||
ByteCount unused;
|
||||
status = ATSUGetAttribute (runStyle, kATSUFontTag, sizeof(ATSUFontID), &runFontID, &unused);
|
||||
if (status != noErr || runFontID == kATSUInvalidFontID) {
|
||||
i += numGlyphs;
|
||||
continue;
|
||||
}
|
||||
|
||||
cairoGlyphs.EnsureSize(numGlyphs);
|
||||
cairo_glyph_t *runGlyphs = cairoGlyphs.glyphs;
|
||||
|
||||
for (ItemCount k = i; k < lastRunGlyph; k++) {
|
||||
runGlyphs->index = glyphInfo->glyphs[k].glyphID;
|
||||
runGlyphs->x = glyphInfo->glyphs[k].idealX; /* screenX */
|
||||
runGlyphs->y = glyphInfo->glyphs[k].deltaY;
|
||||
|
||||
runGlyphs++;
|
||||
}
|
||||
|
||||
gfxAtsuiFont *font = mGroup->FindFontFor(runFontID);
|
||||
|
||||
if (gfxFontTestStore::CurrentStore()) {
|
||||
gfxFontTestStore::CurrentStore()->AddItem(font->GetUniqueName(),
|
||||
cairoGlyphs.glyphs, numGlyphs);
|
||||
}
|
||||
|
||||
cairo_set_scaled_font (cr, font->CairoScaledFont());
|
||||
cairo_show_glyphs (cr, cairoGlyphs.glyphs, numGlyphs);
|
||||
|
||||
i += numGlyphs;
|
||||
}
|
||||
|
||||
PR_Free (glyphInfo);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
gfxFloat
|
||||
gfxAtsuiTextRun::Measure(gfxContext *aContext)
|
||||
{
|
||||
OSStatus status;
|
||||
// Trigger layout so that our callback fires. We don't actually care about
|
||||
// the result of this call.
|
||||
ATSTrapezoid trap;
|
||||
ItemCount numBounds;
|
||||
ItemCount trapCount;
|
||||
ATSUGetGlyphBounds(layout, 0, 0, 1, aLength, kATSUseDeviceOrigins, 1, &trap, &trapCount);
|
||||
|
||||
status = ATSUGetGlyphBounds(mATSULayout,
|
||||
FloatToFixed(0.0),
|
||||
FloatToFixed(0.0),
|
||||
kATSUFromTextBeginning,
|
||||
kATSUToTextEnd,
|
||||
kATSUseFractionalOrigins,
|
||||
1,
|
||||
&trap,
|
||||
&numBounds);
|
||||
if (status != noErr) {
|
||||
fprintf(stderr, "ATSUGetGlyphBounds returned error %d!\n", (int) status);
|
||||
return 0.0;
|
||||
ATSUDisposeTextLayout(layout);
|
||||
|
||||
//fprintf (stderr, "==== End font matching\n");
|
||||
PRUint32 i;
|
||||
for (i = 0; i < stylesToDispose.Length(); ++i) {
|
||||
ATSUDisposeStyle(stylesToDispose[i]);
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf ("Measure: '%s' trap: %f %f %f %f\n",
|
||||
NS_ConvertUTF16toUTF8(mString).get(),
|
||||
FixedToFloat(trap.upperLeft.x), FixedToFloat(trap.lowerLeft.x),
|
||||
FixedToFloat(trap.upperRight.x), FixedToFloat(trap.lowerRight.x));
|
||||
#endif
|
||||
|
||||
float f =
|
||||
FixedToFloat(PR_MAX(trap.upperRight.x, trap.lowerRight.x)) -
|
||||
FixedToFloat(PR_MIN(trap.upperLeft.x, trap.lowerLeft.x));
|
||||
|
||||
return f;
|
||||
gCallbackClosure = nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
gfxAtsuiTextRun::SetSpacing(const nsTArray<gfxFloat> &spacingArray)
|
||||
{
|
||||
// We can implement this, but there's really a mismatch between
|
||||
// the spacings given here and cluster spacings. So we're
|
||||
// going to do nothing until we figure out what the layout API
|
||||
// will look like for this.
|
||||
}
|
||||
|
||||
const nsTArray<gfxFloat> *const
|
||||
gfxAtsuiTextRun::GetSpacing() const
|
||||
{
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -37,6 +37,42 @@
|
||||
|
||||
#include "gfxTextRunCache.h"
|
||||
|
||||
static inline PRBool
|
||||
IsAscii(const char *aString, PRUint32 aLength)
|
||||
{
|
||||
const char *end = aString + aLength;
|
||||
while (aString < end) {
|
||||
if (0x80 & *aString)
|
||||
return PR_FALSE;
|
||||
++aString;
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
static inline PRBool
|
||||
IsAscii(const PRUnichar *aString, PRUint32 aLength)
|
||||
{
|
||||
const PRUnichar *end = aString + aLength;
|
||||
while (aString < end) {
|
||||
if (0x0080 <= *aString)
|
||||
return PR_FALSE;
|
||||
++aString;
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
static inline PRBool
|
||||
Is8Bit(const PRUnichar *aString, PRUint32 aLength)
|
||||
{
|
||||
const PRUnichar *end = aString + aLength;
|
||||
while (aString < end) {
|
||||
if (0x0100 <= *aString)
|
||||
return PR_FALSE;
|
||||
++aString;
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
gfxTextRunCache* gfxTextRunCache::mGlobalCache = nsnull;
|
||||
PRInt32 gfxTextRunCache::mGlobalCacheRefCount = 0;
|
||||
|
||||
@ -85,7 +121,8 @@ gfxTextRunCache::Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
static PRUint32 ComputeFlags(PRBool aIsRTL, PRBool aEnableSpacing)
|
||||
static PRUint32
|
||||
ComputeFlags(PRBool aIsRTL, PRBool aEnableSpacing)
|
||||
{
|
||||
PRUint32 flags = gfxTextRunFactory::TEXT_HAS_SURROGATES;
|
||||
if (aIsRTL) {
|
||||
@ -100,18 +137,23 @@ static PRUint32 ComputeFlags(PRBool aIsRTL, PRBool aEnableSpacing)
|
||||
}
|
||||
|
||||
gfxTextRun*
|
||||
gfxTextRunCache::GetOrMakeTextRun (gfxContext* aContext, gfxFontGroup *aFontGroup,
|
||||
const PRUnichar *aString, PRUint32 aLength,
|
||||
PRUint32 aAppUnitsPerDevUnit, PRBool aIsRTL,
|
||||
PRBool aEnableSpacing, PRBool *aCallerOwns)
|
||||
gfxTextRunCache::GetOrMakeTextRun(gfxContext *aContext, gfxFontGroup *aFontGroup,
|
||||
const PRUnichar *aString, PRUint32 aLength,
|
||||
PRUint32 aAppUnitsPerDevUnit, PRBool aIsRTL,
|
||||
PRBool aEnableSpacing, PRBool *aCallerOwns)
|
||||
{
|
||||
gfxSkipChars skipChars;
|
||||
// Choose pessimistic flags since we don't want to bother analyzing the string
|
||||
gfxTextRunFactory::Parameters params =
|
||||
{ aContext, nsnull, nsnull, &skipChars, nsnull, 0, aAppUnitsPerDevUnit,
|
||||
ComputeFlags(aIsRTL, aEnableSpacing) };
|
||||
gfxTextRunFactory::Parameters params = {
|
||||
aContext, nsnull, nsnull, &skipChars, nsnull, 0, aAppUnitsPerDevUnit,
|
||||
ComputeFlags(aIsRTL, aEnableSpacing)
|
||||
};
|
||||
if (IsAscii(aString, aLength))
|
||||
params.mFlags |= gfxTextRunFactory::TEXT_IS_ASCII;
|
||||
// else if (Is8Bit(aString, aLength))
|
||||
// params.mFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
|
||||
|
||||
gfxTextRun* tr = nsnull;
|
||||
gfxTextRun *tr = nsnull;
|
||||
// Don't cache textruns that use spacing
|
||||
if (!gDisableCache && !aEnableSpacing) {
|
||||
// Evict first, to make sure that the textrun we return is live.
|
||||
@ -156,19 +198,25 @@ gfxTextRunCache::GetOrMakeTextRun (gfxContext* aContext, gfxFontGroup *aFontGrou
|
||||
}
|
||||
|
||||
gfxTextRun*
|
||||
gfxTextRunCache::GetOrMakeTextRun (gfxContext* aContext, gfxFontGroup *aFontGroup,
|
||||
const char *aString, PRUint32 aLength,
|
||||
PRUint32 aAppUnitsPerDevUnit, PRBool aIsRTL,
|
||||
PRBool aEnableSpacing, PRBool *aCallerOwns)
|
||||
gfxTextRunCache::GetOrMakeTextRun(gfxContext *aContext, gfxFontGroup *aFontGroup,
|
||||
const char *aString, PRUint32 aLength,
|
||||
PRUint32 aAppUnitsPerDevUnit, PRBool aIsRTL,
|
||||
PRBool aEnableSpacing, PRBool *aCallerOwns)
|
||||
{
|
||||
gfxSkipChars skipChars;
|
||||
// Choose pessimistic flags since we don't want to bother analyzing the string
|
||||
gfxTextRunFactory::Parameters params =
|
||||
{ aContext, nsnull, nsnull, &skipChars, nsnull, 0, aAppUnitsPerDevUnit,
|
||||
ComputeFlags(aIsRTL, aEnableSpacing) };
|
||||
const PRUint8* str = NS_REINTERPRET_CAST(const PRUint8*, aString);
|
||||
gfxTextRunFactory::Parameters params = {
|
||||
aContext, nsnull, nsnull, &skipChars, nsnull, 0, aAppUnitsPerDevUnit,
|
||||
ComputeFlags(aIsRTL, aEnableSpacing)
|
||||
};
|
||||
if (IsAscii(aString, aLength))
|
||||
params.mFlags |= gfxTextRunFactory::TEXT_IS_ASCII;
|
||||
else
|
||||
params.mFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
|
||||
|
||||
gfxTextRun* tr = nsnull;
|
||||
const PRUint8 *str = reinterpret_cast<const PRUint8*>(aString);
|
||||
|
||||
gfxTextRun *tr = nsnull;
|
||||
// Don't cache textruns that use spacing
|
||||
if (!gDisableCache && !aEnableSpacing) {
|
||||
// Evict first, to make sure that the textrun we return is live.
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user