Bug 96041. Compute precise glyph extents for high-quality text, and when requested. r=vlad,r+sr=dbaron

This commit is contained in:
roc+%cs.cmu.edu 2007-09-24 02:19:16 +00:00
Родитель d503c88193
Коммит dcd17482e7
19 изменённых файлов: 888 добавлений и 387 удалений

Просмотреть файл

@ -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, &currentCTM);
cairo_get_matrix(cr, &currentCTM);
if (mCairoFont) {
// Need to validate that its CTM is OK
@ -933,14 +936,14 @@ gfxPangoFont::SetupCairoFont(cairo_t *aCR)
}
}
if (!mCairoFont) {
mCairoFont = CreateScaledFont(aCR, &currentCTM, GetPangoFont());
mCairoFont = CreateScaledFont(cr, &currentCTM, 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&shy;</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());