зеркало из https://github.com/mozilla/pjs.git
Bug 96041. Compute precise glyph extents for high-quality text, and when requested. r=vlad,r+sr=dbaron
This commit is contained in:
Родитель
d503c88193
Коммит
dcd17482e7
|
@ -1589,8 +1589,8 @@ nsCanvasRenderingContext2D::MozMeasureText(const nsAString& textToMeasure, float
|
|||
|
||||
PRBool tightBoundingBox = PR_FALSE;
|
||||
gfxTextRun::Metrics metrics = textRun->MeasureText(/* offset = */ 0, textToMeasure.Length(),
|
||||
tightBoundingBox,
|
||||
nsnull);
|
||||
tightBoundingBox, mThebesContext,
|
||||
nsnull);
|
||||
*retVal = metrics.mAdvanceWidth/gfxFloat(elemDocument->GetPrimaryShell()->GetPresContext()->AppUnitsPerCSSPixel());
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ typedef struct {
|
|||
HFONT scaled_hfont;
|
||||
HFONT unscaled_hfont;
|
||||
|
||||
cairo_bool_t is_truetype;
|
||||
cairo_bool_t glyph_indexing;
|
||||
|
||||
cairo_bool_t delete_scaled_hfont;
|
||||
|
@ -744,11 +745,11 @@ _cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font)
|
|||
|
||||
}
|
||||
|
||||
if ((metrics.tmPitchAndFamily & TMPF_TRUETYPE) ||
|
||||
(GetFontData (hdc, OPENTYPE_CFF_TAG, 0, NULL, 0) != GDI_ERROR))
|
||||
scaled_font->glyph_indexing = TRUE;
|
||||
else
|
||||
scaled_font->glyph_indexing = FALSE;
|
||||
scaled_font->is_truetype = (metrics.tmPitchAndFamily & TMPF_TRUETYPE) != 0;
|
||||
scaled_font->glyph_indexing = scaled_font->is_truetype ||
|
||||
(GetFontData (hdc, OPENTYPE_CFF_TAG, 0, NULL, 0) != GDI_ERROR);
|
||||
// XXX in what situations does this OPENTYPE_CFF thing not have the
|
||||
// TMPF_TRUETYPE flag? GetFontData says it only works on Truetype fonts...
|
||||
|
||||
_cairo_scaled_font_set_metrics (&scaled_font->base, &extents);
|
||||
|
||||
|
@ -764,18 +765,35 @@ _cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_f
|
|||
cairo_status_t status;
|
||||
cairo_text_extents_t extents;
|
||||
HDC hdc;
|
||||
UINT glyph_index_option;
|
||||
|
||||
hdc = _get_global_font_dc ();
|
||||
if (!hdc)
|
||||
return CAIRO_STATUS_NO_MEMORY;
|
||||
|
||||
if (scaled_font->glyph_indexing)
|
||||
glyph_index_option = GGO_GLYPH_INDEX;
|
||||
else
|
||||
glyph_index_option = 0;
|
||||
if (!scaled_font->is_truetype) {
|
||||
/* GetGlyphOutline will not work. Assume that the glyph does not extend outside the font box. */
|
||||
cairo_font_extents_t font_extents;
|
||||
INT width = 0;
|
||||
UINT charIndex = _cairo_scaled_glyph_index (scaled_glyph);
|
||||
|
||||
if (scaled_font->preserve_axes && scaled_font->base.options.hint_style != CAIRO_HINT_METRICS_OFF) {
|
||||
cairo_scaled_font_extents (&scaled_font->base, &font_extents);
|
||||
|
||||
status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
|
||||
if (!status) {
|
||||
if (!GetCharWidth32(hdc, charIndex, charIndex, &width)) {
|
||||
status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetCharWidth32");
|
||||
width = 0;
|
||||
}
|
||||
}
|
||||
cairo_win32_scaled_font_done_font (&scaled_font->base);
|
||||
|
||||
extents.x_bearing = 0;
|
||||
extents.y_bearing = -font_extents.ascent / scaled_font->y_scale;
|
||||
extents.width = width / scaled_font->x_scale;
|
||||
extents.height = (font_extents.ascent + font_extents.descent) / scaled_font->y_scale;
|
||||
extents.x_advance = extents.width;
|
||||
extents.y_advance = 0;
|
||||
} else if (scaled_font->preserve_axes && scaled_font->base.options.hint_style != CAIRO_HINT_METRICS_OFF) {
|
||||
/* If we aren't rotating / skewing the axes, then we get the metrics
|
||||
* from the GDI in device space and convert to font space.
|
||||
*/
|
||||
|
@ -783,7 +801,7 @@ _cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_f
|
|||
if (status)
|
||||
return status;
|
||||
if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph),
|
||||
GGO_METRICS | glyph_index_option,
|
||||
GGO_METRICS | GGO_GLYPH_INDEX,
|
||||
&metrics, 0, NULL, &matrix) == GDI_ERROR) {
|
||||
status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetGlyphOutlineW");
|
||||
memset (&metrics, 0, sizeof (GLYPHMETRICS));
|
||||
|
@ -822,7 +840,7 @@ _cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_f
|
|||
*/
|
||||
status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc);
|
||||
if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph),
|
||||
GGO_METRICS | glyph_index_option,
|
||||
GGO_METRICS | GGO_GLYPH_INDEX,
|
||||
&metrics, 0, NULL, &matrix) == GDI_ERROR) {
|
||||
status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetGlyphOutlineW");
|
||||
memset (&metrics, 0, sizeof (GLYPHMETRICS));
|
||||
|
|
|
@ -72,6 +72,9 @@ public:
|
|||
|
||||
PRBool HasMirroringInfo();
|
||||
|
||||
virtual void SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID,
|
||||
PRBool aNeedTight, gfxGlyphExtents *aExtents);
|
||||
|
||||
protected:
|
||||
const gfxFontStyle *mFontStyle;
|
||||
|
||||
|
@ -93,7 +96,7 @@ protected:
|
|||
|
||||
void InitMetrics(ATSUFontID aFontID, ATSFontRef aFontRef);
|
||||
|
||||
virtual PRBool SetupCairoFont(cairo_t *aCR);
|
||||
virtual PRBool SetupCairoFont(gfxContext *aContext);
|
||||
};
|
||||
|
||||
class THEBES_API gfxAtsuiFontGroup : public gfxFontGroup {
|
||||
|
|
|
@ -56,7 +56,6 @@ class gfxTextRun;
|
|||
class nsIAtom;
|
||||
class gfxFont;
|
||||
class gfxFontGroup;
|
||||
typedef struct _cairo cairo_t;
|
||||
|
||||
#define FONT_STYLE_NORMAL 0
|
||||
#define FONT_STYLE_ITALIC 1
|
||||
|
@ -230,6 +229,75 @@ protected:
|
|||
nsTHashtable<HashEntry> mFonts;
|
||||
};
|
||||
|
||||
/**
|
||||
* This stores glyph bounds information for a particular gfxFont, at
|
||||
* a particular appunits-per-dev-pixel ratio (because the compressed glyph
|
||||
* width array is stored in appunits).
|
||||
*
|
||||
* We store a hashtable from glyph IDs to float bounding rects. For the
|
||||
* common case where the glyph has no horizontal left bearing, and no
|
||||
* y overflow above the font ascent or below the font descent, and tight
|
||||
* bounding boxes are not required, we avoid storing the glyph ID in the hashtable
|
||||
* and instead consult an array of 16-bit glyph XMost values (in appunits).
|
||||
* This array always has an entry for the font's space glyph --- the width is
|
||||
* assumed to be zero.
|
||||
*/
|
||||
class THEBES_API gfxGlyphExtents {
|
||||
public:
|
||||
gfxGlyphExtents(PRUint32 aAppUnitsPerDevUnit) :
|
||||
mAppUnitsPerDevUnit(aAppUnitsPerDevUnit) {
|
||||
mTightGlyphExtents.Init();
|
||||
}
|
||||
|
||||
enum { INVALID_WIDTH = 0xFFFF };
|
||||
|
||||
// returns INVALID_WIDTH => not a contained glyph
|
||||
// Otherwise the glyph has no before-bearing or vertical bearings,
|
||||
// and the result is its width measured from the baseline origin, in
|
||||
// appunits.
|
||||
PRUint16 GetContainedGlyphWidthAppUnits(PRUint32 aGlyphID) const {
|
||||
if (aGlyphID >= mWidthsForContainedGlyphsAppUnits.Length())
|
||||
return INVALID_WIDTH;
|
||||
return mWidthsForContainedGlyphsAppUnits[aGlyphID];
|
||||
}
|
||||
|
||||
PRBool IsGlyphKnown(PRUint32 aGlyphID) const {
|
||||
if (aGlyphID < mWidthsForContainedGlyphsAppUnits.Length() &&
|
||||
mWidthsForContainedGlyphsAppUnits[aGlyphID] != INVALID_WIDTH)
|
||||
return PR_TRUE;
|
||||
return mTightGlyphExtents.GetEntry(aGlyphID) != nsnull;
|
||||
}
|
||||
|
||||
PRBool IsGlyphKnownWithTightExtents(PRUint32 aGlyphID) const {
|
||||
return mTightGlyphExtents.GetEntry(aGlyphID) != nsnull;
|
||||
}
|
||||
|
||||
// Get glyph extents; a rectangle relative to the left baseline origin
|
||||
gfxRect GetTightGlyphExtentsAppUnits(gfxFont *aFont, gfxContext *aContext, PRUint32 aGlyphID);
|
||||
|
||||
void SetContainedGlyphWidthAppUnits(PRUint32 aGlyphID, PRUint16 aWidth);
|
||||
void SetTightGlyphExtents(PRUint32 aGlyphID, const gfxRect& aExtentsAppUnits);
|
||||
|
||||
PRUint32 GetAppUnitsPerDevUnit() { return mAppUnitsPerDevUnit; }
|
||||
|
||||
private:
|
||||
class HashEntry : public nsUint32HashKey {
|
||||
public:
|
||||
// When constructing a new entry in the hashtable, we'll leave this
|
||||
// blank. The caller of Put() will fill this in.
|
||||
HashEntry(KeyTypePointer aPtr) : nsUint32HashKey(aPtr) {}
|
||||
HashEntry(const HashEntry& toCopy) : nsUint32HashKey(toCopy) {
|
||||
x = toCopy.x; y = toCopy.y; width = toCopy.width; height = toCopy.height;
|
||||
}
|
||||
|
||||
float x, y, width, height;
|
||||
};
|
||||
|
||||
nsTArray<PRUint16> mWidthsForContainedGlyphsAppUnits;
|
||||
nsTHashtable<HashEntry> mTightGlyphExtents;
|
||||
PRUint32 mAppUnitsPerDevUnit;
|
||||
};
|
||||
|
||||
/* a SPECIFIC single font family */
|
||||
class THEBES_API gfxFont {
|
||||
public:
|
||||
|
@ -259,7 +327,7 @@ protected:
|
|||
|
||||
public:
|
||||
gfxFont(const nsAString &aName, const gfxFontStyle *aFontGroup);
|
||||
virtual ~gfxFont() {}
|
||||
virtual ~gfxFont();
|
||||
|
||||
const nsString& GetName() const { return mName; }
|
||||
const gfxFontStyle *GetStyle() const { return &mStyle; }
|
||||
|
@ -389,6 +457,7 @@ public:
|
|||
virtual RunMetrics Measure(gfxTextRun *aTextRun,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
PRBool aTightBoundingBox,
|
||||
gfxContext *aContextForTightBoundingBox,
|
||||
Spacing *aSpacing);
|
||||
/**
|
||||
* Line breaks have been changed at the beginning and/or end of a substring
|
||||
|
@ -405,14 +474,21 @@ public:
|
|||
// Get the glyphID of a space
|
||||
virtual PRUint32 GetSpaceGlyph() = 0;
|
||||
|
||||
protected:
|
||||
// The family name of the font
|
||||
nsString mName;
|
||||
nsExpirationState mExpirationState;
|
||||
gfxFontStyle mStyle;
|
||||
gfxGlyphExtents *GetOrCreateGlyphExtents(PRUint32 aAppUnitsPerDevUnit);
|
||||
|
||||
// You need to call SetupCairoFont on the aCR just before calling this
|
||||
virtual void SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID,
|
||||
PRBool aNeedTight, gfxGlyphExtents *aExtents);
|
||||
|
||||
// This is called by the default Draw() implementation above.
|
||||
virtual PRBool SetupCairoFont(cairo_t *aCR) = 0;
|
||||
virtual PRBool SetupCairoFont(gfxContext *aContext) = 0;
|
||||
|
||||
protected:
|
||||
// The family name of the font
|
||||
nsString mName;
|
||||
nsExpirationState mExpirationState;
|
||||
gfxFontStyle mStyle;
|
||||
nsAutoTArray<gfxGlyphExtents*,1> mGlyphExtentsArray;
|
||||
};
|
||||
|
||||
class THEBES_API gfxTextRunFactory {
|
||||
|
@ -686,6 +762,7 @@ public:
|
|||
*/
|
||||
Metrics MeasureText(PRUint32 aStart, PRUint32 aLength,
|
||||
PRBool aTightBoundingBox,
|
||||
gfxContext *aRefContextForTightBoundingBox,
|
||||
PropertyProvider *aProvider);
|
||||
|
||||
/**
|
||||
|
@ -764,6 +841,8 @@ public:
|
|||
* @param aMetrics if non-null, we fill this in for the returned substring.
|
||||
* If a hyphenation break was used, the hyphen is NOT included in the returned metrics.
|
||||
* @param aTightBoundingBox if true, we make the bounding box in aMetrics tight
|
||||
* @param aRefContextForTightBoundingBox a reference context to get the
|
||||
* tight bounding box, if aTightBoundingBox is true
|
||||
* @param aUsedHyphenation if non-null, records if we selected a hyphenation break
|
||||
* @param aLastBreak if non-null and result is aMaxLength, we set this to
|
||||
* the maximal N such that
|
||||
|
@ -782,6 +861,7 @@ public:
|
|||
PRBool aSuppressInitialBreak,
|
||||
gfxFloat *aTrimWhitespace,
|
||||
Metrics *aMetrics, PRBool aTightBoundingBox,
|
||||
gfxContext *aRefContextForTightBoundingBox,
|
||||
PRBool *aUsedHyphenation,
|
||||
PRUint32 *aLastBreak);
|
||||
|
||||
|
@ -932,6 +1012,8 @@ public:
|
|||
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) {
|
||||
NS_ASSERTION(aCanBreakBefore == PR_FALSE || aCanBreakBefore == PR_TRUE,
|
||||
"Bogus break-before value!");
|
||||
PRUint32 breakMask = aCanBreakBefore*FLAG_CAN_BREAK_BEFORE;
|
||||
PRUint32 toggle = breakMask ^ (mValue & FLAG_CAN_BREAK_BEFORE);
|
||||
mValue ^= toggle;
|
||||
|
@ -1070,6 +1152,8 @@ public:
|
|||
PRUint32 aNumGlyphs);
|
||||
void SetMissingGlyph(PRUint32 aCharIndex, PRUint32 aChar);
|
||||
void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIndex);
|
||||
|
||||
void FetchGlyphExtents(gfxContext *aRefContext);
|
||||
|
||||
// API for access to the raw glyph data, needed by gfxFont::Draw
|
||||
// and gfxFont::GetBoundingBox
|
||||
|
@ -1079,6 +1163,7 @@ public:
|
|||
// Missing glyphs need not have details.
|
||||
return mDetailedGlyphs ? mDetailedGlyphs[aCharIndex].get() : nsnull;
|
||||
}
|
||||
PRBool HasDetailedGlyphs() { return mDetailedGlyphs.get() != nsnull; }
|
||||
PRUint32 CountMissingGlyphs();
|
||||
const GlyphRun *GetGlyphRuns(PRUint32 *aNumGlyphRuns) {
|
||||
*aNumGlyphRuns = mGlyphRuns.Length();
|
||||
|
@ -1157,12 +1242,14 @@ private:
|
|||
gfxFloat GetPartialLigatureWidth(PRUint32 aStart, PRUint32 aEnd, PropertyProvider *aProvider);
|
||||
void AccumulatePartialLigatureMetrics(gfxFont *aFont,
|
||||
PRUint32 aStart, PRUint32 aEnd, PRBool aTight,
|
||||
gfxContext *aRefContext,
|
||||
PropertyProvider *aProvider,
|
||||
Metrics *aMetrics);
|
||||
|
||||
// **** measurement helper ****
|
||||
void AccumulateMetricsForRun(gfxFont *aFont, PRUint32 aStart,
|
||||
PRUint32 aEnd, PRBool aTight,
|
||||
gfxContext *aRefContext,
|
||||
PropertyProvider *aProvider,
|
||||
PRUint32 aSpacingStart, PRUint32 aSpacingEnd,
|
||||
Metrics *aMetrics);
|
||||
|
|
|
@ -111,7 +111,7 @@ protected:
|
|||
void GetCharSize(const char aChar, gfxSize& aInkSize, gfxSize& aLogSize,
|
||||
PRUint32 *aGlyphID = nsnull);
|
||||
|
||||
virtual PRBool SetupCairoFont(cairo_t *aCR);
|
||||
virtual PRBool SetupCairoFont(gfxContext *aContext);
|
||||
};
|
||||
|
||||
class FontSelector;
|
||||
|
|
|
@ -533,7 +533,7 @@ private:
|
|||
|
||||
nsRefPtr<FontEntry> mFontEntry;
|
||||
|
||||
virtual PRBool SetupCairoFont(cairo_t *aCR);
|
||||
virtual PRBool SetupCairoFont(gfxContext *aContext);
|
||||
};
|
||||
|
||||
/**********************************************************************
|
||||
|
|
|
@ -171,8 +171,8 @@ gfxAtsuiFont::InitMetrics(ATSUFontID aFontID, ATSFontRef aFontRef)
|
|||
|
||||
mMetrics.emHeight = size;
|
||||
|
||||
mMetrics.maxAscent = atsMetrics.ascent * size;
|
||||
mMetrics.maxDescent = - (atsMetrics.descent * size);
|
||||
mMetrics.maxAscent = NS_ceil(atsMetrics.ascent * size);
|
||||
mMetrics.maxDescent = NS_ceil(- (atsMetrics.descent * size));
|
||||
|
||||
mMetrics.maxHeight = mMetrics.maxAscent + mMetrics.maxDescent;
|
||||
|
||||
|
@ -226,7 +226,7 @@ gfxAtsuiFont::InitMetrics(ATSUFontID aFontID, ATSFontRef aFontRef)
|
|||
}
|
||||
|
||||
PRBool
|
||||
gfxAtsuiFont::SetupCairoFont(cairo_t *aCR)
|
||||
gfxAtsuiFont::SetupCairoFont(gfxContext *aContext)
|
||||
{
|
||||
cairo_scaled_font_t *scaledFont = CairoScaledFont();
|
||||
if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
|
||||
|
@ -234,7 +234,7 @@ gfxAtsuiFont::SetupCairoFont(cairo_t *aCR)
|
|||
// the cairo_t, precluding any further drawing.
|
||||
return PR_FALSE;
|
||||
}
|
||||
cairo_set_scaled_font(aCR, scaledFont);
|
||||
cairo_set_scaled_font(aContext->GetCairo(), scaledFont);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
@ -329,6 +329,35 @@ CreateFontFallbacksFromFontList(nsTArray< nsRefPtr<gfxFont> > *aFonts,
|
|||
return status == noErr ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void
|
||||
gfxAtsuiFont::SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID,
|
||||
PRBool aNeedTight, gfxGlyphExtents *aExtents)
|
||||
{
|
||||
ATSGlyphScreenMetrics metrics;
|
||||
GlyphID glyph = aGlyphID;
|
||||
OSStatus err = ATSUGlyphGetScreenMetrics(mATSUStyle, 1, &glyph, 0, false, false,
|
||||
&metrics);
|
||||
if (err != noErr)
|
||||
return;
|
||||
PRUint32 appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
|
||||
|
||||
if (!aNeedTight && metrics.topLeft.x >= 0 &&
|
||||
-metrics.topLeft.y + metrics.height <= mMetrics.maxAscent &&
|
||||
metrics.topLeft.y <= mMetrics.maxDescent) {
|
||||
PRUint32 appUnitsWidth =
|
||||
PRUint32(NS_ceil((metrics.topLeft.x + metrics.width)*appUnitsPerDevUnit));
|
||||
if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
|
||||
aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, PRUint16(appUnitsWidth));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
double d2a = appUnitsPerDevUnit;
|
||||
gfxRect bounds(metrics.topLeft.x*d2a, (metrics.topLeft.y - metrics.height)*d2a,
|
||||
metrics.width*d2a, metrics.height*d2a);
|
||||
aExtents->SetTightGlyphExtents(aGlyphID, bounds);
|
||||
}
|
||||
|
||||
PRBool
|
||||
gfxAtsuiFont::HasMirroringInfo()
|
||||
{
|
||||
|
@ -572,6 +601,8 @@ gfxAtsuiFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
|
|||
break;
|
||||
textRun->ResetGlyphRuns();
|
||||
}
|
||||
|
||||
textRun->FetchGlyphExtents(aParams->mContext);
|
||||
|
||||
return textRun;
|
||||
}
|
||||
|
@ -614,6 +645,8 @@ gfxAtsuiFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
|
|||
textRun->ResetGlyphRuns();
|
||||
}
|
||||
|
||||
textRun->FetchGlyphExtents(aParams->mContext);
|
||||
|
||||
return textRun;
|
||||
}
|
||||
|
||||
|
|
|
@ -162,6 +162,17 @@ gfxFont::gfxFont(const nsAString &aName, const gfxFontStyle *aFontStyle) :
|
|||
{
|
||||
}
|
||||
|
||||
gfxFont::~gfxFont()
|
||||
{
|
||||
PRUint32 i;
|
||||
// We destroy the contents of mGlyphExtentsArray explicitly instead of
|
||||
// using nsAutoPtr because VC++ can't deal with nsTArrays of nsAutoPtrs
|
||||
// of classes that lack a proper copy constructor
|
||||
for (i = 0; i < mGlyphExtentsArray.Length(); ++i) {
|
||||
delete mGlyphExtentsArray[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function in case we need to do any rounding or other
|
||||
* processing here.
|
||||
|
@ -213,13 +224,13 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
|
|||
double x = aPt->x;
|
||||
double y = aPt->y;
|
||||
|
||||
cairo_t *cr = aContext->GetCairo();
|
||||
PRBool success = SetupCairoFont(cr);
|
||||
PRBool success = SetupCairoFont(aContext);
|
||||
if (NS_UNLIKELY(!success))
|
||||
return;
|
||||
|
||||
GlyphBuffer glyphs;
|
||||
cairo_glyph_t *glyph;
|
||||
cairo_t *cr = aContext->GetCairo();
|
||||
|
||||
if (aSpacing) {
|
||||
x += direction*aSpacing[0].mBefore;
|
||||
|
@ -306,53 +317,220 @@ gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
|
|||
*aPt = gfxPoint(x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
UnionWithXPoint(gfxRect *aRect, double aX)
|
||||
{
|
||||
if (aX < aRect->pos.x) {
|
||||
aRect->size.width += aRect->pos.x - aX;
|
||||
aRect->pos.x = aX;
|
||||
} else if (aX > aRect->XMost()) {
|
||||
aRect->size.width = aX - aRect->pos.x;
|
||||
}
|
||||
}
|
||||
|
||||
static PRBool
|
||||
NeedsGlyphExtents(gfxTextRun *aTextRun)
|
||||
{
|
||||
return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED) == 0;
|
||||
// return PR_TRUE;
|
||||
}
|
||||
|
||||
gfxFont::RunMetrics
|
||||
gfxFont::Measure(gfxTextRun *aTextRun,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
PRBool aTightBoundingBox,
|
||||
PRBool aTightBoundingBox, gfxContext *aRefContext,
|
||||
Spacing *aSpacing)
|
||||
{
|
||||
// XXX temporary code, does not handle glyphs outside the font-box
|
||||
// XXX comment out the assertion for now since it fires too much
|
||||
// NS_ASSERTION(!(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX),
|
||||
// "Glyph extents not yet supported");
|
||||
PRInt32 advance = 0;
|
||||
PRUint32 i;
|
||||
const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
|
||||
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
|
||||
PRBool isRTL = aTextRun->IsRightToLeft();
|
||||
double direction = aTextRun->GetDirection();
|
||||
// Current position in appunits
|
||||
const gfxFont::Metrics& fontMetrics = GetMetrics();
|
||||
|
||||
RunMetrics metrics;
|
||||
metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit;
|
||||
metrics.mDescent = fontMetrics.maxDescent*appUnitsPerDevUnit;
|
||||
metrics.mAdvanceWidth = 0;
|
||||
if (aTightBoundingBox) {
|
||||
metrics.mBoundingBox = gfxRect(0, 0, 0, 0);
|
||||
} else {
|
||||
metrics.mBoundingBox = gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent);
|
||||
}
|
||||
|
||||
gfxGlyphExtents *extents =
|
||||
(!aTightBoundingBox && !NeedsGlyphExtents(aTextRun) && !aTextRun->HasDetailedGlyphs()) ? nsnull
|
||||
: GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
|
||||
double x = 0;
|
||||
if (aSpacing) {
|
||||
x += direction*aSpacing[0].mBefore;
|
||||
}
|
||||
PRUint32 i;
|
||||
for (i = aStart; i < aEnd; ++i) {
|
||||
gfxTextRun::CompressedGlyph g = charGlyphs[i];
|
||||
if (g.IsClusterStart()) {
|
||||
if (g.IsSimpleGlyph()) {
|
||||
advance += charGlyphs[i].GetSimpleAdvance();
|
||||
} else if (g.IsComplexOrMissing()) {
|
||||
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
|
||||
while (details) {
|
||||
advance += details->mAdvance;
|
||||
if (details->mIsLastGlyph)
|
||||
break;
|
||||
++details;
|
||||
const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
|
||||
if (glyphData->IsSimpleGlyph()) {
|
||||
double advance = glyphData->GetSimpleAdvance();
|
||||
// Only get the real glyph horizontal extent if we were asked
|
||||
// for the tight bounding box or we're in quality mode
|
||||
if (aTightBoundingBox || NeedsGlyphExtents(aTextRun)) {
|
||||
PRUint32 glyphIndex = glyphData->GetSimpleGlyph();
|
||||
PRUint16 extentsWidth = extents->GetContainedGlyphWidthAppUnits(glyphIndex);
|
||||
if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH && !aTightBoundingBox) {
|
||||
UnionWithXPoint(&metrics.mBoundingBox, x + direction*extentsWidth);
|
||||
} else {
|
||||
gfxRect glyphRect =
|
||||
extents->GetTightGlyphExtentsAppUnits(this, aRefContext, glyphIndex);
|
||||
if (isRTL) {
|
||||
glyphRect.pos.x -= advance;
|
||||
}
|
||||
glyphRect.pos.x += x;
|
||||
metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
|
||||
}
|
||||
}
|
||||
x += direction*advance;
|
||||
} else if (glyphData->IsComplexCluster()) {
|
||||
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
|
||||
for (;;) {
|
||||
PRUint32 glyphIndex = details->mGlyphID;
|
||||
gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
|
||||
double advance = details->mAdvance;
|
||||
gfxRect glyphRect =
|
||||
extents->GetTightGlyphExtentsAppUnits(this, aRefContext, glyphIndex);
|
||||
if (isRTL) {
|
||||
glyphRect.pos.x -= advance;
|
||||
}
|
||||
glyphRect.pos.x += x;
|
||||
metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
|
||||
x += direction*advance;
|
||||
if (details->mIsLastGlyph)
|
||||
break;
|
||||
++details;
|
||||
}
|
||||
} else if (glyphData->IsMissing()) {
|
||||
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
|
||||
if (details) {
|
||||
double advance = details->mAdvance;
|
||||
gfxRect glyphRect(x, -metrics.mAscent, advance, metrics.mAscent);
|
||||
if (isRTL) {
|
||||
glyphRect.pos.x -= advance;
|
||||
}
|
||||
metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
|
||||
x += direction*advance;
|
||||
}
|
||||
}
|
||||
// Every other glyph type is ignored
|
||||
if (aSpacing) {
|
||||
double space = aSpacing[i - aStart].mAfter;
|
||||
if (i + 1 < aEnd) {
|
||||
space += aSpacing[i + 1 - aStart].mBefore;
|
||||
}
|
||||
x += direction*space;
|
||||
}
|
||||
}
|
||||
|
||||
gfxFloat floatAdvance = advance;
|
||||
if (aSpacing) {
|
||||
for (i = 0; i < aEnd - aStart; ++i) {
|
||||
floatAdvance += aSpacing[i].mBefore + aSpacing[i].mAfter;
|
||||
if (!aTightBoundingBox) {
|
||||
// Make sure the non-tight bounding box includes the entire advance
|
||||
UnionWithXPoint(&metrics.mBoundingBox, x);
|
||||
}
|
||||
if (isRTL) {
|
||||
metrics.mBoundingBox.pos.x -= x;
|
||||
}
|
||||
|
||||
metrics.mAdvanceWidth = x*direction;
|
||||
return metrics;
|
||||
}
|
||||
|
||||
gfxGlyphExtents *
|
||||
gfxFont::GetOrCreateGlyphExtents(PRUint32 aAppUnitsPerDevUnit) {
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mGlyphExtentsArray.Length(); ++i) {
|
||||
if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
|
||||
return mGlyphExtentsArray[i];
|
||||
}
|
||||
gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
|
||||
if (glyphExtents) {
|
||||
mGlyphExtentsArray.AppendElement(glyphExtents);
|
||||
// Initialize the extents of a space glyph, assuming that spaces don't
|
||||
// render anything!
|
||||
glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
|
||||
}
|
||||
return glyphExtents;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFont::SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID, PRBool aNeedTight,
|
||||
gfxGlyphExtents *aExtents)
|
||||
{
|
||||
cairo_glyph_t glyph;
|
||||
glyph.index = aGlyphID;
|
||||
glyph.x = 0;
|
||||
glyph.y = 0;
|
||||
cairo_text_extents_t extents;
|
||||
cairo_glyph_extents(aContext->GetCairo(), &glyph, 1, &extents);
|
||||
|
||||
const Metrics& fontMetrics = GetMetrics();
|
||||
PRUint32 appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
|
||||
if (!aNeedTight && extents.x_bearing >= 0 &&
|
||||
extents.y_bearing >= -fontMetrics.maxAscent &&
|
||||
extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
|
||||
PRUint32 appUnitsWidth =
|
||||
PRUint32(NS_ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
|
||||
if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
|
||||
aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, PRUint16(appUnitsWidth));
|
||||
return;
|
||||
}
|
||||
}
|
||||
RunMetrics metrics;
|
||||
const gfxFont::Metrics& fontMetrics = GetMetrics();
|
||||
metrics.mAdvanceWidth = floatAdvance;
|
||||
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
|
||||
metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit;
|
||||
gfxFloat descentForUnderline =
|
||||
NS_round(fontMetrics.underlineSize) + NS_round(metrics.mAscent - fontMetrics.underlineOffset) - metrics.mAscent;
|
||||
metrics.mDescent = PR_MAX(fontMetrics.maxDescent, descentForUnderline)*appUnitsPerDevUnit;
|
||||
metrics.mBoundingBox =
|
||||
gfxRect(0, -metrics.mAscent, floatAdvance, metrics.mAscent + metrics.mDescent);
|
||||
return metrics;
|
||||
|
||||
double d2a = appUnitsPerDevUnit;
|
||||
gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
|
||||
extents.width*d2a, extents.height*d2a);
|
||||
aExtents->SetTightGlyphExtents(aGlyphID, bounds);
|
||||
}
|
||||
|
||||
gfxRect
|
||||
gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
|
||||
gfxContext *aContext, PRUint32 aGlyphID)
|
||||
{
|
||||
HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
|
||||
if (!entry) {
|
||||
aFont->SetupCairoFont(aContext);
|
||||
aFont->SetupGlyphExtents(aContext, aGlyphID, PR_TRUE, this);
|
||||
entry = mTightGlyphExtents.GetEntry(aGlyphID);
|
||||
if (!entry) {
|
||||
NS_WARNING("Could not get glyph extents");
|
||||
return gfxRect(0,0,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
return gfxRect(entry->x, entry->y, entry->width, entry->height);
|
||||
}
|
||||
|
||||
void
|
||||
gfxGlyphExtents::SetContainedGlyphWidthAppUnits(PRUint32 aGlyphID, PRUint16 aWidth)
|
||||
{
|
||||
PRUint32 len = mWidthsForContainedGlyphsAppUnits.Length();
|
||||
if (aGlyphID >= len) {
|
||||
PRUint16 *elems = mWidthsForContainedGlyphsAppUnits.AppendElements(aGlyphID + 1 - len);
|
||||
if (!elems)
|
||||
return;
|
||||
PRUint32 i;
|
||||
for (i = len; i < aGlyphID; ++i) {
|
||||
mWidthsForContainedGlyphsAppUnits[i] = INVALID_WIDTH;
|
||||
}
|
||||
}
|
||||
mWidthsForContainedGlyphsAppUnits[aGlyphID] = aWidth;
|
||||
}
|
||||
|
||||
void
|
||||
gfxGlyphExtents::SetTightGlyphExtents(PRUint32 aGlyphID, const gfxRect& aExtentsAppUnits)
|
||||
{
|
||||
HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID);
|
||||
if (!entry)
|
||||
return;
|
||||
entry->x = aExtentsAppUnits.pos.x;
|
||||
entry->y = aExtentsAppUnits.pos.y;
|
||||
entry->width = aExtentsAppUnits.size.width;
|
||||
entry->height = aExtentsAppUnits.size.height;
|
||||
}
|
||||
|
||||
gfxFontGroup::gfxFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle)
|
||||
|
@ -579,6 +757,9 @@ gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, PRUint32 aFlags)
|
|||
|
||||
gfxFont *font = GetFontAt(0);
|
||||
textRun->SetSpaceGlyph(font, aParams->mContext, 0);
|
||||
// Note that the gfxGlyphExtents glyph bounds storage for the font will
|
||||
// always contain an entry for the font's space glyph, so we don't have
|
||||
// to call FetchGlyphExtents here.
|
||||
return textRun.forget();
|
||||
}
|
||||
|
||||
|
@ -1103,14 +1284,15 @@ gfxTextRun::DrawToPath(gfxContext *aContext, gfxPoint aPt,
|
|||
void
|
||||
gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
PRBool aTight, PropertyProvider *aProvider,
|
||||
PRBool aTight, gfxContext *aRefContext,
|
||||
PropertyProvider *aProvider,
|
||||
PRUint32 aSpacingStart, PRUint32 aSpacingEnd,
|
||||
Metrics *aMetrics)
|
||||
{
|
||||
nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
|
||||
PRBool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
|
||||
aSpacingStart, aSpacingEnd, &spacingBuffer);
|
||||
Metrics metrics = aFont->Measure(this, aStart, aEnd, aTight,
|
||||
Metrics metrics = aFont->Measure(this, aStart, aEnd, aTight, aRefContext,
|
||||
haveSpacing ? spacingBuffer.Elements() : nsnull);
|
||||
|
||||
if (IsRightToLeft()) {
|
||||
|
@ -1123,7 +1305,7 @@ gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
|
|||
|
||||
void
|
||||
gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
|
||||
PRUint32 aStart, PRUint32 aEnd, PRBool aTight,
|
||||
PRUint32 aStart, PRUint32 aEnd, PRBool aTight, gfxContext *aRefContext,
|
||||
PropertyProvider *aProvider, Metrics *aMetrics)
|
||||
{
|
||||
if (aStart >= aEnd)
|
||||
|
@ -1136,7 +1318,7 @@ gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
|
|||
// First measure the complete ligature
|
||||
Metrics metrics;
|
||||
AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd,
|
||||
aTight, aProvider, aStart, aEnd, &metrics);
|
||||
aTight, aRefContext, aProvider, aStart, aEnd, &metrics);
|
||||
|
||||
// Clip the bounding box to the ligature part
|
||||
gfxFloat bboxLeft = metrics.mBoundingBox.X();
|
||||
|
@ -1164,7 +1346,7 @@ gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
|
|||
|
||||
gfxTextRun::Metrics
|
||||
gfxTextRun::MeasureText(PRUint32 aStart, PRUint32 aLength,
|
||||
PRBool aTightBoundingBox,
|
||||
PRBool aTightBoundingBox, gfxContext *aRefContext,
|
||||
PropertyProvider *aProvider)
|
||||
{
|
||||
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
|
||||
|
@ -1180,7 +1362,7 @@ gfxTextRun::MeasureText(PRUint32 aStart, PRUint32 aLength,
|
|||
ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
|
||||
|
||||
AccumulatePartialLigatureMetrics(font, start, ligatureRunStart,
|
||||
aTightBoundingBox, aProvider, &accumulatedMetrics);
|
||||
aTightBoundingBox, aRefContext, aProvider, &accumulatedMetrics);
|
||||
|
||||
// XXX This sucks. We have to get glyph extents just so we can detect
|
||||
// glyphs outside the font box, even when aTightBoundingBox is false,
|
||||
|
@ -1188,12 +1370,12 @@ gfxTextRun::MeasureText(PRUint32 aStart, PRUint32 aLength,
|
|||
// by getting some ascent/descent from the font and using our stored
|
||||
// advance widths.
|
||||
AccumulateMetricsForRun(font,
|
||||
ligatureRunStart, ligatureRunEnd, aTightBoundingBox, aProvider,
|
||||
ligatureRunStart, ligatureRunEnd,
|
||||
ligatureRunStart, ligatureRunEnd, aTightBoundingBox,
|
||||
aRefContext, aProvider, ligatureRunStart, ligatureRunEnd,
|
||||
&accumulatedMetrics);
|
||||
|
||||
AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end,
|
||||
aTightBoundingBox, aProvider, &accumulatedMetrics);
|
||||
aTightBoundingBox, aRefContext, aProvider, &accumulatedMetrics);
|
||||
}
|
||||
|
||||
return accumulatedMetrics;
|
||||
|
@ -1208,6 +1390,7 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
|
|||
PRBool aSuppressInitialBreak,
|
||||
gfxFloat *aTrimWhitespace,
|
||||
Metrics *aMetrics, PRBool aTightBoundingBox,
|
||||
gfxContext *aRefContext,
|
||||
PRBool *aUsedHyphenation,
|
||||
PRUint32 *aLastBreak)
|
||||
{
|
||||
|
@ -1349,7 +1532,8 @@ gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
|
|||
}
|
||||
|
||||
if (aMetrics) {
|
||||
*aMetrics = MeasureText(aStart, charsFit - trimmableChars, aTightBoundingBox, aProvider);
|
||||
*aMetrics = MeasureText(aStart, charsFit - trimmableChars,
|
||||
aTightBoundingBox, aRefContext, aProvider);
|
||||
}
|
||||
if (aTrimWhitespace) {
|
||||
*aTrimWhitespace = trimmableAdvance;
|
||||
|
@ -1718,3 +1902,55 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIn
|
|||
g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
|
||||
SetCharacterGlyph(aCharIndex, g);
|
||||
}
|
||||
|
||||
void
|
||||
gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
||||
{
|
||||
if (!NeedsGlyphExtents(this) && !mDetailedGlyphs)
|
||||
return;
|
||||
|
||||
PRUint32 i;
|
||||
CompressedGlyph *charGlyphs = mCharacterGlyphs;
|
||||
for (i = 0; i < mGlyphRuns.Length(); ++i) {
|
||||
gfxFont *font = mGlyphRuns[i].mFont;
|
||||
PRUint32 start = mGlyphRuns[i].mCharacterOffset;
|
||||
PRUint32 end = i + 1 < mGlyphRuns.Length()
|
||||
? mGlyphRuns[i + 1].mCharacterOffset : GetLength();
|
||||
PRBool fontIsSetup = PR_FALSE;
|
||||
PRUint32 j;
|
||||
gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
|
||||
|
||||
for (j = start; j < end; ++j) {
|
||||
const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
|
||||
if (glyphData->IsSimpleGlyph()) {
|
||||
// If we're in speed mode, don't set up glyph extents here; we'll
|
||||
// just return "optimistic" glyph bounds later
|
||||
if (NeedsGlyphExtents(this)) {
|
||||
PRUint32 glyphIndex = glyphData->GetSimpleGlyph();
|
||||
if (!extents->IsGlyphKnown(glyphIndex)) {
|
||||
if (!fontIsSetup) {
|
||||
font->SetupCairoFont(aRefContext);
|
||||
fontIsSetup = PR_TRUE;
|
||||
}
|
||||
font->SetupGlyphExtents(aRefContext, glyphIndex, PR_FALSE, extents);
|
||||
}
|
||||
}
|
||||
} else if (glyphData->IsComplexCluster()) {
|
||||
const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
|
||||
for (;;) {
|
||||
PRUint32 glyphIndex = details->mGlyphID;
|
||||
if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
|
||||
if (!fontIsSetup) {
|
||||
font->SetupCairoFont(aRefContext);
|
||||
fontIsSetup = PR_TRUE;
|
||||
}
|
||||
font->SetupGlyphExtents(aRefContext, glyphIndex, PR_TRUE, extents);
|
||||
}
|
||||
if (details->mIsLastGlyph)
|
||||
break;
|
||||
++details;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -828,6 +828,7 @@ gfxPangoFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
|
|||
AppendUTF16toUTF8(unicodeString, utf8);
|
||||
InitTextRun(run, utf8.get(), utf8.Length(), headerLen, PR_TRUE);
|
||||
}
|
||||
run->FetchGlyphExtents(aParams->mContext);
|
||||
return run;
|
||||
}
|
||||
|
||||
|
@ -866,6 +867,7 @@ gfxPangoFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
|
|||
}
|
||||
#endif
|
||||
InitTextRun(run, utf8.get(), utf8.Length(), headerLen, is8Bit);
|
||||
run->FetchGlyphExtents(aParams->mContext);
|
||||
return run;
|
||||
}
|
||||
|
||||
|
@ -916,10 +918,11 @@ CreateScaledFont(cairo_t *aCR, cairo_matrix_t *aCTM, PangoFont *aPangoFont)
|
|||
}
|
||||
|
||||
PRBool
|
||||
gfxPangoFont::SetupCairoFont(cairo_t *aCR)
|
||||
gfxPangoFont::SetupCairoFont(gfxContext *aContext)
|
||||
{
|
||||
cairo_t *cr = aContext->GetCairo();
|
||||
cairo_matrix_t currentCTM;
|
||||
cairo_get_matrix(aCR, ¤tCTM);
|
||||
cairo_get_matrix(cr, ¤tCTM);
|
||||
|
||||
if (mCairoFont) {
|
||||
// Need to validate that its CTM is OK
|
||||
|
@ -933,14 +936,14 @@ gfxPangoFont::SetupCairoFont(cairo_t *aCR)
|
|||
}
|
||||
}
|
||||
if (!mCairoFont) {
|
||||
mCairoFont = CreateScaledFont(aCR, ¤tCTM, GetPangoFont());
|
||||
mCairoFont = CreateScaledFont(cr, ¤tCTM, GetPangoFont());
|
||||
}
|
||||
if (cairo_scaled_font_status(mCairoFont) != CAIRO_STATUS_SUCCESS) {
|
||||
// Don't cairo_set_scaled_font as that would propagate the error to
|
||||
// the cairo_t, precluding any further drawing.
|
||||
return PR_FALSE;
|
||||
}
|
||||
cairo_set_scaled_font(aCR, mCairoFont);
|
||||
cairo_set_scaled_font(cr, mCairoFont);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -434,7 +434,7 @@ gfxWindowsFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
|
|||
}
|
||||
|
||||
PRBool
|
||||
gfxWindowsFont::SetupCairoFont(cairo_t *aCR)
|
||||
gfxWindowsFont::SetupCairoFont(gfxContext *aContext)
|
||||
{
|
||||
cairo_scaled_font_t *scaledFont = CairoScaledFont();
|
||||
if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
|
||||
|
@ -442,7 +442,7 @@ gfxWindowsFont::SetupCairoFont(cairo_t *aCR)
|
|||
// the cairo_t, precluding any further drawing.
|
||||
return PR_FALSE;
|
||||
}
|
||||
cairo_set_scaled_font(aCR, scaledFont);
|
||||
cairo_set_scaled_font(aContext->GetCairo(), scaledFont);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
@ -566,6 +566,8 @@ gfxWindowsFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
|
|||
else
|
||||
InitTextRunGDI(aParams->mContext, textRun, aString, aLength);
|
||||
|
||||
textRun->FetchGlyphExtents(aParams->mContext);
|
||||
|
||||
return textRun;
|
||||
}
|
||||
|
||||
|
@ -605,6 +607,8 @@ gfxWindowsFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
|
|||
}
|
||||
}
|
||||
|
||||
textRun->FetchGlyphExtents(aParams->mContext);
|
||||
|
||||
return textRun;
|
||||
}
|
||||
|
||||
|
|
|
@ -184,6 +184,8 @@ BRFrame::Reflow(nsPresContext* aPresContext,
|
|||
else {
|
||||
aStatus = NS_FRAME_COMPLETE;
|
||||
}
|
||||
|
||||
aMetrics.mOverflowArea = nsRect(0, 0, aMetrics.width, aMetrics.height);
|
||||
|
||||
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
|
||||
return NS_OK;
|
||||
|
|
|
@ -1562,6 +1562,12 @@ nsBulletFrame::Reflow(nsPresContext* aPresContext,
|
|||
aMetrics.height += borderPadding.top + borderPadding.bottom;
|
||||
aMetrics.ascent += borderPadding.top;
|
||||
|
||||
// XXX this is a bit of a hack, we're assuming that no glyphs used for bullets
|
||||
// overflow their font-boxes. It'll do for now; to fix it for real, we really
|
||||
// should rewrite all the text-handling code here to use gfxTextRun (bug
|
||||
// 397294).
|
||||
aMetrics.mOverflowArea.SetRect(0, 0, aMetrics.width, aMetrics.height);
|
||||
|
||||
aStatus = NS_FRAME_COMPLETE;
|
||||
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
|
||||
return NS_OK;
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include "nsTextFragment.h"
|
||||
#include "nsBidiUtils.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsTextFrame.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#undef NOISY_HORIZONTAL_ALIGN
|
||||
|
@ -2420,6 +2421,10 @@ nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD, FrameJustificationState
|
|||
|
||||
aState->mWidthForLettersProcessed = newAllocatedWidthForLetters;
|
||||
}
|
||||
|
||||
if (dw) {
|
||||
pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, PR_TRUE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (nsnull != pfd->mSpan) {
|
||||
|
@ -2643,11 +2648,12 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea)
|
|||
// aggregating it into our combined area.
|
||||
RelativePositionFrames(pfd->mSpan, r);
|
||||
} else {
|
||||
// For simple text frames we take the union of the combined area
|
||||
// and the width/height. I think the combined area should always
|
||||
// equal the bounds in this case, but this is safe.
|
||||
nsRect adjustedBounds(0, 0, pfd->mBounds.width, pfd->mBounds.height);
|
||||
r.UnionRect(pfd->mCombinedArea, adjustedBounds);
|
||||
r = pfd->mCombinedArea;
|
||||
if (pfd->GetFlag(PFD_RECOMPUTEOVERFLOW)) {
|
||||
nsTextFrame* f = static_cast<nsTextFrame*>(frame);
|
||||
r = f->RecomputeOverflowRect();
|
||||
}
|
||||
frame->FinishAndStoreOverflow(&r, frame->GetSize());
|
||||
|
||||
// If we have something that's not an inline but with a complex frame
|
||||
// hierarchy inside that contains views, they need to be
|
||||
|
|
|
@ -502,6 +502,7 @@ protected:
|
|||
#define PFD_ISNONEMPTYTEXTFRAME 0x00000004
|
||||
#define PFD_ISNONWHITESPACETEXTFRAME 0x00000008
|
||||
#define PFD_ISLETTERFRAME 0x00000010
|
||||
#define PFD_RECOMPUTEOVERFLOW 0x00000020
|
||||
#define PFD_ISBULLET 0x00000040
|
||||
#define PFD_SKIPWHENTRIMMINGWHITESPACE 0x00000080
|
||||
#define PFD_LASTFLAG PFD_SKIPWHENTRIMMINGWHITESPACE
|
||||
|
|
|
@ -0,0 +1,346 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Robert O'Callahan <robert@ocallahan.org>
|
||||
* Roger B. Sidje <rbs@maths.uq.edu.au>
|
||||
* Pierre Phaneuf <pp@ludusdesign.com>
|
||||
* Prabhat Hegde <prabhat.hegde@sun.com>
|
||||
* Tomi Leppikangas <tomi.leppikangas@oulu.fi>
|
||||
* Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
|
||||
* Daniel Glazman <glazman@netscape.com>
|
||||
* Neil Deakin <neil@mozdevgroup.com>
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
* Mats Palmgren <mats.palmgren@bredband.net>
|
||||
* Uri Bernstein <uriber@gmail.com>
|
||||
* Stephen Blackheath <entangled.mooched.stephen@blacksapphire.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsTextFrame_h__
|
||||
#define nsTextFrame_h__
|
||||
|
||||
#include "nsFrame.h"
|
||||
#include "nsLineBox.h"
|
||||
#include "gfxFont.h"
|
||||
#include "gfxSkipChars.h"
|
||||
|
||||
class nsTextPaintStyle;
|
||||
class PropertyProvider;
|
||||
|
||||
class nsTextFrame : public nsFrame {
|
||||
public:
|
||||
nsTextFrame(nsStyleContext* aContext) : nsFrame(aContext)
|
||||
{
|
||||
NS_ASSERTION(mContentOffset == 0, "Bogus content offset");
|
||||
}
|
||||
|
||||
// nsIFrame
|
||||
NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsRect& aDirtyRect,
|
||||
const nsDisplayListSet& aLists);
|
||||
|
||||
NS_IMETHOD Init(nsIContent* aContent,
|
||||
nsIFrame* aParent,
|
||||
nsIFrame* aPrevInFlow);
|
||||
|
||||
virtual void Destroy();
|
||||
|
||||
NS_IMETHOD GetCursor(const nsPoint& aPoint,
|
||||
nsIFrame::Cursor& aCursor);
|
||||
|
||||
NS_IMETHOD CharacterDataChanged(nsPresContext* aPresContext,
|
||||
nsIContent* aChild,
|
||||
PRBool aAppend);
|
||||
|
||||
NS_IMETHOD DidSetStyleContext();
|
||||
|
||||
virtual nsIFrame* GetNextContinuation() const {
|
||||
return mNextContinuation;
|
||||
}
|
||||
NS_IMETHOD SetNextContinuation(nsIFrame* aNextContinuation) {
|
||||
NS_ASSERTION (!aNextContinuation || GetType() == aNextContinuation->GetType(),
|
||||
"setting a next continuation with incorrect type!");
|
||||
NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextContinuation, this),
|
||||
"creating a loop in continuation chain!");
|
||||
mNextContinuation = aNextContinuation;
|
||||
if (aNextContinuation)
|
||||
aNextContinuation->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
|
||||
return NS_OK;
|
||||
}
|
||||
virtual nsIFrame* GetNextInFlowVirtual() const { return GetNextInFlow(); }
|
||||
nsIFrame* GetNextInFlow() const {
|
||||
return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ?
|
||||
mNextContinuation : nsnull;
|
||||
}
|
||||
NS_IMETHOD SetNextInFlow(nsIFrame* aNextInFlow) {
|
||||
NS_ASSERTION (!aNextInFlow || GetType() == aNextInFlow->GetType(),
|
||||
"setting a next in flow with incorrect type!");
|
||||
NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextInFlow, this),
|
||||
"creating a loop in continuation chain!");
|
||||
mNextContinuation = aNextInFlow;
|
||||
if (aNextInFlow)
|
||||
aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
|
||||
return NS_OK;
|
||||
}
|
||||
virtual nsIFrame* GetLastInFlow() const;
|
||||
virtual nsIFrame* GetLastContinuation() const;
|
||||
|
||||
virtual nsSplittableType GetSplittableType() const {
|
||||
return NS_FRAME_SPLITTABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "type" of the frame
|
||||
*
|
||||
* @see nsGkAtoms::textFrame
|
||||
*/
|
||||
virtual nsIAtom* GetType() const;
|
||||
|
||||
virtual PRBool IsFrameOfType(PRUint32 aFlags) const
|
||||
{
|
||||
// Set the frame state bit for text frames to mark them as replaced.
|
||||
// XXX kipp: temporary
|
||||
return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced |
|
||||
nsIFrame::eLineParticipant));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_IMETHOD List(FILE* out, PRInt32 aIndent) const;
|
||||
NS_IMETHOD GetFrameName(nsAString& aResult) const;
|
||||
NS_IMETHOD_(nsFrameState) GetDebugStateBits() const ;
|
||||
#endif
|
||||
|
||||
virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint);
|
||||
|
||||
NS_IMETHOD SetSelected(nsPresContext* aPresContext,
|
||||
nsIDOMRange *aRange,
|
||||
PRBool aSelected,
|
||||
nsSpread aSpread);
|
||||
|
||||
virtual PRBool PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset);
|
||||
virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset);
|
||||
virtual PRBool PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect,
|
||||
PRInt32* aOffset, PeekWordState* aState);
|
||||
|
||||
NS_IMETHOD CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval);
|
||||
|
||||
// Update offsets to account for new length. This may clear mTextRun.
|
||||
void SetLength(PRInt32 aLength);
|
||||
|
||||
NS_IMETHOD GetOffsets(PRInt32 &start, PRInt32 &end)const;
|
||||
|
||||
virtual void AdjustOffsetsForBidi(PRInt32 start, PRInt32 end);
|
||||
|
||||
NS_IMETHOD GetPointFromOffset(PRInt32 inOffset,
|
||||
nsPoint* outPoint);
|
||||
|
||||
NS_IMETHOD GetChildFrameContainingOffset(PRInt32 inContentOffset,
|
||||
PRBool inHint,
|
||||
PRInt32* outFrameContentOffset,
|
||||
nsIFrame* *outChildFrame);
|
||||
|
||||
virtual PRBool IsVisibleInSelection(nsISelection* aSelection);
|
||||
|
||||
virtual PRBool IsEmpty();
|
||||
virtual PRBool IsSelfEmpty() { return IsEmpty(); }
|
||||
|
||||
/**
|
||||
* @return PR_TRUE if this text frame ends with a newline character. It
|
||||
* should return PR_FALSE if this is not a text frame.
|
||||
*/
|
||||
virtual PRBool HasTerminalNewline() const;
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
NS_IMETHOD GetAccessible(nsIAccessible** aAccessible);
|
||||
#endif
|
||||
|
||||
virtual void MarkIntrinsicWidthsDirty();
|
||||
virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext);
|
||||
virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext);
|
||||
virtual void AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
|
||||
InlineMinWidthData *aData);
|
||||
virtual void AddInlinePrefWidth(nsIRenderingContext *aRenderingContext,
|
||||
InlinePrefWidthData *aData);
|
||||
virtual nsSize ComputeSize(nsIRenderingContext *aRenderingContext,
|
||||
nsSize aCBSize, nscoord aAvailableWidth,
|
||||
nsSize aMargin, nsSize aBorder, nsSize aPadding,
|
||||
PRBool aShrinkWrap);
|
||||
NS_IMETHOD Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aMetrics,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus);
|
||||
virtual PRBool CanContinueTextRun() const;
|
||||
NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
||||
nsIRenderingContext& aRC,
|
||||
nscoord& aDeltaWidth,
|
||||
PRBool& aLastCharIsJustifiable);
|
||||
virtual nsresult GetRenderedText(nsAString* aString = nsnull,
|
||||
gfxSkipChars* aSkipChars = nsnull,
|
||||
gfxSkipCharsIterator* aSkipIter = nsnull,
|
||||
PRUint32 aSkippedStartOffset = 0,
|
||||
PRUint32 aSkippedMaxLength = PR_UINT32_MAX);
|
||||
|
||||
nsRect RecomputeOverflowRect();
|
||||
|
||||
void AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
|
||||
nsIFrame::InlineMinWidthData *aData);
|
||||
void AddInlinePrefWidthForFlow(nsIRenderingContext *aRenderingContext,
|
||||
InlinePrefWidthData *aData);
|
||||
|
||||
gfxFloat GetSnappedBaselineY(gfxContext* aContext, gfxFloat aY);
|
||||
|
||||
// primary frame paint method called from nsDisplayText
|
||||
void PaintText(nsIRenderingContext* aRenderingContext, nsPoint aPt,
|
||||
const nsRect& aDirtyRect);
|
||||
// helper: paint quirks-mode CSS text decorations
|
||||
void PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect,
|
||||
const gfxPoint& aFramePt,
|
||||
const gfxPoint& aTextBaselinePt,
|
||||
nsTextPaintStyle& aTextStyle,
|
||||
PropertyProvider& aProvider);
|
||||
// helper: paint text frame when we're impacted by at least one selection.
|
||||
// Return PR_FALSE if the text was not painted and we should continue with
|
||||
// the fast path.
|
||||
PRBool PaintTextWithSelection(gfxContext* aCtx,
|
||||
const gfxPoint& aFramePt,
|
||||
const gfxPoint& aTextBaselinePt,
|
||||
const gfxRect& aDirtyRect,
|
||||
PropertyProvider& aProvider,
|
||||
nsTextPaintStyle& aTextPaintStyle);
|
||||
// helper: paint text with foreground and background colors determined
|
||||
// by selection(s). Also computes a mask of all selection types applying to
|
||||
// our text, returned in aAllTypes.
|
||||
void PaintTextWithSelectionColors(gfxContext* aCtx,
|
||||
const gfxPoint& aFramePt,
|
||||
const gfxPoint& aTextBaselinePt,
|
||||
const gfxRect& aDirtyRect,
|
||||
PropertyProvider& aProvider,
|
||||
nsTextPaintStyle& aTextPaintStyle,
|
||||
SelectionDetails* aDetails,
|
||||
SelectionType* aAllTypes);
|
||||
// helper: paint text decorations for text selected by aSelectionType
|
||||
void PaintTextSelectionDecorations(gfxContext* aCtx,
|
||||
const gfxPoint& aFramePt,
|
||||
const gfxPoint& aTextBaselinePt,
|
||||
const gfxRect& aDirtyRect,
|
||||
PropertyProvider& aProvider,
|
||||
nsTextPaintStyle& aTextPaintStyle,
|
||||
SelectionDetails* aDetails,
|
||||
SelectionType aSelectionType);
|
||||
|
||||
PRInt16 GetSelectionStatus(PRInt16* aSelectionFlags);
|
||||
|
||||
#ifdef DEBUG
|
||||
void ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const;
|
||||
#endif
|
||||
|
||||
PRInt32 GetContentOffset() const { return mContentOffset; }
|
||||
PRInt32 GetContentLength() const { return GetContentEnd() - mContentOffset; }
|
||||
PRInt32 GetContentEnd() const;
|
||||
// This returns the length the frame thinks it *should* have after it was
|
||||
// last reflowed (0 if it hasn't been reflowed yet). This should be used only
|
||||
// when setting up the text offsets for a new continuation frame.
|
||||
PRInt32 GetContentLengthHint() const { return mContentLengthHint; }
|
||||
|
||||
// Compute the length of the content mapped by this frame
|
||||
// and all its in-flow siblings. Basically this means starting at mContentOffset
|
||||
// and going to the end of the text node or the next bidi continuation
|
||||
// boundary.
|
||||
PRInt32 GetInFlowContentLength();
|
||||
|
||||
// Clears out mTextRun from this frame and all other frames that hold a reference
|
||||
// to it, then deletes the textrun.
|
||||
void ClearTextRun();
|
||||
/**
|
||||
* Acquires the text run for this content, if necessary.
|
||||
* @param aRC the rendering context to use as a reference for creating
|
||||
* the textrun, if available (if not, we'll create one which will just be slower)
|
||||
* @param aBlock the block ancestor for this frame, or nsnull if unknown
|
||||
* @param aLine the line that this frame is on, if any, or nsnull if unknown
|
||||
* @param aFlowEndInTextRun if non-null, this returns the textrun offset of
|
||||
* end of the text associated with this frame and its in-flow siblings
|
||||
* @return a gfxSkipCharsIterator set up to map DOM offsets for this frame
|
||||
* to offsets into the textrun; its initial offset is set to this frame's
|
||||
* content offset
|
||||
*/
|
||||
gfxSkipCharsIterator EnsureTextRun(nsIRenderingContext* aRC = nsnull,
|
||||
nsIFrame* aLineContainer = nsnull,
|
||||
const nsLineList::iterator* aLine = nsnull,
|
||||
PRUint32* aFlowEndInTextRun = nsnull);
|
||||
|
||||
gfxTextRun* GetTextRun() { return mTextRun; }
|
||||
void SetTextRun(gfxTextRun* aTextRun) { mTextRun = aTextRun; }
|
||||
|
||||
// Get the DOM content range mapped by this frame after excluding
|
||||
// whitespace subject to start-of-line and end-of-line trimming.
|
||||
// The textrun must have been created before calling this.
|
||||
struct TrimmedOffsets {
|
||||
PRInt32 mStart;
|
||||
PRInt32 mLength;
|
||||
PRInt32 GetEnd() { return mStart + mLength; }
|
||||
};
|
||||
TrimmedOffsets GetTrimmedOffsets(const nsTextFragment* aFrag,
|
||||
PRBool aTrimAfter);
|
||||
|
||||
protected:
|
||||
virtual ~nsTextFrame();
|
||||
|
||||
nsIFrame* mNextContinuation;
|
||||
// The key invariant here is that mContentOffset never decreases along
|
||||
// a next-continuation chain. And of course mContentOffset is always <= the
|
||||
// the text node's content length, and the mContentOffset for the first frame
|
||||
// is always 0. Furthermore the text mapped by a frame is determined by
|
||||
// GetContentOffset() and GetContentLength()/GetContentEnd(), which get
|
||||
// the length from the difference between this frame's offset and the next
|
||||
// frame's offset, or the text length if there is no next frame. This means
|
||||
// the frames always map the text node without overlapping or leaving any gaps.
|
||||
PRInt32 mContentOffset;
|
||||
// This does *not* indicate the length of text currently mapped by the frame;
|
||||
// instead it's a hint saying that this frame *wants* to map this much text
|
||||
// so if we create a new continuation, this is where that continuation should
|
||||
// start.
|
||||
PRInt32 mContentLengthHint;
|
||||
nscoord mAscent;
|
||||
gfxTextRun* mTextRun;
|
||||
|
||||
SelectionDetails* GetSelectionDetails();
|
||||
|
||||
void AdjustSelectionPointsForBidi(SelectionDetails *sdptr,
|
||||
PRInt32 textLength,
|
||||
PRBool isRTLChars,
|
||||
PRBool isOddLevel,
|
||||
PRBool isBidiSystem);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -81,6 +81,7 @@
|
|||
#include "nsFrameManager.h"
|
||||
#include "nsTextFrameTextRunCache.h"
|
||||
#include "nsExpirationTracker.h"
|
||||
#include "nsTextFrame.h"
|
||||
#include "nsICaseConversion.h"
|
||||
#include "nsIUGenCategory.h"
|
||||
#include "nsUnicharUtilCIID.h"
|
||||
|
@ -182,9 +183,6 @@
|
|||
* <b>Kit­</b>ty
|
||||
*/
|
||||
|
||||
class nsTextFrame;
|
||||
class PropertyProvider;
|
||||
|
||||
/**
|
||||
* We use an array of these objects to record which text frames
|
||||
* are associated with the textrun. mStartFrame is the start of a list of
|
||||
|
@ -295,289 +293,6 @@ protected:
|
|||
nscolor aBackColor);
|
||||
};
|
||||
|
||||
class nsTextFrame : public nsFrame {
|
||||
public:
|
||||
nsTextFrame(nsStyleContext* aContext) : nsFrame(aContext)
|
||||
{
|
||||
NS_ASSERTION(mContentOffset == 0, "Bogus content offset");
|
||||
}
|
||||
|
||||
// nsIFrame
|
||||
NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsRect& aDirtyRect,
|
||||
const nsDisplayListSet& aLists);
|
||||
|
||||
NS_IMETHOD Init(nsIContent* aContent,
|
||||
nsIFrame* aParent,
|
||||
nsIFrame* aPrevInFlow);
|
||||
|
||||
virtual void Destroy();
|
||||
|
||||
NS_IMETHOD GetCursor(const nsPoint& aPoint,
|
||||
nsIFrame::Cursor& aCursor);
|
||||
|
||||
NS_IMETHOD CharacterDataChanged(nsPresContext* aPresContext,
|
||||
nsIContent* aChild,
|
||||
PRBool aAppend);
|
||||
|
||||
NS_IMETHOD DidSetStyleContext();
|
||||
|
||||
virtual nsIFrame* GetNextContinuation() const {
|
||||
return mNextContinuation;
|
||||
}
|
||||
NS_IMETHOD SetNextContinuation(nsIFrame* aNextContinuation) {
|
||||
NS_ASSERTION (!aNextContinuation || GetType() == aNextContinuation->GetType(),
|
||||
"setting a next continuation with incorrect type!");
|
||||
NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextContinuation, this),
|
||||
"creating a loop in continuation chain!");
|
||||
mNextContinuation = aNextContinuation;
|
||||
if (aNextContinuation)
|
||||
aNextContinuation->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
|
||||
return NS_OK;
|
||||
}
|
||||
virtual nsIFrame* GetNextInFlowVirtual() const { return GetNextInFlow(); }
|
||||
nsIFrame* GetNextInFlow() const {
|
||||
return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ?
|
||||
mNextContinuation : nsnull;
|
||||
}
|
||||
NS_IMETHOD SetNextInFlow(nsIFrame* aNextInFlow) {
|
||||
NS_ASSERTION (!aNextInFlow || GetType() == aNextInFlow->GetType(),
|
||||
"setting a next in flow with incorrect type!");
|
||||
NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextInFlow, this),
|
||||
"creating a loop in continuation chain!");
|
||||
mNextContinuation = aNextInFlow;
|
||||
if (aNextInFlow)
|
||||
aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
|
||||
return NS_OK;
|
||||
}
|
||||
virtual nsIFrame* GetLastInFlow() const;
|
||||
virtual nsIFrame* GetLastContinuation() const;
|
||||
|
||||
virtual nsSplittableType GetSplittableType() const {
|
||||
return NS_FRAME_SPLITTABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "type" of the frame
|
||||
*
|
||||
* @see nsGkAtoms::textFrame
|
||||
*/
|
||||
virtual nsIAtom* GetType() const;
|
||||
|
||||
virtual PRBool IsFrameOfType(PRUint32 aFlags) const
|
||||
{
|
||||
// Set the frame state bit for text frames to mark them as replaced.
|
||||
// XXX kipp: temporary
|
||||
return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced |
|
||||
nsIFrame::eLineParticipant));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_IMETHOD List(FILE* out, PRInt32 aIndent) const;
|
||||
NS_IMETHOD GetFrameName(nsAString& aResult) const;
|
||||
NS_IMETHOD_(nsFrameState) GetDebugStateBits() const ;
|
||||
#endif
|
||||
|
||||
virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint);
|
||||
|
||||
NS_IMETHOD SetSelected(nsPresContext* aPresContext,
|
||||
nsIDOMRange *aRange,
|
||||
PRBool aSelected,
|
||||
nsSpread aSpread);
|
||||
|
||||
virtual PRBool PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset);
|
||||
virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset);
|
||||
virtual PRBool PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect,
|
||||
PRInt32* aOffset, PeekWordState* aState);
|
||||
|
||||
NS_IMETHOD CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval);
|
||||
|
||||
// Update offsets to account for new length. This may clear mTextRun.
|
||||
void SetLength(PRInt32 aLength);
|
||||
|
||||
NS_IMETHOD GetOffsets(PRInt32 &start, PRInt32 &end)const;
|
||||
|
||||
virtual void AdjustOffsetsForBidi(PRInt32 start, PRInt32 end);
|
||||
|
||||
NS_IMETHOD GetPointFromOffset(PRInt32 inOffset,
|
||||
nsPoint* outPoint);
|
||||
|
||||
NS_IMETHOD GetChildFrameContainingOffset(PRInt32 inContentOffset,
|
||||
PRBool inHint,
|
||||
PRInt32* outFrameContentOffset,
|
||||
nsIFrame* *outChildFrame);
|
||||
|
||||
virtual PRBool IsVisibleInSelection(nsISelection* aSelection);
|
||||
|
||||
virtual PRBool IsEmpty();
|
||||
virtual PRBool IsSelfEmpty() { return IsEmpty(); }
|
||||
|
||||
/**
|
||||
* @return PR_TRUE if this text frame ends with a newline character. It
|
||||
* should return PR_FALSE if this is not a text frame.
|
||||
*/
|
||||
virtual PRBool HasTerminalNewline() const;
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
NS_IMETHOD GetAccessible(nsIAccessible** aAccessible);
|
||||
#endif
|
||||
|
||||
virtual void MarkIntrinsicWidthsDirty();
|
||||
virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext);
|
||||
virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext);
|
||||
virtual void AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
|
||||
InlineMinWidthData *aData);
|
||||
virtual void AddInlinePrefWidth(nsIRenderingContext *aRenderingContext,
|
||||
InlinePrefWidthData *aData);
|
||||
virtual nsSize ComputeSize(nsIRenderingContext *aRenderingContext,
|
||||
nsSize aCBSize, nscoord aAvailableWidth,
|
||||
nsSize aMargin, nsSize aBorder, nsSize aPadding,
|
||||
PRBool aShrinkWrap);
|
||||
NS_IMETHOD Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aMetrics,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus);
|
||||
virtual PRBool CanContinueTextRun() const;
|
||||
NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
||||
nsIRenderingContext& aRC,
|
||||
nscoord& aDeltaWidth,
|
||||
PRBool& aLastCharIsJustifiable);
|
||||
virtual nsresult GetRenderedText(nsAString* aString = nsnull,
|
||||
gfxSkipChars* aSkipChars = nsnull,
|
||||
gfxSkipCharsIterator* aSkipIter = nsnull,
|
||||
PRUint32 aSkippedStartOffset = 0,
|
||||
PRUint32 aSkippedMaxLength = PR_UINT32_MAX);
|
||||
|
||||
void AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
|
||||
nsIFrame::InlineMinWidthData *aData);
|
||||
void AddInlinePrefWidthForFlow(nsIRenderingContext *aRenderingContext,
|
||||
InlinePrefWidthData *aData);
|
||||
|
||||
gfxFloat GetSnappedBaselineY(gfxContext* aContext, gfxFloat aY);
|
||||
|
||||
// primary frame paint method called from nsDisplayText
|
||||
void PaintText(nsIRenderingContext* aRenderingContext, nsPoint aPt,
|
||||
const nsRect& aDirtyRect);
|
||||
// helper: paint quirks-mode CSS text decorations
|
||||
void PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect,
|
||||
const gfxPoint& aFramePt,
|
||||
const gfxPoint& aTextBaselinePt,
|
||||
nsTextPaintStyle& aTextStyle,
|
||||
PropertyProvider& aProvider);
|
||||
// helper: paint text frame when we're impacted by at least one selection.
|
||||
// Return PR_FALSE if the text was not painted and we should continue with
|
||||
// the fast path.
|
||||
PRBool PaintTextWithSelection(gfxContext* aCtx,
|
||||
const gfxPoint& aFramePt,
|
||||
const gfxPoint& aTextBaselinePt,
|
||||
const gfxRect& aDirtyRect,
|
||||
PropertyProvider& aProvider,
|
||||
nsTextPaintStyle& aTextPaintStyle);
|
||||
// helper: paint text with foreground and background colors determined
|
||||
// by selection(s). Also computes a mask of all selection types applying to
|
||||
// our text, returned in aAllTypes.
|
||||
void PaintTextWithSelectionColors(gfxContext* aCtx,
|
||||
const gfxPoint& aFramePt,
|
||||
const gfxPoint& aTextBaselinePt,
|
||||
const gfxRect& aDirtyRect,
|
||||
PropertyProvider& aProvider,
|
||||
nsTextPaintStyle& aTextPaintStyle,
|
||||
SelectionDetails* aDetails,
|
||||
SelectionType* aAllTypes);
|
||||
// helper: paint text decorations for text selected by aSelectionType
|
||||
void PaintTextSelectionDecorations(gfxContext* aCtx,
|
||||
const gfxPoint& aFramePt,
|
||||
const gfxPoint& aTextBaselinePt,
|
||||
const gfxRect& aDirtyRect,
|
||||
PropertyProvider& aProvider,
|
||||
nsTextPaintStyle& aTextPaintStyle,
|
||||
SelectionDetails* aDetails,
|
||||
SelectionType aSelectionType);
|
||||
|
||||
PRInt16 GetSelectionStatus(PRInt16* aSelectionFlags);
|
||||
|
||||
#ifdef DEBUG
|
||||
void ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const;
|
||||
#endif
|
||||
|
||||
PRInt32 GetContentOffset() const { return mContentOffset; }
|
||||
PRInt32 GetContentLength() const { return GetContentEnd() - mContentOffset; }
|
||||
PRInt32 GetContentEnd() const;
|
||||
// This returns the length the frame thinks it *should* have after it was
|
||||
// last reflowed (0 if it hasn't been reflowed yet). This should be used only
|
||||
// when setting up the text offsets for a new continuation frame.
|
||||
PRInt32 GetContentLengthHint() const { return mContentLengthHint; }
|
||||
|
||||
// Compute the length of the content mapped by this frame
|
||||
// and all its in-flow siblings. Basically this means starting at mContentOffset
|
||||
// and going to the end of the text node or the next bidi continuation
|
||||
// boundary.
|
||||
PRInt32 GetInFlowContentLength();
|
||||
|
||||
// Clears out mTextRun from this frame and all other frames that hold a reference
|
||||
// to it, then deletes the textrun.
|
||||
void ClearTextRun();
|
||||
/**
|
||||
* Acquires the text run for this content, if necessary.
|
||||
* @param aRC the rendering context to use as a reference for creating
|
||||
* the textrun, if available (if not, we'll create one which will just be slower)
|
||||
* @param aBlock the block ancestor for this frame, or nsnull if unknown
|
||||
* @param aLine the line that this frame is on, if any, or nsnull if unknown
|
||||
* @param aFlowEndInTextRun if non-null, this returns the textrun offset of
|
||||
* end of the text associated with this frame and its in-flow siblings
|
||||
* @return a gfxSkipCharsIterator set up to map DOM offsets for this frame
|
||||
* to offsets into the textrun; its initial offset is set to this frame's
|
||||
* content offset
|
||||
*/
|
||||
gfxSkipCharsIterator EnsureTextRun(nsIRenderingContext* aRC = nsnull,
|
||||
nsIFrame* aLineContainer = nsnull,
|
||||
const nsLineList::iterator* aLine = nsnull,
|
||||
PRUint32* aFlowEndInTextRun = nsnull);
|
||||
|
||||
gfxTextRun* GetTextRun() { return mTextRun; }
|
||||
void SetTextRun(gfxTextRun* aTextRun) { mTextRun = aTextRun; }
|
||||
|
||||
// Get the DOM content range mapped by this frame after excluding
|
||||
// whitespace subject to start-of-line and end-of-line trimming.
|
||||
// The textrun must have been created before calling this.
|
||||
struct TrimmedOffsets {
|
||||
PRInt32 mStart;
|
||||
PRInt32 mLength;
|
||||
PRInt32 GetEnd() { return mStart + mLength; }
|
||||
};
|
||||
TrimmedOffsets GetTrimmedOffsets(const nsTextFragment* aFrag,
|
||||
PRBool aTrimAfter);
|
||||
|
||||
protected:
|
||||
virtual ~nsTextFrame();
|
||||
|
||||
nsIFrame* mNextContinuation;
|
||||
// The key invariant here is that mContentOffset never decreases along
|
||||
// a next-continuation chain. And of course mContentOffset is always <= the
|
||||
// the text node's content length, and the mContentOffset for the first frame
|
||||
// is always 0. Furthermore the text mapped by a frame is determined by
|
||||
// GetContentOffset() and GetContentLength()/GetContentEnd(), which get
|
||||
// the length from the difference between this frame's offset and the next
|
||||
// frame's offset, or the text length if there is no next frame. This means
|
||||
// the frames always map the text node without overlapping or leaving any gaps.
|
||||
PRInt32 mContentOffset;
|
||||
// This does *not* indicate the length of text currently mapped by the frame;
|
||||
// instead it's a hint saying that this frame *wants* to map this much text
|
||||
// so if we create a new continuation, this is where that continuation should
|
||||
// start.
|
||||
PRInt32 mContentLengthHint;
|
||||
nscoord mAscent;
|
||||
gfxTextRun* mTextRun;
|
||||
|
||||
SelectionDetails* GetSelectionDetails();
|
||||
|
||||
void AdjustSelectionPointsForBidi(SelectionDetails *sdptr,
|
||||
PRInt32 textLength,
|
||||
PRBool isRTLChars,
|
||||
PRBool isOddLevel,
|
||||
PRBool isBidiSystem);
|
||||
};
|
||||
|
||||
static void
|
||||
DestroyUserData(void* aUserData)
|
||||
{
|
||||
|
@ -1483,6 +1198,10 @@ GetFontGroupForFrame(nsIFrame* aFrame)
|
|||
|
||||
nsIFontMetrics* metricsRaw = metrics;
|
||||
nsIThebesFontMetrics* fm = static_cast<nsIThebesFontMetrics*>(metricsRaw);
|
||||
// XXX this is a bit bogus, we're releasing 'metrics' so the returned font-group
|
||||
// might actually be torn down, although because of the way the device context
|
||||
// caches font metrics, this seems to not actually happen. But we should fix
|
||||
// this.
|
||||
return fm->GetThebesFontGroup();
|
||||
}
|
||||
|
||||
|
@ -3645,7 +3364,9 @@ public:
|
|||
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
|
||||
return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
|
||||
}
|
||||
virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt) { return mFrame; }
|
||||
virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt) {
|
||||
return nsRect(aBuilder->ToReferenceFrame(mFrame), mFrame->GetSize()).Contains(aPt) ? mFrame : nsnull;
|
||||
}
|
||||
virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
|
||||
const nsRect& aDirtyRect);
|
||||
NS_DISPLAY_DECL_NAME("Text")
|
||||
|
@ -5200,14 +4921,15 @@ nsTextFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
|
|||
|
||||
static void
|
||||
AddCharToMetrics(gfxTextRun* aCharTextRun, gfxTextRun* aBaseTextRun,
|
||||
gfxTextRun::Metrics* aMetrics, PRBool aTightBoundingBox)
|
||||
gfxTextRun::Metrics* aMetrics, PRBool aTightBoundingBox,
|
||||
gfxContext* aContext)
|
||||
{
|
||||
gfxRect charRect;
|
||||
// assume char does not overflow font metrics!!!
|
||||
gfxFloat width = aCharTextRun->GetAdvanceWidth(0, aCharTextRun->GetLength(), nsnull);
|
||||
if (aTightBoundingBox) {
|
||||
gfxTextRun::Metrics charMetrics =
|
||||
aCharTextRun->MeasureText(0, aCharTextRun->GetLength(), PR_TRUE, nsnull);
|
||||
aCharTextRun->MeasureText(0, aCharTextRun->GetLength(), PR_TRUE, aContext, nsnull);
|
||||
charRect = charMetrics.mBoundingBox;
|
||||
} else {
|
||||
charRect = gfxRect(0, -aMetrics->mAscent, width,
|
||||
|
@ -5452,13 +5174,15 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
gfxFloat availWidth = aReflowState.availableWidth;
|
||||
PRBool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() &&
|
||||
textStyle->WhiteSpaceCanWrap();
|
||||
gfxContext* ctx = static_cast<gfxContext*>
|
||||
(aReflowState.rendContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
|
||||
PRUint32 transformedCharsFit =
|
||||
mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
|
||||
(GetStateBits() & TEXT_START_OF_LINE) != 0,
|
||||
availWidth,
|
||||
&provider, suppressInitialBreak,
|
||||
canTrimTrailingWhitespace ? &trimmedWidth : nsnull,
|
||||
&textMetrics, needTightBoundingBox,
|
||||
&textMetrics, needTightBoundingBox, ctx,
|
||||
&usedHyphenation, &transformedLastBreak);
|
||||
// The "end" iterator points to the first character after the string mapped
|
||||
// by this frame. Basically, its original-string offset is offset+charsFit
|
||||
|
@ -5489,7 +5213,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, aReflowState.rendContext));
|
||||
if (hyphenTextRun.get()) {
|
||||
AddCharToMetrics(hyphenTextRun.get(),
|
||||
mTextRun, &textMetrics, needTightBoundingBox);
|
||||
mTextRun, &textMetrics, needTightBoundingBox, ctx);
|
||||
}
|
||||
AddStateBits(TEXT_HYPHEN_BREAK);
|
||||
}
|
||||
|
@ -5526,7 +5250,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
if (GetStateBits() & TEXT_FIRST_LETTER) {
|
||||
textMetrics.mAscent = PR_MAX(0, -textMetrics.mBoundingBox.Y());
|
||||
textMetrics.mDescent = PR_MAX(0, textMetrics.mBoundingBox.YMost());
|
||||
textMetrics.mAdvanceWidth = textMetrics.mBoundingBox.XMost();
|
||||
}
|
||||
|
||||
// Setup metrics for caller
|
||||
|
@ -5736,6 +5459,28 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsTextFrame::RecomputeOverflowRect()
|
||||
{
|
||||
gfxSkipCharsIterator iter = EnsureTextRun();
|
||||
if (!mTextRun)
|
||||
return nsRect(nsPoint(0,0), GetSize());
|
||||
|
||||
PropertyProvider provider(this, iter);
|
||||
provider.InitializeForDisplay(PR_TRUE);
|
||||
|
||||
gfxTextRun::Metrics textMetrics =
|
||||
mTextRun->MeasureText(provider.GetStart().GetSkippedOffset(),
|
||||
ComputeTransformedLength(provider), PR_FALSE, nsnull,
|
||||
&provider);
|
||||
|
||||
nsRect boundingBox =
|
||||
ConvertGfxRectOutward(textMetrics.mBoundingBox + gfxPoint(0, textMetrics.mAscent));
|
||||
boundingBox.UnionRect(boundingBox,
|
||||
nsRect(nsPoint(0,0), GetSize()));
|
||||
return boundingBox;
|
||||
}
|
||||
|
||||
static PRUnichar TransformChar(const nsStyleText* aStyle, gfxTextRun* aTextRun,
|
||||
PRUint32 aSkippedOffset, PRUnichar aChar)
|
||||
{
|
||||
|
@ -5957,6 +5702,11 @@ nsTextFrame::List(FILE* out, PRInt32 aIndent) const
|
|||
}
|
||||
}
|
||||
fprintf(out, " [content=%p]", static_cast<void*>(mContent));
|
||||
nsRect* overflowArea = const_cast<nsTextFrame*>(this)->GetOverflowAreaProperty(PR_FALSE);
|
||||
if (overflowArea) {
|
||||
fprintf(out, " [overflow=%d,%d,%d,%d]", overflowArea->x, overflowArea->y,
|
||||
overflowArea->width, overflowArea->height);
|
||||
}
|
||||
fprintf(out, " sc=%p", static_cast<void*>(mStyleContext));
|
||||
nsIAtom* pseudoTag = mStyleContext->GetPseudoType();
|
||||
if (pseudoTag) {
|
||||
|
|
|
@ -8,13 +8,16 @@ span {
|
|||
font-size: 200%;
|
||||
font-variant: small-caps;
|
||||
font-family: sans-serif;
|
||||
color:red;
|
||||
}
|
||||
span.line {
|
||||
background: yellow;
|
||||
text-decoration: line-through;
|
||||
color:red; background: yellow;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p><span>H</span>ello
|
||||
<div><span>Hello</span><br>Kitty</div>
|
||||
<div><span class="line">Hello</span><br>Kitty</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -8,8 +8,11 @@ span, p:first-letter, div:first-line {
|
|||
font-size: 200%;
|
||||
font-variant: small-caps;
|
||||
font-family: sans-serif;
|
||||
color:red;
|
||||
}
|
||||
span, div:first-line {
|
||||
background: yellow;
|
||||
text-decoration: line-through;
|
||||
color:red; background: yellow;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
|
|
@ -409,7 +409,7 @@ nsSVGGlyphFrame::UpdateCoveredRegion()
|
|||
textRun->DrawToPath(gfx, mPosition, 0, text.Length(), nsnull, nsnull);
|
||||
} else {
|
||||
gfxTextRun::Metrics metrics =
|
||||
textRun->MeasureText(0, text.Length(), PR_FALSE, nsnull);
|
||||
textRun->MeasureText(0, text.Length(), PR_FALSE, nsnull, nsnull);
|
||||
gfx->Rectangle(metrics.mBoundingBox + mPosition);
|
||||
}
|
||||
} else {
|
||||
|
@ -434,7 +434,7 @@ nsSVGGlyphFrame::UpdateCoveredRegion()
|
|||
gfx->Rotate(cp[i].angle);
|
||||
|
||||
gfxTextRun::Metrics metrics =
|
||||
textRun->MeasureText(i, 1, PR_FALSE, nsnull);
|
||||
textRun->MeasureText(i, 1, PR_FALSE, nsnull, nsnull);
|
||||
|
||||
gfx->Rectangle(metrics.mBoundingBox + gfx->CurrentPoint());
|
||||
}
|
||||
|
@ -867,7 +867,7 @@ nsSVGGlyphFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
gfxTextRun::Metrics metrics =
|
||||
textRun->MeasureText(charnum, 1, PR_FALSE, nsnull);
|
||||
textRun->MeasureText(charnum, 1, PR_FALSE, nsnull, nsnull);
|
||||
|
||||
if (cp) {
|
||||
if (cp[charnum].draw == PR_FALSE) {
|
||||
|
@ -943,7 +943,7 @@ nsSVGGlyphFrame::GetBaselineOffset(PRUint16 baselineIdentifier)
|
|||
return 0.0f;
|
||||
|
||||
gfxTextRun::Metrics metrics =
|
||||
textRun->MeasureText(0, text.Length(), PR_FALSE, nsnull);
|
||||
textRun->MeasureText(0, text.Length(), PR_FALSE, nsnull, nsnull);
|
||||
|
||||
switch (baselineIdentifier) {
|
||||
case BASELINE_HANGING:
|
||||
|
@ -1171,7 +1171,7 @@ nsSVGGlyphFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point)
|
|||
}
|
||||
|
||||
gfxTextRun::Metrics metrics =
|
||||
textRun->MeasureText(charnum, 1, PR_FALSE, nsnull);
|
||||
textRun->MeasureText(charnum, 1, PR_FALSE, nsnull, nsnull);
|
||||
|
||||
gfx->Rectangle(metrics.mBoundingBox + gfx->CurrentPoint());
|
||||
|
||||
|
@ -1293,7 +1293,7 @@ nsSVGGlyphFrame::ContainsPoint(float x, float y)
|
|||
}
|
||||
|
||||
gfxTextRun::Metrics metrics =
|
||||
textRun->MeasureText(i, 1, PR_FALSE, nsnull);
|
||||
textRun->MeasureText(i, 1, PR_FALSE, nsnull, nsnull);
|
||||
|
||||
gfx->Rectangle(metrics.mBoundingBox + gfx->CurrentPoint());
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче