Not part of the build. Change nsLineBreaker so that breaks at element boundaries are always governed by the white-space property on the element that's the nearest common ancestor of the DOM nodes on each side of the break point --- fixes bug 382289. Also, pass NBSP through to textruns so that whitespace trimming doesn't trim NBSP. Also, count trimmed spaces correctly when updating mClusterCount.

This commit is contained in:
roc+%cs.cmu.edu 2007-05-30 22:27:28 +00:00
Родитель 2a3bfee38b
Коммит c71de10d30
5 изменённых файлов: 143 добавлений и 151 удалений

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

@ -72,63 +72,50 @@ public:
* into AppendText calls.
*
* The current strategy is that we break the overall text into
* whitespace-delimited "words". Then for words that contain a CJK character,
* we break within the word using JISx4051 rules.
* XXX This approach is not very good and we should replace it with something
* better, such as some variant of UAX#14.
* whitespace-delimited "words". Then for words that contain a "complex"
* character (currently CJK or Thai), we break within the word using complex
* rules (JISx4051 or Pango).
*/
class nsLineBreaker {
public:
nsLineBreaker();
~nsLineBreaker();
// We need finegrained control of the line breaking behaviour to ensure
// that we get tricky CSS semantics right (in particular, the way we currently
// interpret and implement them; there's some ambiguity in the spec). The
// rules for CSS 'white-space' are slightly different for breaks induced by
// whitespace and space induced by nonwhitespace. Breaks induced by
// whitespace are always controlled by the
// 'white-space' property of the text node containing the
// whitespace. Breaks induced by non-whitespace where the break is between
// two nodes are controled by the 'white-space' property on the nearest
// common ancestor node. Therefore we provide separate control over
// a) whether whitespace in this text induces breaks b) whether we can
// break between nonwhitespace inside this text and c) whether we can break
// between nonwhitespace between the last text and this text.
// Normally, break opportunities exist at the end of each run of whitespace
// (Unicode ZWSP (U+200B) and ASCII space (U+0020)). Break opportunities can
// also exist inside runs of non-whitespace, as determined by nsILineBreaker.
// We provide flags to control on a per-chunk basis where breaks are allowed.
// At any character boundary, exactly one text chunk governs whether a
// break is allowed at that boundary.
//
// "Whitespace" below means Unicode ZWSP (U+200B) and ASCII space (U+0020). We
// operate on text after whitespace processing has been applied, so
// We operate on text after whitespace processing has been applied, so
// other characters (e.g. tabs and newlines) may have been converted to
// spaces.
enum {
/**
* Allow breaks where a non-whitespace character in this block of text
* is preceded by a whitespace character.
* Allow a break opportunity at the start of this chunk of text.
*/
BREAK_WHITESPACE_END = 0x01,
BREAK_ALLOW_INITIAL = 0x01,
/**
* Allow breaks between eligible nonwhitespace characters when the break
* is in the interior of this block of text.
* Allow a break opportunity in the interior of this chunk of text.
*/
BREAK_NONWHITESPACE_INSIDE = 0x02,
/**
* Allow break between eligible nonwhitespace characters when the break
* is at the beginning of this block of text.
*/
BREAK_NONWHITESPACE_BEFORE = 0x04
BREAK_ALLOW_INSIDE = 0x02,
};
/**
* Feed Unicode text into the linebreaker for analysis.
* If aLength is zero, then we assume the string is "invisible whitespace"
* which can induce breaks.
* Append "invisible whitespace". This acts like whitespace, but there is
* no actual text associated with it.
*/
nsresult AppendInvisibleWhitespace();
/**
* Feed Unicode text into the linebreaker for analysis. aLength must be
* nonzero.
*/
nsresult AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32 aLength,
PRUint32 aFlags, nsILineBreakSink* aSink);
/**
* Feed 8-bit text into the linebreaker for analysis.
* If aLength is zero, then we assume the string is "invisible whitespace"
* which can induce breaks.
* Feed 8-bit text into the linebreaker for analysis. aLength must be nonzero.
*/
nsresult AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aLength,
PRUint32 aFlags, nsILineBreakSink* aSink);
@ -143,6 +130,9 @@ public:
nsresult Reset() { return FlushCurrentWord(); }
private:
// This is a list of text sources that make up the "current word" (i.e.,
// run of text which does not contain any whitespace). All the mLengths
// are are nonzero, these cannot overlap.
struct TextItem {
TextItem(nsILineBreakSink* aSink, PRUint32 aSinkOffset, PRUint32 aLength,
PRUint32 aFlags)
@ -167,9 +157,8 @@ private:
nsAutoTArray<TextItem,2> mTextItems;
PRPackedBool mCurrentWordContainsCJK;
// When mCurrentWord is empty, this indicates whether we should allow a break
// before the next text if it starts with non-whitespace.
PRPackedBool mBreakBeforeNonWhitespace;
// True if the previous character was whitespace
PRPackedBool mAfterSpace;
};
#endif /*NSLINEBREAKER_H_*/

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

@ -65,7 +65,7 @@ IS_CJK_CHAR(PRUnichar u)
nsLineBreaker::nsLineBreaker()
: mCurrentWordContainsCJK(PR_FALSE),
mBreakBeforeNonWhitespace(PR_FALSE)
mAfterSpace(PR_FALSE)
{
}
@ -95,10 +95,10 @@ nsLineBreaker::FlushCurrentWord()
TextItem* ti = &mTextItems[i];
NS_ASSERTION(ti->mLength > 0, "Zero length word contribution?");
if (!(ti->mFlags & BREAK_NONWHITESPACE_BEFORE) && ti->mSinkOffset == 0) {
if (!(ti->mFlags & BREAK_ALLOW_INITIAL) && ti->mSinkOffset == 0) {
breakState[offset] = PR_FALSE;
}
if (!(ti->mFlags & BREAK_NONWHITESPACE_INSIDE)) {
if (!(ti->mFlags & BREAK_ALLOW_INSIDE)) {
PRUint32 exclude = ti->mSinkOffset == 0 ? 1 : 0;
memset(breakState.Elements() + offset + exclude, PR_FALSE, ti->mLength - exclude);
}
@ -122,20 +122,13 @@ nsresult
nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32 aLength,
PRUint32 aFlags, nsILineBreakSink* aSink)
{
if (aLength == 0) {
// Treat as "invisible whitespace"
nsresult rv = FlushCurrentWord();
if (NS_FAILED(rv))
return rv;
mBreakBeforeNonWhitespace = (aFlags & BREAK_WHITESPACE_END) != 0;
return NS_OK;
}
NS_ASSERTION(aLength > 0, "Appending empty text...");
PRUint32 offset = 0;
// Continue the current word
if (mCurrentWord.Length() > 0) {
NS_ASSERTION(!mBreakBeforeNonWhitespace, "These should not be set");
NS_ASSERTION(!mAfterSpace, "These should not be set");
while (offset < aLength && !IS_SPACE(aText[offset])) {
mCurrentWord.AppendElement(aText[offset]);
@ -166,17 +159,17 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
PRUint32 wordStart = offset;
PRBool wordHasCJK = PR_FALSE;
PRBool breakNextIfNonWhitespace = mBreakBeforeNonWhitespace;
for (;;) {
PRUnichar ch = aText[offset];
PRBool isSpace = IS_SPACE(ch);
breakState[offset] = breakNextIfNonWhitespace && !isSpace;
breakNextIfNonWhitespace = PR_FALSE;
breakState[offset] = mAfterSpace && !isSpace &&
(aFlags & (start == 0 ? BREAK_ALLOW_INITIAL : BREAK_ALLOW_INSIDE));
mAfterSpace = isSpace;
if (isSpace) {
if (offset > wordStart && wordHasCJK) {
if (aFlags & BREAK_NONWHITESPACE_INSIDE) {
if (aFlags & BREAK_ALLOW_INSIDE) {
// Save current start-of-word state because GetJISx4051Breaks will
// set it to false
PRPackedBool currentStart = breakState[wordStart];
@ -188,9 +181,6 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
wordHasCJK = PR_FALSE;
}
if (aFlags & BREAK_WHITESPACE_END) {
breakNextIfNonWhitespace = PR_TRUE;
}
++offset;
if (offset >= aLength)
break;
@ -217,7 +207,6 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
}
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
mBreakBeforeNonWhitespace = breakNextIfNonWhitespace;
return NS_OK;
}
@ -225,20 +214,13 @@ nsresult
nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aLength,
PRUint32 aFlags, nsILineBreakSink* aSink)
{
if (aLength == 0) {
// Treat as "invisible whitespace"
nsresult rv = FlushCurrentWord();
if (NS_FAILED(rv))
return rv;
mBreakBeforeNonWhitespace = (aFlags & BREAK_WHITESPACE_END) != 0;
return NS_OK;
}
NS_ASSERTION(aLength > 0, "Appending empty text...");
PRUint32 offset = 0;
// Continue the current word
if (mCurrentWord.Length() > 0) {
NS_ASSERTION(!mBreakBeforeNonWhitespace, "These should not be set");
NS_ASSERTION(!mAfterSpace, "These should not be set");
while (offset < aLength && !IS_SPACE(aText[offset])) {
mCurrentWord.AppendElement(aText[offset]);
@ -267,18 +249,17 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
PRUint32 start = offset;
PRUint32 wordStart = offset;
PRBool breakNextIfNonWhitespace = mBreakBeforeNonWhitespace;
for (;;) {
PRUint8 ch = aText[offset];
PRBool isSpace = IS_SPACE(ch);
breakState[offset] = breakNextIfNonWhitespace && !isSpace;
breakNextIfNonWhitespace = PR_FALSE;
breakState[offset] = mAfterSpace && !isSpace &&
(aFlags & (start == 0 ? BREAK_ALLOW_INITIAL : BREAK_ALLOW_INSIDE));
mAfterSpace = isSpace;
if (isSpace) {
if (aFlags & BREAK_WHITESPACE_END) {
breakNextIfNonWhitespace = PR_TRUE;
}
// The current word can't have any special (CJK/Thai) characters inside it
// because this is 8-bit text, so just ignore it
++offset;
if (offset >= aLength)
break;
@ -307,6 +288,15 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
}
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
mBreakBeforeNonWhitespace = breakNextIfNonWhitespace;
return NS_OK;
}
nsresult
nsLineBreaker::AppendInvisibleWhitespace() {
// Treat as "invisible whitespace"
nsresult rv = FlushCurrentWord();
if (NS_FAILED(rv))
return rv;
mAfterSpace = PR_TRUE;
return NS_OK;
}

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

@ -851,8 +851,8 @@ public:
struct MappedFlow {
nsTextFrame* mStartFrame;
nsTextFrame* mEndFrame;
// When we break between non-whitespace characters, the nearest common
// ancestor of the frames containing the characters is the one whose
// When we consider breaking between elements, the nearest common
// ancestor of the elements containing the characters is the one whose
// CSS 'white-space' property governs. So this records the nearest common
// ancestor of mStartFrame and the previous text frame, or null if there
// was no previous text frame on this line.
@ -1558,12 +1558,40 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
AssignTextRun(textRun);
}
static PRBool
HasCompressedLeadingWhitespace(nsTextFrame* aFrame, PRInt32 aContentEndOffset,
gfxSkipCharsIterator* aIterator)
{
if (!aIterator->IsOriginalCharSkipped())
return PR_FALSE;
PRBool result = PR_FALSE;
PRInt32 savedOffset = aIterator->GetOriginalOffset();
PRInt32 frameContentOffset = aFrame->GetContentOffset();
const nsTextFragment* frag = aFrame->GetContent()->GetText();
while (frameContentOffset < aContentEndOffset &&
aIterator->IsOriginalCharSkipped()) {
if (IsSpace(frag, frameContentOffset)) {
result = PR_TRUE;
break;
}
++frameContentOffset;
}
aIterator->SetOriginalOffset(savedOffset);
return result;
}
void
BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
PRBool aIsExistingTextRun)
{
// textruns have uniform language
nsIAtom* lang = mMappedFlows[0].mStartFrame->GetStyleVisibility()->mLangGroup;
// We keep this pointed at the skip-chars data for the current mappedFlow.
// This lets us cheaply check whether the flow has compressed initial
// whitespace...
gfxSkipCharsIterator iter(aTextRun->GetSkipChars());
PRUint32 i;
for (i = 0; i < mMappedFlows.Length(); ++i) {
MappedFlow* mappedFlow = &mMappedFlows[i];
@ -1578,28 +1606,35 @@ BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
: mMappedFlows[i + 1].mTransformedTextOffset)
- offset;
PRUint32 flags = 0;
if (!mappedFlow->mAncestorControllingInitialBreak ||
mappedFlow->mAncestorControllingInitialBreak->GetStyleText()->WhiteSpaceCanWrap()) {
flags |= nsLineBreaker::BREAK_NONWHITESPACE_BEFORE;
nsTextFrame* startFrame = mappedFlow->mStartFrame;
if (HasCompressedLeadingWhitespace(startFrame, mappedFlow->mContentEndOffset, &iter)) {
mLineBreaker.AppendInvisibleWhitespace();
}
const nsStyleText* textStyle = mappedFlow->mStartFrame->GetStyleText();
if (textStyle->WhiteSpaceCanWrap()) {
// If white-space is preserved, then the only break opportunity is at
// the end of whitespace runs; otherwise there is a break opportunity before
// and after each whitespace character
flags |= nsLineBreaker::BREAK_NONWHITESPACE_INSIDE |
nsLineBreaker::BREAK_WHITESPACE_END;
}
// If length is zero, the linebreaker treats the text as invisible whitespace.
// Thus runs of entirely-skipped whitespace can still induce breaks.
if (aTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
mLineBreaker.AppendText(lang, aTextRun->GetText8Bit() + offset,
length, flags, *breakSink);
} else {
mLineBreaker.AppendText(lang, aTextRun->GetTextUnicode() + offset,
length, flags, *breakSink);
if (length > 0) {
PRUint32 flags = 0;
if (!mappedFlow->mAncestorControllingInitialBreak ||
mappedFlow->mAncestorControllingInitialBreak->GetStyleText()->WhiteSpaceCanWrap()) {
flags |= nsLineBreaker::BREAK_ALLOW_INITIAL;
}
const nsStyleText* textStyle = startFrame->GetStyleText();
if (textStyle->WhiteSpaceCanWrap()) {
// If white-space is preserved, then the only break opportunity is at
// the end of whitespace runs; otherwise there is a break opportunity before
// and after each whitespace character
flags |= nsLineBreaker::BREAK_ALLOW_INSIDE;
}
if (aTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
mLineBreaker.AppendText(lang, aTextRun->GetText8Bit() + offset,
length, flags, *breakSink);
} else {
mLineBreaker.AppendText(lang, aTextRun->GetTextUnicode() + offset,
length, flags, *breakSink);
}
}
iter.AdvanceOriginal(mappedFlow->mContentEndOffset - mappedFlow->mContentOffset);
}
}
@ -1729,17 +1764,17 @@ nsTextFrame::EnsureTextRun(nsIRenderingContext* aRC, nsIFrame* aLineContainer,
}
static PRUint32
GetLengthOfTrimmedText(const nsTextFragment* aFrag,
PRUint32 aStart, PRUint32 aEnd,
gfxSkipCharsIterator* aIterator)
GetEndOfTrimmedText(const nsTextFragment* aFrag,
PRUint32 aStart, PRUint32 aEnd,
gfxSkipCharsIterator* aIterator)
{
aIterator->SetSkippedOffset(aEnd);
while (aIterator->GetSkippedOffset() > aStart) {
aIterator->AdvanceSkipped(-1);
if (!IsSpace(aFrag, aIterator->GetOriginalOffset()))
return aIterator->GetSkippedOffset() + 1 - aStart;
return aIterator->GetSkippedOffset() + 1;
}
return 0;
return aStart;
}
nsTextFrame::TrimmedOffsets
@ -1877,7 +1912,8 @@ public:
nscoord aOffsetFromBlockOriginForTabs)
: mTextRun(aTextRun), mFontGroup(nsnull), mTextStyle(aTextStyle), mFrag(aFrag),
mLineContainer(aLineContainer),
mFrame(aFrame), mStart(aStart), mTabWidths(nsnull), mLength(aLength),
mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
mTabWidths(nsnull), mLength(aLength),
mWordSpacing(StyleToCoord(mTextStyle->mWordSpacing)),
mLetterSpacing(StyleToCoord(mTextStyle->mLetterSpacing)),
mJustificationSpacing(0),
@ -1898,7 +1934,8 @@ public:
mTextStyle(aFrame->GetStyleText()),
mFrag(aFrame->GetContent()->GetText()),
mLineContainer(nsnull),
mFrame(aFrame), mStart(aStart), mTabWidths(nsnull),
mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
mTabWidths(nsnull),
mLength(aFrame->GetContentLength()),
mWordSpacing(StyleToCoord(mTextStyle->mWordSpacing)),
mLetterSpacing(StyleToCoord(mTextStyle->mLetterSpacing)),
@ -1945,6 +1982,8 @@ public:
gfxFloat* GetTabWidths(PRUint32 aTransformedStart, PRUint32 aTransformedLength);
const gfxSkipCharsIterator& GetEndHint() { return mTempIterator; }
protected:
void SetupJustificationSpacing();
@ -1955,6 +1994,7 @@ protected:
nsIFrame* mLineContainer;
nsTextFrame* mFrame;
gfxSkipCharsIterator mStart; // Offset in original and transformed string
gfxSkipCharsIterator mTempIterator;
nsTArray<gfxFloat>* mTabWidths; // widths for each transformed string character
PRInt32 mLength; // DOM string length
gfxFloat mWordSpacing; // space for each whitespace char
@ -4488,10 +4528,10 @@ FindStartAfterSkippingWhitespace(PropertyProvider* aProvider,
if (aData->skipWhitespace && aCollapseWhitespace) {
while (aIterator->GetSkippedOffset() < aFlowEndInTextRun &&
IsSpace(aProvider->GetFragment(), aIterator->GetOriginalOffset())) {
aIterator->AdvanceSkipped(1);
aIterator->AdvanceOriginal(1);
}
}
return aIterator->GetSkippedOffset();
return aIterator->GetSkippedOffset();
}
/* virtual */
@ -4515,11 +4555,11 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
// Pass null for the line container. This will disable tab spacing, but that's
// OK since we can't really handle tabs for intrinsic sizing anyway.
PropertyProvider provider(mTextRun, GetStyleText(), mContent->GetText(), this,
const nsTextFragment* frag = mContent->GetText();
PropertyProvider provider(mTextRun, GetStyleText(), frag, this,
iter, GetInFlowContentLength(), nsnull, 0);
PRBool collapseWhitespace = !provider.GetStyleText()->WhiteSpaceIsSignificant();
PRBool canWrap = provider.GetStyleText()->WhiteSpaceCanWrap();
PRUint32 start =
FindStartAfterSkippingWhitespace(&provider, aData, collapseWhitespace,
&iter, flowEndInTextRun);
@ -4543,17 +4583,10 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
if (collapseWhitespace) {
nscoord trailingWhitespaceWidth;
PRUint32 lengthAfterTrim;
if (canWrap) {
lengthAfterTrim = GetLengthOfTrimmedText(provider.GetFragment(),
wordStart, i, &iter);
} else {
lengthAfterTrim = i - wordStart;
}
if (lengthAfterTrim == 0) {
PRUint32 trimStart = GetEndOfTrimmedText(frag, wordStart, i, &iter);
if (trimStart == start) {
trailingWhitespaceWidth = width;
} else {
PRUint32 trimStart = wordStart + lengthAfterTrim;
trailingWhitespaceWidth =
NSToCoordCeil(mTextRun->GetAdvanceWidth(trimStart, i - trimStart, &provider));
}
@ -4613,7 +4646,6 @@ nsTextFrame::AddInlinePrefWidthForFlow(nsIRenderingContext *aRenderingContext,
iter, GetInFlowContentLength(), nsnull, 0);
PRBool collapseWhitespace = !provider.GetStyleText()->WhiteSpaceIsSignificant();
PRBool canWrap = provider.GetStyleText()->WhiteSpaceCanWrap();
PRUint32 start =
FindStartAfterSkippingWhitespace(&provider, aData, collapseWhitespace,
&iter, flowEndInTextRun);
@ -4623,20 +4655,14 @@ nsTextFrame::AddInlinePrefWidthForFlow(nsIRenderingContext *aRenderingContext,
if (collapseWhitespace) {
// \n line breaks are not honoured, so everything would like to go
// onto one line, so just measure it
PRUint32 lengthAfterTrim;
if (canWrap) {
lengthAfterTrim = GetLengthOfTrimmedText(provider.GetFragment(), start,
flowEndInTextRun, &iter);
} else {
lengthAfterTrim = flowEndInTextRun;
}
aData->currentLine +=
NSToCoordCeil(mTextRun->GetAdvanceWidth(start, flowEndInTextRun - start, &provider));
PRUint32 trimStart = start + lengthAfterTrim;
PRUint32 trimStart = GetEndOfTrimmedText(provider.GetFragment(), start,
flowEndInTextRun, &iter);
nscoord trimWidth =
NSToCoordCeil(mTextRun->GetAdvanceWidth(trimStart, flowEndInTextRun - trimStart, &provider));
if (lengthAfterTrim == 0) {
if (trimStart == start) {
// This is *all* trimmable whitespace, so whatever trailingWhitespace
// we saw previously is still trailing...
aData->trailingWhitespace += trimWidth;
@ -4876,11 +4902,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
needTightBoundingBox = PR_TRUE;
}
#endif
// The "end" iterator points to the first character after the string mapped
// by this frame. Basically, it's original-string offset is offset+charsFit
// after we've computed charsFit.
gfxSkipCharsIterator end(provider.GetStart());
PRBool suppressInitialBreak = PR_FALSE;
if (!lineLayout.LineIsBreakable()) {
suppressInitialBreak = PR_TRUE;
@ -4928,6 +4949,10 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
canTrimTrailingWhitespace ? &trimmedWidth : nsnull,
&textMetrics, needTightBoundingBox,
&usedHyphenation, &transformedLastBreak);
// The "end" iterator points to the first character after the string mapped
// by this frame. Basically, it's original-string offset is offset+charsFit
// after we've computed charsFit.
gfxSkipCharsIterator end(provider.GetEndHint());
end.SetSkippedOffset(transformedOffset + transformedCharsFit);
PRInt32 charsFit = end.GetOriginalOffset() - offset;
// That might have taken us beyond our assigned content range (because
@ -5075,8 +5100,9 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
// but that could change...
if (canTrimTrailingWhitespace) {
// Count trimmed spaces and add them to the cluster count
PRUint32 charIndex = transformedCharsFit;
while (charIndex > 0 && mTextRun->GetChar(charIndex - 1) == ' ') {
PRUint32 charIndex = transformedOffset + transformedCharsFit;
while (charIndex > transformedOffset &&
mTextRun->GetChar(charIndex - 1) == ' ') {
++textMetrics.mClusterCount;
--charIndex;
}

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

@ -115,10 +115,7 @@ nsTextFrameUtils::TransformText(const PRUnichar* aText, PRUint32 aLength,
aSkipChars->SkipChar();
} else {
aSkipChars->KeepChar();
if (ch == CH_NBSP) {
ch = ' ';
flags |= TEXT_WAS_TRANSFORMED;
} else if (ch == '\t') {
if (ch == '\t') {
flags |= TEXT_HAS_TAB;
}
*aOutput++ = ch;
@ -153,10 +150,6 @@ nsTextFrameUtils::TransformText(const PRUnichar* aText, PRUint32 aLength,
aSkipChars->SkipChar();
nowInWhitespace = inWhitespace;
} else {
if (ch == CH_NBSP) {
ch = ' ';
flags |= TEXT_WAS_TRANSFORMED;
}
*aOutput++ = ch;
aSkipChars->KeepChar();
}
@ -203,10 +196,7 @@ nsTextFrameUtils::TransformText(const PRUint8* aText, PRUint32 aLength,
aSkipChars->SkipChar();
} else {
aSkipChars->KeepChar();
if (ch == CH_NBSP) {
ch = ' ';
flags |= TEXT_WAS_TRANSFORMED;
} else if (ch == '\t') {
if (ch == '\t') {
flags |= TEXT_HAS_TAB;
}
*aOutput++ = ch;
@ -224,10 +214,6 @@ nsTextFrameUtils::TransformText(const PRUint8* aText, PRUint32 aLength,
aSkipChars->SkipChar();
nowInWhitespace = inWhitespace;
} else {
if (ch == CH_NBSP) {
ch = ' ';
flags |= TEXT_WAS_TRANSFORMED;
}
*aOutput++ = ch;
aSkipChars->KeepChar();
}

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

@ -62,7 +62,8 @@ public:
TEXT_IS_SIMPLE_FLOW = 0x100000,
TEXT_INCOMING_WHITESPACE = 0x200000,
TEXT_TRAILING_WHITESPACE = 0x400000,
TEXT_IS_UNCACHED = 0x800000
TEXT_COMPRESSED_LEADING_WHITESPACE = 0x800000,
TEXT_IS_UNCACHED = 0x1000000
};
static PRBool