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	9 Aug 2005 10:32:07 -0000
@@ -24,16 +24,17 @@
  *   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>
+ *   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 +433,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 +750,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 +854,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 +881,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 +2916,419 @@ 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 GetCharacterSpacing().
+#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.
+  if (aRenderingContext.GetCharacterSpacing(aBuffer, aLength, -1, 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.65
diff -u -8 -p -r1.65 nsIRenderingContext.h
--- gfx/public/nsIRenderingContext.h	2 May 2005 20:48:26 -0000	1.65
+++ gfx/public/nsIRenderingContext.h	9 Aug 2005 10:32:19 -0000
@@ -888,16 +888,37 @@ public:
    *
    * @param aRect  Rectangle in which to render the EPSF.
    * @param aDataFile - plugin data stored in a file
    * @return NS_OK for success, or a suitable error value.
    *         NS_ERROR_NOT_IMPLEMENTED is returned if the rendering context
    *         doesn't support rendering EPSF, 
    */
   NS_IMETHOD RenderEPS(const nsRect& aRect, FILE *aDataFile) = 0;
+
+  /**
+   * 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) = 0;
 };
 
 //modifiers for text rendering
 
 #define NS_DRAWSTRING_NORMAL            0x0
 #define NS_DRAWSTRING_UNDERLINE         0x1
 #define NS_DRAWSTRING_OVERLINE          0x2
 #define NS_DRAWSTRING_LINE_THROUGH      0x4
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	9 Aug 2005 10:32:23 -0000
@@ -121,16 +121,37 @@ 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);
+
 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	9 Aug 2005 10:32:30 -0000
@@ -222,16 +222,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	9 Aug 2005 10:32:34 -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,108 @@ nsFontMetricsPango::DrawString(const PRU
     g_object_unref(gc);
     g_object_unref(layout);
 
     //    printf("DrawString\n");
 
     return rv;
 }
 
+/**
+ * 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.
+ */
+nsresult
+nsFontMetricsPango::GetCharacterSpacing(const PRUnichar *aString, PRUint32 aLength,
+                      PRInt32 aFontID,
+                      nscoord* aSpacing)
+{
+    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(aSpacing, sizeof(nscoord) * aLength);
+
+        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)
+                aSpacing[curOffset] += utf8spacing[i++];
+    
+            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 +1461,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	9 Aug 2005 10:32:34 -0000
@@ -210,16 +210,37 @@ public:
                                    PRUint32 &aWidth);
 
     virtual nsresult GetRangeWidth(const char *aText,
                                    PRUint32 aLength,
                                    PRUint32 aStart,
                                    PRUint32 aEnd,
                                    PRUint32 &aWidth);
 
+    /**
+     * 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.
+     */
+    virtual nsresult GetCharacterSpacing(const PRUnichar *aString, PRUint32 aLength,
+                          PRInt32 aFontID,
+                          nscoord* aSpacing);
+
     // get hints for the font
     static PRUint32    GetHints     (void);
 
     // drawing surface methods
     static nsresult FamilyExists    (nsIDeviceContext *aDevice,
                                      const nsString &aName);
 
 private:
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	9 Aug 2005 10:32:35 -0000
@@ -139,11 +139,34 @@ public:
                                    PRUint32 aEnd,
                                    PRUint32 &aWidth) = 0;
     virtual nsresult GetRangeWidth(const char *aText,
                                    PRUint32 aLength,
                                    PRUint32 aStart,
                                    PRUint32 aEnd,
                                    PRUint32 &aWidth) = 0;
 
+    /**
+     * 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.
+     */
+    virtual nsresult GetCharacterSpacing(const PRUnichar *aString, PRUint32 aLength,
+                          PRInt32 aFontID,
+                          nscoord* aSpacing)
+    {
+        return NS_ERROR_NOT_IMPLEMENTED;
+    }
 };
 
 #endif /* __nsIFontMetricsGTK_h */
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	9 Aug 2005 10:32:36 -0000
@@ -1489,8 +1489,33 @@ NS_IMETHODIMP nsRenderingContextGTK::Get
   return AllocateBackbuffer(aRequestedSize, aMaxSize, aBackbuffer, PR_FALSE, 0);
 }
  
 NS_IMETHODIMP nsRenderingContextGTK::ReleaseBackbuffer(void) {
   // Do not cache the backbuffer. On GTK it is more efficient to allocate
   // the backbuffer as needed and it doesn't cause a performance hit. @see bug 95952
   return DestroyCachedBackbuffer();
 }
+
+/**
+ * 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_IMETHODIMP nsRenderingContextGTK::GetCharacterSpacing(const PRUnichar *aString, PRUint32 aLength,
+                    PRInt32 aFontID,
+                    nscoord* aSpacing)
+{
+    return mFontMetrics->GetCharacterSpacing(aString, aLength, aFontID, aSpacing);
+}
+
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	9 Aug 2005 10:32:38 -0000
@@ -209,16 +209,37 @@ public:
                            PRUint32 &aWidth);
 
   NS_IMETHOD DrawImage(imgIContainer *aImage, const nsRect & aSrcRect, const nsRect & aDestRect);
 
   NS_IMETHOD GetBackbuffer(const nsRect &aRequestedSize, const nsRect &aMaxSize,
                            PRBool aForBlending, nsIDrawingSurface* &aBackbuffer);
   NS_IMETHOD ReleaseBackbuffer(void);
 
+  /**
+   * 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);
+
 #ifdef MOZ_MATHML
   /**
    * Returns metrics (in app units) of an 8-bit character string
    */
   NS_IMETHOD GetBoundingMetrics(const char*        aString,
                                 PRUint32           aLength,
                                 nsBoundingMetrics& aBoundingMetrics);
   
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	9 Aug 2005 10:33:03 -0000
@@ -474,9 +474,33 @@ nsRenderingContextImpl::GetRangeWidth(co
 }
 
 NS_IMETHODIMP
 nsRenderingContextImpl::RenderEPS(const nsRect& aRect, FILE *aDataFile)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+/**
+ * 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_IMETHODIMP
+nsRenderingContextImpl::GetCharacterSpacing(const PRUnichar *aString, PRUint32 aLength,
+                      PRInt32 aFontID,
+                      nscoord* aSpacing)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
 
Index: gfx/src/windows/nsFontMetricsWin.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/windows/nsFontMetricsWin.cpp,v
retrieving revision 3.230
diff -u -8 -p -r3.230 nsFontMetricsWin.cpp
--- gfx/src/windows/nsFontMetricsWin.cpp	23 Jun 2005 08:03:24 -0000	3.230
+++ gfx/src/windows/nsFontMetricsWin.cpp	9 Aug 2005 10:33:08 -0000
@@ -3849,21 +3849,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;
@@ -3936,117 +3936,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;
@@ -5247,21 +5146,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;
@@ -5313,61 +5212,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.58
diff -u -8 -p -r3.58 nsFontMetricsWin.h
--- gfx/src/windows/nsFontMetricsWin.h	28 Jun 2005 17:20:35 -0000	3.58
+++ gfx/src/windows/nsFontMetricsWin.h	9 Aug 2005 10:33:09 -0000
@@ -180,22 +180,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 +240,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);
@@ -433,26 +426,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	9 Aug 2005 10:33:11 -0000
@@ -649,17 +649,17 @@ nsRenderingContextWin :: GetHints(PRUint
   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;
 }
@@ -1505,17 +1505,17 @@ NS_IMETHODIMP nsRenderingContextWin :: G
   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;
@@ -1959,17 +1959,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;
 
@@ -2145,17 +2145,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);
   }
@@ -2201,99 +2201,132 @@ struct DrawStringData {
   HDC            mDC;         // IN
   HFONT          mFont;       // IN/OUT (running)
   nsTransform2D* mTranMatrix; // 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;
 };
 
 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.
 
     // Slow, but accurate rendering
     const PRUnichar* str = aSubstring;
     const PRUnichar* end = aSubstring + aSubstringLength;
     while (str < end) {
+      nscoord spacing = 0;
+      int chars = 0;
+      // Collect a group together of one character followed a sequence
+      // of any length of characters with a spacing of zero (in most
+      // cases we will handle a single character only).  Thus for
+      // certain languages we pass a character together with its
+      // diacritical marks and let the operating system lay them out
+      // correctly.
+      while (str+chars < end) {
+	if (IS_HIGH_SURROGATE(str[chars]) && 
+	    ((str+chars+1)<end) && 
+	    IS_LOW_SURROGATE(str[chars+1])) 
+	{
+          if (chars != 0 && (data->mSpacing[0] != 0 || data->mSpacing[1] != 0))
+            break;
+	  spacing += *data->mSpacing++;
+	  spacing += *data->mSpacing++;
+          chars += 2;
+	}
+        else {
+          if (chars != 0 && data->mSpacing[0] != 0)
+            break;
+          spacing += *data->mSpacing++;
+          chars++;
+        }
+      }
+      // For right-to-left languages, we subtract the width of the character
+      // BEFORE rendering.
+      if (aRightToLeftText)
+        data->mX -= spacing;
       // XXX can shave some cycles by inlining a version of transform
       // coord where y is constant and transformed once
       x = data->mX;
       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++;
-      ++str;
+      fontWin->DrawString(data->mDC, x, y, str, chars);
+      if (!aRightToLeftText)
+        data->mX += spacing;
+      str += chars;
     }
   }
   else {
+    // For right-to-left languages, we subtract the width of the character
+    // BEFORE rendering.
+    if (aRightToLeftText)
+      data->mX -= fontWin->GetWidth(data->mDC, aSubstring, aSubstringLength);
     fontWin->DrawString(data->mDC, data->mX, data->mY, aSubstring, aSubstringLength);
     // be ready if there is more to come
-    if (data->mLength < data->mMaxLength) {
+    if (!aRightToLeftText && data->mLength < data->mMaxLength) {
       data->mX += fontWin->GetWidth(data->mDC, aSubstring, aSubstringLength);
     }
   }
   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
+    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;  // Add width to start at right-most co-ordinate.
+    }
+  }
   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 +2413,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	9 Aug 2005 10:33:11 -0000
@@ -198,16 +198,40 @@ public:
 
   // nsIRenderingContextWin
   NS_IMETHOD CreateDrawingSurface(HDC aDC, nsIDrawingSurface* &aSurface);
 
   NS_IMETHOD GetBackbuffer(const nsRect &aRequestedSize, const nsRect &aMaxSize, PRBool aForBlending, nsIDrawingSurface* &aBackbuffer);
  
   NS_IMETHOD ReleaseBackbuffer(void);
 
+  /**
+   * 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;
+  }
+
 #ifdef MOZ_MATHML
   NS_IMETHOD
   GetBoundingMetrics(const char*        aString,
                      PRUint32           aLength,
                      nsBoundingMetrics& aBoundingMetrics);
 
   NS_IMETHOD
   GetBoundingMetrics(const PRUnichar*   aString,
