b=449356 font selection through Mozilla's PangoFcFontMap, r=roc
authorKarl Tomlinson <karlt+@karlt.net>
Fri, 07 Nov 2008 15:21:34 +1300
changeset 21407 d062597e5b3db8ee442fd452396a33fc30073763
parent 21406 e92dfeb501cdc365250383fa4d5804e69079ce37
child 21408 707da21015e7d5439867d26e24f3c3ea9163f9da
push id3537
push userktomlinson@mozilla.com
push dateFri, 07 Nov 2008 02:23:06 +0000
treeherdermozilla-central@d062597e5b3d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs449356
milestone1.9.1b2pre
b=449356 font selection through Mozilla's PangoFcFontMap, r=roc
gfx/thebes/public/gfxFont.h
gfx/thebes/public/gfxPangoFonts.h
gfx/thebes/public/gfxPlatformGtk.h
gfx/thebes/src/gfxFontconfigUtils.cpp
gfx/thebes/src/gfxFontconfigUtils.h
gfx/thebes/src/gfxPangoFonts.cpp
--- a/gfx/thebes/public/gfxFont.h
+++ b/gfx/thebes/public/gfxFont.h
@@ -1531,16 +1531,29 @@ public:
 
     PRBool Equals(const gfxFontGroup& other) const {
         return mFamilies.Equals(other.mFamilies) &&
             mStyle.Equals(other.mStyle);
     }
 
     const gfxFontStyle *GetStyle() const { return &mStyle; }
 
+#if !MOZ_WIDGET_GTK2
+#error "don't bother building"
+#endif
+#if 0
+    virtual const gfxFont::Metrics& GetMetrics() const {
+        gfxFont *firstFont = GetFontAt(0);
+        if (!firstFont) // OOM
+            return gfxFont::Metrics();
+        return firstFont->GetMetrics();
+    }
+#endif
+
+    // Used in nsTextRunTransformations: rename to GetSimilar
     virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle) = 0;
 
     /**
      * The listed characters should not be passed in to MakeTextRun and should
      * be treated as invisible and zero-width.
      */
     static PRBool IsInvalidChar(PRUnichar ch);
     
--- a/gfx/thebes/public/gfxPangoFonts.h
+++ b/gfx/thebes/public/gfxPangoFonts.h
@@ -38,42 +38,31 @@
 
 #ifndef GFX_PANGOFONTS_H
 #define GFX_PANGOFONTS_H
 
 #include "cairo.h"
 #include "gfxTypes.h"
 #include "gfxFont.h"
 
+#include "nsAutoRef.h"
+
 #include <pango/pango.h>
+#include <fontconfig/fontconfig.h>
 
 // Control when we bypass Pango
 // Enable this to use FreeType to glyph-convert 8bit-only textruns, but use Pango
 // to shape any textruns with non-8bit characters
 // XXX
 #define ENABLE_FAST_PATH_8BIT
 // Enable this to bypass Pango shaping for all textruns.  Don't expect
 // anything other than simple Latin work though!
 //#define ENABLE_FAST_PATH_ALWAYS
 
-#include "nsDataHashtable.h"
-#include "nsClassHashtable.h"
-
-class gfxPangoTextRun;
-
-// stub class until fuller implementation is flushed out
-class gfxPangoFontEntry : public gfxFontEntry {
-public:
-    gfxPangoFontEntry(const nsAString& aName)
-        : gfxFontEntry(aName)
-    { }
-
-    ~gfxPangoFontEntry() {}
-        
-};
+class gfxFcPangoFontSet;
 
 class THEBES_API gfxPangoFontGroup : public gfxFontGroup {
 public:
     gfxPangoFontGroup (const nsAString& families,
                        const gfxFontStyle *aStyle,
                        gfxUserFontSet *aUserFontSet);
     virtual ~gfxPangoFontGroup ();
 
@@ -84,19 +73,43 @@ public:
                                     const Parameters *aParams, PRUint32 aFlags);
     virtual gfxTextRun *MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
                                     const Parameters *aParams, PRUint32 aFlags);
 
     virtual gfxFont *GetFontAt(PRInt32 i);
 
     static void Shutdown();
 
+    // Interfaces used internally
+    // (but public so that they can be accessed from non-member functions):
+
+    // The FontGroup holds the reference to the PangoFont (through the FontSet).
+    PangoFont *GetBasePangoFont();
+
+    // A language guessed from the gfxFontStyle
+    PangoLanguage *GetPangoLanguage() { return mPangoLanguage; }
+
+    // @param aLang [in] language to use for pref fonts and system default font
+    //        selection, or NULL for the language guessed from the gfxFontStyle.
+    // The FontGroup holds a reference to this set.
+    gfxFcPangoFontSet *GetFontSet(PangoLanguage *aLang = NULL);
+
 protected:
-    PangoFont *mBasePangoFont;
-    gfxFloat mAdjustedSize;
+    class FontSetByLangEntry {
+    public:
+        FontSetByLangEntry(PangoLanguage *aLang, gfxFcPangoFontSet *aFontSet);
+        PangoLanguage *mLang;
+        nsRefPtr<gfxFcPangoFontSet> mFontSet;
+    };
+    // There is only one of entry in this array unless characters from scripts
+    // of other languages are measured.
+    nsAutoTArray<FontSetByLangEntry,1> mFontSets;
+
+    gfxFloat mSizeAdjustFactor;
+    PangoLanguage *mPangoLanguage;
 
     // ****** Textrun glyph conversion helpers ******
 
     /**
      * Fill in the glyph-runs for the textrun.
      * @param aTake8BitPath the text contains only characters below 0x100
      * (TEXT_IS_8BIT can return false when the characters are all below 0x100
      * but stored in UTF16 format)
@@ -118,60 +131,29 @@ protected:
                                   const gchar *aUTF8, PRUint32 aUTF8Length,
                                   PRUint32 aUTF8HeaderLength);
 #if defined(ENABLE_FAST_PATH_8BIT) || defined(ENABLE_FAST_PATH_ALWAYS)
     PRBool CanTakeFastPath(PRUint32 aFlags);
     nsresult CreateGlyphRunsFast(gfxTextRun *aTextRun,
                                  const gchar *aUTF8, PRUint32 aUTF8Length);
 #endif
 
-    void GetFcFamilies(nsAString &aFcFamilies);
-    PangoFont *GetBasePangoFont();
+    void GetFcFamilies(nsStringArray *aFcFamilyList,
+                       const nsACString& aLangGroup);
 
-    // Check GetStyle()->sizeAdjust != 0.0 before calling this 
-    gfxFloat GetAdjustedSize()
+    // @param aLang [in] language to use for pref fonts and system font
+    //        resolution, or NULL to guess a language from the gfxFontStyle.
+    // @param aMatchPattern [out] if non-NULL, will return the pattern used.
+    already_AddRefed<gfxFcPangoFontSet>
+    MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
+                nsAutoRef<FcPattern> *aMatchPattern = NULL);
+
+    gfxFcPangoFontSet *GetBaseFontSet();
+
+    gfxFloat GetSizeAdjustFactor()
     {
-        if (!mBasePangoFont)
-            GetBasePangoFont();
-        return mAdjustedSize;
+        if (mFontSets.Length() == 0)
+            GetBaseFontSet();
+        return mSizeAdjustFactor;
     }
 };
 
-class gfxPangoFontWrapper {
-public:
-    gfxPangoFontWrapper(PangoFont *aFont) {
-        mFont = aFont;
-        g_object_ref(mFont);
-    }
-    ~gfxPangoFontWrapper() {
-        if (mFont)
-            g_object_unref(mFont);
-    }
-    PangoFont* Get() { return mFont; }
-private:
-    PangoFont *mFont;
-};
-
-class gfxPangoFontCache
-{
-public:
-    gfxPangoFontCache();
-    ~gfxPangoFontCache();
-
-    static gfxPangoFontCache* GetPangoFontCache() {
-        if (!sPangoFontCache)
-            sPangoFontCache = new gfxPangoFontCache();
-        return sPangoFontCache;
-    }
-    static void Shutdown() {
-        if (sPangoFontCache)
-            delete sPangoFontCache;
-        sPangoFontCache = nsnull;
-    }
-
-    void Put(const PangoFontDescription *aFontDesc, PangoFont *aPangoFont);
-    PangoFont* Get(const PangoFontDescription *aFontDesc);
-private:
-    static gfxPangoFontCache *sPangoFontCache;
-    nsClassHashtable<nsUint32HashKey,  gfxPangoFontWrapper> mPangoFonts;
-};
-
 #endif /* GFX_PANGOFONTS_H */
--- a/gfx/thebes/public/gfxPlatformGtk.h
+++ b/gfx/thebes/public/gfxPlatformGtk.h
@@ -35,29 +35,35 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_PLATFORM_GTK_H
 #define GFX_PLATFORM_GTK_H
 
 #include "gfxPlatform.h"
+#include "nsAutoRef.h"
 
 extern "C" {
     typedef struct _GdkDrawable GdkDrawable;
 }
 
 class gfxFontconfigUtils;
 #ifndef MOZ_PANGO
 class FontFamily;
 class FontEntry;
 typedef struct FT_LibraryRec_ *FT_Library;
 #endif
 
-
+template <class T>
+class gfxGObjectRefTraits : public nsPointerRefTraits<T> {
+public:
+    static void Release(T *aPtr) { g_object_unref(aPtr); }
+    static void AddRef(T *aPtr) { g_object_ref(aPtr); }
+};
 
 class THEBES_API gfxPlatformGtk : public gfxPlatform {
 public:
     gfxPlatformGtk();
     virtual ~gfxPlatformGtk();
 
     static gfxPlatformGtk *GetPlatform() {
         return (gfxPlatformGtk*) gfxPlatform::GetPlatform();
--- a/gfx/thebes/src/gfxFontconfigUtils.cpp
+++ b/gfx/thebes/src/gfxFontconfigUtils.cpp
@@ -72,27 +72,42 @@ gfxFontconfigUtils::GetThebesStyle(FcPat
             return FONT_STYLE_ITALIC;
         if (slant == FC_SLANT_OBLIQUE)
             return FONT_STYLE_OBLIQUE;
     }
 
     return FONT_STYLE_NORMAL;
 }
 
+/* static */ int
+gfxFontconfigUtils::GetFcSlant(const gfxFontStyle& aFontStyle)
+{
+    if (aFontStyle.style == FONT_STYLE_ITALIC)
+        return FC_SLANT_ITALIC;
+    if (aFontStyle.style == FONT_STYLE_OBLIQUE)
+        return FC_SLANT_OBLIQUE;
+
+    return FC_SLANT_ROMAN;
+}
+
 // OS/2 weight classes were introduced in fontconfig-2.1.93 (2003).
 #ifndef FC_WEIGHT_THIN 
 #define FC_WEIGHT_THIN              0 // 2.1.93
 #define FC_WEIGHT_EXTRALIGHT        40 // 2.1.93
 #define FC_WEIGHT_REGULAR           80 // 2.1.93
 #define FC_WEIGHT_EXTRABOLD         205 // 2.1.93
 #endif
 // book was introduced in fontconfig-2.2.90 (and so fontconfig-2.3.0 in 2005)
 #ifndef FC_WEIGHT_BOOK
 #define FC_WEIGHT_BOOK              75
 #endif
+// extra black was introduced in fontconfig-2.4.91 (2007)
+#ifndef FC_WEIGHT_EXTRABLACK
+#define FC_WEIGHT_EXTRABLACK        215
+#endif
 
 /* static */ PRUint16
 gfxFontconfigUtils::GetThebesWeight(FcPattern *aPattern)
 {
     int weight;
     if (FcPatternGetInteger(aPattern, FC_WEIGHT, 0, &weight) != FcResultMatch)
         return FONT_WEIGHT_NORMAL;
 
@@ -111,49 +126,186 @@ gfxFontconfigUtils::GetThebesWeight(FcPa
         return 600;
     if (weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2)
         return 700;
     if (weight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2)
         return 800;
     if (weight <= FC_WEIGHT_BLACK)
         return 900;
 
-    // FC_WEIGHT_EXTRABLACK was introduced in fontconfig-2.4.91 (2007)
+    // including FC_WEIGHT_EXTRABLACK
     return 901;
 }
 
+/* static */ int
+gfxFontconfigUtils::FcWeightForBaseWeight(PRInt8 aBaseWeight)
+{
+    switch (aBaseWeight) {
+        case 2:
+            return FC_WEIGHT_EXTRALIGHT;
+        case 3:
+            return FC_WEIGHT_LIGHT;
+        case 4:
+            return FC_WEIGHT_REGULAR;
+        case 5:
+            return FC_WEIGHT_MEDIUM;
+        case 6:
+            return FC_WEIGHT_DEMIBOLD;
+        case 7:
+            return FC_WEIGHT_BOLD;
+        case 8:
+            return FC_WEIGHT_EXTRABOLD;
+        case 9:
+            return FC_WEIGHT_BLACK;
+    }
+
+    // extremes
+    return aBaseWeight < 2 ? FC_WEIGHT_THIN : FC_WEIGHT_EXTRABLACK;
+}
+
+// This makes a guess at an FC_WEIGHT corresponding to a base weight and
+// offset (without any knowledge of which weights are available).
+
+/* static */ int
+GuessFcWeight(const gfxFontStyle& aFontStyle)
+{
+    /*
+     * weights come in two parts crammed into one
+     * integer -- the "base" weight is weight / 100,
+     * the rest of the value is the "offset" from that
+     * weight -- the number of steps to move to adjust
+     * the weight in the list of supported font weights,
+     * this value can be negative or positive.
+     */
+    PRInt8 weight;
+    PRInt8 offset;
+    aFontStyle.ComputeWeightAndOffset(&weight, &offset);
+
+    // ComputeWeightAndOffset trimmed the range of weights for us
+    NS_ASSERTION(weight >= 0 && weight <= 10,
+                 "base weight out of range");
+
+    // Most font families do not support every weight.  The tables here are
+    // chosen such that a normal (4) base weight and an offset of +1 will
+    // guess bold.
+
+    // Mapping from weight to a guess of the nearest available lighter weight
+    static const int lighterGuess[11] =
+        { 0, 0, 1, 1, 2, 3, 4, 4, 6, 7, 8 };
+    // Mapping from weight to a guess of the nearest available bolder weight
+    static const int bolderGuess[11] =
+        { 2, 3, 4, 6, 7, 7, 8, 9, 10, 10, 10 };
+
+    while (offset < 0) {
+        weight = lighterGuess[weight];
+        offset++;
+    }
+    while (offset > 0) {
+        weight = bolderGuess[weight];
+        offset--;
+    }
+
+    return gfxFontconfigUtils::FcWeightForBaseWeight(weight);
+}
+
+static void
+AddString(FcPattern *aPattern, const char *object, const char *aString)
+{
+    // Cast from signed chars used in nsString to unsigned in fontconfig
+    const FcChar8 *fcString = gfxFontconfigUtils::ToFcChar8(aString);
+    // and cast away the const for fontconfig, that will merely make a copy.
+    FcPatternAddString(aPattern, object, const_cast<FcChar8*>(fcString));
+}
+
+static void
+AddLangGroup(FcPattern *aPattern, const nsACString& aLangGroup)
+{
+    // Translate from mozilla's internal mapping into fontconfig's
+    nsCAutoString lang;
+    gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
+
+    if (!lang.IsEmpty()) {
+        AddString(aPattern, FC_LANG, lang.get());
+    }
+}
+
+
+nsReturnRef<FcPattern>
+gfxFontconfigUtils::NewPattern(const nsStringArray& aFamilies,
+                               const gfxFontStyle& aFontStyle,
+                               const char *aLang)
+{
+    nsAutoRef<FcPattern> pattern(FcPatternCreate());
+    if (!pattern)
+        return nsReturnRef<FcPattern>();
+
+    FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aFontStyle.size);
+    FcPatternAddInteger(pattern, FC_SLANT, GetFcSlant(aFontStyle));
+    FcPatternAddInteger(pattern, FC_WEIGHT, GuessFcWeight(aFontStyle));
+
+    if (aLang) {
+        AddString(pattern, FC_LANG, aLang);
+    }
+
+    for (PRInt32 i = 0; i < aFamilies.Count(); ++i) {
+        NS_ConvertUTF16toUTF8 family(*aFamilies[i]);
+        AddString(pattern, FC_FAMILY, family.get());
+    }
+
+    return pattern.out();
+}
+
 gfxFontconfigUtils::gfxFontconfigUtils()
     : mLastConfig(NULL)
 {
-    mAliasTable.Init(50);
+    mFontsByFamily.Init(50);
+    mLangSupportTable.Init(20);
+    UpdateFontListInternal();
 }
 
+#if 0
+PR_STATIC_CALLBACK(PLDHashOperator)
+GetFamilyNamesCallback(FamilyHashEntry *aEntry, void *aData)
+{
+    nsStringArray *names = static_cast<nsStringArray *>(aData);
+
+    const FcChar8 *name = aEntry->GetName();
+
+    // XXXkt only want to return entries for which GetStandardFamilyName
+    // returns the same family.
+    names.AppendString(NS_ConvertUTF8toUTF16(ToCString(sname)));
+}
+
+    if (aLangGroup.IsEmpty()) {
+        nsresult rv = UpdateFontListInternal();
+        if (NS_FAILED(rv))
+            return rv;
+
+        mFonts.EnumerateEntries(GetFamilyNamesCallback, &aListOfFonts);
+
+    } else {
+    }
+#endif
+
 nsresult
 gfxFontconfigUtils::GetFontList(const nsACString& aLangGroup,
                                 const nsACString& aGenericFamily,
                                 nsStringArray& aListOfFonts)
 {
     aListOfFonts.Clear();
 
-    nsresult rv = UpdateFontListInternal();
+    nsCStringArray fonts;
+    nsresult rv = GetFontListInternal(fonts, aLangGroup);
     if (NS_FAILED(rv))
         return rv;
 
-    nsCStringArray tmpFonts;
-    nsCStringArray *fonts = &mFonts;
-    if (!aLangGroup.IsEmpty() || !aGenericFamily.IsEmpty()) {
-        rv = GetFontListInternal(tmpFonts, &aLangGroup);
-        if (NS_FAILED(rv))
-            return rv;
-        fonts = &tmpFonts;
+    for (PRInt32 i = 0; i < fonts.Count(); ++i) {
+        aListOfFonts.AppendString(NS_ConvertUTF8toUTF16(*fonts.CStringAt(i)));
     }
 
-    for (PRInt32 i = 0; i < fonts->Count(); ++i)
-         aListOfFonts.AppendString(NS_ConvertUTF8toUTF16(*fonts->CStringAt(i)));
-
     aListOfFonts.Sort();
 
     PRInt32 serif = 0, sansSerif = 0, monospace = 0;
 
     // Fontconfig supports 3 generic fonts, "serif", "sans-serif", and
     // "monospace", slightly different from CSS's 5.
     if (aGenericFamily.IsEmpty())
         serif = sansSerif = monospace = 1;
@@ -249,17 +401,17 @@ TryLangForGroup(const nsACString& aOSLan
 /* static */ void
 gfxFontconfigUtils::GetSampleLangForGroup(const nsACString& aLangGroup,
                                           nsACString *aFcLang)
 {
     NS_PRECONDITION(aFcLang != nsnull, "aFcLang must not be NULL");
 
     const MozLangGroupData *langGroup = nsnull;
 
-    for (unsigned int i=0; i < NS_ARRAY_LENGTH(MozLangGroups); ++i) {
+    for (unsigned int i = 0; i < NS_ARRAY_LENGTH(MozLangGroups); ++i) {
         if (aLangGroup.Equals(MozLangGroups[i].mozLangGroup,
                               nsCaseInsensitiveCStringComparator())) {
             langGroup = &MozLangGroups[i];
             break;
         }
     }
 
     if (!langGroup) {
@@ -304,35 +456,19 @@ gfxFontconfigUtils::GetSampleLangForGrou
 
     if (langGroup->defaultLang) {
         aFcLang->Assign(langGroup->defaultLang);
     } else {
         aFcLang->Truncate();
     }
 }
 
-static void
-AddLangGroup(FcPattern *aPattern, const nsACString& aLangGroup)
-{
-    // Translate from mozilla's internal mapping into fontconfig's
-    nsCAutoString lang;
-    gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
-
-    if (!lang.IsEmpty()) {
-        // cast from signed chars used in nsString to unsigned in fontconfig
-        const FcChar8 *fcString = reinterpret_cast<const FcChar8*>(lang.get());
-        // and cast away the const for fontconfig, that will merely make a copy.
-        FcPatternAddString(aPattern, FC_LANG, const_cast<FcChar8*>(fcString));
-    }
-}
-
-
 nsresult
 gfxFontconfigUtils::GetFontListInternal(nsCStringArray& aListOfFonts,
-                                        const nsACString *aLangGroup)
+                                        const nsACString& aLangGroup)
 {
     FcPattern *pat = NULL;
     FcObjectSet *os = NULL;
     FcFontSet *fs = NULL;
     nsresult rv = NS_ERROR_FAILURE;
 
     aListOfFonts.Clear();
 
@@ -340,18 +476,18 @@ gfxFontconfigUtils::GetFontListInternal(
     if (!pat)
         goto end;
 
     os = FcObjectSetBuild(FC_FAMILY, NULL);
     if (!os)
         goto end;
 
     // take the pattern and add the lang group to it
-    if (aLangGroup && !aLangGroup->IsEmpty()) {
-        AddLangGroup(pat, *aLangGroup);
+    if (!aLangGroup.IsEmpty()) {
+        AddLangGroup(pat, aLangGroup);
     }
 
     fs = FcFontList(NULL, pat, os);
     if (!fs)
         goto end;
 
     for (int i = 0; i < fs->nfont; i++) {
         char *family;
@@ -408,26 +544,48 @@ gfxFontconfigUtils::UpdateFontListIntern
     // before destroying the old config, so the only way that we'd miss an
     // update is if fontconfig did more than one update and the memory for the
     // most recent config happened to be at the same location as the original
     // config.
     FcConfig *currentConfig = FcConfigGetCurrent();
     if (currentConfig == mLastConfig)
         return NS_OK;
 
-    mFonts.Clear();
-    mAliasForSingleFont.Clear();
+    // This FcFontSet is owned by fontconfig
+    FcFontSet *fontSet = FcConfigGetFonts(currentConfig, FcSetSystem);
+
+    mFontsByFamily.Clear();
+    mLangSupportTable.Clear();
     mAliasForMultiFonts.Clear();
-    mNonExistingFonts.Clear();
+
+    // Record the existing font families
+    for (int f = 0; f < fontSet->nfont; ++f) {
+        FcPattern *font = fontSet->fonts[f];
 
-    mAliasTable.Clear();
+        FcChar8 *family;
+        for (int v = 0;
+             FcPatternGetString(font, FC_FAMILY, v, &family) == FcResultMatch;
+             ++v) {
+            FontsByFcStrEntry *entry = mFontsByFamily.PutEntry(family);
+            if (entry) {
+                PRBool added = entry->AddFont(font);
 
-    nsresult rv = GetFontListInternal(mFonts);
-    if (NS_FAILED(rv))
-        return rv;
+                if (!entry->mKey) {
+                    // The reference to the font pattern keeps the pointer to
+                    // string for the key valid.  If adding the font failed
+                    // then the entry must be removed.
+                    if (added) {
+                        entry->mKey = family;
+                    } else {
+                        mFontsByFamily.RawRemoveEntry(entry);
+                    }
+                }
+            }
+        }
+    }
 
     // XXX we don't support all alias names.
     // Because if we don't check whether the given font name is alias name,
     // fontconfig converts the non existing font to sans-serif.
     // This is not good if the web page specifies font-family
     // that has Windows font name in the first.
     nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (!prefs)
@@ -458,83 +616,21 @@ gfxFontconfigUtils::UpdateFontListIntern
                 /* nothing */ ;
             nsCAutoString name(Substring(start, p));
             name.CompressWhitespace(PR_FALSE, PR_TRUE);
             mAliasForMultiFonts.AppendCString(name);
             p++;
         }
     }
 
-    for (PRInt32 i = 0; i < mAliasForMultiFonts.Count(); i++) {
-        nsRefPtr<gfxFontNameList> fonts = new gfxFontNameList;
-        nsCAutoString fontname(*mAliasForMultiFonts.CStringAt(i));
-        rv = GetResolvedFonts(fontname, fonts);
-        if (NS_FAILED(rv))
-            return rv;
-
-        nsCAutoString key;
-        ToLowerCase(fontname, key);
-        mAliasTable.Put(key, fonts);
-    }
-
     mLastConfig = currentConfig;
     return NS_OK;
 }
 
 nsresult
-gfxFontconfigUtils::GetResolvedFonts(const nsACString& aName,
-                                     gfxFontNameList* aResult)
-{
-    FcPattern *pat = NULL;
-    FcFontSet *fs = NULL;
-    FcResult fresult;
-    aResult->Clear();
-    nsresult rv = NS_ERROR_FAILURE;
-
-    pat = FcPatternCreate();
-    if (!pat)
-        goto end;
-
-    FcDefaultSubstitute(pat);
-    FcPatternAddString(pat, FC_FAMILY,
-                       (FcChar8 *)nsPromiseFlatCString(aName).get());
-    // Delete the lang param. We need lang independent alias list.
-    FcPatternDel(pat, FC_LANG);
-    FcConfigSubstitute(NULL, pat, FcMatchPattern);
-
-    fs = FcFontSort(NULL, pat, FcTrue, NULL, &fresult);
-    if (!fs)
-        goto end;
-
-    rv = NS_OK;
-    for (int i = 0; i < fs->nfont; i++) {
-        char *family;
-
-        if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
-                               (FcChar8 **) &family) != FcResultMatch ||
-            mAliasForMultiFonts.IndexOfIgnoreCase(nsDependentCString(family)) >= 0 ||
-            IsExistingFont(nsDependentCString(family)) == 0)
-        {
-            continue;
-        }
-        NS_ConvertUTF8toUTF16 actualName(family);
-        if (aResult->Exists(actualName))
-            continue;
-        aResult->AppendElement(actualName);
-    }
-
-  end:
-    if (pat)
-        FcPatternDestroy(pat);
-    if (fs)
-        FcFontSetDestroy(fs);
-    return rv;
-}
-
-nsresult
 gfxFontconfigUtils::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
 {
     aFamilyName.Truncate();
 
     // The fontconfig has generic family names in the font list.
     if (aFontName.EqualsLiteral("serif") ||
         aFontName.EqualsLiteral("sans-serif") ||
         aFontName.EqualsLiteral("monospace")) {
@@ -543,24 +639,21 @@ gfxFontconfigUtils::GetStandardFamilyNam
     }
 
     nsresult rv = UpdateFontListInternal();
     if (NS_FAILED(rv))
         return rv;
 
     NS_ConvertUTF16toUTF8 fontname(aFontName);
 
-    if (mFonts.IndexOf(fontname) >= 0) {
-        aFamilyName.Assign(aFontName);
-        return NS_OK;
-    }
-
-    if (mNonExistingFonts.IndexOf(fontname) >= 0)
+    // return empty string if no such family exists
+    if (!IsExistingFamily(fontname))
         return NS_OK;
 
+    // XXXkt this could use mFontsByFamily.GetEntry()
     FcPattern *pat = NULL;
     FcObjectSet *os = NULL;
     FcFontSet *givenFS = NULL;
     nsCStringArray candidates;
     FcFontSet *candidateFS = NULL;
     rv = NS_ERROR_FAILURE;
 
     pat = FcPatternCreate();
@@ -650,100 +743,215 @@ gfxFontconfigUtils::ResolveFontName(cons
 {
     aAborted = PR_FALSE;
 
     nsresult rv = UpdateFontListInternal();
     if (NS_FAILED(rv))
         return rv;
 
     NS_ConvertUTF16toUTF8 fontname(aFontName);
-    if (mAliasForMultiFonts.IndexOfIgnoreCase(fontname) >= 0) {
-        nsCAutoString key;
-        ToLowerCase(fontname, key);
-        nsRefPtr<gfxFontNameList> fonts;
-        if (!mAliasTable.Get(key, &fonts))
-            NS_ERROR("The mAliasTable was broken!");
-        for (PRUint32 i = 0; i < fonts->Length(); i++) {
-            aAborted = !(*aCallback)(fonts->ElementAt(i), aClosure);
-            if (aAborted)
-                break;
-        }
-    } else {
-        PRInt32 result = IsExistingFont(fontname);
-        if (result < 0)
-            return NS_ERROR_FAILURE;
-
-        if (result > 0)
-            aAborted = !(*aCallback)(aFontName, aClosure);
-    }
+    // Sometimes, the font has two or more names (e.g., "Sazanami Gothic" has
+    // Japanese localized name).  We should not resolve to a single name
+    // because different names sometimes have different behavior. e.g., with
+    // the default settings of "Sazanami" on Fedora Core 5, the non-localized
+    // name uses anti-alias, but the localized name uses it.  So, we should
+    // check just whether the font is existing, without resolving to regular
+    // name.
+    //
+    // The family names in mAliasForMultiFonts are names understood by
+    // fontconfig.  The actual font to which they resolve depends on the
+    // entire match pattern.  That info is not available here, but there
+    // will be a font so leave the resolving to the gfxFontGroup.
+    if (IsExistingFamily(fontname) ||
+        mAliasForMultiFonts.IndexOfIgnoreCase(fontname))
+        aAborted = !(*aCallback)(aFontName, aClosure);
 
     return NS_OK;
 }
 
-PRInt32
-gfxFontconfigUtils::IsExistingFont(const nsACString &aFontName)
+PRBool
+gfxFontconfigUtils::IsExistingFamily(const nsCString& aFamilyName)
+{
+    return mFontsByFamily.GetEntry(ToFcChar8(aFamilyName.get())) != nsnull;
+}
+
+const nsTArray< nsCountedRef<FcPattern> >&
+gfxFontconfigUtils::GetFontsForFamily(const FcChar8 *aFamilyName)
 {
-    // Very many sites may specify the font-family only for Windows and Mac.
-    // We should check negative cache at first.
-    if (mNonExistingFonts.IndexOf(aFontName) >= 0)
-        return 0;
-    if (mAliasForSingleFont.IndexOf(aFontName) >= 0)
-        return 1;
-    if (mFonts.IndexOf(aFontName) >= 0)
-        return 1;
+    FontsByFcStrEntry *entry = mFontsByFamily.GetEntry(aFamilyName);
+
+    if (!entry)
+        return mEmptyPatternArray;
+
+    return entry->GetFonts();
+}
 
-    // XXX Sometimes, the font has two or more names (e.g., "Sazanami Gothic"
-    // has Japanese localized name). The another name doesn't including the
-    // cache. Therefore, we need to check the name.
-    // But we don't need to resolve the name. Because both names are not same
-    // behavior. E.g., the default settings of "Sazanami" on Fedora Core 5,
-    // the non-localized name uses Anti-alias, but the localized name uses it.
-    // So, we should check just whether the font is existing, don't resolve
-    // to regular name.
+static FcLangResult
+CompareLangString(const FcChar8 *aLangA, const FcChar8 *aLangB) {
+    FcLangResult result = FcLangDifferentLang;
+    for (PRUint32 i = 0; ; ++i) {
+        FcChar8 a = FcToLower(aLangA[i]);
+        FcChar8 b = FcToLower(aLangB[i]);
+
+        if (a != b) {
+            if ((a == '\0' && b == '-') || (a == '-' && b == '\0'))
+                return FcLangDifferentCountry;
+
+            return result;
+        }
+        if (a == '\0')
+            return FcLangEqual;
 
-    FcPattern *pat = NULL;
-    FcObjectSet *os = NULL;
-    FcFontSet *fs = NULL;
-    PRInt32 result = -1;
-
-    pat = FcPatternCreate();
-    if (!pat)
-        goto end;
-
-    FcPatternAddString(pat, FC_FAMILY,
-                       (FcChar8 *)nsPromiseFlatCString(aFontName).get());
+        if (a == '-') {
+            result = FcLangDifferentCountry;
+        }
+    }
+}
 
-    os = FcObjectSetBuild(FC_FAMILY, NULL);
-    if (!os)
-        goto end;
-
-    fs = FcFontList(NULL, pat, os);
-    if (!fs)
-        goto end;
+/* static */
+FcLangResult
+gfxFontconfigUtils::GetLangSupport(FcPattern *aFont, const FcChar8 *aLang)
+{
+    // When fontconfig builds a pattern for the font, it will set a single
+    // LangSet property value for the font.  That value may be removed and
+    // additional string values may be added through FcConfigSubsitute with
+    // FcMatchScan.  Values that are neither LangSet nor string are considered
+    // errors in fontconfig sort and match functions.
+    FcValue value;
+    FcLangResult best = FcLangDifferentLang;
+    int v = 0;
+    while (FcPatternGet(aFont, FC_LANG, v, &value) == FcResultMatch) {
+        ++v;
 
-    // There can be more than one matching set of family names: see bug 393819.
-    if (fs->nfont > 0) {
-        mAliasForSingleFont.AppendCString(aFontName);
-        result = 1;
-    } else {
-        mNonExistingFonts.AppendCString(aFontName);
-        result = 0;
+        FcLangResult support;
+        switch (value.type) {
+            case FcTypeLangSet:
+                support = FcLangSetHasLang(value.u.l, aLang);
+                break;
+            case FcTypeString:
+                support = CompareLangString(value.u.s, aLang);
+                break;
+            default:
+                // error
+                return FcLangEqual;
+        }
+
+        if (support < best) { // lower is better
+            if (support == FcLangEqual)
+                return support;
+            best = support;
+        }        
     }
 
-  end:
-    if (pat)
-        FcPatternDestroy(pat);
-    if (os)
-        FcObjectSetDestroy(os);
-    if (fs)
-        FcFontSetDestroy(fs);
-    return result;
+    // A missing FC_LANG property is considered a match in fontconfig sort
+    // and match functions.
+    if (v == 0)
+        return FcLangEqual;        
+
+    return best;
+}
+
+gfxFontconfigUtils::LangSupportEntry *
+gfxFontconfigUtils::GetLangSupportEntry(const FcChar8 *aLang, PRBool aWithFonts)
+{
+    // Currently any unrecognized languages from documents will be converted
+    // to x-unicode by nsILanguageAtomService, so there is a limit on the
+    // langugages that will be added here.  Reconsider when/if document
+    // languages are passed to this routine.
+
+    LangSupportEntry *entry = mLangSupportTable.PutEntry(aLang);
+    if (!entry)
+        return nsnull;
+
+    FcLangResult best = FcLangDifferentLang;
+
+    if (!entry->IsKeyInitialized()) {
+        entry->InitKey(aLang);
+    } else {
+        // mSupport is already initialized.
+        if (!aWithFonts)
+            return entry;
+
+        best = entry->mSupport;
+        // If there is support for this language, an empty font list indicates
+        // that the list hasn't been initialized yet.
+        if (best == FcLangDifferentLang || entry->mFonts.Length() > 0)
+            return entry;
+    }
+
+    // This FcFontSet is owned by fontconfig
+    FcFontSet *fontSet = FcConfigGetFonts(NULL, FcSetSystem);
+
+    nsAutoTArray<FcPattern*,100> fonts;
+
+    for (int f = 0; f < fontSet->nfont; ++f) {
+        FcPattern *font = fontSet->fonts[f];
+
+        FcLangResult support = GetLangSupport(font, aLang);
+
+        if (support < best) { // lower is better
+            best = support;
+            if (aWithFonts) {
+                fonts.Clear();
+            } else if (best == FcLangEqual) {
+                break;
+            }
+        }
+
+        // The font list in the LangSupportEntry is expected to be used only
+        // when no default fonts support the language.  There would be a large
+        // number of fonts in entries for languages using Latin script but
+        // these do not need to be created because default fonts already
+        // support these languages.
+        if (aWithFonts && support != FcLangDifferentLang && support == best) {
+            fonts.AppendElement(font);
+        }
+    }
+
+    entry->mSupport = best;
+    if (aWithFonts) {
+        if (fonts.Length() != 0) {
+            entry->mFonts.AppendElements(fonts.Elements(), fonts.Length());
+        } else if (best != FcLangDifferentLang) {
+            // Previously there was a font that supported this language at the
+            // level of entry->mSupport, but it has now disappeared.  At least
+            // entry->mSupport needs to be recalculated, but this is an
+            // indication that the set of installed fonts has changed, so
+            // update all caches.
+            mLastConfig = NULL; // invalidates caches
+            UpdateFontListInternal(PR_TRUE);
+            return GetLangSupportEntry(aLang, aWithFonts);
+        }
+    }
+
+    return entry;
+}
+
+FcLangResult
+gfxFontconfigUtils::GetBestLangSupport(const FcChar8 *aLang)
+{
+    UpdateFontListInternal();
+
+    LangSupportEntry *entry = GetLangSupportEntry(aLang, PR_FALSE);
+    if (!entry)
+        return FcLangEqual;
+
+    return entry->mSupport;
+}
+
+const nsTArray< nsCountedRef<FcPattern> >&
+gfxFontconfigUtils::GetFontsForLang(const FcChar8 *aLang)
+{
+    LangSupportEntry *entry = GetLangSupportEntry(aLang, PR_TRUE);
+    if (!entry)
+        return mEmptyPatternArray;
+
+    return entry->mFonts;
 }
 
 PRBool
 gfxFontNameList::Exists(nsAString& aName) {
     for (PRUint32 i = 0; i < Length(); i++) {
         if (aName.Equals(ElementAt(i)))
             return PR_TRUE;
     }
     return PR_FALSE;
 }
-
--- a/gfx/thebes/src/gfxFontconfigUtils.h
+++ b/gfx/thebes/src/gfxFontconfigUtils.h
@@ -15,16 +15,17 @@
  * The Original Code is Mozilla Japan code.
  *
  * The Initial Developer of the Original Code is Mozilla Japan.
  * Portions created by the Initial Developer are Copyright (C) 2007
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Masayuki Nakano <masayuki@d-toybox.com>
+ *   Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -35,25 +36,50 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_FONTCONFIG_UTILS_H
 #define GFX_FONTCONFIG_UTILS_H
 
 #include "gfxPlatform.h"
 
+#include "nsAutoRef.h"
 #include "nsTArray.h"
-#include "nsDataHashtable.h"
+#include "nsTHashtable.h"
 
 #include <fontconfig/fontconfig.h>
 
+
+NS_SPECIALIZE_TEMPLATE
+class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern>
+{
+public:
+    static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); }
+    static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); }
+};
+
+NS_SPECIALIZE_TEMPLATE
+class nsAutoRefTraits<FcFontSet> : public nsPointerRefTraits<FcFontSet>
+{
+public:
+    static void Release(FcFontSet *ptr) { FcFontSetDestroy(ptr); }
+};
+
+NS_SPECIALIZE_TEMPLATE
+class nsAutoRefTraits<FcCharSet> : public nsPointerRefTraits<FcCharSet>
+{
+public:
+    static void Release(FcCharSet *ptr) { FcCharSetDestroy(ptr); }
+};
+
+
 class gfxFontNameList : public nsTArray<nsString>
 {
 public:
-    THEBES_INLINE_DECL_REFCOUNTING(gfxFontList)
+    THEBES_INLINE_DECL_REFCOUNTING(gfxFontNameList)
     PRBool Exists(nsAString& aName);
 };
 
 class gfxFontconfigUtils {
 public:
     gfxFontconfigUtils();
 
     static gfxFontconfigUtils* GetFontconfigUtils() {
@@ -71,41 +97,185 @@ public:
     nsresult UpdateFontList();
 
     nsresult ResolveFontName(const nsAString& aFontName,
                              gfxPlatform::FontResolverCallback aCallback,
                              void *aClosure, PRBool& aAborted);
 
     nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
 
+    const nsTArray< nsCountedRef<FcPattern> >&
+    GetFontsForFamily(const FcChar8 *aFamilyName);
+
+    // Returns the best support that any font offers for |aLang|. 
+    FcLangResult GetBestLangSupport(const FcChar8 *aLang);
+    // Returns the fonts offering this best level of support.
+    const nsTArray< nsCountedRef<FcPattern> >&
+    GetFontsForLang(const FcChar8 *aLang);
+
+    // Retuns the language support for a fontconfig font pattern
+    static FcLangResult GetLangSupport(FcPattern *aFont, const FcChar8 *aLang);
+
+    // Conversions between FcChar8*, which is unsigned char*,
+    // and (signed) char*, that check the type of the argument.
+    static const FcChar8 *ToFcChar8(const char *aCharPtr)
+    {
+        return reinterpret_cast<const FcChar8*>(aCharPtr);
+    }
+    static const char *ToCString(const FcChar8 *aChar8Ptr)
+    {
+        return reinterpret_cast<const char*>(aChar8Ptr);
+    }
+
     static PRUint8 GetThebesStyle(FcPattern *aPattern); // slant
     static PRUint16 GetThebesWeight(FcPattern *aPattern);
 
+    static int GetFcSlant(const gfxFontStyle& aFontStyle);
+    // Returns a precise FC_WEIGHT from CSS weight |aBaseWeight|.
+    static int FcWeightForBaseWeight(PRInt8 aBaseWeight);
+
+    // This doesn't consider which faces exist, and so initializes the pattern
+    // using a guessed weight, and doesn't consider sizeAdjust.
+    static nsReturnRef<FcPattern>
+    NewPattern(const nsStringArray& aFamilies, const gfxFontStyle& aFontStyle,
+               const char *aLang);
+
     /**
      * @param aLangGroup [in] a Mozilla langGroup.
      * @param aFcLang [out] returns a language suitable for fontconfig
      *        matching |aLangGroup| or an empty string if no match is found.
      */
     static void GetSampleLangForGroup(const nsACString& aLangGroup,
                                       nsACString *aFcLang);
 
 protected:
+    // Base class for hash table entries with case-insensitive FcChar8
+    // string keys.
+    class FcStrEntryBase : public PLDHashEntryHdr {
+    public:
+        typedef const FcChar8 *KeyType;
+        typedef const FcChar8 *KeyTypePointer;
+
+        static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+        // Case-insensitive hash.
+        //
+        // fontconfig always ignores case of ASCII characters in family
+        // names and languages, but treatment of whitespace in families is
+        // not consistent.  FcFontSort and FcFontMatch ignore whitespace
+        // except for whitespace in the first character, while FcFontList
+        // and config subsitution tests require whitespace to match
+        // exactly.  CSS 2.1 implies that whitespace is important in the
+        // font-family property.  FcStrCmpIgnoreCase considers whitespace
+        // important.
+        static PLDHashNumber HashKey(const FcChar8 *aKey) {
+            PRUint32 hash = 0;
+            for (const FcChar8 *c = aKey; *c != '\0'; ++c) {
+                hash = PR_ROTATE_LEFT32(hash, 3) ^ FcToLower(*c);
+            }
+            return hash;
+        }
+        enum { ALLOW_MEMMOVE = PR_TRUE };
+    };
+
+public:
+    // Hash entry with a dependent const FcChar8* pointer to an external
+    // string for a key (and no data).  The user must ensure that the string
+    // associated with the pointer is not destroyed.  This entry type is
+    // useful for family name keys as the family name string is held in the
+    // font pattern.
+    class DepFcStrEntry : public FcStrEntryBase {
+    public:
+        // When constructing a new entry in the hashtable, the key is left
+        // blank.  The caller of PutEntry() must fill in mKey when NULL.  This
+        // provides a mechanism for the caller of PutEntry() to determine
+        // whether the entry has been initialized.
+        DepFcStrEntry(KeyTypePointer aName)
+            : mKey(NULL) { }
+
+        DepFcStrEntry(const DepFcStrEntry& toCopy)
+            : mKey(toCopy.mKey) { }
+
+        PRBool KeyEquals(KeyTypePointer aKey) const {
+            return FcStrCmpIgnoreCase(aKey, mKey) == 0;
+        }
+
+        const FcChar8 *mKey;
+    };
+
+    // Hash entry that uses a copy of an FcChar8 string to store the key.
+    // This entry type is useful for language keys, as languages are usually
+    // not stored as strings in font patterns.
+    class CopiedFcStrEntry : public FcStrEntryBase {
+    public:
+        // When constructing a new entry in the hashtable, the key is void.
+        // The caller of PutEntry() must call InitKey() when IsKeyInitialized()
+        // returns false.  This provides a mechanism for the caller of
+        // PutEntry() to determine whether the entry has been initialized.
+        CopiedFcStrEntry(KeyTypePointer aName) {
+            mKey.SetIsVoid(PR_TRUE);
+        }
+
+        CopiedFcStrEntry(const CopiedFcStrEntry& toCopy)
+            : mKey(toCopy.mKey) { }
+
+        PRBool KeyEquals(KeyTypePointer aKey) const {
+            return FcStrCmpIgnoreCase(aKey, ToFcChar8(mKey.get())) == 0;
+        }
+
+        PRBool IsKeyInitialized() { return !mKey.IsVoid(); }
+        void InitKey(const FcChar8* aKey) { mKey.Assign(ToCString(aKey)); }
+
+    private:
+        nsCString mKey;
+    };
+
+protected:
+    class FontsByFcStrEntry : public DepFcStrEntry {
+    public:
+        FontsByFcStrEntry(KeyTypePointer aName)
+            : DepFcStrEntry(aName) { }
+
+        FontsByFcStrEntry(const FontsByFcStrEntry& toCopy)
+            : DepFcStrEntry(toCopy), mFonts(toCopy.mFonts) { }
+
+        PRBool AddFont(FcPattern *aFont) {
+            return mFonts.AppendElement(aFont) != nsnull;
+        }
+        const nsTArray< nsCountedRef<FcPattern> >& GetFonts() {
+            return mFonts;
+        }
+    private:
+        nsTArray< nsCountedRef<FcPattern> > mFonts;
+    };
+
+    class LangSupportEntry : public CopiedFcStrEntry {
+    public:
+        LangSupportEntry(KeyTypePointer aName)
+            : CopiedFcStrEntry(aName) { }
+
+        LangSupportEntry(const LangSupportEntry& toCopy)
+            : CopiedFcStrEntry(toCopy), mSupport(toCopy.mSupport) { }
+
+        FcLangResult mSupport;
+        nsTArray< nsCountedRef<FcPattern> > mFonts;
+    };
+
     static gfxFontconfigUtils* sUtils;
 
-    PRInt32 IsExistingFont(const nsACString& aFontName);
-    nsresult GetResolvedFonts(const nsACString& aName,
-                              gfxFontNameList* aResult);
+    PRBool IsExistingFamily(const nsCString& aFamilyName);
 
     nsresult GetFontListInternal(nsCStringArray& aListOfFonts,
-                                 const nsACString *aLangGroup = nsnull);
+                                 const nsACString& aLangGroup);
     nsresult UpdateFontListInternal(PRBool aForce = PR_FALSE);
 
-    nsCStringArray mFonts;
-    nsCStringArray mNonExistingFonts;
-    nsCStringArray mAliasForSingleFont;
+    LangSupportEntry *GetLangSupportEntry(const FcChar8 *aLang,
+                                          PRBool aWithFonts);
+
+    nsTHashtable<FontsByFcStrEntry> mFontsByFamily;
+    nsTHashtable<LangSupportEntry> mLangSupportTable;
+    const nsTArray< nsCountedRef<FcPattern> > mEmptyPatternArray;
+
     nsCStringArray mAliasForMultiFonts;
 
-    nsDataHashtable<nsCStringHashKey, nsRefPtr<gfxFontNameList> > mAliasTable;
-
     FcConfig *mLastConfig;
 };
 
 #endif /* GFX_FONTCONFIG_UTILS_H */
--- a/gfx/thebes/src/gfxPangoFonts.cpp
+++ b/gfx/thebes/src/gfxPangoFonts.cpp
@@ -44,16 +44,18 @@
 
 #define PANGO_ENABLE_BACKEND
 
 #include "prtypes.h"
 #include "prlink.h"
 #include "gfxTypes.h"
 
 #include "nsMathUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsILanguageAtomService.h"
 
 #include "gfxContext.h"
 #include "gfxPlatformGtk.h"
 #include "gfxPangoFonts.h"
 #include "gfxFontconfigUtils.h"
 
 #include <freetype/tttables.h>
 
@@ -83,23 +85,38 @@
 #define IS_MISSING_GLYPH(g) ((g) & PANGO_GLYPH_UNKNOWN_FLAG)
 #define IS_EMPTY_GLYPH(g) ((g) == PANGO_GLYPH_EMPTY)
 
 // Same as pango_units_from_double from Pango 1.16 (but not in older versions)
 int moz_pango_units_from_double(double d) {
     return NS_lround(d * FLOAT_PANGO_SCALE);
 }
 
-static PangoLanguage *GetPangoLanguage(const nsACString& aLangGroup);
+static PangoLanguage *GuessPangoLanguage(const nsACString& aLangGroup);
 
 static cairo_scaled_font_t *CreateScaledFont(FcPattern *aPattern);
 
-/* static */ gfxPangoFontCache* gfxPangoFontCache::sPangoFontCache = nsnull;
-
 static PangoFontMap *gPangoFontMap;
+static PangoFontMap *GetPangoFontMap();
+
+static nsILanguageAtomService* gLangService;
+
+NS_SPECIALIZE_TEMPLATE
+class nsAutoRefTraits<PangoFont> : public gfxGObjectRefTraits<PangoFont> { };
+
+// stub class until fuller implementation is flushed out
+class gfxPangoFontEntry : public gfxFontEntry {
+public:
+    gfxPangoFontEntry(const nsAString& aName)
+        : gfxFontEntry(aName)
+    { }
+
+    ~gfxPangoFontEntry() {}
+        
+};
 
 /*
  * gfxFcFont
  *
  * This is a gfxFont implementation using a CAIRO_FONT_TYPE_FT
  * cairo_scaled_font created from an FcPattern.
  */
 
@@ -168,16 +185,20 @@ private:
     FT_Face mFace;
 };
 
 /**
  * gfxPangoFcFont:
  *
  * An implementation of PangoFcFont that wraps a gfxFont so that it can be
  * passed to PangoRenderFc shapers.
+ *
+ * Many of these will be created for pango_itemize, but most will only be
+ * tested for coverage of individual characters (and sometimes not even that).
+ * Therefore the gfxFont is only constructed if and when needed.
  */
 
 #define GFX_TYPE_PANGO_FC_FONT              (gfx_pango_fc_font_get_type())
 #define GFX_PANGO_FC_FONT(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FC_FONT, gfxPangoFcFont))
 #define GFX_IS_PANGO_FC_FONT(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FC_FONT))
 
 /* static */
 GType gfx_pango_fc_font_get_type (void);
@@ -185,23 +206,91 @@ GType gfx_pango_fc_font_get_type (void);
 #define GFX_PANGO_FC_FONT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FC_FONT, gfxPangoFcFontClass))
 #define GFX_IS_PANGO_FC_FONT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FC_FONT))
 #define GFX_PANGO_FC_FONT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FC_FONT, gfxPangoFcFontClass))
 
 // This struct is POD so that it can be used as a GObject.
 struct gfxPangoFcFont {
     PangoFcFont parent_instance;
 
+    FcPattern *mRequestedPattern;
     gfxFcFont *mGfxFont;
 
+    static nsReturnRef<PangoFont>
+    NewFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern)
+    {
+        // A pattern is needed for pango_fc_font_finalize.
+        //
+        // Adding a ref to the requested pattern and one of fontconfig's
+        // patterns uses much less memory than using the fully resolved
+        // pattern here, and saves calling FcFontRenderPrepare when the
+        // PangoFont is only tested for character coverage.
+        //
+        // Normally the is_hinted field of the PangoFcFont is set based on the
+        // FC_HINTING property on the pattern at construction, but this
+        // property is not known until after RenderPrepare.  is_hinted is used
+        // by pango_fc_font_kern_glyphs, which is sometimes used by
+        // pango_ot_buffer_output.  is_hinted will be set when the gfxFont is
+        // constructed for PangoFcFont::lock_face.
+        gfxPangoFcFont *font = static_cast<gfxPangoFcFont*>
+            (g_object_new(GFX_TYPE_PANGO_FC_FONT,
+                          "pattern", aFontPattern, NULL));
+
+        // Save the requested pattern for FcFontRenderPrepare.
+        FcPatternReference(aRequestedPattern);
+        font->mRequestedPattern = aRequestedPattern;
+
+        // pango_fc_font::get_coverage wants a FcFontMap.  (PangoFcFontMap
+        // usually sets this after calling PangoFcFontMap::create_font().)
+        PangoFcFont *fc_font = &font->parent_instance;
+        fc_font->fontmap = GetPangoFontMap();
+        g_object_ref(fc_font->fontmap);
+
+        return nsReturnRef<PangoFont>(PANGO_FONT(font));
+    }
+
     static gfxFcFont *GfxFont(gfxPangoFcFont *self)
     {
         if (!self->mGfxFont) {
-            FcPattern *pattern = PANGO_FC_FONT(self)->font_pattern;
-            self->mGfxFont = gfxFcFont::GetOrMakeFont(pattern).get();
+            PangoFcFont *fc_font = &self->parent_instance;
+
+            if (NS_LIKELY(self->mRequestedPattern)) {
+                // Created with gfxPangoFcFont::NewFont()
+                nsAutoRef<FcPattern> renderPattern
+                    (FcFontRenderPrepare(NULL, self->mRequestedPattern,
+                                         fc_font->font_pattern));
+                if (!renderPattern)
+                    return nsnull;
+
+                FcBool hinting = FcTrue;
+                FcPatternGetBool(renderPattern, FC_HINTING, 0, &hinting);
+                fc_font->is_hinted = hinting;
+
+                // is_transformed does not appear to be used anywhere but looks
+                // like it should be set.
+                FcMatrix *matrix;
+                FcResult result = FcPatternGetMatrix(renderPattern,
+                                                     FC_MATRIX, 0, &matrix);
+                fc_font->is_transformed =
+                    result == FcResultMatch &&
+                    (matrix->xy != 0.0 || matrix->yx != 0.0 ||
+                     matrix->xx != 1.0 || matrix->yy != 1.0);
+
+                self->mGfxFont = gfxFcFont::GetOrMakeFont(renderPattern).get();
+                if (self->mGfxFont) {
+                    // Finished with the requested pattern
+                    FcPatternDestroy(self->mRequestedPattern);
+                    self->mRequestedPattern = NULL;
+                }
+
+            } else {
+                // Created with gfxPangoFontMap::create_font()
+                self->mGfxFont =
+                    gfxFcFont::GetOrMakeFont(fc_font->font_pattern).get();
+            }                
         }
         return self->mGfxFont;
     }
 
     static cairo_scaled_font_t *CairoFont(gfxPangoFcFont *self)
     {
         return gfxPangoFcFont::GfxFont(self)->CairoScaledFont();
     }
@@ -209,32 +298,69 @@ struct gfxPangoFcFont {
 
 struct gfxPangoFcFontClass {
     PangoFcFontClass parent_class;
 };
 
 G_DEFINE_TYPE (gfxPangoFcFont, gfx_pango_fc_font, PANGO_TYPE_FC_FONT)
 
 static void
-gfx_pango_fc_font_init(gfxPangoFcFont *fontset)
+gfx_pango_fc_font_init(gfxPangoFcFont *font)
 {
 }
 
 
 static void
 gfx_pango_fc_font_finalize(GObject *object)
 {
     gfxPangoFcFont *self = GFX_PANGO_FC_FONT(object);
 
-    if (self->mGfxFont)
-        self->mGfxFont->Release();
+    if (self->mRequestedPattern)
+        FcPatternDestroy(self->mRequestedPattern);
+    NS_IF_RELEASE(self->mGfxFont);
+
+    // The parent class removes the reference to parent_instance->fontmap.
 
     G_OBJECT_CLASS(gfx_pango_fc_font_parent_class)->finalize(object);
 }
 
+static PangoFontDescription *
+gfx_pango_fc_font_describe(PangoFont *font)
+{
+    gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
+    PangoFcFont *fcFont = &self->parent_instance;
+    PangoFontDescription *result =
+        pango_font_description_copy(fcFont->description);
+
+    gfxFcFont *gfxFont = gfxPangoFcFont::GfxFont(self);
+    if (gfxFont) {
+        double pixelsize = gfxFont->GetStyle()->size;
+        double dpi = gfxPlatformGtk::DPI();
+        gint size = moz_pango_units_from_double(pixelsize * dpi / 72.0);
+        pango_font_description_set_size(result, size);
+    }
+    return result;
+}
+
+static PangoFontDescription *
+gfx_pango_fc_font_describe_absolute(PangoFont *font)
+{
+    gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
+    PangoFcFont *fcFont = &self->parent_instance;
+    PangoFontDescription *result =
+        pango_font_description_copy(fcFont->description);
+
+    gfxFcFont *gfxFont = gfxPangoFcFont::GfxFont(self);
+    if (gfxFont) {
+        double size = gfxFont->GetStyle()->size * PANGO_SCALE;
+        pango_font_description_set_absolute_size(result, size);
+    }
+    return result;
+}
+
 static void
 gfx_pango_fc_font_get_glyph_extents(PangoFont *font, PangoGlyph glyph,
                                     PangoRectangle *ink_rect,
                                     PangoRectangle *logical_rect)
 {
     gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
     gfxFcFont *gfxFont = gfxPangoFcFont::GfxFont(self);
 
@@ -280,26 +406,45 @@ gfx_pango_fc_font_get_glyph_extents(Pang
         ink_rect->height = moz_pango_units_from_double(extents.height);
     }
     if (logical_rect) {
         logical_rect->x = 0;
         logical_rect->width = moz_pango_units_from_double(extents.x_advance);
     }
 }
 
-#ifdef DEBUG
 static PangoFontMetrics *
 gfx_pango_fc_font_get_metrics(PangoFont *font, PangoLanguage *language)
 {
-    NS_WARNING("Using PangoFcFont::get_metrics");
-
-    return PANGO_FONT_CLASS(gfx_pango_fc_font_parent_class)->
-        get_metrics(font, language);
+    gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
+
+    // This uses g_slice_alloc which will abort on OOM rather than return NULL.
+    PangoFontMetrics *result = pango_font_metrics_new();
+
+    gfxFcFont *gfxFont = gfxPangoFcFont::GfxFont(self);
+    if (gfxFont) {
+        const gfxFont::Metrics& metrics = gfxFont->GetMetrics();
+
+        result->ascent = moz_pango_units_from_double(metrics.maxAscent);
+        result->descent = moz_pango_units_from_double(metrics.maxDescent);
+        result->approximate_char_width =
+            moz_pango_units_from_double(metrics.aveCharWidth);
+        result->approximate_digit_width =
+            moz_pango_units_from_double(metrics.zeroOrAveCharWidth);
+        result->underline_position =
+            moz_pango_units_from_double(metrics.underlineOffset);
+        result->underline_thickness =
+            moz_pango_units_from_double(metrics.underlineSize);
+        result->strikethrough_position =
+            moz_pango_units_from_double(metrics.strikeoutOffset);
+        result->strikethrough_thickness =
+            moz_pango_units_from_double(metrics.strikeoutSize);
+    }
+    return result;
 }
-#endif
 
 static FT_Face
 gfx_pango_fc_font_lock_face(PangoFcFont *font)
 {
     gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
     return cairo_ft_scaled_font_lock_face(gfxPangoFcFont::CairoFont(self));
 }
 
@@ -317,54 +462,509 @@ gfx_pango_fc_font_class_init (gfxPangoFc
     PangoFontClass *font_class = PANGO_FONT_CLASS (klass);
     PangoFcFontClass *fc_font_class = PANGO_FC_FONT_CLASS (klass);
 
     object_class->finalize = gfx_pango_fc_font_finalize;
 
 #if 0
     // This will need overriding for user fonts to defeat the PangoFcFontMap
     // caching, unless each user font is guaranteed to have a unique filename.
-    font_class->get_coverage =
+    font_class->get_coverage = gfx_pango_fc_font_get_coverage;
 #endif
+    // describe is called on errors in pango_shape.
+    font_class->describe = gfx_pango_fc_font_describe;
     font_class->get_glyph_extents = gfx_pango_fc_font_get_glyph_extents;
-#ifdef DEBUG
-    // non-DEBUG inherits from fc_font_class as this won't be used anyway.
+    // get_metrics and describe_absolute are not likely to be used but
+    //   implemented because the class makes them available.
     font_class->get_metrics = gfx_pango_fc_font_get_metrics;
-#endif
+    font_class->describe_absolute = gfx_pango_fc_font_describe_absolute;
+    // font_class->find_shaper,get_font_map are inherited from PangoFcFontClass
+
     // fc_font_class->has_char,get_glyph are inherited
     fc_font_class->lock_face = gfx_pango_fc_font_lock_face;
     fc_font_class->unlock_face = gfx_pango_fc_font_unlock_face;
 }
 
 /**
- * Recording a base PangoFont on a PangoContext
+ * Recording a gfxPangoFontGroup on a PangoContext
  */
 
-static GQuark GetBaseFontQuark()
+static GQuark GetFontGroupQuark()
 {
     // Not using g_quark_from_static_string() because this module may be
     // unloaded (which would leave a dangling pointer).  Using
     // g_quark_from_string() instead, which creates a small shutdown leak.
-    static GQuark quark = g_quark_from_string("moz-base-font");
+    static GQuark quark = g_quark_from_string("moz-font-group");
     return quark;
 }
 
-static PangoFont *
-GetBaseFont(PangoContext *aContext)
+static void
+gfxFontGroup_unref(gpointer data)
 {
-    return static_cast<PangoFont*>
-        (g_object_get_qdata(G_OBJECT(aContext), GetBaseFontQuark()));
+    gfxPangoFontGroup *fontGroup = static_cast<gfxPangoFontGroup*>(data);
+    NS_RELEASE(fontGroup);
 }
 
 static void
-SetBaseFont(PangoContext *aContext, PangoFont *aBaseFont)
+SetFontGroup(PangoContext *aContext, gfxPangoFontGroup *aFontGroup)
+{
+    NS_ADDREF(aFontGroup);
+    g_object_set_qdata_full(G_OBJECT(aContext), GetFontGroupQuark(),
+                            aFontGroup, gfxFontGroup_unref);
+}
+
+static gfxPangoFontGroup *
+GetFontGroup(PangoContext *aContext)
+{
+    return static_cast<gfxPangoFontGroup*>
+        (g_object_get_qdata(G_OBJECT(aContext), GetFontGroupQuark()));
+}
+
+/**
+ * gfxFcPangoFontSet:
+ *
+ * Translation from a desired FcPattern to a sorted set of font references
+ * (fontconfig cache data) and (when needed) PangoFonts.
+ */
+
+#if 0
+static int font_sets = 0;
+static int max_font_sets = 0;
+#endif
+
+class gfxFcPangoFontSet {
+public:
+    THEBES_INLINE_DECL_REFCOUNTING(gfxFcPangoFontSet)
+    
+    explicit gfxFcPangoFontSet(FcPattern *aPattern)
+        : mSortPattern(aPattern),
+          mFcFontSet(SortPreferredFonts()), mFcFontsTrimmed(0),
+          mHaveFallbackFonts(PR_FALSE)
+    {
+#if 0
+        ++font_sets;
+        if (font_sets > max_font_sets) {
+            max_font_sets = font_sets;
+            fprintf(stderr, "font sets: %i\n", font_sets);
+        }
+#endif
+    }
+
+    ~gfxFcPangoFontSet()
+    {
+#if 0
+        --font_sets;
+#endif
+        PRInt32 last = mFonts.Length() - 1;
+        for (; last >= 0 && !mFonts[last].mFont; --last) ;
+        fprintf(stderr, "patterns/fonts allocated: %i/%i\n",
+                mFonts.Length(), last + 1);
+    }
+
+    // A reference is held by the FontSet.
+    // The caller may add a ref to keep the font alive longer than the FontSet.
+    PangoFont *GetFontAt(PRUint32 i)
+    {
+        if (i >= mFonts.Length() || !mFonts[i].mFont) { 
+            // GetFontPatternAt sets up mFonts
+            FcPattern *fontPattern = GetFontPatternAt(i);
+            if (!fontPattern)
+                return NULL;
+
+            mFonts[i].mFont =
+                gfxPangoFcFont::NewFont(mSortPattern, fontPattern);
+        }
+        return mFonts[i].mFont;
+    }
+
+    FcPattern *GetFontPatternAt(PRUint32 i);
+
+private:
+    nsReturnRef<FcFontSet> SortPreferredFonts();
+    nsReturnRef<FcFontSet> SortFallbackFonts();
+
+    struct FontEntry {
+        explicit FontEntry(FcPattern *aPattern) : mPattern(aPattern) {}
+        nsCountedRef<FcPattern> mPattern;
+        nsCountedRef<PangoFont> mFont;
+    };
+
+    struct LangSupportEntry {
+        LangSupportEntry(FcChar8 *aLang, FcLangResult aSupport) :
+            mLang(aLang), mBestSupport(aSupport) {}
+        FcChar8 *mLang;
+        FcLangResult mBestSupport;
+    };
+
+public:
+    // public for nsTArray
+    class LangComparator {
+    public:
+        PRBool Equals(const LangSupportEntry& a, const FcChar8 *b) const
+        {
+            return FcStrCmpIgnoreCase(a.mLang, b) == 0;
+        }
+    };
+
+private:
+    // The requested pattern
+    nsCountedRef<FcPattern> mSortPattern;
+    // A (trimmed) list of font patterns and PangoFonts that is built up as
+    // required.
+    nsTArray<FontEntry> mFonts;
+    // Holds a list of font patterns that will be trimmed.  This is first set
+    // to a list of preferred fonts.  Then, if/when all the preferred fonts
+    // have been trimmed and added to mFonts, this is set to a list of
+    // fallback fonts.
+    nsAutoRef<FcFontSet> mFcFontSet;
+    // The set of characters supported by the fonts in mFonts.
+    nsAutoRef<FcCharSet> mCharSet;
+    // The index of the next font in mFcFontSet that has not yet been
+    // considered for mFonts.
+    int mFcFontsTrimmed;
+    // True iff fallback fonts are either stored in mFcFontSet or have been
+    // trimmed and added to mFonts (so that mFcFontSet is NULL).
+    PRPackedBool mHaveFallbackFonts;
+};
+
+typedef FcBool (*FcPatternRemoveFunction)(FcPattern *p, const char *object,
+                                          int id);
+
+static FcPatternRemoveFunction
+GetFcPatternRemove()
+{
+    PRLibrary *lib = nsnull;
+    PRFuncPtr result =
+        PR_FindFunctionSymbolAndLibrary("FcPatternRemove", &lib);
+    if (lib) {
+        PR_UnloadLibrary(lib);
+    }
+
+    return reinterpret_cast<FcPatternRemoveFunction>(result);
+}
+
+// FcPatternRemove is available in fontconfig-2.3.0 (2005)
+// CentOS 5 has fontconfig-2.4.1
+static FcBool
+moz_FcPatternRemove(FcPattern *p, const char *object, int id)
+{
+    static FcPatternRemoveFunction sFcPatternRemovePtr = GetFcPatternRemove();
+
+    if (!sFcPatternRemovePtr)
+        return FcFalse;
+
+    return (*sFcPatternRemovePtr)(p, object, id);
+}
+
+// fontconfig always prefers a matching family to a matching slant, but CSS
+// mostly prioritizes slant.  The logic here is from CSS 2.1.
+static PRBool
+SlantIsAcceptable(FcPattern *aFont, int aRequestedSlant)
+{
+    // CSS accepts (possibly synthetic) oblique for italic.
+    if (aRequestedSlant == FC_SLANT_ITALIC)
+        return PR_TRUE;
+
+    int slant;
+    FcResult result = FcPatternGetInteger(aFont, FC_SLANT, 0, &slant);
+    // Not having a value would be strange.
+    // fontconfig sort and match functions would consider no value a match.
+    if (result != FcResultMatch)
+        return PR_TRUE;
+
+    switch (aRequestedSlant) {
+        case FC_SLANT_ROMAN:
+            // CSS requires an exact match
+            return slant == aRequestedSlant;
+        case FC_SLANT_OBLIQUE:
+            // Accept synthetic oblique from Roman,
+            // but CSS doesn't accept italic.
+            return slant != FC_SLANT_ITALIC;
+    }
+
+    return PR_TRUE;
+}
+
+// fontconfig prefers a matching family or lang to pixelsize of bitmap
+// fonts.  CSS suggests a tolerance of 20% on pixelsize.
+static PRBool
+SizeIsAcceptable(FcPattern *aFont, double aRequestedSize)
+{
+    double size;
+    int v = 0;
+    while (FcPatternGetDouble(aFont,
+                              FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
+        ++v;
+        if (5.0 * fabs(size - aRequestedSize) < aRequestedSize)
+            return PR_TRUE;
+    }
+
+    // No size means scalable
+    return v == 0;
+}
+
+// Sorting only the preferred fonts first usually saves having to sort through
+// every font on the system.
+nsReturnRef<FcFontSet>
+gfxFcPangoFontSet::SortPreferredFonts()
 {
-    g_object_ref(aBaseFont);
-    g_object_set_qdata_full(G_OBJECT(aContext), GetBaseFontQuark(),
-                            aBaseFont, g_object_unref);
+    gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils();
+    if (!utils)
+        return nsReturnRef<FcFontSet>();
+
+    // The list of families in mSortPattern has values with both weak and
+    // strong bindings.  Values with strong bindings should be preferred.
+    // Values with weak bindings are default fonts that should be considered
+    // only when the font provides the best support for a requested language
+    // or after other fonts have satisfied all the requested languages.
+    //
+    // There are no direct fontconfig APIs to get the binding type.  The
+    // binding only takes effect in the sort and match functions.
+
+    // |requiredLangs| is a list of requested languages that have not yet been
+    // satisfied.  gfxFontconfigUtils only sets one FC_LANG property value,
+    // but FcConfigSubstitute may add more values (e.g. prepending "en" to
+    // "ja" will use western fonts to render Latin/Arabic numerals in Japanese
+    // text.)
+    nsAutoTArray<LangSupportEntry,10> requiredLangs;
+    for (int v = 0; ; ++v) {
+        FcChar8 *lang;
+        FcResult result = FcPatternGetString(mSortPattern, FC_LANG, v, &lang);
+        if (result != FcResultMatch) {
+            // No need to check FcPatternGetLangSet() because
+            // gfxFontconfigUtils sets only a string value for FC_LANG and
+            // FcConfigSubstitute cannot add LangSets.
+            NS_ASSERTION(result != FcResultTypeMismatch,
+                         "Expected a string for FC_LANG");
+            break;
+        }
+
+        if (!requiredLangs.Contains(lang, LangComparator())) {
+            FcLangResult bestLangSupport = utils->GetBestLangSupport(lang);
+            if (bestLangSupport != FcLangDifferentLang) {
+                requiredLangs.
+                    AppendElement(LangSupportEntry(lang, bestLangSupport));
+            }
+        }
+    }
+
+    nsAutoRef<FcFontSet> fontSet(FcFontSetCreate());
+    if (!fontSet)
+        return fontSet.out();
+
+    // FcDefaultSubstitute() ensures a slant on mSortPattern, but, if that ever
+    // doesn't happen, Roman will be used.
+    int requestedSlant = FC_SLANT_ROMAN;
+    FcPatternGetInteger(mSortPattern, FC_SLANT, 0, &requestedSlant);
+    double requestedSize = -1.0;
+    FcPatternGetDouble(mSortPattern, FC_PIXEL_SIZE, 0, &requestedSize);
+
+    nsTHashtable<gfxFontconfigUtils::DepFcStrEntry> existingFamilies;
+    existingFamilies.Init(50);
+    FcChar8 *family;
+    for (int v = 0;
+         FcPatternGetString(mSortPattern,
+                            FC_FAMILY, v, &family) == FcResultMatch; ++v) {
+        const nsTArray< nsCountedRef<FcPattern> >& familyFonts =
+            utils->GetFontsForFamily(family);
+
+        if (familyFonts.Length() == 0) {
+            // There are no fonts matching this family, so there is not point
+            // in searching for this family in the FontSort.
+            //
+            // Perhaps the original pattern should be retained for
+            // FcFontRenderPrepare.  However, the only a useful config
+            // substitution test against missing families that i can imagine
+            // would only be interested in the preferred family
+            // (qual="first"), so always keep the first family and use the
+            // same pattern for Sort and RenderPrepare.
+            if (v != 0 && moz_FcPatternRemove(mSortPattern, FC_FAMILY, v)) {
+                --v;
+            }
+            continue;
+        }
+
+        // Aliases seem to often end up occurring more than once, but
+        // duplicate families can't be removed from the sort pattern without
+        // knowing whether duplicates have the same binding.
+        gfxFontconfigUtils::DepFcStrEntry *entry =
+            existingFamilies.PutEntry(family);
+        if (entry) {
+            if (entry->mKey) // old entry
+                continue;
+
+            entry->mKey = family; // initialize new entry
+        }
+
+        for (PRUint32 f = 0; f < familyFonts.Length(); ++f) {
+            FcPattern *font = familyFonts[f];
+
+            if (!SlantIsAcceptable(font, requestedSlant))
+                continue;
+            if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
+                continue;
+
+            for (PRUint32 r = 0; r < requiredLangs.Length(); ++r) {
+                const LangSupportEntry& entry = requiredLangs[r];
+                FcLangResult support =
+                    gfxFontconfigUtils::GetLangSupport(font, entry.mLang);
+                if (support <= entry.mBestSupport) { // lower is better
+                    requiredLangs.RemoveElementAt(r);
+                    --r;
+                }
+            }
+
+            // FcFontSetDestroy will remove a reference but FcFontSetAdd
+            // does _not_ take a reference!
+            if (FcFontSetAdd(fontSet, font)) {
+                FcPatternReference(font);
+            }
+        }
+    }
+
+    FcPattern *truncateMarker = NULL;
+    for (PRUint32 r = 0; r < requiredLangs.Length(); ++r) {
+        const nsTArray< nsCountedRef<FcPattern> >& langFonts =
+            utils->GetFontsForLang(requiredLangs[r].mLang);
+
+        PRBool haveLangFont = PR_FALSE;
+        for (PRUint32 f = 0; f < langFonts.Length(); ++f) {
+            FcPattern *font = langFonts[f];
+            if (!SlantIsAcceptable(font, requestedSlant))
+                continue;
+            if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
+                continue;
+
+            haveLangFont = PR_TRUE;
+            if (FcFontSetAdd(fontSet, font)) {
+                FcPatternReference(font);
+            }
+        }
+
+        if (!haveLangFont && langFonts.Length() > 0) {
+            // There is a font that supports this language but it didn't pass
+            // the slant and size criteria.  Weak default font families should
+            // not be considered until the language has been satisfied.
+            //
+            // Insert a font that supports the language so that it will mark
+            // the position of fonts from weak families in the sorted set and
+            // they can be removed.  The language and weak families will be
+            // considered in the fallback fonts, which use fontconfig's
+            // algorithm.
+            //
+            // Of the fonts that don't meet slant and size criteria, strong
+            // default font families should be considered before (other) fonts
+            // for this language, so this marker font will be removed (as well
+            // as the fonts from weak families), and strong families will be
+            // reconsidered in the fallback fonts.
+            FcPattern *font = langFonts[0];
+            if (FcFontSetAdd(fontSet, font)) {
+                FcPatternReference(font);
+                truncateMarker = font;
+            }
+            break;
+        }
+    }
+
+    FcFontSet *sets[1] = { fontSet };
+    FcResult result;
+    fontSet.own(FcFontSetSort(NULL, sets, 1, mSortPattern,
+                              FcFalse, NULL, &result));
+
+    if (truncateMarker != NULL && fontSet) {
+        nsAutoRef<FcFontSet> truncatedSet(FcFontSetCreate());
+
+        for (int f = 0; f < fontSet->nfont; ++f) {
+            FcPattern *font = fontSet->fonts[f];
+            if (font == truncateMarker)
+                break;
+
+            if (FcFontSetAdd(truncatedSet, font)) {
+                FcPatternReference(font);
+            }
+        }
+
+        fontSet.steal(truncatedSet);
+    }
+
+    return fontSet.out();
+}
+
+nsReturnRef<FcFontSet>
+gfxFcPangoFontSet::SortFallbackFonts()
+{
+    // Setting trim to FcTrue would provide a much smaller (~ 1/10) FcFontSet,
+    // but would take much longer due to comparing all the character sets.
+    //
+    // The references to fonts in this FcFontSet are almost free
+    // as they are pointers into mmaped cache files.
+    //
+    // GetFontPatternAt() will trim lazily if and as needed, which will also
+    // remove duplicates of preferred fonts.
+    FcResult result;
+    return nsReturnRef<FcFontSet>(FcFontSort(NULL, mSortPattern,
+                                             FcFalse, NULL, &result));
+}
+
+// GetFontAt relies on this setting up all patterns up to |i|.
+FcPattern *
+gfxFcPangoFontSet::GetFontPatternAt(PRUint32 i)
+{
+    while (i >= mFonts.Length()) {
+        while (!mFcFontSet) {
+            if (mHaveFallbackFonts)
+                return nsnull;
+
+            mFcFontSet = SortFallbackFonts();
+            mHaveFallbackFonts = PR_TRUE;
+            mFcFontsTrimmed = 0;
+            // Loop to test that mFcFontSet is non-NULL.
+        }
+
+        while (mFcFontsTrimmed < mFcFontSet->nfont) {
+            FcPattern *font = mFcFontSet->fonts[mFcFontsTrimmed];
+            ++mFcFontsTrimmed;
+
+            if (mFonts.Length() != 0) {
+                // See if the next font provides support for any extra
+                // characters.  Most often the next font is not going to
+                // support more characters so check for a SubSet first before
+                // allocating a new CharSet with Union.
+                FcCharSet *supportedChars = mCharSet;
+                if (!supportedChars) {
+                    FcPatternGetCharSet(mFonts[mFonts.Length() - 1].mPattern,
+                                        FC_CHARSET, 0, &supportedChars);
+                }
+
+                if (supportedChars) {
+                    FcCharSet *newChars = NULL;
+                    FcPatternGetCharSet(font, FC_CHARSET, 0, &newChars);
+                    if (newChars) {
+                        if (FcCharSetIsSubset(newChars, supportedChars))
+                            continue;
+
+                        mCharSet.own(FcCharSetUnion(supportedChars, newChars));
+                    } else if (!mCharSet) {
+                        mCharSet.own(FcCharSetCopy(supportedChars));
+                    }
+                }
+            }
+
+            mFonts.AppendElement(font);
+            if (mFonts.Length() >= i)
+                break;
+        }
+
+        if (mFcFontsTrimmed == mFcFontSet->nfont) {
+            // finished with this font set
+            mFcFontSet.reset();
+        }
+    }
+
+    return mFonts[i].mPattern;
 }
 
 /**
  * gfxPangoFontset: An implementation of a PangoFontset for gfxPangoFontMap
  */
 
 #define GFX_TYPE_PANGO_FONTSET              (gfx_pango_fontset_get_type())
 #define GFX_PANGO_FONTSET(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FONTSET, gfxPangoFontset))
@@ -376,219 +976,234 @@ GType gfx_pango_fontset_get_type (void);
 #define GFX_PANGO_FONTSET_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FONTSET, gfxPangoFontsetClass))
 #define GFX_IS_PANGO_FONTSET_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FONTSET))
 #define GFX_PANGO_FONTSET_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FONTSET, gfxPangoFontsetClass))
 
 // This struct is POD so that it can be used as a GObject.
 struct gfxPangoFontset {
     PangoFontset parent_instance;
 
-    PangoFontMap *mFontMap;
-    PangoContext *mContext;
-    PangoFontDescription *mFontDesc;
     PangoLanguage *mLanguage;
+    gfxFcPangoFontSet *mGfxFontSet;
     PangoFont *mBaseFont;
-    PangoFontset *mFallbackFontset;
+    gfxPangoFontGroup *mFontGroup;
 
     static PangoFontset *
-    NewFontset(PangoFontMap *aFontMap,
-               PangoContext *aContext,
-               const PangoFontDescription *aFontDesc,
+    NewFontset(gfxPangoFontGroup *aFontGroup,
                PangoLanguage *aLanguage)
     {
         gfxPangoFontset *fontset = static_cast<gfxPangoFontset *>
             (g_object_new(GFX_TYPE_PANGO_FONTSET, NULL));
 
-        fontset->mFontMap = aFontMap;
-        g_object_ref(aFontMap);
-
-        fontset->mContext = aContext;
-        g_object_ref(aContext);
-
-        fontset->mFontDesc = pango_font_description_copy(aFontDesc);
         fontset->mLanguage = aLanguage;
 
-        fontset->mBaseFont = GetBaseFont(aContext);
-        if(fontset->mBaseFont)
-            g_object_ref(fontset->mBaseFont);
+        // Use the font group's fontset if the language matches
+        if (aFontGroup->GetPangoLanguage() == aLanguage) {
+            fontset->mGfxFontSet = aFontGroup->GetFontSet();
+            NS_IF_ADDREF(fontset->mGfxFontSet);
+
+        } else {
+            // Otherwise, fallback fonts depend on the language so get
+            // another font-set for the language if/when the base font is
+            // not suitable.  Save the font group for this.
+            fontset->mFontGroup = aFontGroup;
+            NS_ADDREF(fontset->mFontGroup);
+
+            // Using the same base font irrespective of the language that
+            // Pango chooses for the script means that PANGO_SCRIPT_COMMON
+            // characters are consistently rendered with the same font.
+            // (Bug 339513 and bug 416725).
+            //
+            // However, use the default Pango behavior (selecting generic
+            // fonts from the script of the characters) in two situations:
+            //
+            //   1. When we don't have a language to make a good choice for
+            //      the primary font.
+            //
+            //   2. For system fonts, use the default Pango behavior to give
+            //      consistency with other apps.  (This probably wouldn't be
+            //      necessary but for bug 91190.)
+            if (aFontGroup->GetPangoLanguage() &&
+                !aFontGroup->GetStyle()->systemFont) {
+                fontset->mBaseFont = aFontGroup->GetBasePangoFont();
+                if (fontset->mBaseFont)
+                    g_object_ref(fontset->mBaseFont);
+            }
+        }
 
         return PANGO_FONTSET(fontset);
     }
 };
 
 struct gfxPangoFontsetClass {
     PangoFontsetClass parent_class;
 };
 
 G_DEFINE_TYPE (gfxPangoFontset, gfx_pango_fontset, PANGO_TYPE_FONTSET)
 
 static void
 gfx_pango_fontset_init(gfxPangoFontset *fontset)
 {
 }
 
-
 static void
 gfx_pango_fontset_finalize(GObject *object)
 {
     gfxPangoFontset *self = GFX_PANGO_FONTSET(object);
 
-    if (self->mContext)
-        g_object_unref(self->mContext);
-    if (self->mFontDesc)
-        pango_font_description_free(self->mFontDesc);
     if (self->mBaseFont)
         g_object_unref(self->mBaseFont);
-    if (self->mFontMap)
-        g_object_unref(self->mFontMap);
-    if (self->mFallbackFontset)
-        g_object_unref(self->mFallbackFontset);
+    NS_IF_RELEASE(self->mGfxFontSet);
+    NS_IF_RELEASE(self->mFontGroup);
 
     G_OBJECT_CLASS(gfx_pango_fontset_parent_class)->finalize(object);
 }
 
 static PangoLanguage *
 gfx_pango_fontset_get_language(PangoFontset *fontset)
 {
     gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
     return self->mLanguage;
 }
 
-struct ForeachExceptBaseData {
-    PangoFont *mBaseFont;
-    PangoFontset *mFontset;
-    PangoFontsetForeachFunc mFunc;
-    gpointer mData;
-};
-
-static gboolean
-foreach_except_base_cb(PangoFontset *fontset, PangoFont *font, gpointer data)
+static gfxFcPangoFontSet *
+GetGfxFontSet(gfxPangoFontset *self)
 {
-    ForeachExceptBaseData *baseData =
-        static_cast<ForeachExceptBaseData *>(data);
-    
-    // returning false means continue with the other fonts in the set
-    return font != baseData->mBaseFont &&
-        (*baseData->mFunc)(baseData->mFontset, font, baseData->mData);
-}
-
-static PangoFontset *
-gfx_pango_font_map_load_fallback_fontset(PangoFontMap *fontmap,
-                                         PangoContext *context,
-                                         const PangoFontDescription *desc,
-                                         PangoLanguage *language);
-
-static PangoFontset *
-EnsureFallbackFontset(gfxPangoFontset *self)
-{
-    if (!self->mFallbackFontset) {
-        // To consider:
-        //
-        // * If this is happening often (e.g. Chinese/Japanese pages where a
-        //   Latin font is specified first), and Pango's 64-entry pattern
-        //   cache is not large enough, then a fontset cache here could be
-        //   helpful.  Ideally we'd only cache the fonts that are actually
-        //   accessed rather than all the fonts from the FcFontSort.
-        //
-        // * Mozilla's langGroup font prefs could be used to specify preferred
-        //   fallback fonts for the script of the characters (as indicated by
-        //   Pango in mLanguage), by doing the conversion from gfxFontGroup
-        //   "families" to PangoFcFontMap "family" here.
-        self->mFallbackFontset =
-            gfx_pango_font_map_load_fallback_fontset(self->mFontMap,
-                                                     self->mContext,
-                                                     self->mFontDesc,
-                                                     self->mLanguage);
+    if (!self->mGfxFontSet && self->mFontGroup) {
+        self->mGfxFontSet = self->mFontGroup->GetFontSet(self->mLanguage);
+        // Finished with the font group
+        NS_RELEASE(self->mFontGroup);
+
+        if (!self->mGfxFontSet)
+            return nsnull;
+
+        NS_ADDREF(self->mGfxFontSet);
     }
-    return self->mFallbackFontset;
+    return self->mGfxFontSet;
 }
 
 static void
 gfx_pango_fontset_foreach(PangoFontset *fontset, PangoFontsetForeachFunc func,
                           gpointer data)
 {
     gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
 
-    if (self->mBaseFont && (*func)(fontset, self->mBaseFont, data))
-        return;
+    FcPattern *baseFontPattern = NULL;
+    if (self->mBaseFont) {
+        if ((*func)(fontset, self->mBaseFont, data))
+            return;
+
+        baseFontPattern = PANGO_FC_FONT(self->mBaseFont)->font_pattern;
+    }        
 
     // Falling back to secondary fonts
-    PangoFontset *childFontset = EnsureFallbackFontset(self);
-    ForeachExceptBaseData baseData = { self->mBaseFont, fontset, func, data };
-    pango_fontset_foreach(childFontset, foreach_except_base_cb, &baseData);
+    gfxFcPangoFontSet *gfxFontSet = GetGfxFontSet(self);
+    if (!gfxFontSet)
+        return;
+
+    for (PRUint32 i = 0;
+         FcPattern *pattern = gfxFontSet->GetFontPatternAt(i);
+         ++i) {
+        // Skip this font if it is the same face as the base font
+        if (pattern == baseFontPattern) {
+            continue;
+        }
+        PangoFont *font = gfxFontSet->GetFontAt(i);
+        if (font) {
+            if ((*func)(fontset, font, data))
+                return;
+        }
+    }
+}
+
+static PRBool HasChar(FcPattern *aFont, FcChar32 wc)
+{
+    FcCharSet *charset = NULL;
+    FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
+
+    return charset && FcCharSetHasChar(charset, wc);
 }
 
 static PangoFont *
 gfx_pango_fontset_get_font(PangoFontset *fontset, guint wc)
 {
     gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
 
-    PangoCoverageLevel baseLevel = PANGO_COVERAGE_NONE;
+    PangoFont *result = NULL;
+
+    FcPattern *baseFontPattern = NULL;
     if (self->mBaseFont) {
-        // PangoFcFontMap caches this:
-        PangoCoverage *coverage =
-            pango_font_get_coverage(self->mBaseFont, self->mLanguage);
-        if (coverage) {
-            baseLevel = pango_coverage_get(coverage, wc);
-            pango_coverage_unref(coverage);
+        baseFontPattern = PANGO_FC_FONT(self->mBaseFont)->font_pattern;
+
+        if (HasChar(baseFontPattern, wc)) {
+            result = self->mBaseFont;
         }
     }
 
-    if (baseLevel != PANGO_COVERAGE_EXACT) {
-        PangoFontset *childFontset = EnsureFallbackFontset(self);
-        PangoFont *childFont = pango_fontset_get_font(childFontset, wc);
-        if (!self->mBaseFont || childFont == self->mBaseFont)
-            return childFont;
-
-        if (childFont) {
-            PangoCoverage *coverage =
-                pango_font_get_coverage(childFont, self->mLanguage);
-            if (coverage) {
-                PangoCoverageLevel childLevel =
-                    pango_coverage_get(coverage, wc);
-                pango_coverage_unref(coverage);
-
-                // Only use the child font if better than the base font.
-                if (childLevel > baseLevel)
-                    return childFont;
+    if (!result) {
+        // Falling back to secondary fonts
+        gfxFcPangoFontSet *gfxFontSet = GetGfxFontSet(self);
+
+        if (gfxFontSet) {
+            for (PRUint32 i = 0;
+                 FcPattern *pattern = gfxFontSet->GetFontPatternAt(i);
+                 ++i) {
+                // Skip this font if it is the same face as the base font
+                if (pattern == baseFontPattern) {
+                    continue;
+                }
+
+                if (HasChar(pattern, wc)) {
+                    result = gfxFontSet->GetFontAt(i);
+                    break;
+                }
             }
-            g_object_unref(childFont);
+        }
+
+        if (!result) {
+            // Nothing found.  Return the first font.
+            if (self->mBaseFont) {
+                result = self->mBaseFont;
+            } else if (gfxFontSet) {
+                result = gfxFontSet->GetFontAt(0);
+            }
         }
     }
 
-    g_object_ref(self->mBaseFont);
-    return self->mBaseFont;
+    if (!result)
+        return NULL;
+
+    g_object_ref(result);
+    return result;
 }
 
 static void
 gfx_pango_fontset_class_init (gfxPangoFontsetClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
     PangoFontsetClass *fontset_class = PANGO_FONTSET_CLASS (klass);
 
     object_class->finalize = gfx_pango_fontset_finalize;
+    // get_font is not likely to be used but implemented because the class
+    //   makes it available.
     fontset_class->get_font = gfx_pango_fontset_get_font;
-    // inherit fontset_class->get_metrics (which won't be used anyway)
+    // inherit fontset_class->get_metrics (which is not likely to be used)
     fontset_class->get_language = gfx_pango_fontset_get_language;
     fontset_class->foreach = gfx_pango_fontset_foreach;
 }
 
 /**
  * gfxPangoFontMap: An implementation of a PangoFontMap.
  *
- * This allows the primary (base) font to be specified.  There are two
- * advantages to this:
+ * This is passed to pango_itemize() through the PangoContext parameter, and
+ * provides font selection through the gfxPangoFontGroup.
  *
- * 1. Always using the same base font irrespective of the language that Pango
- *    chooses for the script means that PANGO_SCRIPT_COMMON characters are
- *    consistently rendered with the same font.  (Bug 339513 and bug 416725)
- *
- * 2. We normally have the base font from the gfxFont cache so this saves a
- *    FcFontSort when the entry has expired from Pango's much smaller pattern
- *    cache.
+ * It is intended that the font group is recorded on the PangoContext with
+ * SetFontGroup().  The font group is then queried for fonts, with
+ * gfxFcPangoFontSet doing the font selection.
  */
 
 #define GFX_TYPE_PANGO_FONT_MAP              (gfx_pango_font_map_get_type())
 #define GFX_PANGO_FONT_MAP(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMap))
 #define GFX_IS_PANGO_FONT_MAP(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FONT_MAP))
 
 GType gfx_pango_font_map_get_type (void);
 
@@ -621,136 +1236,124 @@ static void
 gfx_pango_font_map_init(gfxPangoFontMap *fontset)
 {
 }
 
 static PangoFont *
 gfx_pango_font_map_load_font(PangoFontMap *fontmap, PangoContext *context,
                              const PangoFontDescription *description)
 {
-    PangoFont *baseFont = GetBaseFont(context);
-    if (baseFont) {
+    gfxPangoFontGroup *fontGroup = GetFontGroup(context);
+    if (NS_UNLIKELY(!fontGroup)) {
+        return PANGO_FONT_MAP_CLASS(gfx_pango_font_map_parent_class)->
+            load_font(fontmap, context, description);
+    }
+        
+    PangoFont *baseFont = fontGroup->GetBasePangoFont();
+    if (NS_LIKELY(baseFont)) {
         g_object_ref(baseFont);
-        return baseFont;
     }
-
-    return PANGO_FONT_MAP_CLASS(gfx_pango_font_map_parent_class)->
-        load_font(fontmap, context, description);
+    return baseFont;
 }
 
 static PangoFontset *
 gfx_pango_font_map_load_fontset(PangoFontMap *fontmap, PangoContext *context,
-                               const PangoFontDescription *desc,
-                               PangoLanguage *language)
+                                const PangoFontDescription *desc,
+                                PangoLanguage *language)
 {
-    return gfxPangoFontset::NewFontset(fontmap, context, desc, language);
-}
-
-static PangoFontset *
-gfx_pango_font_map_load_fallback_fontset(PangoFontMap *fontmap,
-                                         PangoContext *context,
-                                         const PangoFontDescription *desc,
-                                         PangoLanguage *language)
-{
-    return PANGO_FONT_MAP_CLASS(gfx_pango_font_map_parent_class)->
-        load_fontset(fontmap, context, desc, language);
-}
-
-static void
-gfx_pango_font_map_list_families(PangoFontMap *fontmap,
-                                 PangoFontFamily ***families, int *n_families)
-{
-    return PANGO_FONT_MAP_CLASS(gfx_pango_font_map_parent_class)->
-        list_families(fontmap, families, n_families);
+    gfxPangoFontGroup *fontGroup = GetFontGroup(context);
+    if (NS_UNLIKELY(!fontGroup)) {
+        return PANGO_FONT_MAP_CLASS(gfx_pango_font_map_parent_class)->
+            load_fontset(fontmap, context, desc, language);
+    }
+
+    return gfxPangoFontset::NewFontset(fontGroup, language);
 }
 
 static double
 gfx_pango_font_map_get_resolution(PangoFcFontMap *fcfontmap,
                                   PangoContext *context)
 {
     // This merely enables the FC_SIZE field of the pattern to be accurate.
     // We use gfxPlatformGtk::DPI() much of the time...
     return gfxPlatformGtk::DPI();
 }
 
+// Apply user settings and defaults to pattern in preparation for matching.
+static void
+PrepareSortPattern(FcPattern *aPattern, double aFallbackSize,
+                   double aSizeAdjustFactor)
+{
+    FcConfigSubstitute(NULL, aPattern, FcMatchPattern);
+
+    // This gets cairo_font_options_t for the Screen.  We should have
+    // different font options for printing (no hinting) but we are not told
+    // what we are measuring for.
+    //
+    // If cairo adds support for lcd_filter, gdk will not provide the default
+    // setting for that option.  We could get the default setting by creating
+    // an xlib surface once, recording its font_options, and then merging the
+    // gdk options.
+    const cairo_font_options_t *options =
+        gdk_screen_get_font_options(gdk_screen_get_default());
+
+    cairo_ft_font_options_substitute(options, aPattern);
+
+    // Protect against any fontconfig settings that may have incorrectly
+    // modified the pixelsize, and consider aSizeAdjustFactor.
+    double size = aFallbackSize;
+    if (FcPatternGetDouble(aPattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch
+        || aSizeAdjustFactor != 1.0) {
+        FcPatternDel(aPattern, FC_PIXEL_SIZE);
+        FcPatternAddDouble(aPattern, FC_PIXEL_SIZE, size * aSizeAdjustFactor);
+    }
+
+    FcDefaultSubstitute(aPattern);
+}
+
 static void
 gfx_pango_font_map_context_substitute(PangoFcFontMap *fontmap,
                                       PangoContext *context,
                                       FcPattern *pattern)
 {
-    FcConfigSubstitute(NULL, pattern, FcMatchPattern);
-
-    // XXXkt This gets cairo_font_options_t for the Screen.  We should have
-    // different font options for printing (no hinting) but we are not told
-    // what we are measuring for.
-    const cairo_font_options_t *options =
-        gdk_screen_get_font_options(gdk_screen_get_default());
-
-    cairo_ft_font_options_substitute(options, pattern);
-
-    FcDefaultSubstitute(pattern);
+    // owned by the context
+    PangoFontDescription *desc = pango_context_get_font_description(context);
+    double size = pango_font_description_get_size(desc) / FLOAT_PANGO_SCALE;
+    PrepareSortPattern(pattern, size, 1.0);
 }
 
 static PangoFcFont *
 gfx_pango_font_map_create_font(PangoFcFontMap *fontmap,
                                PangoContext *context,
                                const PangoFontDescription *desc,
                                FcPattern *pattern)
 {
-    // A pattern is needed for pango_fc_font_finalize.
-    //
-    // Adding a ref to one of fontconfig's patterns would use much less memory
-    // than using the fully resolved pattern here.  This could be done by
-    // setting the PangoFcFont field is_hinted directly.  (is_hinted is used
-    // by pango_fc_font_kern_glyphs, which is sometimes used by
-    // pango_ot_buffer_output.)  is_transformed is not used but maybe it
-    // should be set too.  describe_absolute would need to be overridden if
-    // required, and description would need to be set if describe is required
-    // but not overridden.
-    //
-    // An alternative memory-saving effort might remove elements from the
-    // resolved pattern that are unnecessary.
-
-    // Protect against any fontconfig settings that may have incorrectly
-    // modified the pixelsize.
-    PRBool newPattern = PR_FALSE;
-    double size;
-    if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch) {
-        size = pango_font_description_get_size(desc) / FLOAT_PANGO_SCALE;
-        pattern = FcPatternDuplicate(pattern);
-        newPattern = PR_TRUE;
-        FcPatternDel(pattern, FC_PIXEL_SIZE);
-        FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
-    }
-
-    PangoFcFont *font = PANGO_FC_FONT(g_object_new(GFX_TYPE_PANGO_FC_FONT,
-                                                   "pattern", pattern, NULL));
-
-    if (newPattern) {
-        FcPatternDestroy(pattern);
-    }
-    return font;
+    return PANGO_FC_FONT(g_object_new(GFX_TYPE_PANGO_FC_FONT,
+                                      "pattern", pattern, NULL));
 }
 
 static void
 gfx_pango_font_map_class_init(gfxPangoFontMapClass *klass)
 {
-    // inherit GObjectClass::finalize from the parent as there is nothing to
-    // finalize in this object.
+    // inherit GObjectClass::finalize from parent as this class adds no data.
 
     PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (klass);
     fontmap_class->load_font = gfx_pango_font_map_load_font;
+    // inherit fontmap_class->list_families (which is not likely to be used)
+    //   from PangoFcFontMap
     fontmap_class->load_fontset = gfx_pango_font_map_load_fontset;
-    fontmap_class->list_families = gfx_pango_font_map_list_families;
     // inherit fontmap_class->shape_engine_type from PangoFcFontMap
 
     PangoFcFontMapClass *fcfontmap_class = PANGO_FC_FONT_MAP_CLASS (klass);
     fcfontmap_class->get_resolution = gfx_pango_font_map_get_resolution;
     // context_key_* virtual functions are only necessary if we want to
     // dynamically respond to changes in the screen cairo_font_options_t.
+
+    // context_substitute and get_font are not likely to be used but
+    //   implemented because the class makes them available.
     fcfontmap_class->context_substitute = gfx_pango_font_map_context_substitute;
     fcfontmap_class->create_font = gfx_pango_font_map_create_font;
 }
 
 /**
  ** gfxPangoFontGroup
  **/
 
@@ -782,76 +1385,128 @@ FontCallback (const nsAString& fontName,
 
     return PR_TRUE;
 }
 
 gfxPangoFontGroup::gfxPangoFontGroup (const nsAString& families,
                                       const gfxFontStyle *aStyle,
                                       gfxUserFontSet *aUserFontSet)
     : gfxFontGroup(families, aStyle, aUserFontSet),
-      mBasePangoFont(nsnull), mAdjustedSize(0)
+      mPangoLanguage(GuessPangoLanguage(aStyle->langGroup))
 {
     mFonts.AppendElements(1);
 }
 
 gfxPangoFontGroup::~gfxPangoFontGroup()
 {
-    if (mBasePangoFont)
-        g_object_unref(mBasePangoFont);
 }
 
 gfxFontGroup *
 gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle)
 {
     return new gfxPangoFontGroup(mFamilies, aStyle, mUserFontSet);
 }
 
-// A string of family names suitable for fontconfig
+// An array of family names suitable for fontconfig
 void
-gfxPangoFontGroup::GetFcFamilies(nsAString& aFcFamilies)
+gfxPangoFontGroup::GetFcFamilies(nsStringArray *aFcFamilyList,
+                                 const nsACString& aLangGroup)
 {
-    nsStringArray familyArray;
-
     // Leave non-existing fonts in the list so that fontconfig can get the
     // best match.
-    ForEachFontInternal(mFamilies, mStyle.langGroup, PR_TRUE, PR_FALSE,
-                        FontCallback, &familyArray);
-
-    if (familyArray.Count()) {
-        int i = 0;
-        while (1) {
-            aFcFamilies.Append(*familyArray[i]);
-            ++i;
-            if (i >= familyArray.Count())
-                break;
-            aFcFamilies.Append(NS_LITERAL_STRING(","));
-        }
-    }
-    else {
-        // XXX If there are no fonts, we should use dummy family.
-        // Pango will resolve from this.
-        // behdad: yep, looks good.
-        // printf("%s(%s)\n", NS_ConvertUTF16toUTF8(families).get(),
-        //                    aStyle->langGroup.get());
-        aFcFamilies.Append(NS_LITERAL_STRING("sans-serif"));
-    }
+    ForEachFontInternal(mFamilies, aLangGroup, PR_TRUE, PR_FALSE,
+                        FontCallback, aFcFamilyList);
+}
+
+PangoFont *
+gfxPangoFontGroup::GetBasePangoFont()
+{
+    return GetBaseFontSet()->GetFontAt(0);
 }
 
 gfxFont *
 gfxPangoFontGroup::GetFontAt(PRInt32 i) {
     NS_PRECONDITION(i == 0, "Only have one font");
 
     if (!mFonts[0]) {
         PangoFont *pangoFont = GetBasePangoFont();
         mFonts[0] = gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(pangoFont));
     }
 
     return mFonts[0];
 }
 
+already_AddRefed<gfxFcPangoFontSet>
+gfxPangoFontGroup::MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
+                               nsAutoRef<FcPattern> *aMatchPattern)
+{
+    const char *lang = pango_language_to_string(aLang);
+
+    const char *langGroup = nsnull;
+    if (aLang != mPangoLanguage) {
+        // Set up langGroup for Mozilla's font prefs.
+        if (!gLangService) {
+            CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
+        }
+        if (gLangService) {
+            nsIAtom *atom =
+                gLangService->LookupLanguage(NS_ConvertUTF8toUTF16(lang));
+            if (atom) {
+                atom->GetUTF8String(&langGroup);
+            }
+        }
+    }
+
+    nsStringArray fcFamilyList;
+    GetFcFamilies(&fcFamilyList,
+                  langGroup ? nsDependentCString(langGroup) : mStyle.langGroup);
+
+    // To consider: A fontset cache here could be helpful.
+
+    // Get a pattern suitable for matching.
+    nsAutoRef<FcPattern> pattern
+        (gfxFontconfigUtils::NewPattern(fcFamilyList, mStyle, lang));
+
+    PrepareSortPattern(pattern, mStyle.size, aSizeAdjustFactor);
+
+    nsRefPtr<gfxFcPangoFontSet> fontset = new gfxFcPangoFontSet(pattern);
+
+    if (aMatchPattern)
+        aMatchPattern->steal(pattern);
+
+    return fontset.forget();
+}
+
+gfxPangoFontGroup::
+FontSetByLangEntry::FontSetByLangEntry(PangoLanguage *aLang,
+                                       gfxFcPangoFontSet *aFontSet)
+    : mLang(aLang), mFontSet(aFontSet)
+{
+}
+
+gfxFcPangoFontSet *
+gfxPangoFontGroup::GetFontSet(PangoLanguage *aLang)
+{
+    GetBaseFontSet(); // sets mSizeAdjustFactor and mFontSets[0]
+
+    if (!aLang)
+        return mFontSets[0].mFontSet;
+
+    for (PRUint32 i = 0; i < mFontSets.Length(); ++i) {
+        if (mFontSets[i].mLang == aLang)
+            return mFontSets[i].mFontSet;
+    }
+
+    nsRefPtr<gfxFcPangoFontSet> fontSet =
+        MakeFontSet(aLang, mSizeAdjustFactor);
+    mFontSets.AppendElement(FontSetByLangEntry(aLang, fontSet));
+
+    return fontSet;
+}
+
 /**
  ** gfxFcFont
  **/
 
 cairo_user_data_key_t gfxFcFont::sGfxFontKey;
 
 gfxFcFont::gfxFcFont(cairo_scaled_font_t *aCairoFont,
                      gfxPangoFontEntry *aFontEntry,
@@ -868,107 +1523,42 @@ gfxFcFont::~gfxFcFont()
 {
     cairo_scaled_font_set_user_data(mCairoFont, &sGfxFontKey, NULL, NULL);
     cairo_scaled_font_destroy(mCairoFont);
 }
 
 /* static */ void
 gfxPangoFontGroup::Shutdown()
 {
-    gfxPangoFontCache::Shutdown();
-
     if (gPangoFontMap) {
         if (PANGO_IS_FC_FONT_MAP (gPangoFontMap)) {
             // This clears circular references from the fontmap to itself
             // through its fonts.
             pango_fc_font_map_shutdown(PANGO_FC_FONT_MAP(gPangoFontMap));
         }
         g_object_unref(gPangoFontMap);
         gPangoFontMap = NULL;
     }
+
+    NS_IF_RELEASE(gLangService);
+#if 0
+    fprintf(stderr, "font sets: %i\n", font_sets);
+#endif
 }
 
-static PangoStyle
-ThebesStyleToPangoStyle (const gfxFontStyle *fs)
-{
-    if (fs->style == FONT_STYLE_ITALIC)
-        return PANGO_STYLE_ITALIC;
-    if (fs->style == FONT_STYLE_OBLIQUE)
-        return PANGO_STYLE_OBLIQUE;
-
-    return PANGO_STYLE_NORMAL;
-}
-
-static PangoWeight
-ThebesStyleToPangoWeight (const gfxFontStyle *fs)
+static double
+GetPixelSize(FcPattern *aPattern)
 {
-    PRInt32 w = fs->weight;
-
-    /*
-     * weights come in two parts crammed into one
-     * integer -- the "base" weight is weight / 100,
-     * the rest of the value is the "offset" from that
-     * weight -- the number of steps to move to adjust
-     * the weight in the list of supported font weights,
-     * this value can be negative or positive.
-     */
-    PRInt32 baseWeight = (w + 50) / 100;
-    PRInt32 offset = w - baseWeight * 100;
-
-    /* clip weights to range 0 to 9 */
-    if (baseWeight < 0)
-        baseWeight = 0;
-    if (baseWeight > 9)
-        baseWeight = 9;
-
-    /* Map from weight value to fcWeights index */
-    static const int fcWeightLookup[10] = {
-        0, 0, 0, 0, 1, 1, 2, 3, 3, 4,
-    };
-
-    PRInt32 fcWeight = fcWeightLookup[baseWeight];
-
-    /*
-     * adjust by the offset value, make sure we stay inside the 
-     * fcWeights table
-     */
-    fcWeight += offset;
-
-    if (fcWeight < 0)
-        fcWeight = 0;
-    if (fcWeight > 4)
-        fcWeight = 4;
-
-    /* Map to final PANGO_WEIGHT value */
-    static const int fcWeights[5] = {
-        349,
-        449,
-        649,
-        749,
-        900
-    };
-
-    return (PangoWeight)fcWeights[fcWeight];
-}
-
-/* Note this doesn't check sizeAdjust */
-static PangoFontDescription *
-NewPangoFontDescription(const nsAString &aName, const gfxFontStyle *aFontStyle)
-{
-    PangoFontDescription *fontDesc = pango_font_description_new();
-
-    pango_font_description_set_family(fontDesc,
-                                      NS_ConvertUTF16toUTF8(aName).get());
-    pango_font_description_set_absolute_size
-        (fontDesc, moz_pango_units_from_double(aFontStyle->size));
-    pango_font_description_set_style(fontDesc,
-                                     ThebesStyleToPangoStyle(aFontStyle));
-    pango_font_description_set_weight(fontDesc,
-                                      ThebesStyleToPangoWeight(aFontStyle));
-    return fontDesc;
+    double size;
+    if (FcPatternGetDouble(aPattern,
+                           FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
+        return size;
+
+    NS_NOTREACHED("No size on pattern");
+    return 0.0;
 }
 
 /**
  * The following gfxPangoFonts are accessed from the PangoFont, not from the
  * gfxFontCache hash table.  The gfxFontCache hash table is keyed by desired
  * family and style, whereas here we only know actual family and style.  There
  * may be more than one of these fonts with the same family and style, but
  * different PangoFont and actual font face.
@@ -984,22 +1574,17 @@ already_AddRefed<gfxFcFont>
 gfxFcFont::GetOrMakeFont(FcPattern *aPattern)
 {
     cairo_scaled_font_t *cairoFont = CreateScaledFont(aPattern);
 
     nsRefPtr<gfxFcFont> font = static_cast<gfxFcFont*>
         (cairo_scaled_font_get_user_data(cairoFont, &sGfxFontKey));
 
     if (!font) {
-        double size;
-        if (FcPatternGetDouble(aPattern,
-                               FC_PIXEL_SIZE, 0, &size) != FcResultMatch) {
-            NS_NOTREACHED("No size on pattern");
-            size = 0.0;
-        }
+        gfxFloat size = GetPixelSize(aPattern);
 
         // 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(aPattern);
         PRUint16 weight = gfxFontconfigUtils::GetThebesWeight(aPattern);
 
         // 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
@@ -1007,17 +1592,17 @@ gfxFcFont::GetOrMakeFont(FcPattern *aPat
         NS_NAMED_LITERAL_CSTRING(langGroup, "x-unicode");
         gfxFontStyle fontStyle(style, weight, size, langGroup, 0.0,
                                PR_TRUE, PR_FALSE);
 
         FcChar8 *fc_file; // unsigned char
         const char *file; // signed for Mozilla string APIs
         if (FcPatternGetString(aPattern,
                                FC_FILE, 0, &fc_file) == FcResultMatch) {
-            file = reinterpret_cast<char*>(fc_file);
+            file = gfxFontconfigUtils::ToCString(fc_file);
         } else {
             // cairo won't know which font to open without a file.
             // (We don't create fonts from an FT_Face.)
             NS_NOTREACHED("Fonts without a file are not supported");
             static const char *noFile = "NO FILE";
             file = noFile;
         }
         int index;
@@ -1046,95 +1631,78 @@ gfxFcFont::GetOrMakeFont(FcPattern *aPat
         // that affect rendering but are not included in the gfxFontStyle.
         font = new gfxFcFont(cairoFont, fe, &fontStyle);
     }
 
     cairo_scaled_font_destroy(cairoFont);
     return font.forget();
 }
 
+static PangoFontMap *
+GetPangoFontMap()
+{
+    if (!gPangoFontMap) {
+        gPangoFontMap = gfxPangoFontMap::NewFontMap();
+    }
+    return gPangoFontMap;
+}
+
 static PangoContext *
 GetPangoContext()
 {
     PangoContext *context = pango_context_new();
-
-    // Change the font map to recognize a base font on the context
-    if (!gPangoFontMap) {
-        gPangoFontMap = gfxPangoFontMap::NewFontMap();
-    }
-    pango_context_set_font_map(context, gPangoFontMap);
-
+    pango_context_set_font_map(context, GetPangoFontMap());
     return context;
 }
 
-static PangoFont*
-LoadPangoFont(PangoContext *aPangoCtx, const PangoFontDescription *aPangoFontDesc)
+gfxFcPangoFontSet *
+gfxPangoFontGroup::GetBaseFontSet()
 {
-    gfxPangoFontCache *cache = gfxPangoFontCache::GetPangoFontCache();
-    if (!cache)
-        return nsnull; // Error
-    PangoFont* pangoFont = cache->Get(aPangoFontDesc);
-    if (!pangoFont) {
-        pangoFont = pango_context_load_font(aPangoCtx, aPangoFontDesc);
-        if (pangoFont) {
-            cache->Put(aPangoFontDesc, pangoFont);
+    if (mFontSets.Length() > 0)
+        return mFontSets[0].mFontSet;
+
+    mSizeAdjustFactor = 1.0; // will be adjusted below if necessary
+    nsAutoRef<FcPattern> pattern;
+    nsRefPtr<gfxFcPangoFontSet> fontSet =
+        MakeFontSet(mPangoLanguage, mSizeAdjustFactor, &pattern);
+
+    double size = GetPixelSize(pattern);
+    if (size != 0.0 && mStyle.sizeAdjust != 0.0) {
+        gfxFcFont *font =
+            gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(fontSet->GetFontAt(0)));
+        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 =
+                    mStyle.sizeAdjust * metrics.emHeight / metrics.xHeight;
+
+                size *= mSizeAdjustFactor;
+                FcPatternDel(pattern, FC_PIXEL_SIZE);
+                FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
+
+                fontSet = new gfxFcPangoFontSet(pattern);
+            }
         }
     }
-    return pangoFont;
-}
-
-// The font group holds the reference to the PangoFont
-PangoFont *
-gfxPangoFontGroup::GetBasePangoFont()
-{
-    if (mBasePangoFont)
-        return mBasePangoFont;
-
-    nsAutoString fcFamilies;
-    GetFcFamilies(fcFamilies);
-    PangoFontDescription *pangoFontDesc = 
-        NewPangoFontDescription(fcFamilies, GetStyle());
-
-    PangoContext *pangoCtx = GetPangoContext();
-
-    if (!GetStyle()->langGroup.IsEmpty()) {
-        PangoLanguage *lang = GetPangoLanguage(GetStyle()->langGroup);
-        if (lang)
-            pango_context_set_language(pangoCtx, lang);
+
+    PangoLanguage *pangoLang = mPangoLanguage;
+    FcChar8 *fcLang;
+    if (!pangoLang &&
+        FcPatternGetString(pattern, FC_LANG, 0, &fcLang) == FcResultMatch) {
+        pangoLang =
+            pango_language_from_string(gfxFontconfigUtils::ToCString(fcLang));
     }
 
-    PangoFont *pangoFont = LoadPangoFont(pangoCtx, pangoFontDesc);
-
-    gfxFloat size = GetStyle()->size;
-    if (size != 0.0 && GetStyle()->sizeAdjust != 0.0) {
-        LockedFTFace
-            face(gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(pangoFont)));
-        // Could try xHeight from TrueType/OpenType fonts.
-        cairo_text_extents_t extents;
-        if (face.GetCharExtents('x', &extents) &&
-            extents.y_bearing < 0.0) {
-            gfxFloat aspect = -extents.y_bearing / size;
-            size = GetStyle()->GetAdjustedSize(aspect);
-
-            pango_font_description_set_absolute_size
-                (pangoFontDesc, moz_pango_units_from_double(size));
-            g_object_unref(pangoFont);
-            pangoFont = LoadPangoFont(pangoCtx, pangoFontDesc);
-        }
-    }
-
-    if (pangoFontDesc)
-        pango_font_description_free(pangoFontDesc);
-    if (pangoCtx)
-        g_object_unref(pangoCtx);
-
-    mBasePangoFont = pangoFont;
-    mAdjustedSize = size;
-
-    return pangoFont;
+    mFontSets.AppendElement(FontSetByLangEntry(pangoLang, fontSet));
+
+    return fontSet;
 }
 
 void
 gfxFcFont::GetGlyphExtents(PRUint32 aGlyph, cairo_text_extents_t* aExtents)
 {
     NS_PRECONDITION(aExtents != NULL, "aExtents must not be NULL");
 
     cairo_glyph_t glyphs[1];
@@ -1581,22 +2149,17 @@ gfxPangoFontGroup::InitTextRun(gfxTextRu
 #endif
 }
 
 // This will fetch an existing scaled_font if one exists.
 static cairo_scaled_font_t *
 CreateScaledFont(FcPattern *aPattern)
 {
     cairo_font_face_t *face = cairo_ft_font_face_create_for_pattern(aPattern);
-    double size;
-    if (FcPatternGetDouble(aPattern,
-                           FC_PIXEL_SIZE, 0, &size) != FcResultMatch) {
-        NS_NOTREACHED("No size on pattern");
-        size = 0.0;
-    }
+    double size = GetPixelSize(aPattern);
         
     cairo_matrix_t fontMatrix;
     FcMatrix *fcMatrix;
     if (FcPatternGetMatrix(aPattern, FC_MATRIX, 0, &fcMatrix) == FcResultMatch)
         cairo_matrix_init(&fontMatrix, fcMatrix->xx, -fcMatrix->yx, -fcMatrix->xy, fcMatrix->yy, 0, 0);
     else
         cairo_matrix_init_identity(&fontMatrix);
     cairo_matrix_scale(&fontMatrix, size, size);
@@ -2152,50 +2715,41 @@ gfxPangoFontGroup::CreateGlyphRunsFast(g
 }
 #endif
 
 void 
 gfxPangoFontGroup::CreateGlyphRunsItemizing(gfxTextRun *aTextRun,
                                             const gchar *aUTF8, PRUint32 aUTF8Length,
                                             PRUint32 aUTF8HeaderLen)
 {
+    // This font group and gfxPangoFontMap are recorded on the PangoContext
+    // passed to pango_itemize_with_base_dir().
+    //
+    // pango_itemize_with_base_dir() divides the string into substrings for
+    // each language, and queries gfxPangoFontMap::load_fontset() to provide
+    // ordered lists of fonts for each language.  gfxPangoFontMap passes the
+    // request back to this font group, which returns a gfxFcPangoFontSet
+    // handling the font sorting/selection.
+    //
+    // For each character, pango_itemize_with_base_dir searches through these
+    // lists of fonts for a font with support for the character.  The
+    // PangoItems returned represent substrings (or runs) of consectutive
+    // characters to be shaped with the same PangoFont and having the same
+    // script.
+    //
+    // The PangoFonts in the PangoItems are from the gfxPangoFontMap and so
+    // each have a gfxFont.  This gfxFont represents the same face as the
+    // PangoFont and so can render the same glyphs in the same way as
+    // pango_shape measures.
 
     PangoContext *context = GetPangoContext();
-
-    // The font description could be cached on the gfxPangoFontGroup...
-    nsAutoString fcFamilies;
-    GetFcFamilies(fcFamilies);
-    PangoFontDescription *fontDesc =
-        NewPangoFontDescription(fcFamilies, GetStyle());
-    if (GetStyle()->sizeAdjust != 0.0) {
-        gfxFloat size = GetAdjustedSize();
-        pango_font_description_set_absolute_size
-            (fontDesc, moz_pango_units_from_double(size));
-    }
-
-    pango_context_set_font_description(context, fontDesc);
-    pango_font_description_free(fontDesc);
-
-    PangoLanguage *lang = GetPangoLanguage(GetStyle()->langGroup);
-
     // we should set this to null if we don't have a text language from the page...
     // except that we almost always have something...
-    pango_context_set_language(context, lang);
-
-    // Set the primary font for consistent font selection for common
-    // characters, but use the default Pango behavior
-    // (selecting generic fonts from the script of the characters)
-    // in two situations:
-    //   1. When we don't have a language to make a good choice for the
-    //      primary font.
-    //   2. For system fonts, use the default Pango behavior
-    //      to give consistency with other apps.
-    if (lang && !GetStyle()->systemFont) {
-        SetBaseFont(context, GetBasePangoFont());
-    }
+    pango_context_set_language(context, mPangoLanguage);
+    SetFontGroup(context, this);
 
     PangoDirection dir = aTextRun->IsRightToLeft() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
     GList *items = pango_itemize_with_base_dir(context, dir, aUTF8, 0, aUTF8Length, nsnull, nsnull);
 
     PRUint32 utf16Offset = 0;
 #ifdef DEBUG
     PRBool isRTL = aTextRun->IsRightToLeft();
 #endif
@@ -2264,53 +2818,20 @@ out:
     if (items)
         g_list_free(items);
 
     g_object_unref(context);
 }
 
 /* static */
 PangoLanguage *
-GetPangoLanguage(const nsACString& aLangGroup)
+GuessPangoLanguage(const nsACString& aLangGroup)
 {
-    // see if the lang group needs to be translated from mozilla's
+    // See if the lang group needs to be translated from Mozilla's
     // internal mapping into fontconfig's
     nsCAutoString lang;
     gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
 
     if (lang.IsEmpty())
         return NULL;
 
     return pango_language_from_string(lang.get());
 }
-
-gfxPangoFontCache::gfxPangoFontCache()
-{
-    mPangoFonts.Init(500);
-}
-
-gfxPangoFontCache::~gfxPangoFontCache()
-{
-}
-
-void
-gfxPangoFontCache::Put(const PangoFontDescription *aFontDesc, PangoFont *aPangoFont)
-{
-    if (mPangoFonts.Count() > 5000)
-        mPangoFonts.Clear();
-    PRUint32 key = pango_font_description_hash(aFontDesc);
-    gfxPangoFontWrapper *value = new gfxPangoFontWrapper(aPangoFont);
-    if (!value)
-        return;
-    mPangoFonts.Put(key, value);
-}
-
-PangoFont*
-gfxPangoFontCache::Get(const PangoFontDescription *aFontDesc)
-{
-    PRUint32 key = pango_font_description_hash(aFontDesc);
-    gfxPangoFontWrapper *value;
-    if (!mPangoFonts.Get(key, &value))
-        return nsnull;
-    PangoFont *font = value->Get();
-    g_object_ref(font);
-    return font;
-}