Not part of the build (well, gfxSkipChars is built but not used): fix bugs in gfxSkipChars, and new textframe fixes for soft hyphens, justification, and caret movement (PeekOffset*) among other things

This commit is contained in:
roc+@cs.cmu.edu 2007-03-28 16:54:32 -07:00
parent 959c2ec0d0
commit 6599844c53
2 changed files with 100 additions and 87 deletions

View File

@ -186,10 +186,14 @@ gfxSkipCharsIterator::IsOriginalCharSkipped(PRInt32* aRunLength) const
// figure out which segment we're in // figure out which segment we're in
PRUint32 currentRunLength = mSkipChars->mList[mListPrefixLength]; PRUint32 currentRunLength = mSkipChars->mList[mListPrefixLength];
NS_ASSERTION(PRUint32(mOriginalStringOffset) >= mListPrefixCharCount,
"Invariant violation");
PRUint32 offsetIntoCurrentRun =
PRUint32(mOriginalStringOffset) - mListPrefixCharCount;
if (mListPrefixLength >= mSkipChars->mListLength - 1 && if (mListPrefixLength >= mSkipChars->mListLength - 1 &&
PRUint32(mOriginalStringOffset) >= mListPrefixCharCount + currentRunLength) { offsetIntoCurrentRun >= currentRunLength) {
NS_ASSERTION(mListPrefixLength == mSkipChars->mListLength - 1 && NS_ASSERTION(mListPrefixLength == mSkipChars->mListLength - 1 &&
PRUint32(mOriginalStringOffset) == mListPrefixCharCount + currentRunLength, offsetIntoCurrentRun == currentRunLength,
"Overran end of string"); "Overran end of string");
// We're at the end of the string // We're at the end of the string
if (aRunLength) { if (aRunLength) {
@ -203,12 +207,13 @@ gfxSkipCharsIterator::IsOriginalCharSkipped(PRInt32* aRunLength) const
// Long runs of all-skipped or all-kept characters will be encoded as // Long runs of all-skipped or all-kept characters will be encoded as
// sequences of 255, 0, 255, 0 etc. Compute the maximum run length by skipping // sequences of 255, 0, 255, 0 etc. Compute the maximum run length by skipping
// over zero entries. // over zero entries.
for (PRUint32 i = mListPrefixLength + 2; i < mSkipChars->mListLength; ++i) { PRUint32 runLength = currentRunLength - offsetIntoCurrentRun;
if (isSkipped == !IsKeepEntry(i) && mSkipChars->mList[i - 1] == 0) { for (PRUint32 i = mListPrefixLength + 2; i < mSkipChars->mListLength; i += 2) {
currentRunLength += mSkipChars->mList[i]; if (mSkipChars->mList[i - 1] != 0)
} break;
runLength += mSkipChars->mList[i];
} }
*aRunLength = currentRunLength; *aRunLength = runLength;
} }
return isSkipped; return isSkipped;
} }

View File

@ -1155,7 +1155,7 @@ GetFontGroupForFrame(nsIFrame* aFrame)
nsCOMPtr<nsIFontMetrics> metrics; nsCOMPtr<nsIFontMetrics> metrics;
devContext->GetMetricsFor(fontStyle->mFont, visibilityStyle->mLangGroup, devContext->GetMetricsFor(fontStyle->mFont, visibilityStyle->mLangGroup,
*getter_AddRefs(metrics)); *getter_AddRefs(metrics));
if (!metrics) if (!metrics)
return nsnull; return nsnull;
nsIFontMetrics* metricsRaw = metrics; nsIFontMetrics* metricsRaw = metrics;
@ -1679,7 +1679,9 @@ static PRBool IsJustifiableCharacter(const nsTextFragment* aFrag, PRInt32 aPos,
PRBool aLangIsCJ) PRBool aLangIsCJ)
{ {
PRUnichar ch = aFrag->CharAt(aPos); PRUnichar ch = aFrag->CharAt(aPos);
if (0x20u == ch || 0xa0u == ch) { if (ch == '\n' || ch == '\t')
return PR_TRUE;
if (ch == ' ') {
// Don't justify spaces that are combined with diacriticals // Don't justify spaces that are combined with diacriticals
if (!aFrag->Is2b()) if (!aFrag->Is2b())
return PR_TRUE; return PR_TRUE;
@ -2085,7 +2087,7 @@ PropertyProvider::GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
// Iterate through the original-string character runs // Iterate through the original-string character runs
nsSkipCharsRunIterator nsSkipCharsRunIterator
run(mStart, nsSkipCharsRunIterator::LENGTH_INCLUDES_SKIPPED, aLength); run(mStart, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aLength);
run.SetSkippedOffset(aStart); run.SetSkippedOffset(aStart);
// We need to visit skipped characters so that we can detect SHY // We need to visit skipped characters so that we can detect SHY
run.SetVisitSkipped(); run.SetVisitSkipped();
@ -2167,6 +2169,14 @@ PropertyProvider::SetupJustificationSpacing()
gfxFloat naturalWidth = gfxFloat naturalWidth =
mTextRun->GetAdvanceWidth(mStart.GetSkippedOffset(), mTextRun->GetAdvanceWidth(mStart.GetSkippedOffset(),
GetSkippedDistance(mStart, realEnd), this); GetSkippedDistance(mStart, realEnd), this);
if (mFrame->GetStateBits() & TEXT_HYPHEN_BREAK) {
gfxTextRun* specialTextRun =
GetSpecialString(GetFontGroup(), gfxFontGroup::STRING_HYPHEN, mTextRun);
if (specialTextRun) {
naturalWidth +=
specialTextRun->GetAdvanceWidth(0, specialTextRun->GetLength(), nsnull);
}
}
gfxFloat totalJustificationSpace = mFrame->GetSize().width - naturalWidth; gfxFloat totalJustificationSpace = mFrame->GetSize().width - naturalWidth;
if (totalJustificationSpace <= 0) { if (totalJustificationSpace <= 0) {
// No space available // No space available
@ -3481,43 +3491,39 @@ SelectionIterator::SelectionIterator(SelectionType* aSelectionBuffer,
PRBool SelectionIterator::GetNextSegment(gfxFloat* aXOffset, PRBool SelectionIterator::GetNextSegment(gfxFloat* aXOffset,
PRUint32* aOffset, PRUint32* aLength, gfxFloat* aHyphenWidth, SelectionType* aType) PRUint32* aOffset, PRUint32* aLength, gfxFloat* aHyphenWidth, SelectionType* aType)
{ {
for (;;) { if (mIterator.GetOriginalOffset() >= mOriginalEnd)
if (mIterator.GetOriginalOffset() >= mOriginalEnd) return PR_FALSE;
return PR_FALSE;
// save offset into transformed string now // save offset into transformed string now
PRUint32 runOffset = mIterator.GetSkippedOffset(); PRUint32 runOffset = mIterator.GetSkippedOffset();
PRInt32 index = mIterator.GetOriginalOffset() - mOriginalStart; PRInt32 index = mIterator.GetOriginalOffset() - mOriginalStart;
SelectionType type = mSelectionBuffer[index]; SelectionType type = mSelectionBuffer[index];
do { do {
++index; ++index;
if (mSelectionBuffer[index] != type) if (mSelectionBuffer[index] != type)
break; break;
} while (mOriginalStart + index < mOriginalEnd); } while (mOriginalStart + index < mOriginalEnd);
mIterator.SetOriginalOffset(index + mOriginalStart); mIterator.SetOriginalOffset(index + mOriginalStart);
// Advance to the next cluster boundary // Advance to the next cluster boundary
while (mIterator.GetOriginalOffset() < mOriginalEnd && while (mIterator.GetOriginalOffset() < mOriginalEnd &&
!mIterator.IsOriginalCharSkipped() && !mIterator.IsOriginalCharSkipped() &&
!mTextRun->IsClusterStart(mIterator.GetSkippedOffset())) { !mTextRun->IsClusterStart(mIterator.GetSkippedOffset())) {
mIterator.AdvanceOriginal(1); mIterator.AdvanceOriginal(1);
}
// Check whether we actually got some non-skipped text in this segment
if (runOffset < mIterator.GetSkippedOffset()) {
*aOffset = runOffset;
*aLength = mIterator.GetSkippedOffset() - runOffset;
*aXOffset = mXOffset;
*aHyphenWidth = 0;
if (mIterator.GetOriginalOffset() == mOriginalEnd &&
(mProvider.GetFrame()->GetStateBits() & TEXT_HYPHEN_BREAK)) {
*aHyphenWidth = mProvider.GetHyphenWidth();
}
*aType = type;
return PR_TRUE;
}
} }
PRBool haveHyphenBreak =
(mProvider.GetFrame()->GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
*aOffset = runOffset;
*aLength = mIterator.GetSkippedOffset() - runOffset;
*aXOffset = mXOffset;
*aHyphenWidth = 0;
if (mIterator.GetOriginalOffset() == mOriginalEnd && haveHyphenBreak) {
*aHyphenWidth = mProvider.GetHyphenWidth();
}
*aType = type;
return PR_TRUE;
} }
// Paints selection backgrounds and text in the correct colors. Also computes // Paints selection backgrounds and text in the correct colors. Also computes
@ -4140,7 +4146,6 @@ nsTextFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset)
PRInt32 startOffset = mContentOffset + (*aOffset < 0 ? mContentLength : *aOffset); PRInt32 startOffset = mContentOffset + (*aOffset < 0 ? mContentLength : *aOffset);
if (!aForward) { if (!aForward) {
*aOffset = 0;
PRInt32 i; PRInt32 i;
for (i = PR_MIN(trimmed.mStart + trimmed.mLength, startOffset) - 1; for (i = PR_MIN(trimmed.mStart + trimmed.mLength, startOffset) - 1;
i >= trimmed.mStart; --i) { i >= trimmed.mStart; --i) {
@ -4151,20 +4156,22 @@ nsTextFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset)
return PR_TRUE; return PR_TRUE;
} }
} }
*aOffset = 0;
} else { } else {
*aOffset = mContentLength;
PRInt32 i; PRInt32 i;
// XXX there's something weird here about end-of-line. Need to test for (i = startOffset + 1; i <= trimmed.mStart + trimmed.mLength; ++i) {
// caret movement through line endings. The old code could return mContentLength,
// but we can't anymore...
for (i = startOffset; i < trimmed.mStart + trimmed.mLength; ++i) {
iter.SetOriginalOffset(i); iter.SetOriginalOffset(i);
if (!iter.IsOriginalCharSkipped() && // XXX we can't necessarily stop at the end of this frame,
mTextRun->IsClusterStart(iter.GetSkippedOffset())) { // but we really have no choice right now. We need to do a deeper
// fix/restructuring of PeekOffsetCharacter
if (i == trimmed.mStart + trimmed.mLength ||
(!iter.IsOriginalCharSkipped() &&
mTextRun->IsClusterStart(iter.GetSkippedOffset()))) {
*aOffset = i - mContentOffset; *aOffset = i - mContentOffset;
return PR_TRUE; return PR_TRUE;
} }
} }
*aOffset = mContentLength;
} }
return PR_FALSE; return PR_FALSE;
@ -4207,19 +4214,20 @@ nsTextFrame::PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool
PRBool stopAfterPunctuation = nsTextTransformer::GetWordSelectStopAtPunctuation(); PRBool stopAfterPunctuation = nsTextTransformer::GetWordSelectStopAtPunctuation();
PRBool stopBeforePunctuation = stopAfterPunctuation && aIsKeyboardSelect; PRBool stopBeforePunctuation = stopAfterPunctuation && aIsKeyboardSelect;
PRInt32 direction = aForward ? 1 : -1; PRInt32 direction = aForward ? 1 : -1;
PRInt32 dirAdjust = aForward ? 0 : -1;
*aOffset = aForward ? mContentLength : 0; *aOffset = aForward ? mContentLength : 0;
if (startOffset + direction < offset || if (startOffset + direction < offset ||
startOffset + direction >= offset + length) startOffset + direction >= offset + length)
return PR_FALSE; return PR_FALSE;
PRInt32 wordLen = nsTextFrameUtils::FindWordBoundary(frag, PRInt32 wordLen = nsTextFrameUtils::FindWordBoundary(frag,
mTextRun, &iter, offset, length, startOffset + direction, mTextRun, &iter, offset, length, startOffset + dirAdjust,
direction, stopBeforePunctuation, stopAfterPunctuation, &isWhitespace); direction, stopBeforePunctuation, stopAfterPunctuation, &isWhitespace);
if (wordLen < 0) if (wordLen < 0)
return PR_FALSE; return PR_FALSE;
if (aWordSelectEatSpace == isWhitespace || !*aSawBeforeType) { if (aWordSelectEatSpace == isWhitespace || !*aSawBeforeType) {
PRInt32 nextWordFirstChar = startOffset + direction*wordLen; PRInt32 nextWordFirstChar = startOffset + dirAdjust + direction*wordLen;
*aOffset = (aForward ? nextWordFirstChar : nextWordFirstChar + 1) - mContentOffset; *aOffset = (aForward ? nextWordFirstChar : nextWordFirstChar + 1) - mContentOffset;
if (aWordSelectEatSpace == isWhitespace) { if (aWordSelectEatSpace == isWhitespace) {
*aSawBeforeType = PR_TRUE; *aSawBeforeType = PR_TRUE;
@ -4230,7 +4238,7 @@ nsTextFrame::PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool
return PR_FALSE; return PR_FALSE;
wordLen = nsTextFrameUtils::FindWordBoundary(frag, wordLen = nsTextFrameUtils::FindWordBoundary(frag,
mTextRun, &iter, offset, length, nextWordFirstChar, mTextRun, &iter, offset, length, nextWordFirstChar,
direction, stopBeforePunctuation, stopAfterPunctuation, &isWhitespace); direction, stopBeforePunctuation, stopAfterPunctuation, &isWhitespace) - 1;
if (wordLen < 0 || if (wordLen < 0 ||
(aWordSelectEatSpace ? !isWhitespace : *aSawBeforeType)) (aWordSelectEatSpace ? !isWhitespace : *aSawBeforeType))
break; break;
@ -4310,39 +4318,6 @@ FindFirstLetterRange(const nsTextFragment* aFrag,
return PR_TRUE; return PR_TRUE;
} }
static void
AddCharToMetrics(gfxFloat aWidth, PropertyProvider* aProvider,
gfxFontGroup::SpecialString aSpecial, gfxTextRun* aTextRun,
gfxTextRun::Metrics* aMetrics, PRBool aTightBoundingBox)
{
gfxRect charRect;
if (aTightBoundingBox) {
gfxTextRun* specialTextRun =
GetSpecialString(aProvider->GetFontGroup(), aSpecial, aTextRun);
gfxTextRun::Metrics charMetrics;
if (specialTextRun) {
charMetrics =
specialTextRun->MeasureText(0, specialTextRun->GetLength(), PR_TRUE, nsnull);
}
charRect = charMetrics.mBoundingBox;
} else {
// assume char does not overflow font metrics!!!
charRect = gfxRect(0, -aMetrics->mAscent, aWidth,
aMetrics->mAscent + aMetrics->mDescent);
}
if (aTextRun->IsRightToLeft()) {
// Char comes before text, so the bounding box is moved to the
// right by aWidth
aMetrics->mBoundingBox.MoveBy(gfxPoint(aWidth, 0));
} else {
// char is moved to the right by mAdvanceWidth
charRect.MoveBy(gfxPoint(aMetrics->mAdvanceWidth, 0));
}
aMetrics->mBoundingBox = aMetrics->mBoundingBox.Union(charRect);
aMetrics->mAdvanceWidth += aWidth;
}
static nsRect ConvertGfxRectOutward(const gfxRect& aRect) static nsRect ConvertGfxRectOutward(const gfxRect& aRect)
{ {
nsRect r; nsRect r;
@ -4553,6 +4528,39 @@ nsTextFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
} }
static void
AddCharToMetrics(gfxFloat aWidth, PropertyProvider* aProvider,
gfxFontGroup::SpecialString aSpecial, gfxTextRun* aTextRun,
gfxTextRun::Metrics* aMetrics, PRBool aTightBoundingBox)
{
gfxRect charRect;
if (aTightBoundingBox) {
gfxTextRun* specialTextRun =
GetSpecialString(aProvider->GetFontGroup(), aSpecial, aTextRun);
gfxTextRun::Metrics charMetrics;
if (specialTextRun) {
charMetrics =
specialTextRun->MeasureText(0, specialTextRun->GetLength(), PR_TRUE, nsnull);
}
charRect = charMetrics.mBoundingBox;
} else {
// assume char does not overflow font metrics!!!
charRect = gfxRect(0, -aMetrics->mAscent, aWidth,
aMetrics->mAscent + aMetrics->mDescent);
}
if (aTextRun->IsRightToLeft()) {
// Char comes before text, so the bounding box is moved to the
// right by aWidth
aMetrics->mBoundingBox.MoveBy(gfxPoint(aWidth, 0));
} else {
// char is moved to the right by mAdvanceWidth
charRect.MoveBy(gfxPoint(aMetrics->mAdvanceWidth, 0));
}
aMetrics->mBoundingBox = aMetrics->mBoundingBox.Union(charRect);
aMetrics->mAdvanceWidth += aWidth;
}
NS_IMETHODIMP NS_IMETHODIMP
nsTextFrame::Reflow(nsPresContext* aPresContext, nsTextFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aMetrics, nsHTMLReflowMetrics& aMetrics,