Index: layout/generic/nsTextFrame.cpp
===================================================================
RCS file: /cvsroot/mozilla/layout/generic/nsTextFrame.cpp,v
retrieving revision 1.513
diff -u -r1.513 nsTextFrame.cpp
--- layout/generic/nsTextFrame.cpp	16 Jul 2005 19:58:26 -0000	1.513
+++ layout/generic/nsTextFrame.cpp	3 Aug 2005 14:04:08 -0000
@@ -29,6 +29,7 @@
  *   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"),
@@ -437,6 +438,20 @@
   return NS_OK;
 }
 
+
+//----------------------------------------------------------------------
+
+class nsTextFrame_Scanner
+{
+  public:
+    nsTextFrame_Scanner() {}
+    virtual ~nsTextFrame_Scanner() {}
+
+    virtual void scan(nsIRenderingContext& aRenderingContext,
+                      PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsFinal) = 0;
+};
+
+
 //----------------------------------------------------------------------
 
 class nsTextFrame : public nsFrame {
@@ -740,6 +755,11 @@
                        TextPaintStyle& aStyle,
                        nscoord aX, nscoord aY);
 
+  nsIFontMetrics* ScanString(nsIRenderingContext& aRenderingContext,
+                          TextPaintStyle& aTextStyle,
+                          PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
+                          nsTextFrame_Scanner& scanner);
+
   // The passed-in rendering context must have its color set to the color the
   // text should be rendered in.
   void RenderString(nsIRenderingContext& aRenderingContext,
@@ -835,6 +855,8 @@
   void ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const;
 #endif
 
+  PRBool IsJustifiableCharacter(PRUnichar aChar, PRBool aLangIsCJ);
+
 protected:
   virtual ~nsTextFrame();
 
@@ -860,7 +882,6 @@
   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);
@@ -2896,61 +2917,259 @@
   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*/)
+class nsTextFrame_MeasureScanner : public nsTextFrame_Scanner
 {
-  PRUnichar buf[TEXT_BUF_SIZE];
-  PRUnichar* bp0 = buf;
+  private:
+    nsTextFrame::TextPaintStyle& mTextStyle;
+    nscoord* mSpacing;
+
+  public:
+    nsTextFrame_MeasureScanner(nsTextFrame::TextPaintStyle& aTextStyle, nscoord* aSpacing);
+    virtual ~nsTextFrame_MeasureScanner();
 
-  nscoord spacingMem[TEXT_BUF_SIZE];
-  nscoord* sp0 = spacingMem; 
-  
-  PRBool spacing = (0 != aTextStyle.mLetterSpacing) ||
-    (0 != aTextStyle.mWordSpacing) || aTextStyle.mJustifying;
+    virtual void scan(nsIRenderingContext& aRenderingContext,
+                      PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsFinal);
+};
 
-  PRBool justifying = aTextStyle.mJustifying &&
-    (aTextStyle.mNumJustifiableCharacterReceivingExtraJot != 0 || aTextStyle.mExtraSpacePerJustifiableCharacter != 0);
+nsTextFrame_MeasureScanner::nsTextFrame_MeasureScanner(nsTextFrame::TextPaintStyle& aTextStyle, nscoord* aSpacing)
+  : mTextStyle(aTextStyle),
+    mSpacing(aSpacing)
+{
+}
 
-  PRBool isCJ = IsChineseJapaneseLangGroup();
-  PRBool isEndOfLine = aIsEndOfFrame && IsEndOfLine(mState);
+nsTextFrame_MeasureScanner::~nsTextFrame_MeasureScanner()
+{
+}
+
+void nsTextFrame_MeasureScanner::scan(nsIRenderingContext& aRenderingContext,
+                  PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool /*aIsFinal*/)
+{
+    /* If the font rendering engine can do it, then that's very good. */
+  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;
+  }
+
+    /* Otherwise we do it the inefficient way. */
+  PRInt32 i;
+  nscoord lastSegmentWidth = 0;
+  PRInt32 segmentStart = 0;
+
+  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;
+      }
+      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;
+    }
+  }
+}
+
+class nsTextFrame_InflateScanner : public nsTextFrame_Scanner
+{
+  private:
+    nsTextFrame* mTextFrame;
+    nsTextFrame::TextPaintStyle& mTextStyle;
+    PRBool mIsCJ;
+    PRBool mIsEndOfLine;
+    PRBool mJustify;
+    nscoord* mSpacing;
+
+  public:
+    nsTextFrame_InflateScanner(
+        nsTextFrame* aTextFrame,
+        nsTextFrame::TextPaintStyle& aTextStyle,
+        PRBool aIsCJ,
+        PRBool aIsEndOfLine,
+        PRBool aJustify,
+        nscoord* aSpacing);
+    virtual ~nsTextFrame_InflateScanner();
 
+    virtual void scan(nsIRenderingContext& aRenderingContext,
+                      PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsFinal);
+};
+
+nsTextFrame_InflateScanner::nsTextFrame_InflateScanner(
+        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_InflateScanner::~nsTextFrame_InflateScanner()
+{
+}
+
+void nsTextFrame_InflateScanner::scan(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_RenderScanner : public nsTextFrame_Scanner
+{
+  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_RenderScanner(
+        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_RenderScanner();
+
+    virtual void scan(nsIRenderingContext& aRenderingContext,
+                      PRInt32 aOffset, PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsFinal);
+};
+
+nsTextFrame_RenderScanner::nsTextFrame_RenderScanner(
+        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_RenderScanner::~nsTextFrame_RenderScanner()
+{
+}
+
+void nsTextFrame_RenderScanner::scan(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];
+
+  // Note: use aY not small-y so that decorations are drawn with
+  // respect to the normal-font not the current font.
+  mTextFrame->PaintTextDecorations(aRenderingContext, mStyleContext, mPresContext,
+                       mTextStyle, mX, mY, width, aBuffer, mDetails,
+                       aOffset, aLength, mDoSpacing ? mSpacing+aOffset : nsnull);
+
+  mX += width;
+}
+
+nsIFontMetrics*
+nsTextFrame::ScanString(nsIRenderingContext& aRenderingContext,
+                        TextPaintStyle& aTextStyle,
+                        PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
+                        nsTextFrame_Scanner& aScanner)
+{
+  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;
+  aRenderingContext.SetFont(lastFont);
   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))) {
@@ -2960,26 +3179,11 @@
       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);
+        aScanner.scan(aRenderingContext, countSoFar, runStart, pendingCount, PR_FALSE);
         countSoFar += pendingCount;
-        aWidth -= width;
-        aX += width;
         runStart = bp = bp0;
-        sp = sp0;
-        width = 0;
       }
       aRenderingContext.SetFont(nextFont);
       lastFont = nextFont;
@@ -2991,74 +3195,92 @@
         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;
-  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);
   }
-  aTextStyle.mLastFont = lastFont;
+  PRInt32 pendingCount = bp - runStart;
+  if (0 != pendingCount)
+    aScanner.scan(aRenderingContext, countSoFar, runStart, pendingCount, PR_TRUE);
 
-  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];
+
+  // Measure character spacing.
+  {
+    nsTextFrame_MeasureScanner measureScanner(aTextStyle, sp0);
+    ScanString(aRenderingContext, aTextStyle, aBuffer, aLength, aIsEndOfFrame, measureScanner);
+  }
+  // If justifying, add the necessary spacing to whitespaces.
+  if (justifying || spacing) {
+    PRBool isCJ = IsChineseJapaneseLangGroup();
+    PRBool isEndOfLine = aIsEndOfFrame && IsEndOfLine(mState);
+    nsTextFrame_InflateScanner inflateScanner(this, aTextStyle, isCJ, isEndOfLine, justifying, sp0);
+    ScanString(aRenderingContext, aTextStyle, aBuffer, aLength, aIsEndOfFrame, inflateScanner);
+  }
+  // Render the text.
+  {
+    // Save the color we want to use for the text, since calls to
+    // PaintTextDecorations in renderScanner will call SetColor() on the rendering
+    // context.
+    nscolor textColor;
+    aRenderingContext.GetColor(textColor);
+
+    nsTextFrame_RenderScanner renderScanner(
+      this,
+      aStyleContext,
+      aPresContext,
+      aTextStyle,
+      textColor,
+      sp0,
+      spacing,
+      aX,
+      aY,
+      mAscent,
+      aDetails);
+    aTextStyle.mLastFont = ScanString(aRenderingContext, aTextStyle, aBuffer, aLength, aIsEndOfFrame, renderScanner);
   }
   if (sp0 != spacingMem) {
     delete [] sp0;
Index: gfx/public/nsIRenderingContext.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/public/nsIRenderingContext.h,v
retrieving revision 1.65
diff -u -r1.65 nsIRenderingContext.h
--- gfx/public/nsIRenderingContext.h	2 May 2005 20:48:26 -0000	1.65
+++ gfx/public/nsIRenderingContext.h	3 Aug 2005 14:04:11 -0000
@@ -893,6 +893,28 @@
    *         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_FAILURE if this functionality is not supported. 
+   *
+   * @param aString A PRUnichar of the string
+   * @param aLength The length of the aString
+   * @param aX Horizontal starting point of baseline
+   * @param aY Vertical starting point of baseline.
+   * @param aFontID an optional parameter used to speed font
+   *        selection for complex unicode strings. the value
+   *        passed is returned by the DrawString() methods.
+   * @param aSpacing inter-character spacing that the renderer will use
+   */
+  NS_IMETHOD GetCharacterSpacing(const PRUnichar *aString, PRUint32 aLength,
+                        PRInt32 aFontID,
+                        nscoord* aSpacing)
+  {
+      return NS_ERROR_FAILURE;
+  }
 };
 
 //modifiers for text rendering
Index: gfx/src/gtk/nsFontMetricsPango.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/gtk/nsFontMetricsPango.cpp,v
retrieving revision 1.16
diff -u -r1.16 nsFontMetricsPango.cpp
--- gfx/src/gtk/nsFontMetricsPango.cpp	26 Jul 2005 00:13:51 -0000	1.16
+++ gfx/src/gtk/nsFontMetricsPango.cpp	3 Aug 2005 14:04:13 -0000
@@ -733,7 +733,7 @@
     aContext->UpdateGC();
     GdkGC *gc = aContext->GetGC();
 
-    if (aSpacing && *aSpacing) {
+    if (aSpacing) {
         DrawStringSlowly(aString, NULL, aLength, aSurface->GetDrawable(),
                          gc, x, y, line, aSpacing);
     }
@@ -791,7 +791,7 @@
     }
     line = pango_layout_get_line(layout, 0);
 
-    if (aSpacing && *aSpacing) {
+    if (aSpacing) {
         DrawStringSlowly(text, aString, aLength, aSurface->GetDrawable(),
                          gc, x, y, line, aSpacing);
     }
@@ -812,6 +812,96 @@
     return rv;
 }
 
+/**
+ * Get the exact spacing that the rendering engine will use to render the
+ * specified text in the current font.
+ *
+ * Returns NS_ERROR_FAILURE if this functionality is not supported. 
+ *
+ * @param aString A PRUnichar of the string
+ * @param aLength The length of the aString
+ * @param aX Horizontal starting point of baseline
+ * @param aY Vertical starting point of baseline.
+ * @param aFontID an optional parameter used to speed font
+ *        selection for complex unicode strings. the value
+ *        passed is returned by the DrawString() methods.
+ * @param aSpacing inter-character spacing that the renderer will use
+ */
+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,
@@ -1374,8 +1464,10 @@
              */
             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); */
Index: gfx/src/gtk/nsFontMetricsPango.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/gtk/nsFontMetricsPango.h,v
retrieving revision 1.7
diff -u -r1.7 nsFontMetricsPango.h
--- gfx/src/gtk/nsFontMetricsPango.h	2 May 2005 20:48:30 -0000	1.7
+++ gfx/src/gtk/nsFontMetricsPango.h	3 Aug 2005 14:04:13 -0000
@@ -215,6 +215,25 @@
                                    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_FAILURE if this functionality is not supported. 
+     *
+     * @param aString A PRUnichar of the string
+     * @param aLength The length of the aString
+     * @param aX Horizontal starting point of baseline
+     * @param aY Vertical starting point of baseline.
+     * @param aFontID an optional parameter used to speed font
+     *        selection for complex unicode strings. the value
+     *        passed is returned by the DrawString() methods.
+     * @param aSpacing inter-character spacing that the renderer will use
+     */
+    virtual nsresult GetCharacterSpacing(const PRUnichar *aString, PRUint32 aLength,
+                          PRInt32 aFontID,
+                          nscoord* aSpacing);
+
     // get hints for the font
     static PRUint32    GetHints     (void);
 
Index: gfx/src/gtk/nsIFontMetricsGTK.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/gtk/nsIFontMetricsGTK.h,v
retrieving revision 1.5
diff -u -r1.5 nsIFontMetricsGTK.h
--- gfx/src/gtk/nsIFontMetricsGTK.h	2 May 2005 20:48:30 -0000	1.5
+++ gfx/src/gtk/nsIFontMetricsGTK.h	3 Aug 2005 14:04:13 -0000
@@ -144,6 +144,27 @@
                                    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_FAILURE if this functionality is not supported. 
+     *
+     * @param aString A PRUnichar of the string
+     * @param aLength The length of the aString
+     * @param aX Horizontal starting point of baseline
+     * @param aY Vertical starting point of baseline.
+     * @param aFontID an optional parameter used to speed font
+     *        selection for complex unicode strings. the value
+     *        passed is returned by the DrawString() methods.
+     * @param aSpacing inter-character spacing that the renderer will use
+     */
+    virtual nsresult GetCharacterSpacing(const PRUnichar *aString, PRUint32 aLength,
+                          PRInt32 aFontID,
+                          nscoord* aSpacing)
+    {
+        return NS_ERROR_FAILURE;
+    }
 };
 
 #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 -r1.193 nsRenderingContextGTK.cpp
--- gfx/src/gtk/nsRenderingContextGTK.cpp	3 Jun 2005 10:26:23 -0000	1.193
+++ gfx/src/gtk/nsRenderingContextGTK.cpp	3 Aug 2005 14:04:14 -0000
@@ -1494,3 +1494,26 @@
   // 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_FAILURE if this functionality is not supported. 
+*
+* @param aString A PRUnichar of the string
+* @param aLength The length of the aString
+* @param aX Horizontal starting point of baseline
+* @param aY Vertical starting point of baseline.
+* @param aFontID an optional parameter used to speed font
+*        selection for complex unicode strings. the value
+*        passed is returned by the DrawString() methods.
+* @param aSpacing inter-character spacing that the renderer will use
+*/
+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 -r1.80 nsRenderingContextGTK.h
--- gfx/src/gtk/nsRenderingContextGTK.h	2 May 2005 20:48:30 -0000	1.80
+++ gfx/src/gtk/nsRenderingContextGTK.h	3 Aug 2005 14:04:14 -0000
@@ -214,6 +214,25 @@
                            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_FAILURE if this functionality is not supported. 
+   *
+   * @param aString A PRUnichar of the string
+   * @param aLength The length of the aString
+   * @param aX Horizontal starting point of baseline
+   * @param aY Vertical starting point of baseline.
+   * @param aFontID an optional parameter used to speed font
+   *        selection for complex unicode strings. the value
+   *        passed is returned by the DrawString() methods.
+   * @param aSpacing inter-character spacing that the renderer will use
+   */
+  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
Index: gfx/src/windows/nsFontMetricsWin.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/windows/nsFontMetricsWin.cpp,v
retrieving revision 3.230
diff -u -r3.230 nsFontMetricsWin.cpp
--- gfx/src/windows/nsFontMetricsWin.cpp	23 Jun 2005 08:03:24 -0000	3.230
+++ gfx/src/windows/nsFontMetricsWin.cpp	3 Aug 2005 14:04:18 -0000
@@ -3854,11 +3854,11 @@
 }
 
 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;
@@ -3941,107 +3941,6 @@
   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)
@@ -5252,11 +5151,11 @@
 }
 
 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;
@@ -5318,51 +5217,6 @@
   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()
Index: gfx/src/windows/nsFontMetricsWin.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/windows/nsFontMetricsWin.h,v
retrieving revision 3.58
diff -u -r3.58 nsFontMetricsWin.h
--- gfx/src/windows/nsFontMetricsWin.h	28 Jun 2005 17:20:35 -0000	3.58
+++ gfx/src/windows/nsFontMetricsWin.h	3 Aug 2005 14:04:18 -0000
@@ -185,12 +185,12 @@
 /**
  * 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.
  */
 
@@ -245,18 +245,11 @@
   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);
@@ -438,18 +431,11 @@
   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);
Index: gfx/src/windows/nsRenderingContextWin.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/windows/nsRenderingContextWin.cpp,v
retrieving revision 3.177
diff -u -r3.177 nsRenderingContextWin.cpp
--- gfx/src/windows/nsRenderingContextWin.cpp	8 Jun 2005 16:48:42 -0000	3.177
+++ gfx/src/windows/nsRenderingContextWin.cpp	3 Aug 2005 14:04:20 -0000
@@ -654,7 +654,7 @@
   }
 #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
@@ -1510,7 +1510,7 @@
   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) {
@@ -1964,7 +1964,7 @@
     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
@@ -2150,7 +2150,7 @@
   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;
@@ -2206,6 +2206,7 @@
   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
@@ -2218,6 +2219,7 @@
 
   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);
@@ -2233,31 +2235,55 @@
     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);
     }
   }
@@ -2272,23 +2298,30 @@
   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
@@ -2385,7 +2418,7 @@
   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;
   }
