Commit 8c504de1 authored by rbs%maths.uq.edu.au's avatar rbs%maths.uq.edu.au
Browse files

When resolving the glyphs needed to measure a text that doesn't fit, we have...

When resolving the glyphs needed to measure a text that doesn't fit, we have to ensure that we remain at a word boundary. We may have to back up past the current font, in which case we have to reselect previous fonts to deal with the part of the string being chopped off, bug 241485, r=jshin, sr=tor
parent 2ceeb6b6
Loading
Loading
Loading
Loading
+56 −27
Original line number Diff line number Diff line
@@ -4326,7 +4326,7 @@ struct BreakGetTextDimensionsData {
  // Remember the fonts that we use so that we can deal with
  // line-breaking in-between fonts later. mOffsets[0] is also used
  // to initialize the current offset from where to start measuring
  nsVoidArray* mFonts;   // OUT
  nsVoidArray* mFonts;   // IN/OUT
  nsVoidArray* mOffsets; // IN/OUT
};

@@ -4347,10 +4347,10 @@ do_BreakGetTextDimensions(const nsFontSwitchGTK *aFontSwitch,
  PRInt32 numCharsFit = data->mNumCharsFit;
  nscoord width = data->mWidth;
  PRInt32 start = (PRInt32)(aSubstring - pstr);
  PRInt32 i = start + aSubstringLength;
  PRInt32 end = start + aSubstringLength;
  PRBool allDone = PR_FALSE;

  while (start < i) {
  while (start < end) {
    // Estimate how many characters will fit. Do that by dividing the
    // available space by the average character width
    PRInt32 estimatedNumChars = data->mEstimatedNumChars;
@@ -4370,9 +4370,9 @@ do_BreakGetTextDimensions(const nsFontSwitchGTK *aFontSwitch,

    // Avoid scanning the break array in the case where we think all
    // the text should fit
    if (i <= estimatedBreakOffset) {
    if (end <= estimatedBreakOffset) {
      // Everything should fit
      numChars = i - start;
      numChars = end - start;
    }
    else {
      // Find the nearest place to break that is less than or equal to
@@ -4392,20 +4392,20 @@ do_BreakGetTextDimensions(const nsFontSwitchGTK *aFontSwitch,
      if (start < data->mBreaks[breakIndex]) {
        // The text crosses at least one segment boundary so measure to the
        // break point just before the estimated break offset
        numChars = PR_MIN(data->mBreaks[breakIndex] - start, (PRInt32)aSubstringLength);
        numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
      } 
      else {
        // See whether there is another segment boundary between this one
        // and the end of the text
        if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < i)) {
        if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < end)) {
          ++breakIndex;
          numChars = PR_MIN(data->mBreaks[breakIndex] - start, (PRInt32)aSubstringLength);
          numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
        }
        else {
          NS_ASSERTION(i != data->mBreaks[breakIndex], "don't expect to be at segment boundary");
          NS_ASSERTION(end != data->mBreaks[breakIndex], "don't expect to be at segment boundary");

          // The text is all within the same segment
          numChars = i - start;
          numChars = end - start;

          // Remember we're in the middle of a segment and not between
          // two segments
@@ -4469,17 +4469,15 @@ do_BreakGetTextDimensions(const nsFontSwitchGTK *aFontSwitch,

      // We can't just revert to the previous break state. Find the break
      // index just before the end of the text
      i = start + numChars;
      if (breakIndex == -1) {
      end = start + numChars;
      breakIndex = 0;
        if (data->mBreaks[breakIndex] < i) {
          while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < i)) {
      if (data->mBreaks[breakIndex] < end) {
        while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < end)) {
          ++breakIndex;
        }
      }
      }

      if ((0 == breakIndex) && (i <= data->mBreaks[0])) {
      if ((0 == breakIndex) && (end <= data->mBreaks[0])) {
        // There's no place to back up to, so even though the text doesn't fit
        // return it anyway
        numCharsFit += numChars;
@@ -4508,21 +4506,52 @@ do_BreakGetTextDimensions(const nsFontSwitchGTK *aFontSwitch,
      // all the way back to the first word
      width += twWidth;
      while ((breakIndex >= 0) && (width > data->mAvailWidth)) {
        twWidth = 0;
        start = data->mBreaks[breakIndex];
        numChars = i - start;
        numChars = end - start;
        numCharsFit = start;
        if ((1 == numChars) && (pstr[start] == ' ')) {
          twWidth = data->mSpaceWidth;
          width -= data->mSpaceWidth;
        }
        else if (numChars > 0) {
        else if (pstr + start >= aSubstring) {
          // The entire fragment to chop is within the current font.
          pxWidth = fontGTK->GetWidth(&pstr[start], numChars);
          twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
          width -= NSToCoordRound(float(pxWidth) * data->mP2T);
        }
        else {
          // The fragment that we want to chop extends back into previous fonts.
          // We need to reverse into previous fonts. Fortunately,
          // data->mFonts[] and data->mOffsets[] tell us which fonts are used
          // and when. 
          end = data->mNumCharsFit; // same as aSubstring - pstr
          data->mNumCharsFit = numCharsFit; // has got shorter...
          PRInt32 k = data->mFonts->Count() - 1;
          for ( ; k >= 0 && start < end; --k, end -= numChars) {
            fontGTK = (nsFontGTK*)data->mFonts->ElementAt(k);
            const PRUnichar* ps = (const PRUnichar*)data->mOffsets->ElementAt(k);
            if (ps < pstr + start)
              ps = pstr + start;

            numChars = pstr + end - ps;
            NS_ASSERTION(numChars > 0, "empty string");

            pxWidth = fontGTK->GetWidth(ps, numChars);
            data->mWidth -= NSToCoordRound(float(pxWidth) * data->mP2T);

            // By construction, mFonts[k] is the last font, and
            // mOffsets[k+1] is the last offset.
            data->mFonts->RemoveElementAt(k);
            data->mOffsets->RemoveElementAt(k+1);
          }

          // We are done, update the data now because we won't do it later.
          // The |if (data->mNumCharsFit != numCharsFit)| won't apply below
          data->mFonts->AppendElement(fontGTK);
          data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
          break;
        }

        width -= twWidth;
        numCharsFit = start;
        --breakIndex;
        i = start;
        end = start;
      }
    }

@@ -4530,7 +4559,7 @@ do_BreakGetTextDimensions(const nsFontSwitchGTK *aFontSwitch,
  }

#ifdef DEBUG_rbs
  NS_ASSERTION(allDone || start == i, "internal error");
  NS_ASSERTION(allDone || start == end, "internal error");
  NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error");
#endif /* DEBUG_rbs */

+58 −27
Original line number Diff line number Diff line
@@ -1464,7 +1464,7 @@ struct BreakGetTextDimensionsData {
  // Remember the fonts that we use so that we can deal with
  // line-breaking in-between fonts later. mOffsets[0] is also used
  // to initialize the current offset from where to start measuring
  nsVoidArray* mFonts;   // OUT
  nsVoidArray* mFonts;   // IN/OUT
  nsVoidArray* mOffsets; // IN/OUT
};

@@ -1500,10 +1500,10 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
  PRInt32 numCharsFit = data->mNumCharsFit;
  nscoord width = data->mWidth;
  PRInt32 start = (PRInt32)(aSubstring - pstr);
  PRInt32 i = start + aSubstringLength;
  PRInt32 end = start + aSubstringLength;
  PRBool allDone = PR_FALSE;

  while (start < i) {
  while (start < end) {
    // Estimate how many characters will fit. Do that by dividing the
    // available space by the average character width
    PRInt32 estimatedNumChars = data->mEstimatedNumChars;
@@ -1523,9 +1523,9 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,

    // Avoid scanning the break array in the case where we think all
    // the text should fit
    if (i <= estimatedBreakOffset) {
    if (end <= estimatedBreakOffset) {
      // Everything should fit
      numChars = i - start;
      numChars = end - start;
    }
    else {
      // Find the nearest place to break that is less than or equal to
@@ -1545,20 +1545,20 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
      if (start < data->mBreaks[breakIndex]) {
        // The text crosses at least one segment boundary so measure to the
        // break point just before the estimated break offset
        numChars = PR_MIN(data->mBreaks[breakIndex] - start, (PRInt32)aSubstringLength);
        numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
      } 
      else {
        // See whether there is another segment boundary between this one
        // and the end of the text
        if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < i)) {
        if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < end)) {
          ++breakIndex;
          numChars = PR_MIN(data->mBreaks[breakIndex] - start, (PRInt32)aSubstringLength);
          numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
        }
        else {
          NS_ASSERTION(i != data->mBreaks[breakIndex], "don't expect to be at segment boundary");
          NS_ASSERTION(end != data->mBreaks[breakIndex], "don't expect to be at segment boundary");

          // The text is all within the same segment
          numChars = i - start;
          numChars = end - start;

          // Remember we're in the middle of a segment and not between
          // two segments
@@ -1622,17 +1622,15 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,

      // We can't just revert to the previous break state. Find the break
      // index just before the end of the text
      i = start + numChars;
      if (breakIndex == -1) {
      end = start + numChars;
      breakIndex = 0;
        if (data->mBreaks[breakIndex] < i) {
          while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < i)) {
      if (data->mBreaks[breakIndex] < end) {
        while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < end)) {
          ++breakIndex;
        }
      }
      }

      if ((0 == breakIndex) && (i <= data->mBreaks[0])) {
      if ((0 == breakIndex) && (end <= data->mBreaks[0])) {
        // There's no place to back up to, so even though the text doesn't fit
        // return it anyway
        numCharsFit += numChars;
@@ -1661,21 +1659,54 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
      // all the way back to the first word
      width += twWidth;
      while ((breakIndex >= 0) && (width > data->mAvailWidth)) {
        twWidth = 0;
        start = data->mBreaks[breakIndex];
        numChars = i - start;
        numChars = end - start;
        numCharsFit = start;
        if ((1 == numChars) && (pstr[start] == ' ')) {
          twWidth = data->mSpaceWidth;
          width -= data->mSpaceWidth;
        }
        else if (numChars > 0) {
        else if (pstr + start >= aSubstring) {
          // The entire fragment to chop is within the current font.
          pxWidth = font->GetWidth(data->mPS, &pstr[start], numChars);
          twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
          width -= NSToCoordRound(float(pxWidth) * data->mP2T);
        }
        else {
          // The fragment that we want to chop extends back into previous fonts.
          // We need to reverse into previous fonts. Fortunately,
          // data->mFonts[] and data->mOffsets[] tell us which fonts are used
          // and when. 
          end = data->mNumCharsFit; // same as aSubstring - pstr
          data->mNumCharsFit = numCharsFit; // has got shorter...
          PRInt32 k = data->mFonts->Count() - 1;
          for ( ; k >= 0 && start < end; --k, end -= numChars) {
            font = (nsFontOS2*)data->mFonts->ElementAt(k);
            const PRUnichar* ps = (const PRUnichar*)data->mOffsets->ElementAt(k);
            if (ps < pstr + start)
              ps = pstr + start;

            numChars = pstr + end - ps;
            NS_ASSERTION(numChars > 0, "empty string");

            data->mFont = font;
            data->mSurface->SelectFont(data->mFont);
            pxWidth = font->GetWidth(data->mPS, ps, numChars);
            data->mWidth -= NSToCoordRound(float(pxWidth) * data->mP2T);

            // By construction, mFonts[k] is the last font, and
            // mOffsets[k+1] is the last offset.
            data->mFonts->RemoveElementAt(k);
            data->mOffsets->RemoveElementAt(k+1);
          }

          // We are done, update the data now because we won't do it later.
          // The |if (data->mNumCharsFit != numCharsFit)| won't apply below
          data->mFonts->AppendElement(fontWin);
          data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
          break;
        }

        width -= twWidth;
        numCharsFit = start;
        --breakIndex;
        i = start;
        end = start;
      }
    }

@@ -1683,7 +1714,7 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
  }

#ifdef DEBUG_rbs
  NS_ASSERTION(allDone || start == i, "internal error");
  NS_ASSERTION(allDone || start == end, "internal error");
  NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error");
#endif /* DEBUG_rbs */

+32 −0
Original line number Diff line number Diff line
@@ -4221,6 +4221,32 @@ nsFontWin::GetBoundingMetrics(HDC aDC,
}
#endif

#ifdef DEBUG
static void
VerifyFontHasGlyph(nsFontWin* aFont, const PRUnichar* aString, PRInt32 aLength)
{
  const PRUnichar* curr = aString;
  const PRUnichar* last = aString + aLength;
  PRUint32 ch;
  while (curr < last) {
    if (IS_HIGH_SURROGATE(*curr) && (curr+1) < last && 
        IS_LOW_SURROGATE(*(curr+1))) {
      ch = SURROGATE_TO_UCS4(*curr, *(curr+1));
      curr += 2;
    }
    else {
      ch = *curr;
      curr += 1;
    }
    NS_ASSERTION(aFont->HasGlyph(ch), "internal error");
  }
}
#define DEBUG_VERIFY_FONT_HASGLYPH(font, string, length) \
VerifyFontHasGlyph(font, string, length)
#else
#define DEBUG_VERIFY_FONT_HASGLYPH(font, string, length)
#endif

nsFontWinUnicode::nsFontWinUnicode(LOGFONT* aLogFont, HFONT aFont,
  PRUint16* aCCMap) : nsFontWin(aLogFont, aFont, aCCMap)
{
@@ -4238,6 +4264,7 @@ nsFontWinUnicode::~nsFontWinUnicode()
PRInt32
nsFontWinUnicode::GetWidth(HDC aDC, const PRUnichar* aString, PRUint32 aLength)
{
  DEBUG_VERIFY_FONT_HASGLYPH(this, aString, aLength);
  SIZE size;
  ::GetTextExtentPoint32W(aDC, aString, aLength, &size);
  size.cx -= mOverhangCorrection;
@@ -4248,6 +4275,7 @@ void
nsFontWinUnicode::DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
  const PRUnichar* aString, PRUint32 aLength)
{
  DEBUG_VERIFY_FONT_HASGLYPH(this, aString, aLength);
  // Due to a bug in WIN95 unicode rendering of truetype fonts
  // with underline or strikeout, we need to set a clip rect
  // to prevent the underline and/or strikethru from being rendered
@@ -4288,6 +4316,7 @@ nsFontWinUnicode::GetBoundingMetrics(HDC aDC,
                                     PRUint32           aLength,
                                     nsBoundingMetrics& aBoundingMetrics)
{
  DEBUG_VERIFY_FONT_HASGLYPH(this, aString, aLength);
  aBoundingMetrics.Clear();
  nsAutoChar16Buffer buffer;

@@ -4331,6 +4360,7 @@ PRInt32
nsFontWinNonUnicode::GetWidth(HDC aDC, const PRUnichar* aString,
  PRUint32 aLength)
{
  DEBUG_VERIFY_FONT_HASGLYPH(this, aString, aLength);
  nsAutoCharBuffer buffer;

  PRInt32 destLength = aLength;
@@ -4353,6 +4383,7 @@ void
nsFontWinNonUnicode::DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
  const PRUnichar* aString, PRUint32 aLength)
{
  DEBUG_VERIFY_FONT_HASGLYPH(this, aString, aLength);
  nsAutoCharBuffer buffer;
  PRInt32 destLength = aLength;

@@ -4374,6 +4405,7 @@ nsFontWinNonUnicode::GetBoundingMetrics(HDC aDC,
                                        PRUint32           aLength,
                                        nsBoundingMetrics& aBoundingMetrics)
{
  DEBUG_VERIFY_FONT_HASGLYPH(this, aString, aLength);
  aBoundingMetrics.Clear();
  nsAutoCharBuffer buffer;
  PRInt32 destLength = aLength;
+58 −27
Original line number Diff line number Diff line
@@ -1697,7 +1697,7 @@ struct BreakGetTextDimensionsData {
  // Remember the fonts that we use so that we can deal with
  // line-breaking in-between fonts later. mOffsets[0] is also used
  // to initialize the current offset from where to start measuring
  nsVoidArray* mFonts;   // OUT
  nsVoidArray* mFonts;   // IN/OUT
  nsVoidArray* mOffsets; // IN/OUT

};
@@ -1723,10 +1723,10 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
  PRInt32 numCharsFit = data->mNumCharsFit;
  nscoord width = data->mWidth;
  PRInt32 start = (PRInt32)(aSubstring - pstr);
  PRInt32 i = start + aSubstringLength;
  PRInt32 end = start + aSubstringLength;
  PRBool allDone = PR_FALSE;

  while (start < i) {
  while (start < end) {
    // Estimate how many characters will fit. Do that by dividing the
    // available space by the average character width
    PRInt32 estimatedNumChars = data->mEstimatedNumChars;
@@ -1746,9 +1746,9 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,

    // Avoid scanning the break array in the case where we think all
    // the text should fit
    if (i <= estimatedBreakOffset) {
    if (end <= estimatedBreakOffset) {
      // Everything should fit
      numChars = i - start;
      numChars = end - start;
    }
    else {
      // Find the nearest place to break that is less than or equal to
@@ -1768,20 +1768,20 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
      if (start < data->mBreaks[breakIndex]) {
        // The text crosses at least one segment boundary so measure to the
        // break point just before the estimated break offset
        numChars = PR_MIN(data->mBreaks[breakIndex] - start, (PRInt32)aSubstringLength);
        numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
      } 
      else {
        // See whether there is another segment boundary between this one
        // and the end of the text
        if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < i)) {
        if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < end)) {
          ++breakIndex;
          numChars = PR_MIN(data->mBreaks[breakIndex] - start, (PRInt32)aSubstringLength);
          numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
        }
        else {
          NS_ASSERTION(i != data->mBreaks[breakIndex], "don't expect to be at segment boundary");
          NS_ASSERTION(end != data->mBreaks[breakIndex], "don't expect to be at segment boundary");

          // The text is all within the same segment
          numChars = i - start;
          numChars = end - start;

          // Remember we're in the middle of a segment and not between
          // two segments
@@ -1845,17 +1845,15 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,

      // We can't just revert to the previous break state. Find the break
      // index just before the end of the text
      i = start + numChars;
      if (breakIndex == -1) {
      end = start + numChars;
      breakIndex = 0;
        if (data->mBreaks[breakIndex] < i) {
          while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < i)) {
      if (data->mBreaks[breakIndex] < end) {
        while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < end)) {
          ++breakIndex;
        }
      }
      }

      if ((0 == breakIndex) && (i <= data->mBreaks[0])) {
      if ((0 == breakIndex) && (end <= data->mBreaks[0])) {
        // There's no place to back up to, so even though the text doesn't fit
        // return it anyway
        numCharsFit += numChars;
@@ -1884,21 +1882,54 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
      // all the way back to the first word
      width += twWidth;
      while ((breakIndex >= 0) && (width > data->mAvailWidth)) {
        twWidth = 0;
        start = data->mBreaks[breakIndex];
        numChars = i - start;
        numChars = end - start;
        numCharsFit = start;
        if ((1 == numChars) && (pstr[start] == ' ')) {
          twWidth = data->mSpaceWidth;
          width -= data->mSpaceWidth;
        }
        else if (numChars > 0) {
        else if (pstr + start >= aSubstring) {
          // The entire fragment to chop is within the current font.
          pxWidth = fontWin->GetWidth(data->mDC, &pstr[start], numChars);
          twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
          width -= NSToCoordRound(float(pxWidth) * data->mP2T);
        }
        else {
          // The fragment that we want to chop extends back into previous fonts.
          // We need to reverse into previous fonts. Fortunately,
          // data->mFonts[] and data->mOffsets[] tell us which fonts are used
          // and when. 
          end = data->mNumCharsFit; // same as aSubstring - pstr
          data->mNumCharsFit = numCharsFit; // has got shorter...
          PRInt32 k = data->mFonts->Count() - 1;
          for ( ; k >= 0 && start < end; --k, end -= numChars) {
            fontWin = (nsFontWin*)data->mFonts->ElementAt(k);
            const PRUnichar* ps = (const PRUnichar*)data->mOffsets->ElementAt(k);
            if (ps < pstr + start)
              ps = pstr + start;

            numChars = pstr + end - ps;
            NS_ASSERTION(numChars > 0, "empty string");

            data->mFont = fontWin->mFont;
            ::SelectObject(data->mDC, data->mFont);
            pxWidth = fontWin->GetWidth(data->mDC, ps, numChars);
            data->mWidth -= NSToCoordRound(float(pxWidth) * data->mP2T);

            // By construction, mFonts[k] is the last font, and
            // mOffsets[k+1] is the last offset.
            data->mFonts->RemoveElementAt(k);
            data->mOffsets->RemoveElementAt(k+1);
          }

          // We are done, update the data now because we won't do it later.
          // The |if (data->mNumCharsFit != numCharsFit)| won't apply below
          data->mFonts->AppendElement(fontWin);
          data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
          break;
        }

        width -= twWidth;
        numCharsFit = start;
        --breakIndex;
        i = start;
        end = start;
      }
    }

@@ -1906,7 +1937,7 @@ do_BreakGetTextDimensions(const nsFontSwitch* aFontSwitch,
  }

#ifdef DEBUG_rbs
  NS_ASSERTION(allDone || start == i, "internal error");
  NS_ASSERTION(allDone || start == end, "internal error");
  NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error");
#endif /* DEBUG_rbs */

+56 −27

File changed.

Preview size limit exceeded, changes collapsed.