bug 703100 - pt 2.4 - adapt Linux/Pango font code to work with gfxShapedWord caches. r=roc
authorJonathan Kew <jfkthame@gmail.com>
Tue, 06 Dec 2011 12:39:19 +0000
changeset 85100 fd151d941e295d92fbee5627831321459e5d3920
parent 85099 4bc3a3ee012de9147e3e5642873ff7611e5e66c6
child 85101 561d067101078fa0b209de589c5d1ea57ac7e140
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs703100
milestone12.0a1
bug 703100 - pt 2.4 - adapt Linux/Pango font code to work with gfxShapedWord caches. r=roc
gfx/thebes/gfxPangoFonts.cpp
--- a/gfx/thebes/gfxPangoFonts.cpp
+++ b/gfx/thebes/gfxPangoFonts.cpp
@@ -114,18 +114,19 @@ struct gfxPangoFcFont;
 int moz_pango_units_from_double(double d) {
     return NS_lround(d * FLOAT_PANGO_SCALE);
 }
 
 static PangoLanguage *GuessPangoLanguage(nsIAtom *aLanguage);
 
 static cairo_scaled_font_t *
 CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace);
-static void SetMissingGlyphs(gfxTextRun *aTextRun, const gchar *aUTF8,
-                             PRUint32 aUTF8Length, PRUint32 *aUTF16Offset);
+static void SetMissingGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8,
+                             PRUint32 aUTF8Length, PRUint32 *aUTF16Offset,
+                             gfxFont *aFont);
 
 static PangoFontMap *gPangoFontMap;
 static PangoFontMap *GetPangoFontMap();
 static bool gUseFontMapProperty;
 
 static FT_Library gFTLibrary;
 
 template <class T>
@@ -320,17 +321,17 @@ gfxFcFontEntry::ShouldUseHarfBuzz(PRInt3
         FcPatternGetString(mPatterns[0],
                            FC_CAPABILITY, 0, &capability) == FcResultNoMatch ||
         !FcStrStr(capability, gfxFontconfigUtils::ToFcChar8("ttable:Silf")))
     {
         mSkipGraphiteCheck = true;
         return true;
     }
 
-    // Mimicing gfxHarfBuzzShaper::InitTextRun
+    // Mimicing gfxHarfBuzzShaper::ShapeWord
     hb_script_t script =
         aRunScript <= HB_SCRIPT_INHERITED ? HB_SCRIPT_LATIN
         : static_cast<hb_script_t>(aRunScript);
 
     // Prefer HarfBuzz if the font also has support for OpenType shaping of
     // this script.
     const FcChar8 otCapTemplate[] = "otlayout:XXXX";
     FcChar8 otCap[NS_ARRAY_LENGTH(otCapTemplate)];
@@ -790,42 +791,37 @@ gfxDownloadedFcFontEntry::GetPangoCovera
  * This is a gfxFont implementation using a CAIRO_FONT_TYPE_FT
  * cairo_scaled_font created from an FcPattern.
  */
 
 class gfxFcFont : public gfxFT2FontBase {
 public:
     virtual ~gfxFcFont();
     static already_AddRefed<gfxFcFont>
-    GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern);
+    GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern,
+                  const gfxFontStyle *aFontStyle);
 
     // The PangoFont returned is owned by the gfxFcFont
     PangoFont *GetPangoFont() {
         if (!mPangoFont) {
             MakePangoFont();
         }
         return mPangoFont;
     }
 
 protected:
-    virtual bool InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript,
-                               bool aPreferPlatformShaping);
-
-    bool InitGlyphRunWithPango(gfxTextRun *aTextRun,
-                                 const PRUnichar *aString,
-                                 PRUint32 aRunStart, PRUint32 aRunLength,
-                                 PangoScript aScript);
+    virtual bool ShapeWord(gfxContext *aContext,
+                           gfxShapedWord *aShapedWord,
+                           const PRUnichar *aString,
+                           bool aPreferPlatformShaping);
+
+    bool InitGlyphRunWithPango(gfxShapedWord *aTextRun,
+                               const PRUnichar *aString);
 
 private:
-    static already_AddRefed<gfxFcFont> GetOrMakeFont(FcPattern *aPattern);
     gfxFcFont(cairo_scaled_font_t *aCairoFont, gfxFcFontEntry *aFontEntry,
               const gfxFontStyle *aFontStyle);
 
     void MakePangoFont();
 
     PangoFont *mPangoFont;
 
     // key for locating a gfxFcFont corresponding to a cairo_scaled_font
@@ -1199,26 +1195,27 @@ public:
     {
         bool waitForUserFont;
         mFcFontSet = SortPreferredFonts(waitForUserFont);
         mWaitingForUserFont = waitForUserFont;
     }
 
     // A reference is held by the FontSet.
     // The caller may add a ref to keep the font alive longer than the FontSet.
-    gfxFcFont *GetFontAt(PRUint32 i)
+    gfxFcFont *GetFontAt(PRUint32 i, const gfxFontStyle *aFontStyle)
     {
         if (i >= mFonts.Length() || !mFonts[i].mFont) { 
             // GetFontPatternAt sets up mFonts
             FcPattern *fontPattern = GetFontPatternAt(i);
             if (!fontPattern)
                 return NULL;
 
             mFonts[i].mFont =
-                gfxFcFont::GetOrMakeFont(mSortPattern, fontPattern);
+                gfxFcFont::GetOrMakeFont(mSortPattern, fontPattern,
+                                         aFontStyle);
         }
         return mFonts[i].mFont;
     }
 
     FcPattern *GetFontPatternAt(PRUint32 i);
 
     bool WaitingForUserFont() const {
         return mWaitingForUserFont;
@@ -1940,17 +1937,17 @@ gfxPangoFontGroup::GetFcFamilies(nsTArra
     ForEachFontInternal(mFamilies, aLanguage, true, false, true,
                         FamilyCallback, &data);
 }
 
 gfxFcFont *
 gfxPangoFontGroup::GetBaseFont()
 {
     if (!mFonts[0]) {
-        mFonts[0] = GetBaseFontSet()->GetFontAt(0);
+        mFonts[0] = GetBaseFontSet()->GetFontAt(0, GetStyle());
     }
 
     return static_cast<gfxFcFont*>(mFonts[0].get());
 }
 
 gfxFont *
 gfxPangoFontGroup::GetFontAt(PRInt32 i) {
     // If it turns out to be hard for all clients that cache font
@@ -2136,17 +2133,17 @@ gfxPangoFontGroup::FindFontForChar(PRUin
          FcPattern *pattern = fontSet->GetFontPatternAt(i);
          ++i) {
         if (pattern == basePattern) {
             continue; // already checked basePattern
         }
 
         if (HasChar(pattern, aCh)) {
             *aMatchType = gfxTextRange::kFontGroup;
-            return nsRefPtr<gfxFont>(fontSet->GetFontAt(i)).forget();
+            return nsRefPtr<gfxFont>(fontSet->GetFontAt(i, GetStyle())).forget();
         }
     }
 
     return nsnull;
 }
 
 /**
  ** gfxFcFont
@@ -2197,62 +2194,53 @@ gfxFcFont::~gfxFcFont()
     cairo_scaled_font_set_user_data(mScaledFont, &sGfxFontKey, NULL, NULL);
     if (mPangoFont) {
         g_object_remove_toggle_ref(G_OBJECT(mPangoFont),
                                    PangoFontToggleNotify, this);
     }
 }
 
 bool
-gfxFcFont::InitTextRun(gfxContext *aContext,
-                       gfxTextRun *aTextRun,
-                       const PRUnichar *aString,
-                       PRUint32 aRunStart,
-                       PRUint32 aRunLength,
-                       PRInt32 aRunScript,
-                       bool aPreferPlatformShaping)
+gfxFcFont::ShapeWord(gfxContext *aContext,
+                     gfxShapedWord *aShapedWord,
+                     const PRUnichar *aString,
+                     bool aPreferPlatformShaping)
 {
     gfxFcFontEntry *fontEntry = static_cast<gfxFcFontEntry*>(GetFontEntry());
 
 #ifdef MOZ_GRAPHITE
     if (FontCanSupportGraphite()) {
         if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
             if (!mGraphiteShaper) {
                 mGraphiteShaper = new gfxGraphiteShaper(this);
             }
-            if (mGraphiteShaper->InitTextRun(aContext, aTextRun, aString,
-                                             aRunStart, aRunLength,
-                                             aRunScript)) {
+            if (mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString)) {
                 return true;
             }
         }
     }
 #endif
 
-    if (fontEntry->ShouldUseHarfBuzz(aRunScript)) {
+    if (fontEntry->ShouldUseHarfBuzz(aShapedWord->Script())) {
         if (!mHarfBuzzShaper) {
             gfxFT2LockedFace face(this);
             mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
             // Used by gfxHarfBuzzShaper, currently only for kerning
             mFUnitsConvFactor = face.XScale();
         }
-        if (mHarfBuzzShaper->
-            InitTextRun(aContext, aTextRun, aString,
-                        aRunStart, aRunLength, aRunScript)) {
+        if (mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString)) {
             return true;
         }
 
         // Wrong font type for HarfBuzz
         fontEntry->SkipHarfBuzz();
         mHarfBuzzShaper = nsnull;
     }
 
-    const PangoScript script = static_cast<PangoScript>(aRunScript);
-    bool ok = InitGlyphRunWithPango(aTextRun,
-                                      aString, aRunStart, aRunLength, script);
+    bool ok = InitGlyphRunWithPango(aShapedWord, aString);
 
     NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
     return ok;
 }
 
 /* static */ void
 gfxPangoFontGroup::Shutdown()
 {
@@ -2388,17 +2376,18 @@ GetPixelSize(FcPattern *aPattern)
  * The point of this is to record the exact font face for gfxTextRun glyph
  * indices.  The style of this font does not necessarily represent the exact
  * gfxFontStyle used to build the text run.  Notably, the language is not
  * recorded.
  */
 
 /* static */
 already_AddRefed<gfxFcFont>
-gfxFcFont::GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern)
+gfxFcFont::GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern,
+                         const gfxFontStyle *aFontStyle)
 {
     nsAutoRef<FcPattern> renderPattern
         (FcFontRenderPrepare(NULL, aRequestedPattern, aFontPattern));
     cairo_font_face_t *face =
         cairo_ft_font_face_create_for_pattern(renderPattern);
 
     // Reuse an existing font entry if available.
     nsRefPtr<gfxFcFontEntry> fe = gfxFcFontEntry::LookupFontEntry(face);
@@ -2439,53 +2428,40 @@ gfxFcFont::GetOrMakeFont(FcPattern *aReq
                     name.AppendInt(index);
                 }
             }
 
             fe = new gfxSystemFcFontEntry(face, aFontPattern, name);
         }
     }
 
-    cairo_scaled_font_t *cairoFont = CreateScaledFont(renderPattern, face);
-
-    nsRefPtr<gfxFcFont> font = static_cast<gfxFcFont*>
-        (cairo_scaled_font_get_user_data(cairoFont, &sGfxFontKey));
-
+    gfxFontStyle style(*aFontStyle);
+    style.size = GetPixelSize(renderPattern);
+    style.style = gfxFontconfigUtils::GetThebesStyle(renderPattern);
+    style.weight = gfxFontconfigUtils::GetThebesWeight(renderPattern);
+
+    nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(fe, &style);
     if (!font) {
-        gfxFloat size = GetPixelSize(renderPattern);
-
-        // Shouldn't actually need to take too much care about the correct
-        // name or style, as size is the only thing expected to be important.
-        PRUint8 style = gfxFontconfigUtils::GetThebesStyle(renderPattern);
-        PRUint16 weight = gfxFontconfigUtils::GetThebesWeight(renderPattern);
-
-        // The LangSet in the FcPattern does not have an order so there is no
-        // one particular language to choose and converting the set to a
-        // string through FcNameUnparse() is more trouble than it's worth.
-        nsIAtom *language = gfxAtoms::en; // TODO: get the correct language?
-        // FIXME: Pass a real stretch based on renderPattern!
-        gfxFontStyle fontStyle(style, weight, NS_FONT_STRETCH_NORMAL,
-                               size, language, 0.0,
-                               true, false,
-                               NS_LITERAL_STRING(""),
-                               NS_LITERAL_STRING(""));
-
         // Note that a file/index pair (or FT_Face) and the gfxFontStyle are
         // not necessarily enough to provide a key that will describe a unique
         // font.  cairoFont contains information from renderPattern, which is a
         // fully resolved pattern from FcFontRenderPrepare.
         // FcFontRenderPrepare takes the requested pattern and the face
         // pattern as input and can modify elements of the resulting pattern
         // that affect rendering but are not included in the gfxFontStyle.
-        font = new gfxFcFont(cairoFont, fe, &fontStyle);
+        cairo_scaled_font_t *cairoFont = CreateScaledFont(renderPattern, face);
+        font = new gfxFcFont(cairoFont, fe, &style);
+        gfxFontCache::GetCache()->AddNew(font);
+        cairo_scaled_font_destroy(cairoFont);
     }
 
-    cairo_scaled_font_destroy(cairoFont);
     cairo_font_face_destroy(face);
-    return font.forget();
+
+    nsRefPtr<gfxFcFont> retval(static_cast<gfxFcFont*>(font.get()));
+    return retval.forget();
 }
 
 static PangoFontMap *
 GetPangoFontMap()
 {
     if (!gPangoFontMap) {
         // This is the same FontMap used by GDK, so that the same
         // PangoCoverage cache is shared.
@@ -2512,17 +2488,17 @@ gfxPangoFontGroup::GetBaseFontSet()
 
     mSizeAdjustFactor = 1.0; // will be adjusted below if necessary
     nsAutoRef<FcPattern> pattern;
     nsRefPtr<gfxFcFontSet> fontSet =
         MakeFontSet(mPangoLanguage, mSizeAdjustFactor, &pattern);
 
     double size = GetPixelSize(pattern);
     if (size != 0.0 && mStyle.sizeAdjust != 0.0) {
-        gfxFcFont *font = fontSet->GetFontAt(0);
+        gfxFcFont *font = fontSet->GetFontAt(0, GetStyle());
         if (font) {
             const gfxFont::Metrics& metrics = font->GetMetrics();
 
             // The factor of 0.1 ensures that xHeight is sane so fonts don't
             // become huge.  Strictly ">" ensures that xHeight and emHeight are
             // not both zero.
             if (metrics.xHeight > 0.1 * metrics.emHeight) {
                 mSizeAdjustFactor =
@@ -2742,141 +2718,93 @@ CreateScaledFont(FcPattern *aPattern, ca
 
     cairo_font_options_destroy(fontOptions);
 
     NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
                  "Failed to create scaled font");
     return scaledFont;
 }
 
-static void
-SetupClusterBoundaries(gfxTextRun* aTextRun, const gchar *aUTF8, PRUint32 aUTF8Length,
-                       PRUint32 aUTF16Offset, PangoAnalysis *aAnalysis)
-{
-    if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) {
-        // 8-bit text doesn't have clusters.
-        // XXX is this true in all languages???
-        // behdad: don't think so.  Czech for example IIRC has a
-        // 'ch' grapheme.
-        return;
-    }
-
-    // Pango says "the array of PangoLogAttr passed in must have at least N+1
-    // elements, if there are N characters in the text being broken".
-    // Could use g_utf8_strlen(aUTF8, aUTF8Length) + 1 but the memory savings
-    // may not be worth the call.
-    nsAutoTArray<PangoLogAttr,2000> buffer;
-    if (!buffer.AppendElements(aUTF8Length + 1))
-        return;
-
-    pango_break(aUTF8, aUTF8Length, aAnalysis,
-                buffer.Elements(), buffer.Length());
-
-    const gchar *p = aUTF8;
-    const gchar *end = aUTF8 + aUTF8Length;
-    const PangoLogAttr *attr = buffer.Elements();
-    gfxTextRun::CompressedGlyph g;
-    while (p < end) {
-        if (!attr->is_cursor_position) {
-            aTextRun->SetGlyphs(aUTF16Offset, g.SetComplex(false, true, 0), nsnull);
-        }
-        ++aUTF16Offset;
-        
-        gunichar ch = g_utf8_get_char(p);
-        NS_ASSERTION(ch != 0, "Shouldn't have NUL in pango_break");
-        NS_ASSERTION(!IS_SURROGATE(ch), "Shouldn't have surrogates in UTF8");
-        if (ch >= 0x10000) {
-            // set glyph info for the UTF-16 low surrogate
-            aTextRun->SetGlyphs(aUTF16Offset, g.SetComplex(false, false, 0), nsnull);
-            ++aUTF16Offset;
-        }
-        // We produced this utf8 so we don't need to worry about malformed stuff
-        p = g_utf8_next_char(p);
-        ++attr;
-    }
-}
-
 static PRInt32
 ConvertPangoToAppUnits(PRInt32 aCoordinate, PRUint32 aAppUnitsPerDevUnit)
 {
     PRInt64 v = (PRInt64(aCoordinate)*aAppUnitsPerDevUnit + PANGO_SCALE/2)/PANGO_SCALE;
     return PRInt32(v);
 }
 
 /**
  * Given a run of Pango 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 nsresult
 SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
-                           gfxTextRun *aTextRun,
+                           gfxShapedWord *aShapedWord,
                            const gchar *aUTF8, PRUint32 aUTF8Length,
                            PRUint32 *aUTF16Offset,
                            PangoGlyphUnit aOverrideSpaceWidth)
 {
     PRUint32 utf16Offset = *aUTF16Offset;
-    PRUint32 textRunLength = aTextRun->GetLength();
-    const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
-    const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
+    PRUint32 wordLength = aShapedWord->Length();
+    const PRUint32 appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
 
     // Override the width of a space, but only for spaces that aren't
     // clustered with something else (like a freestanding diacritical mark)
     PangoGlyphUnit width = aGlyphs[0].geometry.width;
     if (aOverrideSpaceWidth && aUTF8[0] == ' ' &&
-        (utf16Offset + 1 == textRunLength ||
-         charGlyphs[utf16Offset].IsClusterStart())) {
+        (utf16Offset + 1 == wordLength ||
+         aShapedWord->IsClusterStart(utf16Offset))) {
         width = aOverrideSpaceWidth;
     }
     PRInt32 advance = ConvertPangoToAppUnits(width, appUnitsPerDevUnit);
 
-    gfxTextRun::CompressedGlyph g;
-    bool atClusterStart = aTextRun->IsClusterStart(utf16Offset);
+    gfxShapedWord::CompressedGlyph g;
+    bool atClusterStart = aShapedWord->IsClusterStart(utf16Offset);
     // See if we fit in the compressed area.
     if (aGlyphCount == 1 && advance >= 0 && atClusterStart &&
         aGlyphs[0].geometry.x_offset == 0 &&
         aGlyphs[0].geometry.y_offset == 0 &&
         !IS_EMPTY_GLYPH(aGlyphs[0].glyph) &&
-        gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
-        gfxTextRun::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
-        aTextRun->SetSimpleGlyph(utf16Offset,
-                                 g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
+        gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
+        gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
+        aShapedWord->SetSimpleGlyph(utf16Offset,
+                                    g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
     } else {
-        nsAutoTArray<gfxTextRun::DetailedGlyph,10> detailedGlyphs;
+        nsAutoTArray<gfxShapedWord::DetailedGlyph,10> detailedGlyphs;
         if (!detailedGlyphs.AppendElements(aGlyphCount))
             return NS_ERROR_OUT_OF_MEMORY;
 
-        PRInt32 direction = aTextRun->IsRightToLeft() ? -1 : 1;
+        PRInt32 direction = aShapedWord->IsRightToLeft() ? -1 : 1;
         PRUint32 pangoIndex = direction > 0 ? 0 : aGlyphCount - 1;
         PRUint32 detailedIndex = 0;
         for (PRUint32 i = 0; i < aGlyphCount; ++i) {
             const PangoGlyphInfo &glyph = aGlyphs[pangoIndex];
             pangoIndex += direction;
             // The zero width characters return empty glyph ID at
             // shaping; we should skip these.
             if (IS_EMPTY_GLYPH(glyph.glyph))
                 continue;
 
-            gfxTextRun::DetailedGlyph *details = &detailedGlyphs[detailedIndex];
+            gfxShapedWord::DetailedGlyph *details = &detailedGlyphs[detailedIndex];
             ++detailedIndex;
 
             details->mGlyphID = glyph.glyph;
             NS_ASSERTION(details->mGlyphID == glyph.glyph,
                          "Seriously weird glyph ID detected!");
             details->mAdvance =
                 ConvertPangoToAppUnits(glyph.geometry.width,
                                        appUnitsPerDevUnit);
             details->mXOffset =
                 float(glyph.geometry.x_offset)*appUnitsPerDevUnit/PANGO_SCALE;
             details->mYOffset =
                 float(glyph.geometry.y_offset)*appUnitsPerDevUnit/PANGO_SCALE;
         }
         g.SetComplex(atClusterStart, true, detailedIndex);
-        aTextRun->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
+        aShapedWord->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
     }
 
     // Check for ligatures and set *aUTF16Offset.
     const gchar *p = aUTF8;
     const gchar *end = aUTF8 + aUTF8Length;
     while (1) {
         // Skip the CompressedGlyph that we have added, but check if the
         // character was supposed to be ignored. If it's supposed to be ignored,
@@ -2891,32 +2819,33 @@ SetGlyphsForCharacterGroup(const PangoGl
                      "Invalid character detected");
         ++utf16Offset;
 
         // We produced this UTF8 so we don't need to worry about malformed stuff
         p = g_utf8_next_char(p);
         if (p >= end)
             break;
 
-        if (utf16Offset >= textRunLength) {
+        if (utf16Offset >= wordLength) {
             NS_ERROR("Someone has added too many glyphs!");
             return NS_ERROR_FAILURE;
         }
 
-        g.SetComplex(aTextRun->IsClusterStart(utf16Offset), false, 0);
-        aTextRun->SetGlyphs(utf16Offset, g, nsnull);
+        g.SetComplex(aShapedWord->IsClusterStart(utf16Offset), false, 0);
+        aShapedWord->SetGlyphs(utf16Offset, g, nsnull);
     }
     *aUTF16Offset = utf16Offset;
     return NS_OK;
 }
 
 static nsresult
-SetGlyphs(gfxTextRun *aTextRun, const gchar *aUTF8, PRUint32 aUTF8Length,
+SetGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8, PRUint32 aUTF8Length,
           PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs,
-          PangoGlyphUnit aOverrideSpaceWidth)
+          PangoGlyphUnit aOverrideSpaceWidth,
+          gfxFont *aFont)
 {
     gint numGlyphs = aGlyphs->num_glyphs;
     PangoGlyphInfo *glyphs = aGlyphs->glyphs;
     const gint *logClusters = aGlyphs->log_clusters;
     // We cannot make any assumptions about the order of glyph clusters
     // provided by pango_shape (see 375864), so we work through the UTF8 text
     // and process the glyph clusters in logical order.
 
@@ -2939,32 +2868,32 @@ SetGlyphs(gfxTextRun *aTextRun, const gc
             lastCluster = thisCluster;
             NS_ASSERTION(0 <= thisCluster && thisCluster < gint(aUTF8Length),
                          "garbage from pango_shape - this is bad");
             logGlyphs[thisCluster] = glyphIndex;
         }
     }
 
     PRUint32 utf16Offset = *aUTF16Offset;
-    PRUint32 textRunLength = aTextRun->GetLength();
+    PRUint32 wordLength = aShapedWord->Length();
     utf8Index = 0;
     // The next glyph cluster in logical order. 
     gint nextGlyphClusterStart = logGlyphs[utf8Index];
     NS_ASSERTION(nextGlyphClusterStart >= 0, "No glyphs! - NUL in string?");
     while (utf8Index < aUTF8Length) {
-        if (utf16Offset >= textRunLength) {
+        if (utf16Offset >= wordLength) {
           NS_ERROR("Someone has added too many glyphs!");
           return NS_ERROR_FAILURE;
         }
         gint glyphClusterStart = nextGlyphClusterStart;
         // Find the utf8 text associated with this glyph cluster.
         PRUint32 clusterUTF8Start = utf8Index;
-        // Check we are consistent with pango_break data.
-        NS_ASSERTION(aTextRun->GetCharacterGlyphs()->IsClusterStart(),
-                     "Glyph cluster not aligned on character cluster.");
+        // Check whether we are consistent with pango_break data.
+        NS_WARN_IF_FALSE(aShapedWord->IsClusterStart(utf16Offset),
+                         "Glyph cluster not aligned on character cluster.");
         do {
             ++utf8Index;
             nextGlyphClusterStart = logGlyphs[utf8Index];
         } while (nextGlyphClusterStart < 0);
         const gchar *clusterUTF8 = &aUTF8[clusterUTF8Start];
         PRUint32 clusterUTF8Length = utf8Index - clusterUTF8Start;
 
         bool haveMissingGlyph = false;
@@ -2979,92 +2908,91 @@ SetGlyphs(gfxTextRun *aTextRun, const gc
                 haveMissingGlyph = true;
             }
             glyphIndex++;
         } while (glyphIndex < numGlyphs && 
                  logClusters[glyphIndex] == gint(clusterUTF8Start));
 
         nsresult rv;
         if (haveMissingGlyph) {
-            SetMissingGlyphs(aTextRun, clusterUTF8, clusterUTF8Length,
-                             &utf16Offset);
+            SetMissingGlyphs(aShapedWord, clusterUTF8, clusterUTF8Length,
+                             &utf16Offset, aFont);
         } else {
             rv = SetGlyphsForCharacterGroup(&glyphs[glyphClusterStart],
                                             glyphIndex - glyphClusterStart,
-                                            aTextRun,
+                                            aShapedWord,
                                             clusterUTF8, clusterUTF8Length,
                                             &utf16Offset, aOverrideSpaceWidth);
             NS_ENSURE_SUCCESS(rv,rv);
         }
     }
     *aUTF16Offset = utf16Offset;
     return NS_OK;
 }
 
 static void
-SetMissingGlyphs(gfxTextRun *aTextRun, const gchar *aUTF8,
-                 PRUint32 aUTF8Length, PRUint32 *aUTF16Offset)
+SetMissingGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8,
+                 PRUint32 aUTF8Length, PRUint32 *aUTF16Offset,
+                 gfxFont *aFont)
 {
     PRUint32 utf16Offset = *aUTF16Offset;
-    PRUint32 textRunLength = aTextRun->GetLength();
+    PRUint32 wordLength = aShapedWord->Length();
     for (PRUint32 index = 0; index < aUTF8Length;) {
-        if (utf16Offset >= textRunLength) {
+        if (utf16Offset >= wordLength) {
             NS_ERROR("Someone has added too many glyphs!");
             break;
         }
         gunichar ch = g_utf8_get_char(aUTF8 + index);
-        aTextRun->SetMissingGlyph(utf16Offset, ch);
+        aShapedWord->SetMissingGlyph(utf16Offset, ch, aFont);
 
         ++utf16Offset;
         NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
         if (ch >= 0x10000)
             ++utf16Offset;
         // We produced this UTF8 so we don't need to worry about malformed stuff
         index = g_utf8_next_char(aUTF8 + index) - aUTF8;
     }
 
     *aUTF16Offset = utf16Offset;
 }
 
 static void
-InitGlyphRunWithPangoAnalysis(gfxTextRun *aTextRun,
+InitGlyphRunWithPangoAnalysis(gfxShapedWord *aShapedWord,
                               const gchar *aUTF8, PRUint32 aUTF8Length,
-                              PRUint32 *aUTF16Offset,
                               PangoAnalysis *aAnalysis,
-                              PangoGlyphUnit aOverrideSpaceWidth)
+                              PangoGlyphUnit aOverrideSpaceWidth,
+                              gfxFont *aFont)
 {
-    PRUint32 utf16Offset = *aUTF16Offset;
+    PRUint32 utf16Offset = 0;
     PangoGlyphString *glyphString = pango_glyph_string_new();
 
     const gchar *p = aUTF8;
     const gchar *end = p + aUTF8Length;
     while (p < end) {
         if (*p == 0) {
-            aTextRun->SetMissingGlyph(utf16Offset, 0);
+            aShapedWord->SetMissingGlyph(utf16Offset, 0, aFont);
             ++p;
             ++utf16Offset;
             continue;
         }
 
         // It's necessary to loop over pango_shape as it treats
         // NULs as string terminators
         const gchar *text = p;
         do {
             ++p;
         } while(p < end && *p != 0);
         gint len = p - text;
 
         pango_shape(text, len, aAnalysis, glyphString);
-        SetupClusterBoundaries(aTextRun, text, len, utf16Offset, aAnalysis);
-        SetGlyphs(aTextRun, text, len, &utf16Offset, glyphString,
-                  aOverrideSpaceWidth);
+        SetGlyphs(aShapedWord, text, len, &utf16Offset, glyphString,
+                  aOverrideSpaceWidth, aFont);
     }
 
     pango_glyph_string_free(glyphString);
-    *aUTF16Offset = utf16Offset;
 }
 
 // PangoAnalysis is part of Pango's ABI but over time extra fields have been
 // inserted into padding.  This union is used so that the code here can be
 // compiled against older Pango versions but run against newer versions.
 typedef union {
     PangoAnalysis pango;
     // This struct matches PangoAnalysis from Pango version
@@ -3078,49 +3006,50 @@ typedef union {
         guint8 flags;
         guint8 script; /* PangoScript */
         PangoLanguage *language;
         GSList *extra_attrs;
     } local;
 } PangoAnalysisUnion;
 
 bool
-gfxFcFont::InitGlyphRunWithPango(gfxTextRun *aTextRun,
-                                 const PRUnichar *aString,
-                                 PRUint32 aRunStart, PRUint32 aRunLength,
-                                 PangoScript aScript)
+gfxFcFont::InitGlyphRunWithPango(gfxShapedWord *aShapedWord,
+                                 const PRUnichar *aString)
 {
-    NS_ConvertUTF16toUTF8 utf8(aString + aRunStart, aRunLength);
+    const PangoScript script = static_cast<PangoScript>(aShapedWord->Script());
+    NS_ConvertUTF16toUTF8 utf8(aString, aShapedWord->Length());
 
     PangoFont *font = GetPangoFont();
-    gfxPangoFontGroup *fontGroup =
-        static_cast<gfxPangoFontGroup*>(aTextRun->GetFontGroup());
 
     hb_language_t languageOverride = NULL;
-    if (fontGroup->GetStyle()->languageOverride) {
+    if (GetStyle()->languageOverride) {
         languageOverride =
-            hb_ot_tag_to_language(fontGroup->GetStyle()->languageOverride);
+            hb_ot_tag_to_language(GetStyle()->languageOverride);
     } else if (GetFontEntry()->mLanguageOverride) {
         languageOverride =
             hb_ot_tag_to_language(GetFontEntry()->mLanguageOverride);
     }
 
     PangoLanguage *language;
     if (languageOverride) {
         language =
             pango_language_from_string(hb_language_to_string(languageOverride));
     } else {
+#if 0 // FIXME ??
         language = fontGroup->GetPangoLanguage();
+#endif
+        // FIXME: should probably cache this in the gfxFcFont
+        language = GuessPangoLanguage(GetStyle()->language);
+
         // The language that we have here is often not as good an indicator for
         // the run as the script.  This is not so important for the PangoMaps
         // here as all the default Pango shape and lang engines are selected
         // by script only (not language) anyway, but may be important in the
         // PangoAnalysis as the shaper sometimes accesses language-specific
         // tables.
-        const PangoScript script = static_cast<PangoScript>(aScript);
         PangoLanguage *scriptLang;
         if ((!language ||
              !pango_language_includes_script(language, script)) &&
             (scriptLang = pango_script_get_sample_language(script))) {
             language = scriptLang;
         }
     }
 
@@ -3136,17 +3065,17 @@ gfxFcFont::InitGlyphRunWithPango(gfxText
         g_quark_from_static_string(PANGO_RENDER_TYPE_FC);
     PangoMap *shapeMap = pango_find_map(language, engineShapeId, renderFcId);
     if (!shapeMap) {
         return false;
     }
 
     // The preferred shape engine for language and script
     PangoEngineShape *shapeEngine =
-        PANGO_ENGINE_SHAPE(pango_map_get_engine(shapeMap, aScript));
+        PANGO_ENGINE_SHAPE(pango_map_get_engine(shapeMap, script));
     if (!shapeEngine) {
         return false;
     }
 
     PangoEngineShapeClass *shapeClass = static_cast<PangoEngineShapeClass*>
         (g_type_class_peek(PANGO_TYPE_ENGINE_SHAPE));
 
     // The |covers| method in the PangoEngineShape base class, which is the
@@ -3157,17 +3086,17 @@ gfxFcFont::InitGlyphRunWithPango(gfxText
     //
     // With SIL Graphite shapers, however, |covers| also checks that the font
     // is a Graphite font.  (bug 397860)
     if (!shapeClass ||
         PANGO_ENGINE_SHAPE_GET_CLASS(shapeEngine)->covers != shapeClass->covers)
     {
         GSList *exact_engines;
         GSList *fallback_engines;
-        pango_map_get_engines(shapeMap, aScript,
+        pango_map_get_engines(shapeMap, script,
                               &exact_engines, &fallback_engines);
 
         GSList *engines = g_slist_concat(exact_engines, fallback_engines);
         for (GSList *link = engines; link; link = link->next) {
             PangoEngineShape *engine = PANGO_ENGINE_SHAPE(link->data);
             PangoCoverageLevel (*covers)(PangoEngineShape*, PangoFont*,
                                          PangoLanguage*, gunichar) =
                 PANGO_ENGINE_SHAPE_GET_CLASS(shapeEngine)->covers;
@@ -3184,45 +3113,44 @@ gfxFcFont::InitGlyphRunWithPango(gfxText
 
     PangoAnalysisUnion analysis;
     memset(&analysis, 0, sizeof(analysis));
 
     // For pango_shape
     analysis.local.shape_engine = shapeEngine;
     // For pango_break
     analysis.local.lang_engine =
-        PANGO_ENGINE_LANG(pango_map_get_engine(langMap, aScript));
+        PANGO_ENGINE_LANG(pango_map_get_engine(langMap, script));
 
     analysis.local.font = font;
-    analysis.local.level = aTextRun->IsRightToLeft() ? 1 : 0;
+    analysis.local.level = aShapedWord->IsRightToLeft() ? 1 : 0;
     // gravity and flags are used in Pango 1.14.10 and newer.
     //
     // PANGO_GRAVITY_SOUTH is what we want for upright horizontal text.  The
     // constant is not available when compiling with older Pango versions, but
     // is zero so the zero memset initialization is sufficient.
     //
     // Pango uses non-zero flags for vertical gravities only
     // (up to version 1.28 at least), so using zero is fine for flags too.
 #if 0
     analysis.local.gravity = PANGO_GRAVITY_SOUTH;
     analysis.local.flags = 0;
 #endif
     // Only used in Pango 1.16.5 and newer.
-    analysis.local.script = aScript;
+    analysis.local.script = script;
 
     analysis.local.language = language;
     // Non-font attributes.  Not used here.
     analysis.local.extra_attrs = NULL;
 
     PangoGlyphUnit spaceWidth =
         moz_pango_units_from_double(GetMetrics().spaceWidth);
 
-    PRUint32 utf16Offset = aRunStart;
-    InitGlyphRunWithPangoAnalysis(aTextRun, utf8.get(), utf8.Length(),
-                                  &utf16Offset, &analysis.pango, spaceWidth);
+    InitGlyphRunWithPangoAnalysis(aShapedWord, utf8.get(), utf8.Length(),
+                                  &analysis.pango, spaceWidth, this);
     return true;
 }
 
 /* static */
 PangoLanguage *
 GuessPangoLanguage(nsIAtom *aLanguage)
 {
     if (!aLanguage)