Index: layout/generic/nsTextFrame.cpp
===================================================================
RCS file: /cvsroot/mozilla/layout/generic/nsTextFrame.cpp,v
retrieving revision 1.513
diff -u -8 -p -r1.513 nsTextFrame.cpp
--- layout/generic/nsTextFrame.cpp	16 Jul 2005 19:58:26 -0000	1.513
+++ layout/generic/nsTextFrame.cpp	12 Aug 2005 02:37:31 -0000
@@ -24,16 +24,18 @@
  *   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>
+ *   Simon Montagu <smontagu@smontagu.org>
+ *   Stephen Blackheath <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
@@ -432,16 +434,30 @@ nsresult nsBlinkTimer::RemoveBlinkFrame(
   if (!blinkTimer) return NS_OK;
   
   blinkTimer->RemoveFrame(aFrame);  
   NS_RELEASE(blinkTimer);
   
   return NS_OK;
 }
 
+
+//----------------------------------------------------------------------
+
+class nsTextFrame_Walker
+{
+  public:
+    nsTextFrame_Walker() {}
+    virtual ~nsTextFrame_Walker() {}
+
+    virtual void visit(nsIRenderingContext& aRenderingContext,
+                      PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsFinal) = 0;
+};
+
+
 //----------------------------------------------------------------------
 
 class nsTextFrame : public nsFrame {
 public:
   nsTextFrame();
 
   // nsIFrame
   NS_IMETHOD Paint(nsPresContext*      aPresContext,
@@ -735,16 +751,25 @@ public:
                             const nscoord* aSpacing = nsnull);
 
   void PaintTextSlowly(nsPresContext* aPresContext,
                        nsIRenderingContext& aRenderingContext,
                        nsStyleContext* aStyleContext,
                        TextPaintStyle& aStyle,
                        nscoord aX, nscoord aY);
 
+  /*!
+   * Walk string for processing the rendering of it.  Visit the specified
+   * NULL-terminated list of nsTextFrame_Walker implementations.
+   */
+  nsIFontMetrics* WalkString(nsIRenderingContext& aRenderingContext,
+                          TextPaintStyle& aTextStyle,
+                          PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
+                          nsTextFrame_Walker* aWalkers[]);
+
   // The passed-in rendering context must have its color set to the color the
   // text should be rendered in.
   void RenderString(nsIRenderingContext& aRenderingContext,
                     nsStyleContext* aStyleContext,
                     nsPresContext* aPresContext,
                     TextPaintStyle& aStyle,
                     PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
                     nscoord aX, nscoord aY,
@@ -830,16 +855,18 @@ public:
                                    PRUint32 &aWordBufLen,
                                    PRUint32 aWordBufSize,
                                    PRBool aCanBreakBefore);
 
 #ifdef DEBUG
   void ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const;
 #endif
 
+  PRBool IsJustifiableCharacter(PRUnichar aChar, PRBool aLangIsCJ);
+
 protected:
   virtual ~nsTextFrame();
 
   nsIFrame* mNextInFlow;
   PRInt32   mContentOffset;
   PRInt32   mContentLength;
   PRInt32   mColumn;
   nscoord   mAscent;
@@ -855,17 +882,16 @@ protected:
                                     PRInt32 textLength,
                                     PRBool isRTLChars,
                                     PRBool isOddLevel,
                                     PRBool isBidiSystem);
 
   void SetOffsets(PRInt32 start, PRInt32 end);
 
   PRBool IsChineseJapaneseLangGroup();
-  PRBool IsJustifiableCharacter(PRUnichar aChar, PRBool aLangIsCJ);
 
   void FillClusterBuffer(nsPresContext *aPresContext, const PRUnichar *aText,
                          PRUint32 aLength, PRUint8 *aClusterStarts);
 };
 
 #ifdef ACCESSIBILITY
 NS_IMETHODIMP nsTextFrame::GetAccessible(nsIAccessible** aAccessible)
 {
@@ -2891,179 +2917,420 @@ nsTextFrame::GetPositionSlowly(nsPresCon
   }
 
   *aNewContent = mContent;
   if (*aNewContent)
     (*aNewContent)->AddRef();
   return NS_OK;
 }
 
-void
-nsTextFrame::RenderString(nsIRenderingContext& aRenderingContext,
-                          nsStyleContext* aStyleContext,
-                          nsPresContext* aPresContext,
-                          TextPaintStyle& aTextStyle,
-                          PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
-                          nscoord aX, nscoord aY,
-                          nscoord aWidth, 
-                          SelectionDetails *aDetails /*=nsnull*/)
+// Returns TRUE if this character could require a complex layout algorithm, that
+// is, we cannot safely assume that the display width of a string containing
+// this character equals the sum of the widths of the component characters.
+// This test needs to be quick.
+// In future, rather than improving this test, the nsIRenderingContext
+// implementations should implement GetWidthArray().
+#define MAY_BE_COMPLEX_LAYOUT(ch) \
+    (ch >= 0x300)
+
+class nsTextFrame_MeasureWalker : public nsTextFrame_Walker
+{
+  private:
+    nsTextFrame::TextPaintStyle& mTextStyle;
+    nscoord* mSpacing;
+
+  public:
+    nsTextFrame_MeasureWalker(nsTextFrame::TextPaintStyle& aTextStyle, nscoord* aSpacing);
+    virtual ~nsTextFrame_MeasureWalker();
+
+    virtual void visit(nsIRenderingContext& aRenderingContext,
+                      PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsFinal);
+};
+
+nsTextFrame_MeasureWalker::nsTextFrame_MeasureWalker(nsTextFrame::TextPaintStyle& aTextStyle, nscoord* aSpacing)
+  : mTextStyle(aTextStyle),
+    mSpacing(aSpacing)
 {
-  PRUnichar buf[TEXT_BUF_SIZE];
-  PRUnichar* bp0 = buf;
+}
 
-  nscoord spacingMem[TEXT_BUF_SIZE];
-  nscoord* sp0 = spacingMem; 
-  
-  PRBool spacing = (0 != aTextStyle.mLetterSpacing) ||
-    (0 != aTextStyle.mWordSpacing) || aTextStyle.mJustifying;
+nsTextFrame_MeasureWalker::~nsTextFrame_MeasureWalker()
+{
+}
 
-  PRBool justifying = aTextStyle.mJustifying &&
-    (aTextStyle.mNumJustifiableCharacterReceivingExtraJot != 0 || aTextStyle.mExtraSpacePerJustifiableCharacter != 0);
+void nsTextFrame_MeasureWalker::visit(nsIRenderingContext& aRenderingContext,
+                  PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool /*aIsFinal*/)
+{
+  // If the font rendering engine can measure the character spacing for this
+  // string for us, then that's best.
+  nscoord width;
+  if (aRenderingContext.GetWidthArray(aBuffer, aLength, width, mSpacing+aOffset) == NS_OK) {
+    PRInt32 i;
+    for (i = 0; i < aLength; i++) {
+      PRUnichar ch = aBuffer[i];
+      PRInt32 spIdx = aOffset+i;
+      if (ch == ' ')
+        mSpacing[spIdx] = mTextStyle.mSpaceWidth + mTextStyle.mWordSpacing;
+      else
+        if (IS_HIGH_SURROGATE(ch) && (i+1) < aLength && IS_LOW_SURROGATE(aBuffer[i+1]))
+          ++i;
+    }
+    return;
+  }
 
-  PRBool isCJ = IsChineseJapaneseLangGroup();
-  PRBool isEndOfLine = aIsEndOfFrame && IsEndOfLine(mState);
+  // Otherwise we do it the inefficient way.
+  PRInt32 i;
+  nscoord lastSegmentWidth = 0;
+  PRInt32 segmentStart = 0;
+  PRBool mayBeComplexLayout = false;
+
+  for (i = 0; i < aLength; i++) {
+    PRUnichar ch = aBuffer[i];
+    PRInt32 spIdx = aOffset+i;
+    if (ch == ' ') {
+      mSpacing[spIdx] = mTextStyle.mSpaceWidth + mTextStyle.mWordSpacing;
+      lastSegmentWidth = 0;
+      segmentStart = i+1;
+    }
+    else {
+      if (IS_HIGH_SURROGATE(ch) && (i+1) < aLength && IS_LOW_SURROGATE(aBuffer[i+1])) {
+        mSpacing[spIdx+1] = 0;
+        ++i;
+        mayBeComplexLayout = true;
+      }
+      else {
+        if (MAY_BE_COMPLEX_LAYOUT(ch))
+          mayBeComplexLayout = true;
+      }
+
+      nscoord newSegmentWidth;
+      aRenderingContext.GetWidth(aBuffer+segmentStart, i-segmentStart+1, newSegmentWidth);
+      nscoord charWidth = newSegmentWidth - lastSegmentWidth;
+      if (charWidth > 0)
+        lastSegmentWidth = newSegmentWidth;
+      else
+        charWidth = 0;
+      mSpacing[spIdx] = charWidth;
 
+      // If we have not seen any characters that require a potentially complex
+      // layout algorithm, then we reset the 'segment' so we measure
+      // character-by-character (so it is slightly more efficient).
+      if (!mayBeComplexLayout) {
+        lastSegmentWidth = 0;
+        segmentStart = i+1;
+      }
+    }
+  }
+}
+
+class nsTextFrame_InflateWalker : public nsTextFrame_Walker
+{
+  private:
+    nsTextFrame* mTextFrame;
+    nsTextFrame::TextPaintStyle& mTextStyle;
+    PRBool mIsCJ;
+    PRBool mIsEndOfLine;
+    PRBool mJustify;
+    nscoord* mSpacing;
+
+  public:
+    nsTextFrame_InflateWalker(
+        nsTextFrame* aTextFrame,
+        nsTextFrame::TextPaintStyle& aTextStyle,
+        PRBool aIsCJ,
+        PRBool aIsEndOfLine,
+        PRBool aJustify,
+        nscoord* aSpacing);
+    virtual ~nsTextFrame_InflateWalker();
+
+    virtual void visit(nsIRenderingContext& aRenderingContext,
+                      PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsFinal);
+};
+
+nsTextFrame_InflateWalker::nsTextFrame_InflateWalker(
+        nsTextFrame* aTextFrame,
+        nsTextFrame::TextPaintStyle& aTextStyle,
+        PRBool aIsCJ,
+        PRBool aIsEndOfLine,
+        PRBool aJustify,
+        nscoord* aSpacing)
+  : mTextFrame(aTextFrame),
+    mTextStyle(aTextStyle),
+    mIsCJ(aIsCJ),
+    mIsEndOfLine(aIsEndOfLine),
+    mJustify(aJustify),
+    mSpacing(aSpacing)
+{
+}
+
+nsTextFrame_InflateWalker::~nsTextFrame_InflateWalker()
+{
+}
+
+void nsTextFrame_InflateWalker::visit(nsIRenderingContext& aRenderingContext,
+                  PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsFinal)
+{
+  PRInt32 i;
+  PRInt32 lastIncreasableIdx = 0;
+
+  for (i = 0; i < aLength; i++) {
+    PRUnichar ch = aBuffer[i];
+    PRInt32 spIdx = i + aOffset;
+    if (IS_HIGH_SURROGATE(ch) && (i+1) < aLength && IS_LOW_SURROGATE(aBuffer[i+1]))
+      ++i;
+    if (mSpacing[spIdx] != 0)
+      lastIncreasableIdx = spIdx;
+    mSpacing[lastIncreasableIdx] += mTextStyle.mLetterSpacing; 
+    if (mJustify && (!mIsEndOfLine || !aIsFinal || (i+1) < aLength)
+        && mTextFrame->IsJustifiableCharacter(ch, mIsCJ)) {
+      mSpacing[lastIncreasableIdx] += mTextStyle.mExtraSpacePerJustifiableCharacter;
+      if ((PRUint32)--mTextStyle.mNumJustifiableCharacterToRender
+            < (PRUint32)mTextStyle.mNumJustifiableCharacterReceivingExtraJot) {
+        mSpacing[lastIncreasableIdx] ++;
+      }
+    }
+  }
+}
+
+class nsTextFrame_RenderWalker : public nsTextFrame_Walker
+{
+  private:
+    nsTextFrame* mTextFrame;
+    nsStyleContext* mStyleContext;
+    nsPresContext* mPresContext;
+    nsTextFrame::TextPaintStyle& mTextStyle;
+    nscolor mTextColor;
+    nscoord* mSpacing;
+    PRBool mDoSpacing;
+    nscoord mX;
+    nscoord mY;
+    nscoord mAscent;
+    SelectionDetails *mDetails;
+
+  public:
+    nsTextFrame_RenderWalker(
+        nsTextFrame* aTextFrame,
+        nsStyleContext* aStyleContext,
+        nsPresContext* aPresContext,
+        nsTextFrame::TextPaintStyle& aTextStyle,
+        nscolor aTextColor,
+        nscoord* aSpacing,
+        PRBool aDoSpacing,
+        nscoord aX,
+        nscoord aY,
+        nscoord aAscent,
+        SelectionDetails *aDetails);
+    virtual ~nsTextFrame_RenderWalker();
+
+    virtual void visit(nsIRenderingContext& aRenderingContext,
+                      PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsFinal);
+};
+
+nsTextFrame_RenderWalker::nsTextFrame_RenderWalker(
+        nsTextFrame* aTextFrame,
+        nsStyleContext* aStyleContext,
+        nsPresContext* aPresContext,
+        nsTextFrame::TextPaintStyle& aTextStyle,
+        nscolor aTextColor,
+        nscoord* aSpacing,
+        PRBool aDoSpacing,
+        nscoord aX,
+        nscoord aY,
+        nscoord aAscent,
+        SelectionDetails *aDetails)
+  : mTextFrame(aTextFrame),
+    mStyleContext(aStyleContext),
+    mPresContext(aPresContext),
+    mTextStyle(aTextStyle),
+    mTextColor(aTextColor),
+    mSpacing(aSpacing),
+    mDoSpacing(aDoSpacing),
+    mX(aX),
+    mY(aY),
+    mAscent(aAscent),
+    mDetails(aDetails)
+{
+}
+
+nsTextFrame_RenderWalker::~nsTextFrame_RenderWalker()
+{
+}
+
+void nsTextFrame_RenderWalker::visit(nsIRenderingContext& aRenderingContext,
+                  PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool /*aIsFinal*/)
+{
+  // Render the text with the color specified first.
+  aRenderingContext.SetColor(mTextColor);
+  // Measure previous run of characters using the previous font
+  aRenderingContext.DrawString(aBuffer, aLength,
+                               mX, mY + mAscent, -1,
+                               mDoSpacing ? mSpacing+aOffset : nsnull);
+
+  PRInt32 width = 0;
+  PRInt32 i;
+  for (i = aOffset; i < aOffset+aLength; i++)
+    width += mSpacing[i];
+
+  mTextFrame->PaintTextDecorations(aRenderingContext, mStyleContext, mPresContext,
+                       mTextStyle, mX, mY, width, aBuffer, mDetails,
+                       aOffset, aLength, mDoSpacing ? mSpacing+aOffset : nsnull);
+
+  mX += width;
+}
+
+/**
+ * Walk string for processing the rendering of it.  Visit the specified
+ * NULL-terminated list of nsTextFrame_Walker implementations.
+ */
+nsIFontMetrics*
+nsTextFrame::WalkString(nsIRenderingContext& aRenderingContext,
+                        TextPaintStyle& aTextStyle,
+                        PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
+                        nsTextFrame_Walker* aWalkers[])
+{
+  PRUnichar buf[TEXT_BUF_SIZE];
+  PRUnichar* bp0 = buf;
   //German 0x00df might expand to "SS", but no need to count it for speed reason
   if (aTextStyle.mSmallCaps) {
-     if (aLength*2 > TEXT_BUF_SIZE) {
+     if (aLength*2 > TEXT_BUF_SIZE)
        bp0 = new PRUnichar[aLength*2];
-       if (spacing)
-         sp0 = new nscoord[aLength*2];
-     }
   }
-  else if (aLength > TEXT_BUF_SIZE) {
+  else if (aLength > TEXT_BUF_SIZE)
     bp0 = new PRUnichar[aLength];
-    if (spacing)
-      sp0 = new nscoord[aLength];
-  }
 
   PRUnichar* bp = bp0;
-  nscoord* sp = sp0;
+  PRUnichar* runStart = bp;
 
   nsIFontMetrics* lastFont = aTextStyle.mLastFont;
-  PRInt32 pendingCount;
-  PRUnichar* runStart = bp;
-  nscoord charWidth, width = 0;
+  // We could do "aRenderingContext.SetFont(lastFont);" here, but we don't need
+  // to because we can assume it was left consistent in the last call to
+  // RenderString().
   PRInt32 countSoFar = 0;
-  // Save the color we want to use for the text, since calls to
-  // PaintTextDecorations in this method will call SetColor() on the rendering
-  // context.
-  nscolor textColor;
-  aRenderingContext.GetColor(textColor);
   for (; --aLength >= 0; aBuffer++) {
     nsIFontMetrics* nextFont;
-    nscoord glyphWidth = 0;
     PRUnichar ch = *aBuffer;
     if (aTextStyle.mSmallCaps &&
         (IsLowerCase(ch) || (ch == kSZLIG))) {
       nextFont = aTextStyle.mSmallFont;
     }
     else {
       nextFont = aTextStyle.mNormalFont;
     }
     if (nextFont != lastFont) {
-      pendingCount = bp - runStart;
+      PRInt32 pendingCount = bp - runStart;
       if (0 != pendingCount) {
-        // Render the text with the color specified first.
-        aRenderingContext.SetColor(textColor);
-        // Measure previous run of characters using the previous font
-        aRenderingContext.DrawString(runStart, pendingCount,
-                                     aX, aY + mAscent, -1,
-                                     spacing ? sp0 : nsnull);
-
-        // Note: use aY not small-y so that decorations are drawn with
-        // respect to the normal-font not the current font.
-        PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
-                             aTextStyle, aX, aY, width, runStart, aDetails,
-                             countSoFar, pendingCount, spacing ? sp0 : nsnull);
+        PRInt32 i;
+        for (i = 0; aWalkers[i] != NULL; i++)
+          aWalkers[i]->visit(aRenderingContext, countSoFar, runStart, pendingCount, PR_FALSE);
         countSoFar += pendingCount;
-        aWidth -= width;
-        aX += width;
         runStart = bp = bp0;
-        sp = sp0;
-        width = 0;
       }
       aRenderingContext.SetFont(nextFont);
       lastFont = nextFont;
     }
     if (nextFont == aTextStyle.mSmallFont) {
       PRUnichar upper_ch;
       // German szlig should be expanded to "SS".
       if (ch == kSZLIG)
         upper_ch = (PRUnichar)'S';
       else
         upper_ch = ToUpperCase(ch);
-      aRenderingContext.GetWidth(upper_ch, charWidth);
-      glyphWidth += charWidth + aTextStyle.mLetterSpacing;
       if (ch == kSZLIG)   //add an additional 'S' here.
       {
         *bp++ = upper_ch;
-        if (spacing)
-          *sp++ = glyphWidth;
-        width += glyphWidth;
       }
       ch = upper_ch;
     }
-    else if (ch == ' ') {
-      glyphWidth += aTextStyle.mSpaceWidth + aTextStyle.mWordSpacing + aTextStyle.mLetterSpacing;
-    }
     else if (IS_HIGH_SURROGATE(ch) && aLength > 0 &&
            IS_LOW_SURROGATE(*(aBuffer+1))) {
-      
-      // special handling for surrogate pair
-      aRenderingContext.GetWidth(aBuffer, 2, charWidth);
-      glyphWidth += charWidth + aTextStyle.mLetterSpacing;
       // copy the surrogate low
       *bp++ = ch;
       --aLength;
       aBuffer++;
       ch = *aBuffer;
-      // put the width into the space buffer
-      width += glyphWidth;
-      if (spacing)
-        *sp++ = glyphWidth;
-      // set the glyphWidth to 0 so the code later will 
-      // set a 0 for one element in space array for surrogate low to 0
-      glyphWidth = 0;
-    }
-    else {
-      aRenderingContext.GetWidth(ch, charWidth);
-      glyphWidth += charWidth + aTextStyle.mLetterSpacing;
-    }
-    if (justifying && (!isEndOfLine || aLength > 0)
-        && IsJustifiableCharacter(ch, isCJ)) {
-      glyphWidth += aTextStyle.mExtraSpacePerJustifiableCharacter;
-      if ((PRUint32)--aTextStyle.mNumJustifiableCharacterToRender
-            < (PRUint32)aTextStyle.mNumJustifiableCharacterReceivingExtraJot) {
-        glyphWidth++;
-      }
     }
     *bp++ = ch;
-    if (spacing)
-      *sp++ = glyphWidth;
-    width += glyphWidth;
   }
-  pendingCount = bp - runStart;
+  PRInt32 pendingCount = bp - runStart;
   if (0 != pendingCount) {
-    // Render the text with the color specified first.
-    aRenderingContext.SetColor(textColor);
-    // Measure previous run of characters using the previous font
-    aRenderingContext.DrawString(runStart, pendingCount, aX, aY + mAscent, -1,
-                                 spacing ? sp0 : nsnull);
-
-    // Note: use aY not small-y so that decorations are drawn with
-    // respect to the normal-font not the current font.
-    PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
-                         aTextStyle, aX, aY, aWidth, runStart, aDetails,
-                         countSoFar, pendingCount, spacing ? sp0 : nsnull);
+    PRInt32 i;
+    for (i = 0; aWalkers[i] != NULL; i++)
+      aWalkers[i]->visit(aRenderingContext, countSoFar, runStart, pendingCount, PR_FALSE);
   }
-  aTextStyle.mLastFont = lastFont;
 
-  if (bp0 != buf) {
+  if (bp0 != buf)
     delete [] bp0;
+
+  return lastFont;
+}
+
+void
+nsTextFrame::RenderString(nsIRenderingContext& aRenderingContext,
+                          nsStyleContext* aStyleContext,
+                          nsPresContext* aPresContext,
+                          TextPaintStyle& aTextStyle,
+                          PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
+                          nscoord aX, nscoord aY,
+                          nscoord aWidth,
+                          SelectionDetails *aDetails /*=nsnull*/)
+{
+  nscoord spacingMem[TEXT_BUF_SIZE];
+  nscoord* sp0 = spacingMem;
+
+  PRBool spacing = (0 != aTextStyle.mLetterSpacing) ||
+    (0 != aTextStyle.mWordSpacing) || aTextStyle.mJustifying;
+
+  PRBool justifying = aTextStyle.mJustifying &&
+    (aTextStyle.mNumJustifiableCharacterReceivingExtraJot != 0 || aTextStyle.mExtraSpacePerJustifiableCharacter != 0);
+
+  //German 0x00df might expand to "SS", but no need to count it for speed reason
+  if (aTextStyle.mSmallCaps) {
+     if (aLength*2 > TEXT_BUF_SIZE)
+       sp0 = new nscoord[aLength*2];
+  }
+  else if (aLength > TEXT_BUF_SIZE)
+    sp0 = new nscoord[aLength];
+
+  {
+    nsTextFrame_Walker* walkers[4];
+    PRInt32 walkersIdx = 0;
+
+    // Measure character spacing.
+    nsTextFrame_MeasureWalker measureWalker(aTextStyle, sp0);
+    walkers[walkersIdx++] = &measureWalker;
+
+    // If justifying, add the necessary spacing to whitespaces.
+    PRBool isCJ = IsChineseJapaneseLangGroup();
+    PRBool isEndOfLine = aIsEndOfFrame && IsEndOfLine(mState);
+    nsTextFrame_InflateWalker inflateWalker(this, aTextStyle, isCJ, isEndOfLine, justifying, sp0);
+
+    if (justifying || spacing)
+      walkers[walkersIdx++] = &inflateWalker;
+    // Render the text.
+
+    // Save the color we want to use for the text, since calls to
+    // PaintTextDecorations in renderWalker will call SetColor() on the rendering
+    // context.
+    nscolor textColor;
+    aRenderingContext.GetColor(textColor);
+
+    nsTextFrame_RenderWalker renderWalker(
+      this,
+      aStyleContext,
+      aPresContext,
+      aTextStyle,
+      textColor,
+      sp0,
+      spacing,
+      aX,
+      aY,
+      mAscent,
+      aDetails);
+    walkers[walkersIdx++] = &renderWalker;
+    walkers[walkersIdx] = NULL;  // Terminate the list of walkers
+    aTextStyle.mLastFont = WalkString(aRenderingContext, aTextStyle, aBuffer, aLength, aIsEndOfFrame, walkers);
   }
   if (sp0 != spacingMem) {
     delete [] sp0;
   }
 }
 
 inline void
 nsTextFrame::MeasureSmallCapsText(const nsHTMLReflowState& aReflowState,
Index: gfx/public/nsIRenderingContext.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/public/nsIRenderingContext.h,v
retrieving revision 1.66
diff -u -8 -p -r1.66 nsIRenderingContext.h
--- gfx/public/nsIRenderingContext.h	8 Aug 2005 19:34:44 -0000	1.66
+++ gfx/public/nsIRenderingContext.h	12 Aug 2005 02:37:47 -0000
@@ -90,17 +90,17 @@ typedef enum
 {
   nsPenMode_kNone   = 0,
   nsPenMode_kInvert = 1
 } nsPenMode;
 
 
 // IID for the nsIRenderingContext interface
 #define NS_IRENDERING_CONTEXT_IID \
- { 0xd91f728b, 0xd7f4, 0x4e19,{0x8b, 0xdd, 0x98, 0x99, 0x3f, 0xdf, 0xec, 0xc6}}
+ { 0x618d85c8, 0x8f29, 0x4c20,{0x96, 0x75, 0x04, 0xf6, 0xe0, 0xe9, 0x78, 0x2c}}
 
 //----------------------------------------------------------------------
 
 // RenderingContext interface
 class nsIRenderingContext : public nsISupports
 {
 public:
   NS_DEFINE_STATIC_IID_ACCESSOR(NS_IRENDERING_CONTEXT_IID)
@@ -582,16 +582,34 @@ public:
    *        font identifier that can be passed into the DrawString()
    *        methods to speed rendering
    * @return error status
    */
   NS_IMETHOD GetWidth(const PRUnichar *aString, PRUint32 aLength,
                       nscoord &aWidth, PRInt32 *aFontID = nsnull) = 0;
 
   /**
+   * Returns an array of widths (in app units) of a Unicode character string
+   * If no font has been set, the results are undefined.
+   * The length of output array data is always aLength elements.  Where the
+   * layout is complex and a character is rendered above or below a previous
+   * character, this is represented as a zero width in the output array.
+   *
+   * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+   *
+   * @param aString string to measure
+   * @param aLength number of characters in string
+   * @param aWidth out parameter for total width
+   * @param aWidthArray out parameter for per-character widths
+   * @return error status
+   */
+  NS_IMETHOD GetWidthArray(const PRUnichar *aString, PRUint32 aLength,
+                           nscoord& aWidth, nscoord* aWidthArray) = 0;
+
+  /**
    * Returns the dimensions of a string, i.e., the overall extent of a string
    * whose rendering may involve switching between different fonts that have
    * different metrics.
    * @param aString string to measure
    * @param aLength number of characters in string
    * @param aFontID an optional out parameter used to store a
    *        font identifier that can be passed into the DrawString()
    *        methods to speed measurements
Index: gfx/src/nsRenderingContextImpl.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/nsRenderingContextImpl.h,v
retrieving revision 1.14
diff -u -8 -p -r1.14 nsRenderingContextImpl.h
--- gfx/src/nsRenderingContextImpl.h	2 May 2005 20:48:29 -0000	1.14
+++ gfx/src/nsRenderingContextImpl.h	12 Aug 2005 02:37:48 -0000
@@ -121,16 +121,34 @@ public:
   NS_IMETHOD GetRangeWidth(const char *aText,
                            PRUint32 aLength,
                            PRUint32 aStart,
                            PRUint32 aEnd,
                            PRUint32 &aWidth);
 
   NS_IMETHOD RenderEPS(const nsRect& aRect, FILE *aDataFile);
 
+  /**
+   * Returns an array of widths (in app units) of a Unicode character string
+   * If no font has been set, the results are undefined.
+   * The length of output array data is always aLength elements.  Where the
+   * layout is complex and a character is rendered above or below a previous
+   * character, this is represented as a zero width in the output array.
+   *
+   * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+   *
+   * @param aString string to measure
+   * @param aLength number of characters in string
+   * @param aWidth out parameter for total width
+   * @param aWidthArray out parameter for per-character widths
+   * @return error status
+   */
+  NS_IMETHOD GetWidthArray(const PRUnichar *aString, PRUint32 aLength,
+                           nscoord& aWidth, nscoord* aWidthArray);
+
 protected:
   virtual ~nsRenderingContextImpl();
 
   /**
    * Determine if a rect's width and height will fit within a specified width and height
    * @param aRect rectangle to test
    * @param aWidth width to determine if the rectangle's width will fit within
    * @param aHeight height to determine if the rectangles height will fit within
Index: gfx/src/cairo/nsCairoRenderingContext.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/cairo/nsCairoRenderingContext.h,v
retrieving revision 1.13
diff -u -8 -p -r1.13 nsCairoRenderingContext.h
--- gfx/src/cairo/nsCairoRenderingContext.h	5 May 2005 21:30:09 -0000	1.13
+++ gfx/src/cairo/nsCairoRenderingContext.h	12 Aug 2005 02:37:51 -0000
@@ -133,16 +133,36 @@ public:
                         PRInt32 *aFontID = nsnull);
     NS_IMETHOD GetWidth(const nsString& aString, nscoord &aWidth,
                         PRInt32 *aFontID = nsnull);
     NS_IMETHOD GetWidth(const char* aString, nscoord& aWidth);
     NS_IMETHOD GetWidth(const char* aString, PRUint32 aLength,
                         nscoord& aWidth);
     NS_IMETHOD GetWidth(const PRUnichar *aString, PRUint32 aLength,
                         nscoord &aWidth, PRInt32 *aFontID = nsnull);
+    /**
+     * Returns an array of widths (in app units) of a Unicode character string
+     * If no font has been set, the results are undefined.
+     * The length of output array data is always aLength elements.  Where the
+     * layout is complex and a character is rendered above or below a previous
+     * character, this is represented as a zero width in the output array.
+     *
+     * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+     *
+     * @param aString string to measure
+     * @param aLength number of characters in string
+     * @param aWidth out parameter for total width
+     * @param aWidthArray out parameter for per-character widths
+     * @return error status
+     */
+    NS_IMETHOD GetWidthArray(const PRUnichar *aString, PRUint32 aLength,
+                             nscoord& aWidth, nscoord* aWidthArray)
+    {
+        return NS_ERROR_NOT_IMPLEMENTED;
+    }
     NS_IMETHOD GetTextDimensions(const char* aString, PRUint32 aLength,
                                  nsTextDimensions& aDimensions);
     NS_IMETHOD GetTextDimensions(const PRUnichar* aString, PRUint32 aLength,
                                  nsTextDimensions& aDimensions, PRInt32* aFontID = nsnull);
 
 #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)
     NS_IMETHOD GetTextDimensions(const char*     aString,
                                  PRInt32           aLength,
@@ -222,16 +242,40 @@ public:
     NS_IMETHOD GetRangeWidth(const char *aText,
                              PRUint32 aLength,
                              PRUint32 aStart,
                              PRUint32 aEnd,
                              PRUint32 &aWidth);
 
     NS_IMETHOD RenderEPS(const nsRect& aRect, FILE *aDataFile);
 
+    /**
+     * Get the exact spacing that the rendering engine will use to render the
+     * specified text in the current font.
+     *
+     * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+     *
+     * @param aString A PRUnichar of the string
+     * @param aLength The length of the aString
+     * @param aFontID an optional parameter used to speed font selection for
+     *        complex unicode strings.
+     * @param aSpacing If the return value is NS_OK, then this method has put the
+     *        width of each character to be rendered into the aSpacing array
+     *        buffer passed by the caller.  The length of data returned is always
+     *        aLength elements.  Where the layout is complex and a character is
+     *        rendered above or below a previous character, this is typically
+     *        represented as a zero width in the array.
+     */
+    NS_IMETHOD GetCharacterSpacing(const PRUnichar *aString, PRUint32 aLength,
+                          PRInt32 aFontID,
+                          nscoord* aSpacing)
+    {
+        return NS_ERROR_NOT_IMPLEMENTED;
+    }
+
     // Cairo specific stuff
 
     cairo_t *GetCairo() { return mCairo; }
 
     nsTransform2D& CurrentTransform();
 
     void TransformCoord (nscoord *aX, nscoord *aY);
 
Index: gfx/src/gtk/nsFontMetricsPango.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/gtk/nsFontMetricsPango.cpp,v
retrieving revision 1.16
diff -u -8 -p -r1.16 nsFontMetricsPango.cpp
--- gfx/src/gtk/nsFontMetricsPango.cpp	26 Jul 2005 00:13:51 -0000	1.16
+++ gfx/src/gtk/nsFontMetricsPango.cpp	12 Aug 2005 02:37:58 -0000
@@ -728,17 +728,17 @@ nsFontMetricsPango::DrawString(const cha
     if (pango_layout_get_line_count(layout) != 1) {
         printf("Warning: more than one line!\n");
     }
     line = pango_layout_get_line(layout, 0);
 
     aContext->UpdateGC();
     GdkGC *gc = aContext->GetGC();
 
-    if (aSpacing && *aSpacing) {
+    if (aSpacing) {
         DrawStringSlowly(aString, NULL, aLength, aSurface->GetDrawable(),
                          gc, x, y, line, aSpacing);
     }
     else {
         gdk_draw_layout_line(aSurface->GetDrawable(), gc,
                              x, y,
                              line);
     }
@@ -786,17 +786,17 @@ nsFontMetricsPango::DrawString(const PRU
     aContext->GetTranMatrix()->TransformCoord(&x, &y);
 
     PangoLayoutLine *line;
     if (pango_layout_get_line_count(layout) != 1) {
         printf("Warning: more than one line!\n");
     }
     line = pango_layout_get_line(layout, 0);
 
-    if (aSpacing && *aSpacing) {
+    if (aSpacing) {
         DrawStringSlowly(text, aString, aLength, aSurface->GetDrawable(),
                          gc, x, y, line, aSpacing);
     }
     else {
         gdk_draw_layout_line(aSurface->GetDrawable(), gc,
                              x, y,
                              line);
     }
@@ -807,16 +807,109 @@ nsFontMetricsPango::DrawString(const PRU
     g_object_unref(gc);
     g_object_unref(layout);
 
     //    printf("DrawString\n");
 
     return rv;
 }
 
+/**
+ * Returns an array of widths (in app units) of a Unicode character string
+ * If no font has been set, the results are undefined.
+ * The length of output array data is always aLength elements.  Where the
+ * layout is complex and a character is rendered above or below a previous
+ * character, this is represented as a zero width in the output array.
+ *
+ * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+ *
+ * @param aString string to measure
+ * @param aLength number of characters in string
+ * @param aWidth out parameter for total width
+ * @param aWidthArray out parameter for per-character widths
+ * @return error status
+ */
+nsresult
+nsFontMetricsPango::GetWidthArray(const PRUnichar *aString, PRUint32 aLength,
+                         nscoord& aWidth, nscoord* aWidthArray)
+{
+    nsresult rv = NS_OK;
+    PangoLayout *layout = pango_layout_new(mPangoContext);
+
+    gchar *text = g_utf16_to_utf8(aString, aLength,
+                                  NULL, NULL, NULL);
+
+    if (!text) {
+#ifdef DEBUG
+        NS_WARNING("nsFontMetricsPango::DrawString invalid unicode to follow");
+        DUMP_PRUNICHAR(aString, aLength)
+#endif
+        rv = NS_ERROR_FAILURE;
+    }
+    else {
+        pango_layout_set_text(layout, text, strlen(text));
+        FixupSpaceWidths(layout, text);
+    
+        PangoLayoutLine *line;
+        if (pango_layout_get_line_count(layout) != 1) {
+            printf("Warning: more than one line!\n");
+        }
+        line = pango_layout_get_line(layout, 0);
+    
+        float app2dev;
+        app2dev = mDeviceContext->AppUnitsToDevUnits();
+    
+        nscoord *utf8spacing = new nscoord[strlen(text)];
+        bzero(utf8spacing, sizeof(nscoord) * strlen(text));
+        bzero(aWidthArray, sizeof(nscoord) * aLength);
+        aWidth = 0;
+
+        gint remainder = 0;
+        for (GSList *tmpList = line->runs; tmpList && tmpList->data;
+             tmpList = tmpList->next) {
+            PangoLayoutRun *layoutRun = (PangoLayoutRun *)tmpList->data;
+            for (gint i=0; i < layoutRun->glyphs->num_glyphs; i++) {
+                gint glyphWidth = layoutRun->glyphs->glyphs[i].geometry.width;
+                if (glyphWidth != 0) {
+                    glyphWidth += remainder;
+                    nscoord glyphCoordWidth = NSToCoordRound((float)glyphWidth / (app2dev * PANGO_SCALE));
+                    utf8spacing[layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset] +=
+                        glyphCoordWidth;
+                    // Scale it pack to pango co-ordinates, and keep the remainder
+                    // for next time so we avoid accumulating errors.
+                    gint scaledBack = glyphCoordWidth * app2dev * PANGO_SCALE;
+                    remainder = glyphWidth - scaledBack;
+                }
+            }
+        }
+
+        // Covert the utf8 spacing array to utf16
+        const gchar *curChar = text, *nextChar;
+        gint i = 0, j = 0;
+        for (PRUint32 curOffset=0; curOffset < aLength;
+             curOffset++, curChar = nextChar) {
+            nextChar = g_utf8_find_next_char(curChar, NULL);
+            j = nextChar - text;
+            while (i < j) {
+                nscoord thisWidth = utf8spacing[i++];
+                aWidthArray[curOffset] += thisWidth;
+                aWidth += thisWidth;
+            }
+    
+            if (IS_HIGH_SURROGATE(aString[curOffset]))
+                curOffset++;
+        }
+        delete[] utf8spacing;
+    }
+
+    g_free(text);
+    g_object_unref(layout);
+    return rv;
+}
+
 #ifdef MOZ_MATHML
 nsresult
 nsFontMetricsPango::GetBoundingMetrics(const char *aString, PRUint32 aLength,
                                        nsBoundingMetrics &aBoundingMetrics,
                                        nsRenderingContextGTK *aContext)
 {
     printf("GetBoundingMetrics (char *)\n");
     return NS_ERROR_FAILURE;
@@ -1369,18 +1462,20 @@ nsFontMetricsPango::DrawStringSlowly(con
         for (gint i=0; i < layoutRun->glyphs->num_glyphs; i++) {
             /* printf("glyph %d offset %d orig width %d new width %d\n", i,
              *        layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset,
              *        layoutRun->glyphs->glyphs[i].geometry.width,
              *       (gint)(utf8spacing[layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset] * app2dev * PANGO_SCALE));
              */
             gint thisOffset = (gint)(utf8spacing[layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset]
                                      * app2dev * PANGO_SCALE);
-            layoutRun->glyphs->glyphs[i].geometry.width = thisOffset;
-            tmpOffset += thisOffset;
+            if (layoutRun->glyphs->glyphs[i].geometry.width != 0) {
+                layoutRun->glyphs->glyphs[i].geometry.width = thisOffset;
+                tmpOffset += thisOffset;
+            }
         }
 
         /*        printf("    rendering at X coord %d\n", aX + offset); */
         offset += tmpOffset;
     }
 
     gdk_draw_layout_line(aDrawable, aGC, aX, aY, aLine);
 
Index: gfx/src/gtk/nsFontMetricsPango.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/gtk/nsFontMetricsPango.h,v
retrieving revision 1.7
diff -u -8 -p -r1.7 nsFontMetricsPango.h
--- gfx/src/gtk/nsFontMetricsPango.h	2 May 2005 20:48:30 -0000	1.7
+++ gfx/src/gtk/nsFontMetricsPango.h	12 Aug 2005 02:37:59 -0000
@@ -137,16 +137,34 @@ public:
     // nsIFontMetricsGTK (calls from the font rendering layer)
     virtual nsresult GetWidth(const char* aString, PRUint32 aLength,
                               nscoord& aWidth,
                               nsRenderingContextGTK *aContext);
     virtual nsresult GetWidth(const PRUnichar* aString, PRUint32 aLength,
                               nscoord& aWidth, PRInt32 *aFontID,
                               nsRenderingContextGTK *aContext);
 
+    /**
+     * Returns an array of widths (in app units) of a Unicode character string
+     * If no font has been set, the results are undefined.
+     * The length of output array data is always aLength elements.  Where the
+     * layout is complex and a character is rendered above or below a previous
+     * character, this is represented as a zero width in the output array.
+     *
+     * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+     *
+     * @param aString string to measure
+     * @param aLength number of characters in string
+     * @param aWidth out parameter for total width
+     * @param aWidthArray out parameter for per-character widths
+     * @return error status
+     */
+    virtual nsresult GetWidthArray(const PRUnichar *aString, PRUint32 aLength,
+                             nscoord& aWidth, nscoord* aWidthArray);
+
     virtual nsresult GetTextDimensions(const PRUnichar* aString,
                                        PRUint32 aLength,
                                        nsTextDimensions& aDimensions, 
                                        PRInt32* aFontID,
                                        nsRenderingContextGTK *aContext);
     virtual nsresult GetTextDimensions(const char*         aString,
                                        PRInt32             aLength,
                                        PRInt32             aAvailWidth,
Index: gfx/src/gtk/nsIFontMetricsGTK.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/gtk/nsIFontMetricsGTK.h,v
retrieving revision 1.5
diff -u -8 -p -r1.5 nsIFontMetricsGTK.h
--- gfx/src/gtk/nsIFontMetricsGTK.h	2 May 2005 20:48:30 -0000	1.5
+++ gfx/src/gtk/nsIFontMetricsGTK.h	12 Aug 2005 02:38:00 -0000
@@ -55,16 +55,37 @@ public:
     virtual nsresult GetWidth(const char* aString, PRUint32 aLength,
                               nscoord& aWidth,
                               nsRenderingContextGTK *aContext) = 0;
     // aCachedOffset will be updated with a new offset.
     virtual nsresult GetWidth(const PRUnichar* aString, PRUint32 aLength,
                               nscoord& aWidth, PRInt32 *aFontID,
                               nsRenderingContextGTK *aContext) = 0;
 
+    /**
+     * Returns an array of widths (in app units) of a Unicode character string
+     * If no font has been set, the results are undefined.
+     * The length of output array data is always aLength elements.  Where the
+     * layout is complex and a character is rendered above or below a previous
+     * character, this is represented as a zero width in the output array.
+     *
+     * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+     *
+     * @param aString string to measure
+     * @param aLength number of characters in string
+     * @param aWidth out parameter for total width
+     * @param aWidthArray out parameter for per-character widths
+     * @return error status
+     */
+    virtual nsresult GetWidthArray(const PRUnichar *aString, PRUint32 aLength,
+                             nscoord& aWidth, nscoord* aWidthArray)
+    {
+        return NS_ERROR_NOT_IMPLEMENTED;
+    }
+
     // Get the text dimensions for this string
     virtual nsresult GetTextDimensions(const PRUnichar* aString,
                                        PRUint32 aLength,
                                        nsTextDimensions& aDimensions, 
                                        PRInt32* aFontID,
                                        nsRenderingContextGTK *aContext) = 0;
     virtual nsresult GetTextDimensions(const char*         aString,
                                        PRInt32             aLength,
Index: gfx/src/gtk/nsRenderingContextGTK.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/gtk/nsRenderingContextGTK.cpp,v
retrieving revision 1.193
diff -u -8 -p -r1.193 nsRenderingContextGTK.cpp
--- gfx/src/gtk/nsRenderingContextGTK.cpp	3 Jun 2005 10:26:23 -0000	1.193
+++ gfx/src/gtk/nsRenderingContextGTK.cpp	12 Aug 2005 02:38:04 -0000
@@ -1198,16 +1198,38 @@ nsRenderingContextGTK::GetWidth(char aC,
     // Check for the very common case of trying to get the width of a single
     // space.
   if ((aC == ' ') && (nsnull != mFontMetrics)) {
     return mFontMetrics->GetSpaceWidth(aWidth);
   }
   return GetWidth(&aC, 1, aWidth);
 }
 
+/**
+ * Returns an array of widths (in app units) of a Unicode character string
+ * If no font has been set, the results are undefined.
+ * The length of output array data is always aLength elements.  Where the
+ * layout is complex and a character is rendered above or below a previous
+ * character, this is represented as a zero width in the output array.
+ *
+ * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+ *
+ * @param aString string to measure
+ * @param aLength number of characters in string
+ * @param aWidth out parameter for total width
+ * @param aWidthArray out parameter for per-character widths
+ * @return error status
+ */
+NS_IMETHODIMP
+nsRenderingContextGTK::GetWidthArray(const PRUnichar *aString, PRUint32 aLength,
+                         nscoord& aWidth, nscoord* aWidthArray)
+{
+    return mFontMetrics->GetWidthArray(aString, aLength, aWidth, aWidthArray);
+}
+
 NS_IMETHODIMP
 nsRenderingContextGTK::GetWidth(PRUnichar aC, nscoord& aWidth,
                                 PRInt32* aFontID)
 {
   return GetWidth(&aC, 1, aWidth, aFontID);
 }
 
 NS_IMETHODIMP
Index: gfx/src/gtk/nsRenderingContextGTK.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/gtk/nsRenderingContextGTK.h,v
retrieving revision 1.80
diff -u -8 -p -r1.80 nsRenderingContextGTK.h
--- gfx/src/gtk/nsRenderingContextGTK.h	2 May 2005 20:48:30 -0000	1.80
+++ gfx/src/gtk/nsRenderingContextGTK.h	12 Aug 2005 02:38:06 -0000
@@ -148,16 +148,33 @@ public:
   NS_IMETHOD GetWidth(PRUnichar aC, nscoord &aWidth,
                       PRInt32 *aFontID);
   NS_IMETHOD GetWidth(const nsString& aString, nscoord &aWidth,
                       PRInt32 *aFontID);
   NS_IMETHOD GetWidth(const char *aString, nscoord &aWidth);
   NS_IMETHOD GetWidth(const char *aString, PRUint32 aLength, nscoord &aWidth);
   NS_IMETHOD GetWidth(const PRUnichar *aString, PRUint32 aLength, nscoord &aWidth,
                       PRInt32 *aFontID);
+  /**
+   * Returns an array of widths (in app units) of a Unicode character string
+   * If no font has been set, the results are undefined.
+   * The length of output array data is always aLength elements.  Where the
+   * layout is complex and a character is rendered above or below a previous
+   * character, this is represented as a zero width in the output array.
+   *
+   * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+   *
+   * @param aString string to measure
+   * @param aLength number of characters in string
+   * @param aWidth out parameter for total width
+   * @param aWidthArray out parameter for per-character widths
+   * @return error status
+   */
+  NS_IMETHOD GetWidthArray(const PRUnichar *aString, PRUint32 aLength,
+                           nscoord& aWidth, nscoord* aWidthArray);
 
   NS_IMETHOD DrawString(const char *aString, PRUint32 aLength,
                         nscoord aX, nscoord aY,
                         const nscoord* aSpacing);
   NS_IMETHOD DrawString(const PRUnichar *aString, PRUint32 aLength,
                         nscoord aX, nscoord aY,
                         PRInt32 aFontID,
                         const nscoord* aSpacing);
Index: gfx/src/shared/nsRenderingContextImpl.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/shared/nsRenderingContextImpl.cpp,v
retrieving revision 3.54
diff -u -8 -p -r3.54 nsRenderingContextImpl.cpp
--- gfx/src/shared/nsRenderingContextImpl.cpp	2 May 2005 20:48:31 -0000	3.54
+++ gfx/src/shared/nsRenderingContextImpl.cpp	12 Aug 2005 02:38:09 -0000
@@ -474,9 +474,30 @@ nsRenderingContextImpl::GetRangeWidth(co
 }
 
 NS_IMETHODIMP
 nsRenderingContextImpl::RenderEPS(const nsRect& aRect, FILE *aDataFile)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+/**
+ * Returns an array of widths (in app units) of a Unicode character string
+ * If no font has been set, the results are undefined.
+ * The length of output array data is always aLength elements.  Where the
+ * layout is complex and a character is rendered above or below a previous
+ * character, this is represented as a zero width in the output array.
+ *
+ * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+ *
+ * @param aString string to measure
+ * @param aLength number of characters in string
+ * @param aWidth out parameter for total width
+ * @param aWidthArray out parameter for per-character widths
+ * @return error status
+ */
+NS_IMETHODIMP
+nsRenderingContextImpl::GetWidthArray(const PRUnichar *aString, PRUint32 aLength,
+                         nscoord& aWidth, nscoord* aWidthArray)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
 
Index: gfx/src/windows/nsFontMetricsWin.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/windows/nsFontMetricsWin.cpp,v
retrieving revision 3.233
diff -u -8 -p -r3.233 nsFontMetricsWin.cpp
--- gfx/src/windows/nsFontMetricsWin.cpp	9 Aug 2005 08:54:15 -0000	3.233
+++ gfx/src/windows/nsFontMetricsWin.cpp	12 Aug 2005 02:38:40 -0000
@@ -293,19 +293,20 @@ typedef nsAutoBuffer<PRUnichar, CHAR_BUF
 
 class nsFontSubset : public nsFontWin
 {
 public:
   nsFontSubset();
   virtual ~nsFontSubset();
 
   virtual PRInt32 GetWidth(HDC aDC, const PRUnichar* aString,
-                           PRUint32 aLength);
+                           PRUint32 aLength, LPINT aWidthArray);
   virtual void DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
-                          const PRUnichar* aString, PRUint32 aLength);
+                          const PRUnichar* aString, PRUint32 aLength,
+                          PRInt32 *aSpacing);
 #ifdef MOZ_MATHML
   virtual nsresult
   GetBoundingMetrics(HDC                aDC, 
                      const PRUnichar*   aString,
                      PRUint32           aLength,
                      nsBoundingMetrics& aBoundingMetrics);
 #ifdef NS_DEBUG
   virtual void DumpFontInfo();
@@ -2293,20 +2294,22 @@ GetBoundingMetricsCommonA(HDC aDC, LONG 
 // unicode code points are identical to font's encoding indices). 
 // Uses 'W'ide functions (ExtTextOutW, GetTextExtentPoint32W).
 class nsFontWinUnicode : public nsFontWin
 {
 public:
   nsFontWinUnicode(LOGFONT* aLogFont, HFONT aFont, PRUint16* aCCMap);
   virtual ~nsFontWinUnicode();
 
-  virtual PRInt32 GetWidth(HDC aDC, const PRUnichar* aString, PRUint32 aLength);
-  virtual void DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
+  virtual PRInt32 GetWidth(HDC aDC, const PRUnichar* aString,
+                           PRUint32 aLength, LPINT aWidthArray);
 
-    const PRUnichar* aString, PRUint32 aLength);
+  virtual void DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
+                          const PRUnichar* aString, PRUint32 aLength,
+                          PRInt32 *aSpacing);
 #ifdef MOZ_MATHML
   virtual nsresult
   GetBoundingMetrics(HDC                aDC,
                      const PRUnichar*   aString,
                      PRUint32           aLength,
                      nsBoundingMetrics& aBoundingMetrics);
 
 #ifdef NS_DEBUG
@@ -2330,19 +2333,22 @@ private:
 // genuine internal glyph indices used by a font)  of non-Unicode
 // fonts claiming to be Unicode fonts.
 class nsFontWinNonUnicode : public nsFontWin
 {
 public:
   nsFontWinNonUnicode(LOGFONT* aLogFont, HFONT aFont, PRUint16* aCCMap, nsIUnicodeEncoder* aConverter, PRBool aIsWide = PR_FALSE);
   virtual ~nsFontWinNonUnicode();
 
-  virtual PRInt32 GetWidth(HDC aDC, const PRUnichar* aString, PRUint32 aLength);
+  virtual PRInt32 GetWidth(HDC aDC, const PRUnichar* aString,
+                           PRUint32 aLength, LPINT aWidthArray);
+
   virtual void DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
-                          const PRUnichar* aString, PRUint32 aLength);
+                          const PRUnichar* aString, PRUint32 aLength,
+                          PRInt32 *aSpacing);
 #ifdef MOZ_MATHML
   virtual nsresult
   GetBoundingMetrics(HDC                aDC,
                      const PRUnichar*   aString,
                      PRUint32           aLength,
                      nsBoundingMetrics& aBoundingMetrics);
 #ifdef NS_DEBUG
   virtual void DumpFontInfo();
@@ -3941,21 +3947,21 @@ nsFontMetricsWin::LocateFont(HDC aDC, PR
   font = FindFont(aDC, aChar);
   aCount = mLoadedFonts.Count(); // update since FindFont() can change it
   NS_ASSERTION(font && mLoadedFonts.IndexOf(font) >= 0,
                "Could not find a font");
   return font;
 }
 
 nsresult
-nsFontMetricsWin::ResolveForwards(HDC                  aDC,
-                                  const PRUnichar*     aString,
-                                  PRUint32             aLength,
-                                  nsFontSwitchCallback aFunc, 
-                                  void*                aData)
+nsFontMetricsWin::Resolve(HDC                  aDC,
+                          const PRUnichar*     aString,
+                          PRUint32             aLength,
+                          nsFontSwitchCallback aFunc, 
+                          void*                aData)
 {
   NS_ASSERTION(aString || !aLength, "invalid call");
   const PRUnichar* firstChar = aString;
   const PRUnichar* currChar = firstChar;
   const PRUnichar* lastChar  = aString + aLength;
   nsFontWin* currFont;
   nsFontWin* nextFont;
   PRInt32 count;
@@ -4028,117 +4034,16 @@ nsFontMetricsWin::ResolveForwards(HDC   
   }
 
   //do it for last part of the string
   fontSwitch.mFontWin = currFont;
   (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
   return NS_OK;
 }
 
-nsresult
-nsFontMetricsWin::ResolveBackwards(HDC                  aDC,
-                                   const PRUnichar*     aString,
-                                   PRUint32             aLength,
-                                   nsFontSwitchCallback aFunc, 
-                                   void*                aData)
-{
-  NS_ASSERTION(aString || !aLength, "invalid call");
-  const PRUnichar* firstChar = aString + aLength - 1;
-  const PRUnichar* lastChar  = aString - 1;
-  const PRUnichar* currChar  = firstChar;
-  nsFontWin* currFont;
-  nsFontWin* nextFont;
-  PRInt32 count;
-  nsFontSwitch fontSwitch;
-
-  if (firstChar == lastChar)
-    return NS_OK;
-
-  count = mLoadedFonts.Count();
-
-  // see if one of our loaded fonts can represent the current character
-  if (IS_LOW_SURROGATE(*currChar) && (currChar-1) > lastChar && IS_HIGH_SURROGATE(*(currChar-1))) {
-    currFont = LocateFont(aDC, SURROGATE_TO_UCS4(*(currChar-1), *currChar), count);
-    currChar -= 2;
-  }
-  else {
-    currFont = LocateFont(aDC, *currChar, count);
-    --currChar;
-  }
-
-  //This if block is meant to speedup the process in normal situation, when
-  //most characters can be found in first font
-  NS_ASSERTION(count > 1, "only one font loaded");
-  // mLoadedFont[0] == font for invisible ignorable characters
-  PRUint32 firstFont = count > 1 ? 1 : 0; 
-  if (currFont == mLoadedFonts[firstFont]) {
-    while (currChar > lastChar && 
-           (currFont->HasGlyph(*currChar)) &&
-           !CCMAP_HAS_CHAR_EXT(gIgnorableCCMapExt, *currChar) &&
-           !IS_RTL_PRESENTATION_FORM(*currChar))
-      --currChar;
-    fontSwitch.mFontWin = currFont;
-    if (!(*aFunc)(&fontSwitch, currChar+1, firstChar - currChar, aData))
-      return NS_OK;
-    if (currChar == lastChar)
-      return NS_OK;
-    // continue with the next substring, re-using the available loaded fonts
-    firstChar = currChar;
-    if (IS_LOW_SURROGATE(*currChar) && (currChar-1) > lastChar && IS_HIGH_SURROGATE(*(currChar-1))) {
-      currFont = LocateFont(aDC, SURROGATE_TO_UCS4(*(currChar-1), *currChar), count);
-      currChar -= 2;
-    }
-    else {
-      currFont = LocateFont(aDC, *currChar, count);
-      --currChar;
-    }
-  }
-
-  // see if we can keep the same font for adjacent characters
-  PRInt32 lastCharLen;
-  PRUint32 codepoint;
-
-  while (currChar > lastChar) {
-    if (IS_LOW_SURROGATE(*currChar) && (currChar-1) > lastChar && IS_HIGH_SURROGATE(*(currChar-1))) {
-      codepoint =  SURROGATE_TO_UCS4(*(currChar-1), *currChar);
-      nextFont = LocateFont(aDC, codepoint, count);
-      lastCharLen = 2;
-    }
-    else {
-      codepoint = *currChar;
-      nextFont = LocateFont(aDC, codepoint, count);
-      lastCharLen = 1;
-    }
-    if (nextFont != currFont ||
-        /* render Hebrew and Arabic presentation forms and right-to-left
-           characters outside the BMP one by one, because Windows doesn't reorder
-           them. 
-       XXX If a future version of Uniscribe corrects this, we will need to make a
-           run-time check and set a rendering hint accordingly */
-        codepoint > 0xFFFF ||
-        IS_RTL_PRESENTATION_FORM(codepoint)) {
-      // We have a substring that can be represented with the same font, and
-      // we are about to switch fonts, it is time to notify our caller.
-      fontSwitch.mFontWin = currFont;
-      if (!(*aFunc)(&fontSwitch, currChar+1, firstChar - currChar, aData))
-        return NS_OK;
-      // continue with the next substring, re-using the available loaded fonts
-      firstChar = currChar;
-      currFont = nextFont; // use the font found earlier for the char
-    }
-    currChar -= lastCharLen;
-  }
-
-  //do it for last part of the string
-  fontSwitch.mFontWin = currFont;
-  (*aFunc)(&fontSwitch, currChar+1, firstChar - currChar, aData);
-
-  return NS_OK;
-}
-
 //
 
 nsFontWin::nsFontWin(LOGFONT* aLogFont, HFONT aFont, PRUint16* aCCMap)
 {
   if (aLogFont) {
     strcpy(mName, aLogFont->lfFaceName);
   }
   mFont = aFont;
@@ -4231,27 +4136,32 @@ nsFontWinUnicode::nsFontWinUnicode(LOGFO
   mUnderlinedOrStrikeOut = aLogFont->lfUnderline || aLogFont->lfStrikeOut;
 }
 
 nsFontWinUnicode::~nsFontWinUnicode()
 {
 }
 
 PRInt32
-nsFontWinUnicode::GetWidth(HDC aDC, const PRUnichar* aString, PRUint32 aLength)
+nsFontWinUnicode::GetWidth(HDC aDC, const PRUnichar* aString,
+                           PRUint32 aLength, LPINT aWidthArray)
 {
   SIZE size;
-  ::GetTextExtentPoint32W(aDC, aString, aLength, &size);
+  if (aWidthArray)
+    ::GetTextExtentExPointW(aDC, aString, aLength, 0, NULL, 
+                            aWidthArray, &size);
+  else
+    ::GetTextExtentPoint32W(aDC, aString, aLength, &size);
   size.cx -= mOverhangCorrection;
   return size.cx;
 }
 
 void
 nsFontWinUnicode::DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
-  const PRUnichar* aString, PRUint32 aLength)
+  const PRUnichar* aString, PRUint32 aLength, PRInt32 *aSpacing)
 {
   // 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
   // too far to the right of the character string.
   // The WIN95 bug causes the underline to be 20-30% longer than
   // it should be. @see bugzilla bug 8322
                            
@@ -4267,23 +4177,23 @@ nsFontWinUnicode::DrawString(HDC aDC, PR
       ::GetTextExtentPoint32W(aDC, aString, aLength, &size);
       size.cx -= mOverhangCorrection;
       RECT clipRect;
       clipRect.top = aY - size.cy;
       clipRect.bottom = aY + size.cy; // Make it plenty large to allow for character descent.
                                       // Not necessary to clip vertically, only horizontally
       clipRect.left = aX;
       clipRect.right = aX + size.cx;
-      NS_ExtTextOutW(aDC, this, aX, aY, ETO_CLIPPED, &clipRect, aString, aLength, NULL); 
+      NS_ExtTextOutW(aDC, this, aX, aY, ETO_CLIPPED, &clipRect, aString, aLength, aSpacing); 
       return;
     }
   } 
 
   // Do normal non-WIN95 text output without clipping
-  NS_ExtTextOutW(aDC, this, aX, aY, 0, NULL, aString, aLength, NULL);  
+  NS_ExtTextOutW(aDC, this, aX, aY, 0, NULL, aString, aLength, aSpacing);  
 }
 
 #ifdef MOZ_MATHML
 nsresult
 nsFontWinUnicode::GetBoundingMetrics(HDC                aDC, 
                                      const PRUnichar*   aString,
                                      PRUint32           aLength,
                                      nsBoundingMetrics& aBoundingMetrics)
@@ -4324,52 +4234,62 @@ nsFontWinNonUnicode::nsFontWinNonUnicode
 }
 
 nsFontWinNonUnicode::~nsFontWinNonUnicode()
 {
 }
 
 PRInt32
 nsFontWinNonUnicode::GetWidth(HDC aDC, const PRUnichar* aString,
-  PRUint32 aLength)
+  PRUint32 aLength, LPINT aWidthArray)
 {
   nsAutoCharBuffer buffer;
 
   PRInt32 destLength = aLength;
   if (NS_FAILED(ConvertUnicodeToGlyph(aString, aLength, destLength, 
                 mConverter, mIsWide, buffer))) {
     return 0;
   }
 
   SIZE size;
   if (!mIsWide)
-    ::GetTextExtentPoint32A(aDC, buffer.get(), destLength, &size);
+    if (aWidthArray)
+      ::GetTextExtentExPointA(aDC, buffer.get(), destLength, 0, NULL, 
+                              aWidthArray, &size);
+    else
+      ::GetTextExtentPoint32A(aDC, buffer.get(), destLength, &size);
   else
-    ::GetTextExtentPoint32W(aDC, (const PRUnichar*) buffer.get(), destLength / 2, &size);
+    if (aWidthArray)
+      ::GetTextExtentExPointW(aDC, (const PRUnichar*) buffer.get(),
+                              destLength / 2, 0, NULL, 
+                              aWidthArray, &size);
+    else
+      ::GetTextExtentPoint32W(aDC, (const PRUnichar*) buffer.get(),
+                              destLength / 2, &size);
   size.cx -= mOverhangCorrection;
 
   return size.cx;
 }
 
 void
 nsFontWinNonUnicode::DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
-  const PRUnichar* aString, PRUint32 aLength)
+  const PRUnichar* aString, PRUint32 aLength, PRInt32 *aSpacing)
 {
   nsAutoCharBuffer buffer;
   PRInt32 destLength = aLength;
 
   if (NS_FAILED(ConvertUnicodeToGlyph(aString, aLength, destLength, 
                 mConverter, mIsWide, buffer))) {
     return;
   }
 
   if (!mIsWide)
-    NS_ExtTextOutA(aDC, this, aX, aY, 0, NULL, buffer.get(), aLength, NULL);
+    NS_ExtTextOutA(aDC, this, aX, aY, 0, NULL, buffer.get(), aLength, aSpacing);
   else 
-    NS_ExtTextOutW(aDC, this, aX, aY, 0, NULL, (const PRUnichar*) buffer.get(), destLength / 2, NULL);
+    NS_ExtTextOutW(aDC, this, aX, aY, 0, NULL, (const PRUnichar*) buffer.get(), destLength / 2, aSpacing);
 }
 
 #ifdef MOZ_MATHML
 nsresult
 nsFontWinNonUnicode::GetBoundingMetrics(HDC                aDC, 
                                         const PRUnichar*   aString,
                                         PRUint32           aLength,
                                         nsBoundingMetrics& aBoundingMetrics)
@@ -4495,42 +4415,46 @@ SubstituteChars(PRBool              aDis
     result[i] = NS_REPLACEMENT_CHAR;
   }
   *aCount = aLength;
   return NS_OK;
 }
 
 PRInt32
 nsFontWinSubstitute::GetWidth(HDC aDC, const PRUnichar* aString,
-  PRUint32 aLength)
+  PRUint32 aLength, LPINT aWidthArray)
 {
   if (mIsForIgnorable)
     return 0;
   nsAutoChar16Buffer buffer;
   nsresult rv = SubstituteChars(PR_FALSE, aString, aLength, buffer, &aLength);
   if (NS_FAILED(rv) || !aLength) return 0;
 
   SIZE size;
-  ::GetTextExtentPoint32W(aDC, buffer.get(), aLength, &size);
+  if (aWidthArray)
+    ::GetTextExtentExPointW(aDC, buffer.get(), aLength, 0, NULL, 
+                            aWidthArray, &size);
+  else
+    ::GetTextExtentPoint32W(aDC, buffer.get(), aLength, &size);
   size.cx -= mOverhangCorrection;
 
   return size.cx;
 }
 
 void
 nsFontWinSubstitute::DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
-  const PRUnichar* aString, PRUint32 aLength)
+  const PRUnichar* aString, PRUint32 aLength, PRInt32 *aSpacing)
 {
   if (mIsForIgnorable)
     return;
   nsAutoChar16Buffer buffer;
   nsresult rv = SubstituteChars(PR_FALSE, aString, aLength, buffer, &aLength);
   if (NS_FAILED(rv) || !aLength) return;
 
-  NS_ExtTextOutW(aDC, this, aX, aY, 0, NULL, buffer.get(), aLength, NULL);
+  NS_ExtTextOutW(aDC, this, aX, aY, 0, NULL, buffer.get(), aLength, aSpacing);
 }
 
 #ifdef MOZ_MATHML
 nsresult
 nsFontWinSubstitute::GetBoundingMetrics(HDC                aDC, 
                                         const PRUnichar*   aString,
                                         PRUint32           aLength,
                                         nsBoundingMetrics& aBoundingMetrics)
@@ -4686,37 +4610,42 @@ nsFontSubset::Convert(const PRUnichar* a
   if (!nb || !aResult.EnsureElemCapacity(nb)) return;
   char* buf = aResult.get();
   // Convert the Unicode string to ANSI
   *aResultLength = WideCharToMultiByte(mCodePage, 0, aString, aLength,
                                        buf, nb, nsnull, nsnull);
 }
 
 PRInt32
-nsFontSubset::GetWidth(HDC aDC, const PRUnichar* aString, PRUint32 aLength)
+nsFontSubset::GetWidth(HDC aDC, const PRUnichar* aString,
+                       PRUint32 aLength, LPINT aWidthArray)
 {
   nsAutoCharBuffer buffer;
   Convert(aString, aLength, buffer, &aLength);
   if (aLength) {
     SIZE size;
-    ::GetTextExtentPoint32A(aDC, buffer.get(), aLength, &size);
+    if (aWidthArray)
+      ::GetTextExtentExPointA(aDC, buffer.get(), aLength, 0, NULL, 
+                              aWidthArray, &size);
+    else
+      ::GetTextExtentPoint32A(aDC, buffer.get(), aLength, &size);
     size.cx -= mOverhangCorrection;
     return size.cx;
   }
   return 0;
 }
 
 void
 nsFontSubset::DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
-  const PRUnichar* aString, PRUint32 aLength)
+  const PRUnichar* aString, PRUint32 aLength, PRInt32 *aSpacing)
 {
   nsAutoCharBuffer buffer;
   Convert(aString, aLength, buffer, &aLength);
   if (aLength) {
-    NS_ExtTextOutA(aDC, this, aX, aY, 0, NULL, buffer.get(), aLength, NULL);
+    NS_ExtTextOutA(aDC, this, aX, aY, 0, NULL, buffer.get(), aLength, aSpacing);
   }
 }
 
 #ifdef MOZ_MATHML
 nsresult
 nsFontSubset::GetBoundingMetrics(HDC                aDC, 
                                  const PRUnichar*   aString,
                                  PRUint32           aLength,
@@ -4753,27 +4682,27 @@ nsFontSubsetSubstitute::~nsFontSubsetSub
 
 PRInt32
 nsFontSubsetSubstitute::GetWidth(HDC aDC, const PRUnichar* aString,
   PRUint32 aLength)
 {
   if (mIsForIgnorable)
     return 0;
 
-  return nsFontSubset::GetWidth(aDC, aString, aLength);
+  return nsFontSubset::GetWidth(aDC, aString, aLength, NULL);
 }
 
 void
 nsFontSubsetSubstitute::DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
   const PRUnichar* aString, PRUint32 aLength)
 {
   if (mIsForIgnorable)
     return;
 
-  nsFontSubset::DrawString(aDC, aX, aY, aString, aLength);
+  nsFontSubset::DrawString(aDC, aX, aY, aString, aLength, NULL);
 }
 
 #ifdef MOZ_MATHML
 nsresult
 nsFontSubsetSubstitute::GetBoundingMetrics(HDC                aDC, 
                                         const PRUnichar*   aString,
                                         PRUint32           aLength,
                                         nsBoundingMetrics& aBoundingMetrics)
@@ -4901,25 +4830,26 @@ nsFontWinA::GetSubsets(HDC aDC)
       }
     }
   }
 
   return 1;
 }
 
 PRInt32
-nsFontWinA::GetWidth(HDC aDC, const PRUnichar* aString, PRUint32 aLength)
+nsFontWinA::GetWidth(HDC aDC, const PRUnichar* aString,
+                     PRUint32 aLength, LPINT aWidthArray)
 {
   NS_ERROR("must call nsFontSubset's GetWidth");
   return 0;
 }
 
 void
 nsFontWinA::DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
-  const PRUnichar* aString, PRUint32 aLength)
+  const PRUnichar* aString, PRUint32 aLength, PRInt32 *aSpacing)
 {
   NS_ERROR("must call nsFontSubset's DrawString");
 }
 
 nsFontSubset* 
 nsFontWinA::FindSubset(HDC aDC, PRUnichar aChar, nsFontMetricsWinA* aFontMetrics)
 {
   nsFontSubset **subsetPtr = mSubsets;
@@ -5347,21 +5277,21 @@ nsFontMetricsWinA::LocateFontSubset(HDC 
 
   fontSubset = (nsFontSubset*)FindFont(aDC, aChar);
   aFont = nsnull;   //this simply means we don't know and don't bother to figure out
   aCount = mLoadedFonts.Count(); // update since FindFont() can change it
   return fontSubset;
 }
 
 nsresult
-nsFontMetricsWinA::ResolveForwards(HDC                  aDC,
-                                   const PRUnichar*     aString,
-                                   PRUint32             aLength,
-                                   nsFontSwitchCallback aFunc, 
-                                   void*                aData)
+nsFontMetricsWinA::Resolve(HDC                  aDC,
+                           const PRUnichar*     aString,
+                           PRUint32             aLength,
+                           nsFontSwitchCallback aFunc, 
+                           void*                aData)
 {
   NS_ASSERTION(aString || !aLength, "invalid call");
   const PRUnichar* firstChar = aString;
   const PRUnichar* lastChar  = aString + aLength;
   const PRUnichar* currChar  = firstChar;
   nsFontSubset* currSubset;
   nsFontSubset* nextSubset;
   nsFontWinA* currFont;
@@ -5413,61 +5343,16 @@ nsFontMetricsWinA::ResolveForwards(HDC  
   //do it for last part of the string
   fontSwitch.mFontWin = currSubset;
   NS_ASSERTION(currSubset, "invalid font here. ");
   (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
 
   return NS_OK;
 }
 
-nsresult
-nsFontMetricsWinA::ResolveBackwards(HDC                  aDC,
-                                    const PRUnichar*     aString,
-                                    PRUint32             aLength,
-                                    nsFontSwitchCallback aFunc, 
-                                    void*                aData)
-{
-  NS_ASSERTION(aString || !aLength, "invalid call");
-  const PRUnichar* firstChar = aString + aLength - 1;
-  const PRUnichar* lastChar  = aString - 1;
-  const PRUnichar* currChar  = firstChar;
-  nsFontSubset* currSubset;
-  nsFontSubset* nextSubset;
-  nsFontWinA* currFont;
-  PRInt32 count;
-  nsFontSwitch fontSwitch;
-
-  if (firstChar == lastChar)
-    return NS_OK;
-
-  // see if one of our loaded fonts can represent the current character
-  count = mLoadedFonts.Count();
-  currSubset = LocateFontSubset(aDC, *currChar, count, currFont);
-
-  while (--currChar < lastChar) {
-    nextSubset = LocateFontSubset(aDC, *currChar, count, currFont);
-    if (nextSubset != currSubset) {
-      // We have a substring that can be represented with the same font, and
-      // we are about to switch fonts, it is time to notify our caller.
-      fontSwitch.mFontWin = currSubset;
-      if (!(*aFunc)(&fontSwitch, firstChar, firstChar - currChar, aData))
-        return NS_OK;
-      // continue with the next substring, re-using the available loaded fonts
-      firstChar = currChar;
-      currSubset = nextSubset; // use the font found earlier for the char
-    }
-  }
-
-  //do it for last part of the string
-  fontSwitch.mFontWin = currSubset;
-  (*aFunc)(&fontSwitch, firstChar, firstChar - currChar, aData);
-
-  return NS_OK;
-}
-
 // The Font Enumerator
 
 nsFontEnumeratorWin::nsFontEnumeratorWin()
 {
 }
 
 NS_IMPL_ISUPPORTS1(nsFontEnumeratorWin,nsIFontEnumerator)
 
Index: gfx/src/windows/nsFontMetricsWin.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/windows/nsFontMetricsWin.h,v
retrieving revision 3.59
diff -u -8 -p -r3.59 nsFontMetricsWin.h
--- gfx/src/windows/nsFontMetricsWin.h	8 Aug 2005 15:11:14 -0000	3.59
+++ gfx/src/windows/nsFontMetricsWin.h	12 Aug 2005 02:38:43 -0000
@@ -97,23 +97,23 @@ public:
 
   nsFontWin(LOGFONT* aLogFont, HFONT aFont, PRUint16* aCCMap);
   virtual ~nsFontWin();
 
   virtual PRInt32 GetWidth(HDC aDC, const char* aString,
                            PRUint32 aLength);
 
   virtual PRInt32 GetWidth(HDC aDC, const PRUnichar* aString,
-                           PRUint32 aLength) = 0;
+                           PRUint32 aLength, LPINT aWidthArray) = 0;
 
   virtual void DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
                           const char* aString, PRUint32 aLength, INT* aDx0);
 
   virtual void DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
-                          const PRUnichar* aString, PRUint32 aLength) = 0;
+                          const PRUnichar* aString, PRUint32 aLength, PRInt32 *aSpacing) {}
 
   virtual PRBool HasGlyph(PRUint32 ch) {return CCMAP_HAS_CHAR_EXT(mCCMap, ch);};
 #ifdef MOZ_MATHML
   virtual nsresult
   GetBoundingMetrics(HDC                aDC, 
                      const char*        aString,
                      PRUint32           aLength,
                      nsBoundingMetrics& aBoundingMetrics);
@@ -156,19 +156,22 @@ public:
   nsFontWinSubstitute(LOGFONT* aLogFont, HFONT aFont, PRUint16* aCCMap, PRBool aDisplayUnicode);
   nsFontWinSubstitute(PRUint16* aCCMap);
   virtual ~nsFontWinSubstitute();
 
   virtual PRBool HasGlyph(PRUint32 ch) {
 	  return mIsForIgnorable ? CCMAP_HAS_CHAR_EXT(mCCMap, ch) :
 		  IS_IN_BMP(ch) && IS_REPRESENTABLE(mRepresentableCharMap, ch);};
   virtual void SetRepresentable(PRUint32 ch) { if (IS_IN_BMP(ch)) SET_REPRESENTABLE(mRepresentableCharMap, ch); };
-  virtual PRInt32 GetWidth(HDC aDC, const PRUnichar* aString, PRUint32 aLength);
+  virtual PRInt32 GetWidth(HDC aDC, const PRUnichar* aString,
+                           PRUint32 aLength, LPINT aWidthArray);
+
   virtual void DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
-                          const PRUnichar* aString, PRUint32 aLength);
+                          const PRUnichar* aString, PRUint32 aLength,
+                          PRInt32 *aSpacing);
 #ifdef MOZ_MATHML
   virtual nsresult
   GetBoundingMetrics(HDC                aDC,
                      const PRUnichar*   aString,
                      PRUint32           aLength,
                      nsBoundingMetrics& aBoundingMetrics);
 #ifdef NS_DEBUG
   virtual void DumpFontInfo();
@@ -180,22 +183,22 @@ private:
 
   //We need to have a easily operatable charmap for substitute font
   PRUint32 mRepresentableCharMap[UCS2_MAP_LEN];
 };
 
 /**
  * nsFontSwitchCallback
  *
- * Font-switching callback function. Used by ResolveForwards() and
- * ResolveBackwards(). aFontSwitch points to a structure that gives
- * details about the current font needed to represent the current
- * substring. In particular, this struct contains a handler to the font
- * and some metrics of the font. These metrics may be different from
- * the metrics obtained via nsIFontMetrics.
+ * Font-switching callback function. Used by Resolve(). aFontSwitch
+ * points to a structure that gives details about the current font
+ * needed to represent the current substring. In particular, this
+ * struct contains a handler to the font and some metrics of the
+ * font. These metrics may be different from the metrics obtained
+ * via nsIFontMetrics.
  * Return PR_FALSE to stop the resolution of the remaining substrings.
  */
 
 struct nsFontSwitch {
   // Simple wrapper on top of nsFontWin for the moment
   // Could hold other attributes of the font
   nsFontWin* mFontWin;
 };
@@ -240,28 +243,21 @@ public:
   NS_IMETHOD  GetMaxDescent(nscoord &aDescent);
   NS_IMETHOD  GetMaxAdvance(nscoord &aAdvance);
   NS_IMETHOD  GetLangGroup(nsIAtom** aLangGroup);
   NS_IMETHOD  GetFontHandle(nsFontHandle &aHandle);
   NS_IMETHOD  GetAveCharWidth(nscoord &aAveCharWidth);
   NS_IMETHOD  GetSpaceWidth(nscoord &aSpaceWidth);
 
   virtual nsresult
-  ResolveForwards(HDC                  aDC,
-                  const PRUnichar*     aString,
-                  PRUint32             aLength,
-                  nsFontSwitchCallback aFunc, 
-                  void*                aData);
-
-  virtual nsresult
-  ResolveBackwards(HDC                  aDC,
-                   const PRUnichar*     aString,
-                   PRUint32             aLength,
-                   nsFontSwitchCallback aFunc, 
-                   void*                aData);
+  Resolve(HDC                  aDC,
+          const PRUnichar*     aString,
+          PRUint32             aLength,
+          nsFontSwitchCallback aFunc, 
+          void*                aData);
 
   nsFontWin*         FindFont(HDC aDC, PRUint32 aChar);
   virtual nsFontWin* FindUserDefinedFont(HDC aDC, PRUint32 aChar);
   virtual nsFontWin* FindLocalFont(HDC aDC, PRUint32 aChar);
   virtual nsFontWin* FindGenericFont(HDC aDC, PRUint32 aChar);
   virtual nsFontWin* FindPrefFont(HDC aDC, PRUint32 aChar);
   virtual nsFontWin* FindGlobalFont(HDC aDC, PRUint32 aChar);
   virtual nsFontWin* FindSubstituteFont(HDC aDC, PRUint32 aChar);
@@ -381,19 +377,21 @@ class nsFontMetricsWinA;
 
 class nsFontWinA : public nsFontWin
 {
 public:
   nsFontWinA(LOGFONT* aLogFont, HFONT aFont, PRUint16* aCCMap);
   virtual ~nsFontWinA();
 
   virtual PRInt32 GetWidth(HDC aDC, const PRUnichar* aString,
-                           PRUint32 aLength);
+                           PRUint32 aLength, LPINT aWidthArray);
+
   virtual void DrawString(HDC aDC, PRInt32 aX, PRInt32 aY,
-                          const PRUnichar* aString, PRUint32 aLength);
+                          const PRUnichar* aString, PRUint32 aLength,
+                          PRInt32 *aSpacing);
   virtual nsFontSubset* FindSubset(HDC aDC, PRUnichar aChar, nsFontMetricsWinA* aFontMetrics);
 #ifdef MOZ_MATHML
   virtual nsresult
   GetBoundingMetrics(HDC                aDC, 
                      const PRUnichar*   aString,
                      PRUint32           aLength,
                      nsBoundingMetrics& aBoundingMetrics);
 #ifdef NS_DEBUG
@@ -438,26 +436,19 @@ public:
 
   virtual nsFontWin* LoadFont(HDC aDC, const nsString& aName, PRBool aNameQuirks=PR_FALSE);
   virtual nsFontWin* LoadGlobalFont(HDC aDC, nsGlobalFont* aGlobalFontItem);
   virtual nsFontWin* LoadSubstituteFont(HDC aDC, const nsString& aName);
 
   virtual nsFontWin* GetFontFor(HFONT aHFONT);
 
   virtual nsresult
-  ResolveForwards(HDC                  aDC,
-                  const PRUnichar*     aString,
-                  PRUint32             aLength,
-                  nsFontSwitchCallback aFunc, 
-                  void*                aData);
-
-  virtual nsresult
-  ResolveBackwards(HDC                  aDC,
-                   const PRUnichar*     aString,
-                   PRUint32             aLength,
-                   nsFontSwitchCallback aFunc, 
-                   void*                aData);
+  Resolve(HDC                  aDC,
+          const PRUnichar*     aString,
+          PRUint32             aLength,
+          nsFontSwitchCallback aFunc, 
+          void*                aData);
 
 protected:
   nsFontSubset* LocateFontSubset(HDC aDC, PRUnichar aChar, PRInt32 & aCount, nsFontWinA*& aFont);
 };
 
 #endif /* nsFontMetricsWin_h__ */
Index: gfx/src/windows/nsRenderingContextWin.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/windows/nsRenderingContextWin.cpp,v
retrieving revision 3.177
diff -u -8 -p -r3.177 nsRenderingContextWin.cpp
--- gfx/src/windows/nsRenderingContextWin.cpp	8 Jun 2005 16:48:42 -0000	3.177
+++ gfx/src/windows/nsRenderingContextWin.cpp	12 Aug 2005 02:38:56 -0000
@@ -638,28 +638,29 @@ nsRenderingContextWin :: GetDrawingSurfa
 }
 
 NS_IMETHODIMP
 nsRenderingContextWin :: GetHints(PRUint32& aResult)
 {
   PRUint32 result = 0;
   
 #ifndef WINCE
-  result |= NS_RENDERING_HINT_FAST_MEASURE;
+  result |= (NS_RENDERING_HINT_FAST_MEASURE | 
+             NS_RENDERING_HINT_REORDER_SPACED_TEXT);
 #endif
 
   if (gIsWIN95)
     result |= NS_RENDERING_HINT_FAST_8BIT_TEXT;
 
   if (NOT_SETUP == gBidiInfo) {
     InitBidiInfo();
   }
 #ifndef WINCE
   if (GCP_REORDER == (gBidiInfo & GCP_REORDER) )
-    result |= NS_RENDERING_HINT_BIDI_REORDERING;
+    result |= NS_RENDERING_HINT_BIDI_REORDERING|NS_RENDERING_HINT_REORDER_SPACED_TEXT;
   if (GCP_GLYPHSHAPE == (gBidiInfo & GCP_GLYPHSHAPE) )
     result |= NS_RENDERING_HINT_ARABIC_SHAPING;
 #endif
   
   aResult = result;
 
   return NS_OK;
 }
@@ -1488,34 +1489,101 @@ do_GetWidth(const nsFontSwitch* aFontSwi
   nsFontWin* fontWin = aFontSwitch->mFontWin;
 
   GetWidthData* data = (GetWidthData*)aData;
   if (data->mFont != fontWin->mFont) {
     // the desired font is not the current font in the DC
     data->mFont = fontWin->mFont;
     ::SelectObject(data->mDC, data->mFont);
   }
-  data->mWidth += fontWin->GetWidth(data->mDC, aSubstring, aSubstringLength);
+  data->mWidth += fontWin->GetWidth(data->mDC, aSubstring,
+                                    aSubstringLength, NULL);
   return PR_TRUE; // don't stop till the end
 }
 
+struct GetWidthArrayData {
+  HDC      mDC;
+  HFONT    mFont;
+  LONG     mWidth;
+  INT*     mWidthArray;
+  PRInt32  mGlyphsSoFar;
+};
+
+static PRBool PR_CALLBACK
+do_GetWidthArray(const nsFontSwitch* aFontSwitch,
+                 const PRUnichar*    aSubstring,
+                 PRUint32            aSubstringLength,
+                 void*               aData)
+{
+  nsFontWin* fontWin = aFontSwitch->mFontWin;
+
+  GetWidthArrayData* data = (GetWidthArrayData*)aData;
+  if (data->mFont != fontWin->mFont) {
+    // the desired font is not the current font in the DC
+    data->mFont = fontWin->mFont;
+    ::SelectObject(data->mDC, data->mFont);
+  }
+  data->mWidth += fontWin->GetWidth(data->mDC, aSubstring,
+                                    aSubstringLength, 
+                                    &data->mWidthArray[data->mGlyphsSoFar]);
+  INT previousWidth = data->mWidthArray[data->mGlyphsSoFar];
+  INT savedWidth;	
+  for (PRUint32 i = 1; i < aSubstringLength; ++i) {
+    savedWidth = data->mWidthArray[data->mGlyphsSoFar + i];
+    data->mWidthArray[data->mGlyphsSoFar + i] -= previousWidth;
+    previousWidth = savedWidth;
+  }
+  data->mGlyphsSoFar += aSubstringLength;
+  return PR_TRUE; // don't stop till the end
+}
+
+NS_IMETHODIMP nsRenderingContextWin :: GetWidthArray(const PRUnichar *aString,
+                                                     PRUint32 aLength,
+                                                     nscoord& aWidth,
+                                                     nscoord* aWidthArray)
+{
+  if (!mFontMetrics) return NS_ERROR_FAILURE;
+  
+  CheckLength(&aLength);
+  SetupFontAndColor();
+
+  nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
+  int* widthArray = new int[aLength];
+  aWidth = 0;
+  GetWidthArrayData data = {mDC, mCurrFont, 0, widthArray, 0};
+
+  metrics->Resolve(mDC, aString, aLength, do_GetWidthArray, &data);
+
+  for (PRUint32 i = 0; i < aLength; ++i) {
+    aWidthArray[i] = NSToCoordRound(float(data.mWidthArray[i]) * mP2T);
+  }
+  aWidth = NSToCoordRound(float(data.mWidth) * mP2T);
+
+  if (mCurrFont != data.mFont) {
+    // If the font was changed along the way, restore our font
+    ::SelectObject(mDC, mCurrFont);
+  }
+  delete [] widthArray;
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsRenderingContextWin :: GetWidth(const PRUnichar *aString,
                                                 PRUint32 aLength,
                                                 nscoord &aWidth,
                                                 PRInt32 *aFontID)
 {
   if (!mFontMetrics) return NS_ERROR_FAILURE;
 
   CheckLength(&aLength);
   SetupFontAndColor();
 
   nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
   GetWidthData data = {mDC, mCurrFont, 0};
 
-  metrics->ResolveForwards(mDC, aString, aLength, do_GetWidth, &data);
+  metrics->Resolve(mDC, aString, aLength, do_GetWidth, &data);
   aWidth = NSToCoordRound(float(data.mWidth) * mP2T);
 
   if (mCurrFont != data.mFont) {
     // If the font was changed along the way, restore our font
     ::SelectObject(mDC, mCurrFont);
   }
 
   if (aFontID) *aFontID = 0;
@@ -1791,17 +1859,17 @@ do_BreakGetTextDimensions(const nsFontSw
     }
 
     // Measure the text
     nscoord twWidth, pxWidth;
     if ((1 == numChars) && (pstr[start] == ' ')) {
       twWidth = data->mSpaceWidth;
     }
     else {
-      pxWidth = fontWin->GetWidth(data->mDC, &pstr[start], numChars);
+      pxWidth = fontWin->GetWidth(data->mDC, &pstr[start], numChars, NULL);
       twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
     }
 
     // See if the text fits
     PRBool textFits = (twWidth + width) <= data->mAvailWidth;
 
     // If the text fits then update the width and the number of
     // characters that fit
@@ -1886,17 +1954,17 @@ do_BreakGetTextDimensions(const nsFontSw
       while ((breakIndex >= 0) && (width > data->mAvailWidth)) {
         twWidth = 0;
         start = data->mBreaks[breakIndex];
         numChars = i - start;
         if ((1 == numChars) && (pstr[start] == ' ')) {
           twWidth = data->mSpaceWidth;
         }
         else if (numChars > 0) {
-          pxWidth = fontWin->GetWidth(data->mDC, &pstr[start], numChars);
+          pxWidth = fontWin->GetWidth(data->mDC, &pstr[start], numChars, NULL);
           twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
         }
 
         width -= twWidth;
         numCharsFit = start;
         --breakIndex;
         i = start;
       }
@@ -1959,17 +2027,17 @@ nsRenderingContextWin::GetTextDimensions
   nsAutoVoidArray fonts, offsets;
   offsets.AppendElement((void*)aString);
 
   BreakGetTextDimensionsData data = {mDC, mCurrFont, mP2T, 
     aAvailWidth, aBreaks, aNumBreaks, spaceWidth, aveCharWidth,
     0, 0, 0, -1, 0, &fonts, &offsets 
   };
 
-  metrics->ResolveForwards(mDC, aString, aLength, do_BreakGetTextDimensions, &data);
+  metrics->Resolve(mDC, aString, aLength, do_BreakGetTextDimensions, &data);
 
   if (mCurrFont != data.mFont) {
     // If the font was changed along the way, restore our font
     ::SelectObject(mDC, mCurrFont);
   }
 
   if (aFontID) *aFontID = 0;
 
@@ -2119,17 +2187,18 @@ do_GetTextDimensions(const nsFontSwitch*
                      void*               aData)
 {
   nsFontWin* fontWin = aFontSwitch->mFontWin;
   GetTextDimensionsData* data = (GetTextDimensionsData*)aData;
   if (data->mFont != fontWin->mFont) {
     data->mFont = fontWin->mFont;
     ::SelectObject(data->mDC, data->mFont);
   }
-  data->mWidth += fontWin->GetWidth(data->mDC, aSubstring, aSubstringLength);
+  data->mWidth += fontWin->GetWidth(data->mDC, aSubstring,
+                                    aSubstringLength, NULL);
   if (data->mAscent < fontWin->mMaxAscent) {
     data->mAscent = fontWin->mMaxAscent;
   }
   if (data->mDescent < fontWin->mMaxDescent) {
     data->mDescent = fontWin->mMaxDescent;
   }
 
   return PR_TRUE; // don't stop till the end
@@ -2145,17 +2214,17 @@ nsRenderingContextWin::GetTextDimensions
   if (!mFontMetrics) return NS_ERROR_FAILURE;
 
   CheckLength(&aLength);
   SetupFontAndColor();
 
   nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
   GetTextDimensionsData data = {mDC, mCurrFont, 0, 0, 0};
 
-  metrics->ResolveForwards(mDC, aString, aLength, do_GetTextDimensions, &data);
+  metrics->Resolve(mDC, aString, aLength, do_GetTextDimensions, &data);
   aDimensions.width = NSToCoordRound(float(data.mWidth) * mP2T);
   aDimensions.ascent = data.mAscent;
   aDimensions.descent = data.mDescent;
 
   if (mCurrFont != data.mFont) {
     // If the font was changed on the way, restore our font
     ::SelectObject(mDC, mCurrFont);
   }
@@ -2196,104 +2265,130 @@ NS_IMETHODIMP nsRenderingContextWin :: D
 
   return NS_OK;
 }
 
 struct DrawStringData {
   HDC            mDC;         // IN
   HFONT          mFont;       // IN/OUT (running)
   nsTransform2D* mTranMatrix; // IN
+  float          mP2T;        // IN
   nscoord        mX;          // IN/OUT (running)
   nscoord        mY;          // IN
   const nscoord* mSpacing;    // IN
   nscoord        mMaxLength;  // IN (length of the full string)
   nscoord        mLength;     // IN/OUT (running, current length already rendered)
+  PRBool         mRightToLeftText;  // IN (true if right-to-left language)
 };
 
 static PRBool PR_CALLBACK
 do_DrawString(const nsFontSwitch* aFontSwitch,
               const PRUnichar*    aSubstring,
               PRUint32            aSubstringLength,
               void*               aData)
 {
   nsFontWin* fontWin = aFontSwitch->mFontWin;
 
   PRInt32 x, y;
   DrawStringData* data = (DrawStringData*)aData;
+  PRBool aRightToLeftText = data->mRightToLeftText;
   if (data->mFont != fontWin->mFont) {
     data->mFont = fontWin->mFont;
     ::SelectObject(data->mDC, data->mFont);
   }
 
   data->mLength += aSubstringLength;
   if (data->mSpacing) {
-    // XXX Fix path to use a twips transform in the DC and use the
-    // spacing values directly and let windows deal with the sub-pixel
-    // positioning.
+    PRInt32 width = 0;
+    for (PRUint32 i = 0; i < aSubstringLength; i++)
+        width += data->mSpacing[i]; 
+
+    // For right-to-left languages, we subtract the width of the substring
+    // BEFORE rendering.
+    if (aRightToLeftText)
+      data->mX -= width;
 
-    // Slow, but accurate rendering
     const PRUnichar* str = aSubstring;
     const PRUnichar* end = aSubstring + aSubstringLength;
+
+    PRInt32 *spacing = new PRInt32[aSubstringLength];
+    PRInt32 *ptr = spacing;
+    PRInt32 origX, prevX, convertX;
+
+    convertX = data->mX;
+    x = convertX;
+    y = data->mY;
+    data->mTranMatrix->TransformCoord(&x, &y);
+    origX = prevX = x;
+
     while (str < end) {
-      // XXX can shave some cycles by inlining a version of transform
-      // coord where y is constant and transformed once
-      x = data->mX;
+      convertX += *data->mSpacing++;
+      x = convertX;
       y = data->mY;
       data->mTranMatrix->TransformCoord(&x, &y);
-      if (IS_HIGH_SURROGATE(*str) && 
-          ((str+1)<end) && 
-          IS_LOW_SURROGATE(*(str+1))) 
-      {
-        // special case for surrogate pair
-        fontWin->DrawString(data->mDC, x, y, str, 2);
-        // we need to advance data->mX and str twice
-        data->mX += *data->mSpacing++;
-        ++str;
-      } else {
-        fontWin->DrawString(data->mDC, x, y, str, 1);
-      }
-      data->mX += *data->mSpacing++;
+
+      *ptr++ = x - prevX;
+      prevX = x;
+
       ++str;
     }
+
+    fontWin->DrawString(data->mDC, origX, y, aSubstring, aSubstringLength, spacing);
+
+    if (!aRightToLeftText && data->mLength < data->mMaxLength)
+      data->mX += width;
+
+    delete [] spacing;
   }
   else {
-    fontWin->DrawString(data->mDC, data->mX, data->mY, aSubstring, aSubstringLength);
+    // For right-to-left languages, we subtract the width of the substring
+    // BEFORE rendering.
+    if (aRightToLeftText)
+      data->mX -= fontWin->GetWidth(data->mDC, aSubstring, aSubstringLength, NULL);
+    fontWin->DrawString(data->mDC, data->mX, data->mY, aSubstring, aSubstringLength, NULL);
     // be ready if there is more to come
-    if (data->mLength < data->mMaxLength) {
-      data->mX += fontWin->GetWidth(data->mDC, aSubstring, aSubstringLength);
+    if (!aRightToLeftText && data->mLength < data->mMaxLength) {
+      data->mX += fontWin->GetWidth(data->mDC, aSubstring, aSubstringLength, NULL);
     }
   }
   return PR_TRUE; // don't stop till the end
 }
 
 NS_IMETHODIMP nsRenderingContextWin :: DrawString(const PRUnichar *aString, PRUint32 aLength,
                                                   nscoord aX, nscoord aY,
                                                   PRInt32 aFontID,
                                                   const nscoord* aSpacing)
 {
   if (!mFontMetrics) return NS_ERROR_FAILURE;
 
   CheckLength(&aLength);
-  SetupFontAndColor();
 
   nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
-  DrawStringData data = {mDC, mCurrFont, mTranMatrix, 
-    aX, aY, aSpacing, aLength, 0
+  DrawStringData data = {mDC, mCurrFont, mTranMatrix, mP2T,
+    aX, aY, aSpacing, aLength, 0, mRightToLeftText
   };
+  
+  if (mRightToLeftText) {
+    // For RTL languages, we start at the right and work leftwards.
+    if (aSpacing) {
+      for (int i = 0; i < (int)aLength; i++)
+        data.mX += aSpacing[i];
+    }
+    else {
+      nscoord aWidth;
+      GetWidth(aString, aLength, aWidth, NULL);
+      data.mX += aWidth;
+    }
+  }
   if (!aSpacing) { // @see do_DrawString for the spacing case
     mTranMatrix->TransformCoord(&data.mX, &data.mY);
   }
 
-  if (mRightToLeftText) {
-    metrics->ResolveBackwards(mDC, aString, aLength, do_DrawString, &data);
-  }
-  else
-  {
-    metrics->ResolveForwards(mDC, aString, aLength, do_DrawString, &data);
-  }
+  SetupFontAndColor();
+  metrics->Resolve(mDC, aString, aLength, do_DrawString, &data);
 
   if (mCurrFont != data.mFont) {
     // If the font was changed along the way, restore our font
     ::SelectObject(mDC, mCurrFont);
   }
 
   return NS_OK;
 }
@@ -2380,17 +2475,17 @@ nsRenderingContextWin::GetBoundingMetric
   if (!mFontMetrics) return NS_ERROR_FAILURE;
 
   CheckLength(&aLength);
   SetupFontAndColor();
 
   nsFontMetricsWin* metrics = (nsFontMetricsWin*)mFontMetrics;
   GetBoundingMetricsData data = {mDC, mCurrFont, &aBoundingMetrics, PR_TRUE, NS_OK};
 
-  nsresult rv = metrics->ResolveForwards(mDC, aString, aLength, do_GetBoundingMetrics, &data);
+  nsresult rv = metrics->Resolve(mDC, aString, aLength, do_GetBoundingMetrics, &data);
   if (NS_SUCCEEDED(rv)) {
     rv = data.mStatus;
   }
 
   // convert to app units
   aBoundingMetrics.leftBearing = NSToCoordRound(float(aBoundingMetrics.leftBearing) * mP2T);
   aBoundingMetrics.rightBearing = NSToCoordRound(float(aBoundingMetrics.rightBearing) * mP2T);
   aBoundingMetrics.width = NSToCoordRound(float(aBoundingMetrics.width) * mP2T);
Index: gfx/src/windows/nsRenderingContextWin.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/windows/nsRenderingContextWin.h,v
retrieving revision 3.84
diff -u -8 -p -r3.84 nsRenderingContextWin.h
--- gfx/src/windows/nsRenderingContextWin.h	27 Nov 2004 08:22:22 -0000	3.84
+++ gfx/src/windows/nsRenderingContextWin.h	12 Aug 2005 02:38:57 -0000
@@ -151,16 +151,33 @@ public:
   NS_IMETHOD GetWidth(PRUnichar aC, nscoord& aWidth,
                       PRInt32 *aFontID);
   NS_IMETHOD GetWidth(const nsString& aString, nscoord& aWidth,
                       PRInt32 *aFontID);
   NS_IMETHOD GetWidth(const char* aString, nscoord& aWidth);
   NS_IMETHOD GetWidth(const char* aString, PRUint32 aLength, nscoord& aWidth);
   NS_IMETHOD GetWidth(const PRUnichar* aString, PRUint32 aLength,
                       nscoord& aWidth, PRInt32 *aFontID);
+  /**
+   * Returns an array of widths (in app units) of a Unicode character string
+   * If no font has been set, the results are undefined.
+   * The length of output array data is always aLength elements.  Where the
+   * layout is complex and a character is rendered above or below a previous
+   * character, this is represented as a zero width in the output array.
+   *
+   * Returns NS_ERROR_NOT_IMPLEMENTED if this functionality is not supported. 
+   *
+   * @param aString string to measure
+   * @param aLength number of characters in string
+   * @param aWidth out parameter for total width
+   * @param aWidthArray out parameter for per-character widths
+   * @return error status
+   */
+  NS_IMETHOD GetWidthArray(const PRUnichar *aString, PRUint32 aLength,
+                           nscoord& aWidth, nscoord* aWidthArray);
 
   NS_IMETHOD GetTextDimensions(const char* aString, PRUint32 aLength,
                                nsTextDimensions& aDimensions);
   NS_IMETHOD GetTextDimensions(const PRUnichar *aString, PRUint32 aLength,
                                nsTextDimensions& aDimensions, PRInt32 *aFontID);
   NS_IMETHOD GetTextDimensions(const char*       aString,
                                PRInt32           aLength,
                                PRInt32           aAvailWidth,
