зеркало из https://github.com/mozilla/gecko-dev.git
Bug 399531. Rework TrimTrailingWhitespace so that we recompute the overflow area for trimmed textframes and so that soft hyphens in otherwise-empty textframes are activated. r+sr=dbaron
This commit is contained in:
Родитель
f6a0bf6aa0
Коммит
efa3189859
|
@ -3210,17 +3210,6 @@ nsFrame::Reflow(nsPresContext* aPresContext,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
||||
nsIRenderingContext& aRC,
|
||||
nscoord& aDeltaWidth,
|
||||
PRBool& aLastCharIsJustifiable)
|
||||
{
|
||||
aDeltaWidth = 0;
|
||||
aLastCharIsJustifiable = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFrame::CharacterDataChanged(nsPresContext* aPresContext,
|
||||
nsIContent* aChild,
|
||||
|
|
|
@ -329,10 +329,6 @@ public:
|
|||
const nsHTMLReflowState* aReflowState,
|
||||
nsDidReflowStatus aStatus);
|
||||
virtual PRBool CanContinueTextRun() const;
|
||||
NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
||||
nsIRenderingContext& aRC,
|
||||
nscoord& aDeltaWidth,
|
||||
PRBool& aLastCharIsJustifiable);
|
||||
|
||||
// Selection Methods
|
||||
// XXX Doc me... (in nsIFrame.h puhleeze)
|
||||
|
|
|
@ -104,10 +104,10 @@ struct nsMargin;
|
|||
typedef class nsIFrame nsIBox;
|
||||
|
||||
// IID for the nsIFrame interface
|
||||
// 95f75c0a-de85-437a-a195-0304df3f62ce
|
||||
#define NS_IFRAME_IID \
|
||||
{ 0x95f75c0a, 0xde85, 0x437a, \
|
||||
{ 0xa1, 0x95, 0x03, 0x04, 0xdf, 0x3f, 0x62, 0xce } }
|
||||
// 04a7dee5-3435-47dc-bd42-a36c0f66a42c
|
||||
#define NS_IFRAME_IID \
|
||||
{ 0x04a7dee5, 0x3435, 0x47dc, \
|
||||
{ 0xbd, 0x42, 0xa3, 0x6c, 0x0f, 0x66, 0xa4, 0x2c } }
|
||||
|
||||
/**
|
||||
* Indication of how the frame can be split. This is used when doing runaround
|
||||
|
@ -1430,13 +1430,6 @@ public:
|
|||
*/
|
||||
virtual PRBool CanContinueTextRun() const = 0;
|
||||
|
||||
// Justification helper method that is used to remove trailing
|
||||
// whitespace before justification.
|
||||
NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
||||
nsIRenderingContext& aRC,
|
||||
nscoord& aDeltaWidth,
|
||||
PRBool& aLastCharIsJustifiable) = 0;
|
||||
|
||||
/**
|
||||
* Append the rendered text to the passed-in string.
|
||||
* The appended text will often not contain all the whitespace from source,
|
||||
|
|
|
@ -2283,27 +2283,30 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
|
|||
*aDeltaWidth = 0;
|
||||
return PR_TRUE;
|
||||
}
|
||||
else if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) {
|
||||
nscoord deltaWidth = 0;
|
||||
PRBool lastCharIsJustifiable = PR_FALSE;
|
||||
pfd->mFrame->TrimTrailingWhiteSpace(mPresContext,
|
||||
*mBlockReflowState->rendContext,
|
||||
deltaWidth,
|
||||
lastCharIsJustifiable);
|
||||
else if (pfd->GetFlag(PFD_ISTEXTFRAME)) {
|
||||
// Call TrimTrailingWhiteSpace even on empty textframes because they
|
||||
// might have a soft hyphen which should now appear, changing the frame's
|
||||
// width
|
||||
nsTextFrame::TrimOutput trimOutput = static_cast<nsTextFrame*>(pfd->mFrame)->
|
||||
TrimTrailingWhiteSpace(mBlockReflowState->rendContext);
|
||||
#ifdef NOISY_TRIM
|
||||
nsFrame::ListTag(stdout, (psd == mRootSpan
|
||||
? mBlockReflowState->frame
|
||||
: psd->mFrame->mFrame));
|
||||
printf(": trim of ");
|
||||
nsFrame::ListTag(stdout, pfd->mFrame);
|
||||
printf(" returned %d\n", deltaWidth);
|
||||
printf(" returned %d\n", trimOutput.mDeltaWidth);
|
||||
#endif
|
||||
if (lastCharIsJustifiable && pfd->mJustificationNumSpaces > 0) {
|
||||
if (trimOutput.mLastCharIsJustifiable && pfd->mJustificationNumSpaces > 0) {
|
||||
pfd->mJustificationNumSpaces--;
|
||||
}
|
||||
|
||||
if (deltaWidth) {
|
||||
pfd->mBounds.width -= deltaWidth;
|
||||
if (trimOutput.mChanged) {
|
||||
pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, PR_TRUE);
|
||||
}
|
||||
|
||||
if (trimOutput.mDeltaWidth) {
|
||||
pfd->mBounds.width -= trimOutput.mDeltaWidth;
|
||||
|
||||
// See if the text frame has already been placed in its parent
|
||||
if (psd != mRootSpan) {
|
||||
|
@ -2313,7 +2316,7 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
|
|||
}
|
||||
|
||||
// Adjust containing span's right edge
|
||||
psd->mX -= deltaWidth;
|
||||
psd->mX -= trimOutput.mDeltaWidth;
|
||||
|
||||
// Slide any frames that follow the text frame over by the
|
||||
// right amount. The only thing that can follow the text
|
||||
|
@ -2321,7 +2324,7 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
|
|||
// sensible (keeping the combined area honest).
|
||||
while (pfd->mNext) {
|
||||
pfd = pfd->mNext;
|
||||
pfd->mBounds.x -= deltaWidth;
|
||||
pfd->mBounds.x -= trimOutput.mDeltaWidth;
|
||||
if (psd != mRootSpan) {
|
||||
// When the child span is not a direct child of the block
|
||||
// we need to update the child spans frame rectangle
|
||||
|
@ -2329,14 +2332,16 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
|
|||
// that are direct children of the block will be updated
|
||||
// later, however, because the VerticalAlignFrames method
|
||||
// will be run after this method.
|
||||
SlideSpanFrameRect(pfd->mFrame, deltaWidth);
|
||||
SlideSpanFrameRect(pfd->mFrame, trimOutput.mDeltaWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass up to caller so they can shrink their span
|
||||
*aDeltaWidth = deltaWidth;
|
||||
return PR_TRUE;
|
||||
if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME) || trimOutput.mChanged) {
|
||||
// Pass up to caller so they can shrink their span
|
||||
*aDeltaWidth = trimOutput.mDeltaWidth;
|
||||
return PR_TRUE;
|
||||
}
|
||||
}
|
||||
pfd = pfd->mPrev;
|
||||
}
|
||||
|
|
|
@ -208,10 +208,21 @@ public:
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus);
|
||||
virtual PRBool CanContinueTextRun() const;
|
||||
NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
||||
nsIRenderingContext& aRC,
|
||||
nscoord& aDeltaWidth,
|
||||
PRBool& aLastCharIsJustifiable);
|
||||
// Method that is called for a text frame that is logically
|
||||
// adjacent to the end of the line (i.e. followed only by empty text frames,
|
||||
// placeholders or inlines containing such).
|
||||
struct TrimOutput {
|
||||
// true if we trimmed some space or changed metrics in some other way.
|
||||
// In this case, we should call RecomputeOverflowRect on this frame.
|
||||
PRPackedBool mChanged;
|
||||
// true if the last character is not justifiable so should be subtracted
|
||||
// from the count of justifiable characters in the frame, since the last
|
||||
// character in a line is not justifiable.
|
||||
PRPackedBool mLastCharIsJustifiable;
|
||||
// an amount to *subtract* from the frame's width (zero if !mChanged)
|
||||
nscoord mDeltaWidth;
|
||||
};
|
||||
TrimOutput TrimTrailingWhiteSpace(nsIRenderingContext* aRC);
|
||||
virtual nsresult GetRenderedText(nsAString* aString = nsnull,
|
||||
gfxSkipChars* aSkipChars = nsnull,
|
||||
gfxSkipCharsIterator* aSkipIter = nsnull,
|
||||
|
|
|
@ -2017,6 +2017,11 @@ public:
|
|||
* Count the number of justifiable characters in the given DOM range
|
||||
*/
|
||||
PRUint32 ComputeJustifiableCharacters(PRInt32 aOffset, PRInt32 aLength);
|
||||
/**
|
||||
* Find the start and end of the justifiable characters. Does not depend on the
|
||||
* position of aStart or aEnd, although it's most efficient if they are near the
|
||||
* start and end of the text frame.
|
||||
*/
|
||||
void FindJustificationRange(gfxSkipCharsIterator* aStart,
|
||||
gfxSkipCharsIterator* aEnd);
|
||||
|
||||
|
@ -4883,7 +4888,6 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
|
|||
aData->atStartOfLine = PR_FALSE;
|
||||
|
||||
if (collapseWhitespace) {
|
||||
nscoord trailingWhitespaceWidth;
|
||||
PRUint32 trimStart = GetEndOfTrimmedText(frag, wordStart, i, &iter);
|
||||
if (trimStart == start) {
|
||||
// This is *all* trimmable whitespace, so whatever trailingWhitespace
|
||||
|
@ -5513,39 +5517,39 @@ nsTextFrame::CanContinueTextRun() const
|
|||
return PR_TRUE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
||||
nsIRenderingContext& aRC,
|
||||
nscoord& aDeltaWidth,
|
||||
PRBool& aLastCharIsJustifiable)
|
||||
nsTextFrame::TrimOutput
|
||||
nsTextFrame::TrimTrailingWhiteSpace(nsIRenderingContext* aRC)
|
||||
{
|
||||
aLastCharIsJustifiable = PR_FALSE;
|
||||
aDeltaWidth = 0;
|
||||
TrimOutput result;
|
||||
result.mChanged = PR_FALSE;
|
||||
result.mLastCharIsJustifiable = PR_FALSE;
|
||||
result.mDeltaWidth = 0;
|
||||
|
||||
AddStateBits(TEXT_END_OF_LINE);
|
||||
|
||||
PRInt32 contentLength = GetContentLength();
|
||||
if (!contentLength)
|
||||
return NS_OK;
|
||||
return result;
|
||||
|
||||
gfxContext* ctx = static_cast<gfxContext*>
|
||||
(aRC.GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
|
||||
(aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
|
||||
gfxSkipCharsIterator start = EnsureTextRun(ctx);
|
||||
if (!mTextRun)
|
||||
return NS_ERROR_FAILURE;
|
||||
NS_ENSURE_TRUE(mTextRun, result);
|
||||
|
||||
PRUint32 trimmedStart = start.GetSkippedOffset();
|
||||
|
||||
const nsTextFragment* frag = mContent->GetText();
|
||||
TrimmedOffsets trimmed = GetTrimmedOffsets(frag, PR_TRUE);
|
||||
gfxSkipCharsIterator iter = start;
|
||||
gfxSkipCharsIterator trimmedEndIter = start;
|
||||
const nsStyleText* textStyle = GetStyleText();
|
||||
gfxFloat delta = 0;
|
||||
PRUint32 trimmedEnd = iter.ConvertOriginalToSkipped(trimmed.GetEnd());
|
||||
PRUint32 trimmedEnd = trimmedEndIter.ConvertOriginalToSkipped(trimmed.GetEnd());
|
||||
|
||||
if (GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) {
|
||||
aLastCharIsJustifiable = PR_TRUE;
|
||||
// We pre-trimmed this frame, so the last character is justifiable
|
||||
result.mLastCharIsJustifiable = PR_TRUE;
|
||||
} else if (trimmed.GetEnd() < GetContentEnd()) {
|
||||
gfxSkipCharsIterator end = iter;
|
||||
gfxSkipCharsIterator end = trimmedEndIter;
|
||||
PRUint32 endOffset = end.ConvertOriginalToSkipped(GetContentOffset() + contentLength);
|
||||
if (trimmedEnd < endOffset) {
|
||||
// We can't be dealing with tabs here ... they wouldn't be trimmed. So it's
|
||||
|
@ -5556,23 +5560,36 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
|||
// non-compressed whitespace being skipped at end of line -> justifiable
|
||||
// XXX should we actually *count* justifiable characters that should be
|
||||
// removed from the overall count? I think so...
|
||||
aLastCharIsJustifiable = PR_TRUE;
|
||||
result.mLastCharIsJustifiable = PR_TRUE;
|
||||
result.mChanged = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!aLastCharIsJustifiable &&
|
||||
if (trimmed.GetEnd() == GetContentEnd() &&
|
||||
HasSoftHyphenBefore(frag, mTextRun, trimmed.mStart, trimmedEndIter)) {
|
||||
// This is a soft hyphen break.
|
||||
// Fix up metrics to include hyphen
|
||||
result.mChanged = PR_TRUE;
|
||||
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, ctx, this));
|
||||
if (hyphenTextRun.get()) {
|
||||
delta = -hyphenTextRun->GetAdvanceWidth(0, hyphenTextRun->GetLength(), nsnull);
|
||||
}
|
||||
AddStateBits(TEXT_HYPHEN_BREAK);
|
||||
}
|
||||
|
||||
if (!result.mLastCharIsJustifiable &&
|
||||
NS_STYLE_TEXT_ALIGN_JUSTIFY == textStyle->mTextAlign) {
|
||||
// Check if any character in the last cluster is justifiable
|
||||
PropertyProvider provider(mTextRun, textStyle, frag, this, start, contentLength,
|
||||
nsnull, 0);
|
||||
PRBool isCJK = IsChineseJapaneseLangGroup(this);
|
||||
gfxSkipCharsIterator justificationStart(iter), justificationEnd(iter);
|
||||
gfxSkipCharsIterator justificationStart(start), justificationEnd(trimmedEndIter);
|
||||
provider.FindJustificationRange(&justificationStart, &justificationEnd);
|
||||
|
||||
PRInt32 i;
|
||||
for (i = justificationEnd.GetOriginalOffset(); i < trimmed.GetEnd(); ++i) {
|
||||
if (IsJustifiableCharacter(frag, i, isCJK)) {
|
||||
aLastCharIsJustifiable = PR_TRUE;
|
||||
result.mLastCharIsJustifiable = PR_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5581,31 +5598,36 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
|||
mTextRun->SetLineBreaks(trimmedStart, trimmedEnd - trimmedStart,
|
||||
(GetStateBits() & TEXT_START_OF_LINE) != 0, PR_TRUE,
|
||||
&advanceDelta, ctx);
|
||||
if (advanceDelta != 0) {
|
||||
result.mChanged = PR_TRUE;
|
||||
}
|
||||
|
||||
// aDeltaWidth is *subtracted* from our width.
|
||||
// If advanceDelta is positive then setting the line break made us longer,
|
||||
// so aDeltaWidth could go negative.
|
||||
aDeltaWidth = NSToCoordFloor(delta - advanceDelta);
|
||||
// XXX if aDeltaWidth goes negative, that means this frame might not actually fit
|
||||
// anymore!!! We need higher level line layout to recover somehow. This can
|
||||
// really only happen when we have glyphs with special shapes at the end of
|
||||
// lines, I think. Breaking inside a kerning pair won't do it because that
|
||||
// would mean we broke inside this textrun, and BreakAndMeasureText should
|
||||
// make sure the resulting shaped substring fits. Maybe if we passed a
|
||||
// maxTextLength? But that only happens at direction changes (so we
|
||||
// we wouldn't kern across the boundary) or for first-letter (which always fits
|
||||
// because it starts the line!).
|
||||
if (aDeltaWidth < 0) {
|
||||
NS_WARNING("Negative deltawidth, something odd is happening");
|
||||
}
|
||||
|
||||
// XXX what about adjusting bounding metrics?
|
||||
result.mDeltaWidth = NSToCoordFloor(delta - advanceDelta);
|
||||
// If aDeltaWidth goes negative, that means this frame might not actually fit
|
||||
// anymore!!! We need higher level line layout to recover somehow.
|
||||
// If it's because the frame has a soft hyphen that is now being displayed,
|
||||
// this should actually be OK, because our reflow recorded the break
|
||||
// opportunity that allowed the soft hyphen to be used, and we wouldn't
|
||||
// have recorded the opportunity unless the hyphen fit (or was the first
|
||||
// opportunity on the line).
|
||||
// Otherwise this can/ really only happen when we have glyphs with special
|
||||
// shapes at the end of lines, I think. Breaking inside a kerning pair won't
|
||||
// do it because that would mean we broke inside this textrun, and
|
||||
// BreakAndMeasureText should make sure the resulting shaped substring fits.
|
||||
// Maybe if we passed a maxTextLength? But that only happens at direction
|
||||
// changes (so we wouldn't kern across the boundary) or for first-letter
|
||||
// (which always fits because it starts the line!).
|
||||
NS_WARN_IF_FALSE(result.mDeltaWidth >= 0 || (GetStateBits() & TEXT_HYPHEN_BREAK),
|
||||
"Negative deltawidth in a non-hyphen case, something odd is happening");
|
||||
|
||||
#ifdef NOISY_TRIM
|
||||
ListTag(stdout);
|
||||
printf(": trim => %d\n", aDeltaWidth);
|
||||
printf(": trim => %d\n", result.mDeltaWidth);
|
||||
#endif
|
||||
return NS_OK;
|
||||
return result;
|
||||
}
|
||||
|
||||
nsRect
|
||||
|
|
|
@ -48,6 +48,9 @@ include first-line/reftest.list
|
|||
# svg/
|
||||
include svg/reftest.list
|
||||
|
||||
# text/
|
||||
include text/reftest.list
|
||||
|
||||
# text-decoration/
|
||||
include text-decoration/reftest.list
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
<body>
|
||||
|
||||
<div>
|
||||
<p>Hy-<br>phen.
|
||||
<p>Hy-<br>phen.
|
||||
<p>Hy-<br>phen.
|
||||
<p>Hy-<br>phen.
|
||||
<p>Hy-<br>phen.
|
||||
<p>Hy‐<br>phen.
|
||||
<p>Hy‐<br>phen.
|
||||
<p>Hy‐<br>phen.
|
||||
<p>Hy‐<br>phen.
|
||||
<p>Hy‐<br>phen.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
Загрузка…
Ссылка в новой задаче