Bug 427730. Make text for ATSUI layout end in ' ' or ' .' instead of '.', to ensure the last character isn't treated as the start of a kerning pair. r=jdaggett,sr=vlad,a=beltzner
authorroc+@cs.cmu.edu
Mon, 14 Apr 2008 18:48:19 -0700
changeset 14326 a0f2096af6b788d7d533589f19cfcba95011c6c4
parent 14325 ff2c80f34b83e4e86d230c110f16dc655d34b0be
child 14327 548912d1e6951755bafd6a7e5bc005ad8ceaf909
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett, vlad, beltzner
bugs427730
milestone1.9pre
Bug 427730. Make text for ATSUI layout end in ' ' or ' .' instead of '.', to ensure the last character isn't treated as the start of a kerning pair. r=jdaggett,sr=vlad,a=beltzner
gfx/thebes/public/gfxAtsuiFonts.h
gfx/thebes/src/gfxAtsuiFonts.cpp
layout/reftests/bugs/427730-1-ref.html
layout/reftests/bugs/427730-1.html
layout/reftests/bugs/reftest.list
--- a/gfx/thebes/public/gfxAtsuiFonts.h
+++ b/gfx/thebes/public/gfxAtsuiFonts.h
@@ -155,19 +155,37 @@ public:
 
 protected:
     static PRBool FindATSUFont(const nsAString& aName,
                                const nsACString& aGenericName,
                                void *closure);
 
     PRUint32 GuessMaximumStringLength();
 
-    /** Returns true for success */
-    PRBool InitTextRun(gfxTextRun *aRun, const PRUnichar *aString, PRUint32 aLength,
-                       PRBool aWrapped, PRUint32 aSegmentStart, PRUint32 aSegmentLength);
+    /**
+     * @param aRun the text run to fill in
+     * @param aString the complete text including all wrapper characters
+     * @param aLength the length of aString
+     * @param aLayoutStart the first character of aString that should be
+     * at the start of the ATSUI layout; this skips any wrapper character
+     * used to override direction
+     * @param aLayoutLength the length of the characters that should be
+     * in the ATSUI layout; this excludes any trailing wrapper character
+     * used to override direction
+     * @param aTrailingCharsToIgnore the number of trailing characters
+     * in the ATSUI layout that are not part of the text run
+     * (characters added to ensure correct RTL and kerning behaviour)
+     * @param aTextRunOffset the character offset in the textrun where
+     * the glyph data from the ATSUI layout should be copied
+     * @return true for success
+     */
+    PRBool InitTextRun(gfxTextRun *aRun,
+                       const PRUnichar *aString, PRUint32 aLength,
+                       PRUint32 aLayoutStart, PRUint32 aLayoutLength,
+                       PRUint32 aOffsetInTextRun, PRUint32 aLengthInTextRun);
     
     // cache the most recent pref font to avoid general pref font lookup
     nsRefPtr<MacOSFamilyEntry>    mLastPrefFamily;
     nsRefPtr<gfxAtsuiFont>        mLastPrefFont;
     eFontPrefLang                 mLastPrefLang;       // lang group for last pref font
     PRBool                        mLastPrefFirstFont;  // is this the first font in the list of pref fonts for this lang group?
     eFontPrefLang                 mPageLang;
 };
--- a/gfx/thebes/src/gfxAtsuiFonts.cpp
+++ b/gfx/thebes/src/gfxAtsuiFonts.cpp
@@ -544,22 +544,45 @@ SetupClusterBoundaries(gfxTextRun *aText
     UCDisposeTextBreakLocator(&locator);
 }
 
 #define UNICODE_LRO 0x202d
 #define UNICODE_RLO 0x202e
 #define UNICODE_PDF 0x202c
 
 static void
-AppendDirectionalIndicator(PRUint32 aFlags, nsAString& aString)
+AppendDirectionalIndicatorStart(PRUint32 aFlags, nsAString& aString)
 {
     static const PRUnichar overrides[2] = { UNICODE_LRO, UNICODE_RLO };
     aString.Append(overrides[(aFlags & gfxTextRunFactory::TEXT_IS_RTL) != 0]);
 }
 
+// Returns the number of trailing characters that should be part of the
+// layout but should be ignored
+static PRUint32
+AppendDirectionalIndicatorEnd(PRBool aNeedDirection, nsAString& aString)
+{
+    // Ensure that we compute the full advance width for the last character
+    // in the string --- we don't want that character to form a kerning
+    // pair (or a ligature) with the '.' we may append next,
+    // so we append a space now.
+    // Even if the character is the last character in the layout,
+    // we want its width to be determined as if it had a space after it,
+    // for consistency with the bidi path and during textrun caching etc.
+    aString.Append(' ');
+    if (!aNeedDirection)
+        return 1;
+
+    // Ensure that none of the whitespace in the run is considered "trailing"
+    // by ATSUI's bidi algorithm
+    aString.Append('.');
+    aString.Append(UNICODE_PDF);
+    return 2;
+}
+
 /**
  * Given a textrun and an offset into that textrun, we need to choose a length
  * for the substring of the textrun that we should analyze next. The length
  * should be <= aMaxLength if possible. It must always end at a cluster
  * boundary and it should end at the end of the textrun or at the
  * boundary of a space if possible.
  */
 static PRUint32
@@ -641,23 +664,26 @@ gfxAtsuiFontGroup::MakeTextRun(const PRU
     PRUint32 maxLen;
     nsAutoString utf16;
     for (maxLen = GuessMaximumStringLength(); maxLen > 0; maxLen /= 2) {
         PRUint32 start = 0;
         while (start < aLength) {
             PRUint32 len = FindTextRunSegmentLength(textRun, start, maxLen);
 
             utf16.Truncate();
-            AppendDirectionalIndicator(aFlags, utf16);
+            AppendDirectionalIndicatorStart(aFlags, utf16);
+            PRUint32 layoutStart = utf16.Length();
             utf16.Append(aString + start, len);
             // Ensure that none of the whitespace in the run is considered "trailing"
             // by ATSUI's bidi algorithm
-            utf16.Append('.');
-            utf16.Append(UNICODE_PDF);
-            if (!InitTextRun(textRun, utf16.get(), utf16.Length(), PR_TRUE,
+            PRUint32 trailingCharsToIgnore =
+                AppendDirectionalIndicatorEnd(PR_TRUE, utf16);
+            PRUint32 layoutLength = len + trailingCharsToIgnore;
+            if (!InitTextRun(textRun, utf16.get(), utf16.Length(),
+                             layoutStart, layoutLength,
                              start, len) && maxLen > 1)
                 break;
             start += len;
         }
         if (start == aLength)
             break;
         textRun->ResetGlyphRuns();
     }
@@ -683,24 +709,25 @@ gfxAtsuiFontGroup::MakeTextRun(const PRU
         while (start < aLength) {
             PRUint32 len = FindTextRunSegmentLength(textRun, start, maxLen);
 
             nsDependentCSubstring cString(reinterpret_cast<const char*>(aString + start),
                                           reinterpret_cast<const char*>(aString + start + len));
             utf16.Truncate();
             PRBool wrapBidi = (aFlags & TEXT_IS_RTL) != 0;
             if (wrapBidi) {
-              AppendDirectionalIndicator(aFlags, utf16);
+                AppendDirectionalIndicatorStart(aFlags, utf16);
             }
+            PRUint32 layoutStart = utf16.Length();
             AppendASCIItoUTF16(cString, utf16);
-            if (wrapBidi) {
-              utf16.Append('.');
-              utf16.Append(UNICODE_PDF);
-            }
-            if (!InitTextRun(textRun, utf16.get(), utf16.Length(), wrapBidi,
+            PRUint32 trailingCharsToIgnore =
+                AppendDirectionalIndicatorEnd(wrapBidi, utf16);
+            PRUint32 layoutLength = len + trailingCharsToIgnore;
+            if (!InitTextRun(textRun, utf16.get(), utf16.Length(),
+                             layoutStart, layoutLength,
                              start, len) && maxLen > 1)
                 break;
             start += len;
         }
         if (start == aLength)
             break;
         textRun->ResetGlyphRuns();
     }
@@ -917,17 +944,17 @@ GetAdvanceAppUnits(ATSLayoutRecord *aGly
 /**
  * Given a run of ATSUI glyphs that should be treated as a single cluster/ligature,
  * store them in the textrun at the appropriate character and set the
  * other characters involved to be ligature/cluster continuations as appropriate.
  */
 static void
 SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount,
                            Fixed *aBaselineDeltas, PRUint32 aAppUnitsPerDevUnit,
-                           gfxTextRun *aRun, PRUint32 aSegmentStart,
+                           gfxTextRun *aRun, PRUint32 aOffsetInTextRun,
                            const PRPackedBool *aUnmatched,
                            const PRUnichar *aString,
                            const PRUint32 aLength)
 {
     NS_ASSERTION(aGlyphCount > 0, "Must set at least one glyph");
     PRUint32 firstOffset = aGlyphs[0].originalOffset;
     PRUint32 lastOffset = firstOffset;
     PRUint32 i;
@@ -957,42 +984,42 @@ SetGlyphsForCharacterGroup(ATSLayoutReco
                  "Invalid char passed in");
 
     if (!allMatched) {
         for (i = firstOffset; i <= lastOffset; ++i) {
             PRUint32 index = i/2;
             if (NS_IS_HIGH_SURROGATE(aString[index]) &&
                 index + 1 < aLength &&
                 NS_IS_LOW_SURROGATE(aString[index + 1])) {
-                aRun->SetMissingGlyph(aSegmentStart + index,
+                aRun->SetMissingGlyph(aOffsetInTextRun + index,
                                       SURROGATE_TO_UCS4(aString[index],
                                                         aString[index + 1]));
             } else {
-                aRun->SetMissingGlyph(aSegmentStart + index, aString[index]);
+                aRun->SetMissingGlyph(aOffsetInTextRun + index, aString[index]);
             }
         }
         return;
     }
 
     gfxTextRun::CompressedGlyph g;
     PRUint32 offset;
     // Make all but the first character in the group NOT be a ligature boundary,
     // i.e. fuse the group into a ligature.
     // Also make them not be cluster boundaries, i.e., fuse them into a cluster,
     // if the glyphs are out of character order.
     for (offset = firstOffset + 2; offset <= lastOffset; offset += 2) {
-        PRUint32 charIndex = aSegmentStart + offset/2;
+        PRUint32 charIndex = aOffsetInTextRun + offset/2;
         PRBool makeClusterStart = inOrder && aRun->IsClusterStart(charIndex);
         g.SetComplex(makeClusterStart, PR_FALSE, 0);
         aRun->SetGlyphs(charIndex, g, nsnull);
     }
 
     // Grab total advance for all glyphs
     PRInt32 advance = GetAdvanceAppUnits(aGlyphs, aGlyphCount, aAppUnitsPerDevUnit);
-    PRUint32 charIndex = aSegmentStart + firstOffset/2;
+    PRUint32 charIndex = aOffsetInTextRun + firstOffset/2;
     if (regularGlyphCount == 1) {
         if (advance >= 0 &&
             (!aBaselineDeltas || aBaselineDeltas[displayGlyph - aGlyphs] == 0) &&
             gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
             gfxTextRun::CompressedGlyph::IsSimpleGlyphID(displayGlyph->glyphID) &&
             aRun->IsClusterStart(charIndex)) {
             aRun->SetSimpleGlyph(charIndex, g.SetSimpleGlyph(advance, displayGlyph->glyphID));
             return;
@@ -1000,17 +1027,17 @@ SetGlyphsForCharacterGroup(ATSLayoutReco
     }
 
     nsAutoTArray<gfxTextRun::DetailedGlyph,10> detailedGlyphs;
     ATSLayoutRecord *advanceStart = aGlyphs;
     for (i = 0; i < aGlyphCount; ++i) {
         ATSLayoutRecord *glyph = &aGlyphs[i];
         if (glyph->glyphID != ATSUI_SPECIAL_GLYPH_ID) {
             if (glyph->originalOffset > firstOffset) {
-                PRUint32 glyphCharIndex = aSegmentStart + glyph->originalOffset/2;
+                PRUint32 glyphCharIndex = aOffsetInTextRun + glyph->originalOffset/2;
                 PRUint32 glyphRunIndex = aRun->FindFirstGlyphRunContaining(glyphCharIndex);
                 PRUint32 numGlyphRuns;
                 const gfxTextRun::GlyphRun *glyphRun = aRun->GetGlyphRuns(&numGlyphRuns) + glyphRunIndex;
 
                 if (glyphRun->mCharacterOffset > charIndex) {
                     // The font has changed inside the character group. This might
                     // happen in some weird situations, e.g. if
                     // ATSUI decides in LTR text to put the glyph for character
@@ -1043,17 +1070,17 @@ SetGlyphsForCharacterGroup(ATSLayoutReco
                                        aAppUnitsPerDevUnit);
             }
             details->mYOffset = !aBaselineDeltas ? 0.0f
                 : FixedToFloat(aBaselineDeltas[i])*aAppUnitsPerDevUnit;
         }
     }
     if (detailedGlyphs.Length() == 0) {
         NS_WARNING("No glyphs visible at all!");
-        aRun->SetGlyphs(aSegmentStart + charIndex, g.SetMissing(0), nsnull);
+        aRun->SetGlyphs(aOffsetInTextRun + charIndex, g.SetMissing(0), nsnull);
         return;
     }
 
     // The advance width for the whole cluster
     PRInt32 clusterAdvance = GetAdvanceAppUnits(aGlyphs, aGlyphCount, aAppUnitsPerDevUnit);
     if (aRun->IsRightToLeft())
         detailedGlyphs[0].mAdvance = clusterAdvance;
     else
@@ -1062,19 +1089,19 @@ SetGlyphsForCharacterGroup(ATSLayoutReco
     aRun->SetGlyphs(charIndex, g, detailedGlyphs.Elements());
 }
 
 /**
  * Returns true if there are overrunning glyphs
  */
 static PRBool
 PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
-                   const PRUnichar *aString, PRBool aWrapped,
-                   const PRPackedBool *aUnmatched,
-                   PRUint32 aSegmentStart, PRUint32 aSegmentLength)
+                   const PRUnichar *aString, PRUint32 aLayoutLength,
+                   PRUint32 aOffsetInTextRun, PRUint32 aLengthInTextRun,
+                   const PRPackedBool *aUnmatched)
 {
     // AutoLayoutDataArrayPtr advanceDeltasArray(aLine, kATSUDirectDataAdvanceDeltaFixedArray);
     // Fixed *advanceDeltas = static_cast<Fixed *>(advanceDeltasArray.mArray);
     // AutoLayoutDataArrayPtr deviceDeltasArray(aLine, kATSUDirectDataDeviceDeltaSInt16Array);
     AutoLayoutDataArrayPtr baselineDeltasArray(aLine, kATSUDirectDataBaselineDeltaFixedArray);
     Fixed *baselineDeltas = static_cast<Fixed *>(baselineDeltasArray.mArray);
     AutoLayoutDataArrayPtr glyphRecordsArray(aLine, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent);
 
@@ -1090,28 +1117,29 @@ PostLayoutCallback(ATSULineRef aLine, gf
                  "Last glyph should be a terminator glyph");
     --numGlyphs;
     if (numGlyphs == 0)
         return PR_FALSE;
 
     PRUint32 appUnitsPerDevUnit = aRun->GetAppUnitsPerDevUnit();
     PRBool isRTL = aRun->IsRightToLeft();
 
-    if (aWrapped) {
+    PRUint32 trailingCharactersToIgnore = aLayoutLength - aLengthInTextRun;
+    if (trailingCharactersToIgnore > 0) {
         // The glyph array includes a glyph for the artificial trailing
         // non-whitespace character. Strip that glyph from the array now.
         if (isRTL) {
-            NS_ASSERTION(glyphRecords[0].originalOffset == aSegmentLength*2,
+            NS_ASSERTION(glyphRecords[trailingCharactersToIgnore - 1].originalOffset == aLengthInTextRun*2,
                          "Couldn't find glyph for trailing marker");
-            glyphRecords++;
+            glyphRecords += trailingCharactersToIgnore;
         } else {
-            NS_ASSERTION(glyphRecords[numGlyphs - 1].originalOffset == aSegmentLength*2,
+            NS_ASSERTION(glyphRecords[numGlyphs - trailingCharactersToIgnore].originalOffset == aLengthInTextRun*2,
                          "Couldn't find glyph for trailing marker");
         }
-        --numGlyphs;
+        numGlyphs -= trailingCharactersToIgnore;
         if (numGlyphs == 0)
             return PR_FALSE;
     }
 
     PRUint32 allFlags = 0;
     // Now process the glyphs, which should basically be in
     // the textrun's desired order, so process them in textrun order
     PRInt32 direction = PRInt32(aRun->GetDirection());
@@ -1148,65 +1176,64 @@ PostLayoutCallback(ATSULineRef aLine, gf
                 lastOffset = glyphOffset;
             }
             ++glyphCount;
         }
         if (isRTL) {
             SetGlyphsForCharacterGroup(glyphRecords + numGlyphs - glyphCount,
                                        glyphCount,
                                        baselineDeltas ? baselineDeltas + numGlyphs - glyphCount : nsnull,
-                                       appUnitsPerDevUnit, aRun, aSegmentStart,
-                                       aUnmatched, aString, aSegmentLength);
+                                       appUnitsPerDevUnit, aRun, aOffsetInTextRun,
+                                       aUnmatched, aString, aLengthInTextRun);
         } else {
             SetGlyphsForCharacterGroup(glyphRecords,
                                        glyphCount, baselineDeltas,
-                                       appUnitsPerDevUnit, aRun, aSegmentStart,
-                                       aUnmatched, aString, aSegmentLength);
+                                       appUnitsPerDevUnit, aRun, aOffsetInTextRun,
+                                       aUnmatched, aString, aLengthInTextRun);
             glyphRecords += glyphCount;
             if (baselineDeltas) {
                 baselineDeltas += glyphCount;
             }
         }
         numGlyphs -= glyphCount;
     }
     
     return (allFlags & ATSUI_OVERRUNNING_GLYPH_FLAG) != 0;
 }
 
 struct PostLayoutCallbackClosure {
     gfxTextRun                  *mTextRun;
     const PRUnichar             *mString;
+    PRUint32                     mLayoutLength;
+    PRUint32                     mOffsetInTextRun;
+    PRUint32                     mLengthInTextRun;
     // Either null or an array of stringlength booleans set to true for
     // each character that did not match any fonts
     nsAutoArrayPtr<PRPackedBool> mUnmatchedChars;
-    PRUint32                     mSegmentStart;
-    PRUint32                     mSegmentLength;
-    // This is true when we inserted an artifical trailing character at the
-    // end of the string when computing the ATSUI layout.
-    PRPackedBool                 mWrapped;
     // The callback *sets* this to indicate whether there were overrunning glyphs
     PRPackedBool                 mOverrunningGlyphs;
 };
 
 // This is really disgusting, but the ATSUI refCon thing is also disgusting
 static PostLayoutCallbackClosure *gCallbackClosure = nsnull;
 
 static OSStatus
 PostLayoutOperationCallback(ATSULayoutOperationSelector iCurrentOperation, 
                             ATSULineRef iLineRef, 
                             UInt32 iRefCon, 
                             void *iOperationCallbackParameterPtr, 
                             ATSULayoutOperationCallbackStatus *oCallbackStatus)
 {
     gCallbackClosure->mOverrunningGlyphs =
         PostLayoutCallback(iLineRef, gCallbackClosure->mTextRun,
-                           gCallbackClosure->mString, gCallbackClosure->mWrapped,
-                           gCallbackClosure->mUnmatchedChars,
-                           gCallbackClosure->mSegmentStart,
-                           gCallbackClosure->mSegmentLength);
+                           gCallbackClosure->mString,
+                           gCallbackClosure->mLayoutLength,
+                           gCallbackClosure->mOffsetInTextRun,
+                           gCallbackClosure->mLengthInTextRun,
+                           gCallbackClosure->mUnmatchedChars);
     *oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
     return noErr;
 }
 
 // xxx - leaving this here for now, probably belongs in platform code somewhere
 
 eFontPrefLang
 GetFontPrefLangFor(PRUint8 aUnicodeRange)
@@ -1402,66 +1429,67 @@ SetLayoutRangeToFont(ATSUTextLayout layo
     ATSUSetRunStyle (layout, subStyle, offset, length);
 
     return subStyle;
 }
 
 PRBool
 gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
                                const PRUnichar *aString, PRUint32 aLength,
-                               PRBool aWrapped, PRUint32 aSegmentStart,
-                               PRUint32 aSegmentLength)
+                               PRUint32 aLayoutStart, PRUint32 aLayoutLength,
+                               PRUint32 aOffsetInTextRun, PRUint32 aLengthInTextRun)
 {
     OSStatus status;
     gfxAtsuiFont *firstFont = GetFontAt(0);
     ATSUStyle mainStyle = firstFont->GetATSUStyle();
     nsTArray<ATSUStyle> stylesToDispose;
-    PRUint32 headerChars = aWrapped ? 1 : 0;
-    const PRUnichar *realString = aString + headerChars;
-    NS_ASSERTION(aSegmentLength == aLength - (aWrapped ? 3 : 0),
-                 "Length mismatch");
+    const PRUnichar *layoutString = aString + aLayoutStart;
 
 #ifdef DUMP_TEXT_RUNS
-    NS_ConvertUTF16toUTF8 str(realString, aSegmentLength);
+    NS_ConvertUTF16toUTF8 str(layoutString, aLengthInTextRun);
     NS_ConvertUTF16toUTF8 families(mFamilies);
-    PR_LOG(gAtsuiTextRunLog, PR_LOG_DEBUG, ("InitTextRun %p fontgroup %p (%s) lang: %s len %d TEXTRUN \"%s\" ENDTEXTRUN\n", aRun, this, families.get(), mStyle.langGroup.get(), aSegmentLength, str.get()) );
-    PR_LOG(gAtsuiTextRunLog, PR_LOG_DEBUG, ("InitTextRun font: %s\n", NS_ConvertUTF16toUTF8(firstFont->GetUniqueName()).get()) );
+    PR_LOG(gAtsuiTextRunLog, PR_LOG_DEBUG,\
+           ("InitTextRun %p fontgroup %p (%s) lang: %s len %d TEXTRUN \"%s\" ENDTEXTRUN\n",
+            aRun, this, families.get(), mStyle.langGroup.get(), aLengthInTextRun, str.get()) );
+    PR_LOG(gAtsuiTextRunLog, PR_LOG_DEBUG,
+           ("InitTextRun font: %s\n",
+            NS_ConvertUTF16toUTF8(firstFont->GetUniqueName()).get()) );
 #endif
 
     if (aRun->GetFlags() & TEXT_DISABLE_OPTIONAL_LIGATURES) {
         status = ATSUCreateAndCopyStyle(mainStyle, &mainStyle);
         if (status == noErr) {
             stylesToDispose.AppendElement(mainStyle);
             DisableOptionalLigaturesInStyle(mainStyle);
         }
     }
 
-    UniCharCount runLengths = aSegmentLength;
+    UniCharCount runLengths = aLengthInTextRun;
     ATSUTextLayout layout;
     // Create the text layout for the whole string, but only produce glyphs
     // for the text inside LRO/RLO - PDF, if present. For wrapped strings
     // we do need to produce glyphs for the trailing non-whitespace
     // character to ensure that ATSUI treats all whitespace as non-trailing.
     status = ATSUCreateTextLayoutWithTextPtr
         (aString,
-         headerChars,
-         aSegmentLength + (aWrapped ? 1 : 0),
+         aLayoutStart,
+         aLayoutLength,
          aLength,
          1,
          &runLengths,
          &mainStyle,
          &layout);
     // XXX error check here?
 
     PostLayoutCallbackClosure closure;
     closure.mTextRun = aRun;
-    closure.mString = realString;
-    closure.mWrapped = aWrapped;
-    closure.mSegmentStart = aSegmentStart;
-    closure.mSegmentLength = aSegmentLength;
+    closure.mString = layoutString;
+    closure.mLayoutLength = aLayoutLength;
+    closure.mOffsetInTextRun = aOffsetInTextRun;
+    closure.mLengthInTextRun = aLengthInTextRun;
     NS_ASSERTION(!gCallbackClosure, "Reentering InitTextRun? Expect disaster!");
     gCallbackClosure = &closure;
 
     ATSULayoutOperationOverrideSpecifier override;
     override.operationSelector = kATSULayoutOperationPostLayoutAdjustment;
     override.overrideUPP = PostLayoutOperationCallback;
 
     // Set up our layout attributes
@@ -1485,19 +1513,19 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRu
                           layoutTags,
                           layoutArgSizes,
                           layoutArgs);
 
     /* Now go through and update the styles for the text, based on font matching. */
 
     nsAutoArrayPtr<PRUnichar> mirroredStr;
 
-    UniCharArrayOffset runStart = headerChars;
-    UniCharCount runLength = aSegmentLength;
-    UniCharCount totalLength = headerChars + aSegmentLength;
+    UniCharArrayOffset runStart = aLayoutStart;
+    UniCharCount runLength = aLengthInTextRun;
+    UniCharCount totalLength = aLayoutStart + aLengthInTextRun;
     
     /// ---- match fonts using cmap info instead of ATSUI ----
     
     CmapFontMatcher fontMatcher(aString, runStart, runStart + runLength, this);
     
     while (runStart < totalLength) {
         gfxAtsuiFont *matchedFont;
         UniCharCount  matchedLength;
@@ -1514,42 +1542,42 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRu
         // in the RTL case, handle fallback mirroring
         if (aRun->IsRightToLeft() && matchedFont && !matchedFont->HasMirroringInfo()) {
             MirrorSubstring(layout, mirroredStr, aString, aLength, runStart, runLength);
         }       
 
         // if no matched font, mark as unmatched
         if (!matchedFont) {
         
-            aRun->AddGlyphRun(firstFont, aSegmentStart + runStart - headerChars, PR_TRUE);
+            aRun->AddGlyphRun(firstFont, aOffsetInTextRun + runStart - aLayoutStart, PR_TRUE);
             
             if (!closure.mUnmatchedChars) {
                 closure.mUnmatchedChars = new PRPackedBool[aLength];
                 if (closure.mUnmatchedChars) {
                     //printf("initializing %d\n", aLength);
                     memset(closure.mUnmatchedChars.get(), PR_FALSE, aLength);
                 }
             }
     
             if (closure.mUnmatchedChars) {
                 //printf("setting %d unmatched from %d\n", matchedLength, runStart - headerChars);
-                memset(closure.mUnmatchedChars.get() + runStart - headerChars,
+                memset(closure.mUnmatchedChars.get() + runStart - aLayoutStart,
                        PR_TRUE, matchedLength);
             }
             
         } else {
         
             if (matchedFont != firstFont) {
                 // create a new sub-style and add it to the layout
                 ATSUStyle subStyle = SetLayoutRangeToFont(layout, mainStyle, runStart, matchedLength, matchedFont->GetATSUFontID());
                 stylesToDispose.AppendElement(subStyle);
             }
 
             // add a glyph run for the matched substring
-            aRun->AddGlyphRun(matchedFont, aSegmentStart + runStart - headerChars, PR_TRUE);
+            aRun->AddGlyphRun(matchedFont, aOffsetInTextRun + runStart - aLayoutStart, PR_TRUE);
         }
         
         runStart += matchedLength;
         runLength -= matchedLength;    
     }
     
 
     /// -------------------------------------------------
@@ -1557,22 +1585,22 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRu
     // xxx - for some reason, this call appears to be needed to avoid assertions about glyph runs not being coalesced properly
     //       this appears to occur when there are unmatched characters in the text run
     aRun->SortGlyphRuns();
 
     // Trigger layout so that our callback fires. We don't actually care about
     // the result of this call.
     ATSTrapezoid trap;
     ItemCount trapCount;
-    ATSUGetGlyphBounds(layout, 0, 0, headerChars, aSegmentLength,
+    ATSUGetGlyphBounds(layout, 0, 0, aLayoutStart, aLengthInTextRun,
                        kATSUseFractionalOrigins, 1, &trap, &trapCount); 
 
     ATSUDisposeTextLayout(layout);
 
-    aRun->AdjustAdvancesForSyntheticBold(aSegmentStart, aSegmentLength);
+    aRun->AdjustAdvancesForSyntheticBold(aOffsetInTextRun, aLengthInTextRun);
     
     PRUint32 i;
     for (i = 0; i < stylesToDispose.Length(); ++i) {
         ATSUDisposeStyle(stylesToDispose[i]);
     }
     gCallbackClosure = nsnull;
     return !closure.mOverrunningGlyphs;
 }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/427730-1-ref.html
@@ -0,0 +1,5 @@
+<html>
+<body style="font-size:78px;">
+<p><span style="border:1px dotted red;">T</span>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/427730-1.html
@@ -0,0 +1,5 @@
+<html>
+<body style="font-size:78px;">
+<p>&#x200b;<span style="border:1px dotted red;">T</span>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -799,8 +799,10 @@ fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") == 
 == 425972-2.html 425972-2-ref.html
 != 425972-1.html 425972-2.html
 != 427017-1.xhtml about:blank    # crash test (needs reftest-print)
 == 427129-scrollframe.html 427129-ref.html
 == 427129-table.html 427129-ref.html
 == 427129-image.html 427129-ref.html
 == 427129-table-caption.html 427129-table-caption-ref.html
 == 427370-1.html 427370-1-ref.html
+== 427730-1.html 427730-1-ref.html
+