Get and cache code point info from each font on your system and improve font selection speed/results. bug 377950. r=vlad/roc,sr=roc
authorpavlov@pavlov.net
Tue, 29 May 2007 03:24:15 -0700
changeset 1940 ca450b60b1fdcc693155556ec8891ae06c6eaf8e
parent 1939 a759101289ae05116164d750bae84b591e92d742
child 1941 2487927ce6dddc3f15734b52c6526ff0b1ec256b
push idunknown
push userunknown
push dateunknown
reviewersvlad, roc, roc
bugs377950
milestone1.9a5pre
Get and cache code point info from each font on your system and improve font selection speed/results. bug 377950. r=vlad/roc,sr=roc
gfx/thebes/public/gfxPlatform.h
gfx/thebes/public/gfxWindowsFonts.h
gfx/thebes/public/gfxWindowsPlatform.h
gfx/thebes/src/gfxWindowsFonts.cpp
gfx/thebes/src/gfxWindowsPlatform.cpp
--- a/gfx/thebes/public/gfxPlatform.h
+++ b/gfx/thebes/public/gfxPlatform.h
@@ -119,17 +119,17 @@ public:
     virtual nsresult ResolveFontName(const nsAString& aFontName,
                                      FontResolverCallback aCallback,
                                      void *aClosure,
                                      PRBool& aAborted) = 0;
 
     /**
      * Create the appropriate platform font group
      */
-    virtual gfxFontGroup *CreateFontGroup(const nsAString &aFamilies,
+    virtual gfxFontGroup *CreateFontGroup(const nsAString& aFamilies,
                                           const gfxFontStyle *aStyle) = 0;
 
     /* Returns PR_TRUE if the given block of ARGB32 data really has alpha, otherwise PR_FALSE */
     static PRBool DoesARGBImageDataHaveAlpha(PRUint8* data,
                                              PRUint32 width,
                                              PRUint32 height,
                                              PRUint32 stride);
 
--- a/gfx/thebes/public/gfxWindowsFonts.h
+++ b/gfx/thebes/public/gfxWindowsFonts.h
@@ -55,55 +55,16 @@
 #undef min
 #undef max
 #endif
 
 #include <bitset>
 
 #define NO_RANGE_FOUND 126 // bit 126 in the font unicode ranges is required to be 0
 
-/** @description Font Weights
- * Each available font weight is stored as as single bit inside a bitset.
- * e.g. The binary value 0000000000001000 indcates font weight 400 is available.
- * while the binary value 0000000000001001 indicates both font weight 100 and 400 are available
- *
- * The font weights which will be represented include {100, 200, 300, 400, 500, 600, 700, 800, 900}
- * The font weight specified in the mFont->weight may include values which are not an even multiple of 100.
- * If so, the font weight mod 100 indicates the number steps to lighten are make bolder.
- * This corresponds to the CSS lighter and bolder property values. If bolder is applied twice to the font which has
- * a font weight of 400 then the mFont->weight will contain the value 402.
- * If lighter is applied twice to a font of weight 400 then the mFont->weight will contain the value 398.
- * Only nine steps of bolder or lighter are allowed by the CSS XPCODE.
- */
-// XXX change this from using a bitset to something cleaner eventually
-class WeightTable
-{
-public:
-    THEBES_INLINE_DECL_REFCOUNTING(WeightTable)
-
-    WeightTable() : mWeights(0) {}
-    ~WeightTable() {
-
-    }
-    PRBool TriedWeight(PRUint8 aWeight) {
-        return mWeights[aWeight - 1 + 10];
-    }
-    PRBool HasWeight(PRUint8 aWeight) {
-        return mWeights[aWeight - 1];
-    }
-    void SetWeight(PRUint8 aWeight, PRBool aValue) {
-        mWeights[aWeight - 1] = (aValue == PR_TRUE);
-        mWeights[aWeight - 1 + 10] = PR_TRUE;
-    }
-
-private:
-    std::bitset<20> mWeights;
-};
-
-
 /* Unicode subrange table
  *   from: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/intl/unicode_63ub.asp
  *
  * Use something like:
  * perl -pi -e 's/^(\d+)\s+([\dA-Fa-f]+)\s+-\s+([\dA-Fa-f]+)\s+\b(.*)/    { \1, 0x\2, 0x\3,\"\4\" },/' < unicoderanges.txt
  * to generate the below list.
  */
 struct UnicodeRangeTableEntry
@@ -274,30 +235,82 @@ static PRUint8 CharRangeBit(PRUint32 ch)
 
     for (PRUint32 i = 0; i < n; ++i)
         if (ch >= gUnicodeRanges[i].start && ch <= gUnicodeRanges[i].end)
             return gUnicodeRanges[i].bit;
 
     return NO_RANGE_FOUND;
 }
 
+
+class gfxSparseBitSet {
+public:
+    enum { BLOCK_SIZE = 32 };
+
+    PRBool test(PRUint32 aIndex) {
+        PRUint32 blockIndex = aIndex/(BLOCK_SIZE*8);
+        if (blockIndex >= mBlocks.Length())
+            return PR_FALSE;
+        Block *block = mBlocks[blockIndex];
+        if (!block)
+            return PR_FALSE;
+        return ((block->mBits[(aIndex/8) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0;
+    }
+   
+    void set(PRUint32 aIndex) {
+        PRUint32 blockIndex = aIndex/(BLOCK_SIZE*8);
+        if (blockIndex >= mBlocks.Length()) {
+            nsAutoPtr<Block> *blocks = mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
+            if (!blocks) // OOM
+                return;
+        }
+        Block *block = mBlocks[blockIndex];
+        if (!block) {
+            block = new Block;
+            if (!block) // OOM
+                return;
+            memset(block, 0, sizeof(Block));
+            mBlocks[blockIndex] = block;
+        }
+        block->mBits[(aIndex/8) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
+    }
+
+    PRUint32 getsize() {
+        PRUint32 size = 0;
+        for (PRUint32 i = 0; i < mBlocks.Length(); i++)
+            if (mBlocks[i])
+                size += sizeof(Block);
+        return size;
+    }
+
+    // If you want to set a lot of bits very fast, we could have Set variants
+    // that set a lot of bits at once, e.g. Set(PRUint32 aStart, PRUint32 aLength)
+
+private:
+    struct Block {
+        PRUint8 mBits[BLOCK_SIZE];
+    };
+    
+    nsTArray< nsAutoPtr<Block> > mBlocks;
+};
+
 /**
  * FontEntry is a class that describes one of the fonts on the users system
  * It contains information such as the name, font type, charset table and unicode ranges.
  * It may be extended to also keep basic metrics of the fonts so that we can better
  * compare one FontEntry to another.
  */
 class FontEntry
 {
 public:
     THEBES_INLINE_DECL_REFCOUNTING(FontEntry)
 
     FontEntry(const nsAString& aName, PRUint16 aFontType) : 
-        mName(aName), mFontType(aFontType), mUnicodeFont(PR_FALSE),
-        mCharset(0), mUnicodeRanges(0)
+        mName(aName), mFontType(aFontType), mDefaultWeight(0),
+        mUnicodeFont(PR_FALSE), mCharset(0), mUnicodeRanges(0)
     {
     }
 
     PRBool IsCrappyFont() const {
         /* return if it is a bitmap, old school font or not a unicode font */
         return (!mUnicodeFont || mFontType == 0 || mFontType == 1);
     }
 
@@ -380,27 +393,53 @@ public:
 
         return PR_FALSE;
     }
 
     PRBool SupportsRange(PRUint8 range) {
         return mUnicodeRanges[range];
     }
 
+    class WeightTable
+    {
+    public:
+        THEBES_INLINE_DECL_REFCOUNTING(WeightTable)
+            
+        WeightTable() : mWeights(0) {}
+        ~WeightTable() {}
+        PRBool TriedWeight(PRUint8 aWeight) {
+            return mWeights[aWeight - 1 + 10];
+        }
+        PRBool HasWeight(PRUint8 aWeight) {
+            return mWeights[aWeight - 1];
+        }
+        void SetWeight(PRUint8 aWeight, PRBool aValue) {
+            mWeights[aWeight - 1] = aValue;
+            mWeights[aWeight - 1 + 10] = PR_TRUE;
+        }
+    private:
+        std::bitset<20> mWeights;
+    };
+
     // The family name of the font
     nsString mName;
 
     PRUint16 mFontType;
+    PRUint16 mDefaultWeight;
 
     PRUint8 mFamily;
     PRUint8 mPitch;
     PRPackedBool mUnicodeFont;
 
     std::bitset<256> mCharset;
     std::bitset<128> mUnicodeRanges;
+
+    WeightTable mWeightTable;
+
+    gfxSparseBitSet mCharacterMap;
 };
 
 
 /**********************************************************************
  *
  * class gfxWindowsFont
  *
  **********************************************************************/
@@ -424,16 +463,18 @@ public:
                       gfxContext *aContext, PRBool aDrawToPath, gfxPoint *aBaselineOrigin,
                       Spacing *aSpacing);
 
     virtual PRUint32 GetSpaceGlyph() {
         ComputeMetrics();
         return mSpaceGlyph;
     };
 
+    FontEntry *GetFontEntry() { return mFontEntry; }
+
 protected:
     HFONT MakeHFONT();
     cairo_font_face_t *MakeCairoFontFace();
     cairo_scaled_font_t *MakeCairoScaledFont();
     void FillLogFont(gfxFloat aSize, PRInt16 aWeight);
 
     HFONT    mFont;
     gfxFloat mAdjustedSize;
@@ -447,17 +488,17 @@ private:
 
     cairo_font_face_t *mFontFace;
     cairo_scaled_font_t *mScaledFont;
 
     gfxFont::Metrics *mMetrics;
 
     LOGFONTW mLogFont;
 
-    nsRefPtr<WeightTable> mWeightTable;
+    nsRefPtr<FontEntry> mFontEntry;
     
     virtual void SetupCairoFont(cairo_t *aCR);
 };
 
 /**********************************************************************
  *
  * class gfxWindowsFontGroup
  *
--- a/gfx/thebes/public/gfxWindowsPlatform.h
+++ b/gfx/thebes/public/gfxWindowsPlatform.h
@@ -67,41 +67,50 @@ public:
 
     nsresult ResolveFontName(const nsAString& aFontName,
                              FontResolverCallback aCallback,
                              void *aClosure, PRBool& aAborted);
 
     gfxFontGroup *CreateFontGroup(const nsAString &aFamilies,
                                   const gfxFontStyle *aStyle);
 
-    /* local methods */
-    void FindOtherFonts(const PRUnichar *aString, PRUint32 aLength, const char *aLangGroup, const char *aGeneric, nsString& array);
+    /* Given a string and a font we already have find the font that
+     * supports the most code points and most closely resembles aFont
+     *
+     * this involves looking at the fonts on your machine and seeing which
+     * code points they support as well as looking at things like the font
+     * family, style, weight, etc.
+     */
+    FontEntry *FindFontForString(const PRUnichar *aString, PRUint32 aLength, gfxWindowsFont *aFont);
 
-    WeightTable *GetFontWeightTable(const nsAString& aName);
-    void PutFontWeightTable(const nsAString& aName, WeightTable *aWeightTable);
+    /* Find a FontEntry object that represents a font on your system given a name */
+    FontEntry *FindFontEntry(const nsAString& aName);
 
 private:
     void Init();
 
     static int CALLBACK FontEnumProc(const ENUMLOGFONTEXW *lpelfe,
                                      const NEWTEXTMETRICEXW *metrics,
                                      DWORD fontType, LPARAM data);
 
+    static PLDHashOperator PR_CALLBACK FontGetCMapDataProc(nsStringHashKey::KeyType aKey,
+                                                           nsRefPtr<FontEntry>& aFontEntry,
+                                                           void* userArg);
+
     static int CALLBACK FontResolveProc(const ENUMLOGFONTEXW *lpelfe,
                                         const NEWTEXTMETRICEXW *metrics,
                                         DWORD fontType, LPARAM data);
 
     static PLDHashOperator PR_CALLBACK HashEnumFunc(nsStringHashKey::KeyType aKey,
                                                     nsRefPtr<FontEntry>& aData,
                                                     void* userArg);
 
-    static PLDHashOperator PR_CALLBACK FindFontForChar(nsStringHashKey::KeyType aKey,
-                                                       nsRefPtr<FontEntry>& aFontEntry,
-                                                       void* userArg);
+    static PLDHashOperator PR_CALLBACK FindFontForStringProc(nsStringHashKey::KeyType aKey,
+                                                             nsRefPtr<FontEntry>& aFontEntry,
+                                                             void* userArg);
 
     nsDataHashtable<nsStringHashKey, nsRefPtr<FontEntry> > mFonts;
-    nsDataHashtable<nsStringHashKey, nsRefPtr<WeightTable> > mFontWeights;
     nsDataHashtable<nsStringHashKey, nsRefPtr<FontEntry> > mFontAliases;
     nsDataHashtable<nsStringHashKey, nsRefPtr<FontEntry> > mFontSubstitutes;
     nsStringArray mNonExistingFonts;
 };
 
 #endif /* GFX_WINDOWS_PLATFORM_H */
--- a/gfx/thebes/src/gfxWindowsFonts.cpp
+++ b/gfx/thebes/src/gfxWindowsFonts.cpp
@@ -92,16 +92,19 @@ inline HDC GetDCFromSurface(gfxASurface 
  **********************************************************************/
 
 gfxWindowsFont::gfxWindowsFont(const nsAString& aName, const gfxFontStyle *aFontStyle)
     : gfxFont(aName, aFontStyle),
       mFont(nsnull), mAdjustedSize(0), mScriptCache(nsnull),
       mFontFace(nsnull), mScaledFont(nsnull),
       mMetrics(nsnull)
 {
+    // XXX we should work to get this passed in rather than having to find it again.
+    mFontEntry = gfxWindowsPlatform::GetPlatform()->FindFontEntry(aName);
+    NS_ASSERTION(mFontEntry, "Unable to find font entry for font.  Something is whack.");
 }
 
 gfxWindowsFont::~gfxWindowsFont()
 {
     Destroy();
 }
 
 void
@@ -170,58 +173,45 @@ gfxWindowsFont::CairoScaledFont()
 }
 
 HFONT
 gfxWindowsFont::MakeHFONT()
 {
     if (mFont)
         return mFont;
 
-    if (!mWeightTable) {
-        nsString name(mName);
-        ToLowerCase(name);
-
-        gfxWindowsPlatform *platform = gfxWindowsPlatform::GetPlatform();
-
-        mWeightTable = platform->GetFontWeightTable(name);
-        if (!mWeightTable) {
-            mWeightTable = new WeightTable();
-            platform->PutFontWeightTable(name, mWeightTable);
-        }
-    }
-
     PRInt8 baseWeight, weightDistance;
     GetStyle()->ComputeWeightAndOffset(&baseWeight, &weightDistance);
 
     HDC dc = nsnull;
 
     PRUint32 chosenWeight = 0;
 
     if (weightDistance >= 0) {
 
         for (PRUint8 i = baseWeight, k = 0; i < 10; i++) {
-            if (mWeightTable->HasWeight(i)) {
+            if (mFontEntry->mWeightTable.HasWeight(i)) {
                 k++;
                 chosenWeight = i * 100;
-            } else if (mWeightTable->TriedWeight(i)) {
+            } else if (mFontEntry->mWeightTable.TriedWeight(i)) {
                 continue;
             } else {
                 const PRUint32 tryWeight = i * 100;
 
                 if (!dc)
                     dc = GetDC((HWND)nsnull);
 
                 FillLogFont(GetStyle()->size, tryWeight);
                 mFont = CreateFontIndirectW(&mLogFont);
                 HGDIOBJ oldFont = SelectObject(dc, mFont);
                 TEXTMETRIC metrics;
                 GetTextMetrics(dc, &metrics);
 
                 PRBool hasWeight = (metrics.tmWeight == tryWeight);
-                mWeightTable->SetWeight(i, hasWeight);
+                mFontEntry->mWeightTable.SetWeight(i, hasWeight);
                 if (hasWeight) {
                     chosenWeight = i * 100;
                     k++;
                 }
 
                 SelectObject(dc, oldFont);
                 if (k <= weightDistance) {
                     DeleteObject(mFont);
@@ -323,17 +313,17 @@ gfxWindowsFont::ComputeMetrics()
 
     if (0 < GetOutlineTextMetrics(dc, sizeof(oMetrics), &oMetrics)) {
         mMetrics->superscriptOffset = (double)oMetrics.otmptSuperscriptOffset.y;
         mMetrics->subscriptOffset = (double)oMetrics.otmptSubscriptOffset.y;
         mMetrics->strikeoutSize = PR_MAX(1, (double)oMetrics.otmsStrikeoutSize);
         mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition;
         mMetrics->underlineSize = PR_MAX(1, (double)oMetrics.otmsUnderscoreSize);
         mMetrics->underlineOffset = (double)oMetrics.otmsUnderscorePosition;
-        
+
         const MAT2 kIdentityMatrix = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
         GLYPHMETRICS gm;
         DWORD len = GetGlyphOutlineW(dc, PRUnichar('x'), GGO_METRICS, &gm, 0, nsnull, &kIdentityMatrix);
         if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
             // 56% of ascent, best guess for true type
             mMetrics->xHeight = ROUND((double)metrics.tmAscent * 0.56);
         } else {
             mMetrics->xHeight = gm.gmptGlyphOrigin.y;
@@ -669,17 +659,17 @@ IsAnyGlyphMissing(WCHAR *aGlyphs, PRUint
 
 static PRBool
 SetupTextRunFromGlyphs(gfxTextRun *aRun, WCHAR *aGlyphs, HDC aDC,
                        gfxWindowsFont *aFont)
 {
     PRUint32 length = aRun->GetLength();
     if (IsAnyGlyphMissing(aGlyphs, length))
         return PR_FALSE;
-       
+
     SIZE size;
     nsAutoTArray<int,500> partialWidthArray;
     if (!partialWidthArray.AppendElements(length))
         return PR_FALSE;
     BOOL success = GetTextExtentExPointI(aDC,
                                          (WORD*) aGlyphs,
                                          length,
                                          INT_MAX,
@@ -931,82 +921,50 @@ public:
         free(mGlyphs);
         free(mClusters);
         free(mAttr);
         free(mOffsets);
         free(mAdvances);
         free(mAlternativeString);
     }
 
-    const PRUnichar *GetString() const { return mString; }
-    const PRUint32 GetStringLength() const { return mLength; }
-
-
-
     static PRBool AddFontCallback(const nsAString& aName,
                                   const nsACString& aGenericName,
                                   void *closure) {
         if (aName.IsEmpty())
             return PR_TRUE;
 
         UniscribeItem *item = NS_STATIC_CAST(UniscribeItem*, closure);
 
         // XXX do something better than this to remove dups
         PRUint32 len = item->mFonts.Length();
         for (PRUint32 i = 0; i < len; ++i)
             if (aName.Equals(item->mFonts[i]->GetName()))
                 return PR_TRUE;
 
-        nsRefPtr<gfxWindowsFont> font =
-            GetOrMakeFont(aName, item->mGroup->GetStyle());
+        nsRefPtr<gfxWindowsFont> font = GetOrMakeFont(aName, item->mGroup->GetStyle());
         if (font) {
             item->mFonts.AppendElement(font);
         }
         return PR_TRUE;
     }
 
-#ifdef DEBUG_pavlov
-    HRESULT Break() {
-        HRESULT rv;
-
-        SCRIPT_LOGATTR *logAttrs = (SCRIPT_LOGATTR*)malloc(sizeof(SCRIPT_LOGATTR) * mLength);
-
-        rv = ScriptBreak(mString, mLength, &mScriptItem->a, logAttrs);
-
-        for (PRUint32 i = 0; i < mLength; ++i) {
-            PR_LOG(gFontLog, PR_LOG_DEBUG, ("0x%04x - %d %d %d %d %d",
-                                            mString[i],
-                                            logAttrs[i].fSoftBreak,
-                                            logAttrs[i].fWhiteSpace,
-                                            logAttrs[i].fCharStop,
-                                            logAttrs[i].fWordStop,
-                                            logAttrs[i].fInvalid));
-        }
-
-        free(logAttrs);
-        return rv;
-    }
-#endif
-
     /* possible return values:
-       E_PENDING -- means script cache lookup failed, DC needs to be set and font selected in to it.
-       USP_E_SCRIPT_NOT_IN_FONT -- this font doesn't support this text. keep trying new fonts.
-       if you try all possible fonts, then go back to font 0 after calling DisableShaping and try again
-       through all the fonts
-    */
+     * S_OK - things succeeded
+     * GDI_ERROR - things failed to shape.  Might want to try again after calling DisableShaping()
+     */
     HRESULT Shape() {
         HRESULT rv;
 
         HDC shapeDC = nsnull;
 
+        const PRUnichar *str = mAlternativeString ? mAlternativeString : mString;
+
         while (PR_TRUE) {
-            const PRUnichar *str =
-                mAlternativeString ? mAlternativeString : mString;
             mScriptItem->a.fLogicalOrder = PR_TRUE;
-            mScriptItem->a.s.fDisplayZWG = PR_TRUE;
 
             rv = ScriptShape(shapeDC, mCurrentFont->ScriptCache(),
                              str, mLength,
                              mMaxGlyphs, &mScriptItem->a,
                              mGlyphs, mClusters,
                              mAttr, &mNumGlyphs);
 
             if (rv == E_OUTOFMEMORY) {
@@ -1018,84 +976,53 @@ public:
 
             if (rv == E_PENDING) {
                 SelectFont();
 
                 shapeDC = mDC;
                 continue;
             }
 
-#if 0 // debugging only
-            if (rv != USP_E_SCRIPT_NOT_IN_FONT && !shapeDC)
-                printf("skipped select-shape %d\n", rv);
-#endif
             return rv;
         }
     }
 
     PRBool ShapingEnabled() {
         return (mScriptItem->a.eScript != SCRIPT_UNDEFINED);
     }
     void DisableShaping() {
         mScriptItem->a.eScript = SCRIPT_UNDEFINED;
         // Note: If we disable the shaping by using SCRIPT_UNDEFINED and
         // the string has the surrogate pair, ScriptShape API is
         // *sometimes* crashed. Therefore, we should replace the surrogate
         // pair to U+FFFD. See bug 341500.
         GenerateAlternativeString();
     }
 
-    /* this should only be called if there are no surrogates
-     * in the string */
     PRBool IsMissingGlyphsCMap() {
-        HRESULT rv;
-        HDC cmapDC = nsnull;
-
-        while (PR_TRUE) {
-            rv = ScriptGetCMap(cmapDC, mCurrentFont->ScriptCache(),
-                               mString, mLength, 0, mGlyphs);
+        nsRefPtr<FontEntry> fe = GetCurrentFont()->GetFontEntry();
+        for (PRUint32 i = 0; i < mLength; i++) {
+            PRUint32 ch = mString[i];
+            if ((i+1 < mLength) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(mString[i+1])) {
+                i++;
+                ch = SURROGATE_TO_UCS4(ch, mString[i]);
+            }
+            if (!fe->mCharacterMap.test(ch))
+                return PR_TRUE;
+        }
 
-            if (rv == E_PENDING) {
-                SelectFont();
-                cmapDC = mDC;
-                continue;
-            }
-
-            if (rv == S_OK)
-                return PR_FALSE;
-
-            PR_LOG(gFontLog, PR_LOG_WARNING, ("cmap is missing a glyph"));
-            for (PRUint32 i = 0; i < mLength; i++)
-                PR_LOG(gFontLog, PR_LOG_WARNING, (" - %d", mGlyphs[i]));
-            return PR_TRUE;
-        }
+        return PR_FALSE;
     }
 
     PRBool IsGlyphMissing(SCRIPT_FONTPROPERTIES *aSFP, PRUint32 aGlyphIndex) {
         if (mGlyphs[aGlyphIndex] == aSFP->wgDefault)
             return PR_TRUE;
         return PR_FALSE;
     }
 
-    PRBool IsMissingGlyphs() {
-        SCRIPT_FONTPROPERTIES sfp;
-        ScriptFontProperties(&sfp);
-        PRUint32 charIndex = 0;
-        for (int i = 0; i < mNumGlyphs; ++i) {
-            if (IsGlyphMissing(&sfp, i))
-                return PR_TRUE;
-#ifdef DEBUG_pavlov // excess debugging code
-            PR_LOG(gFontLog, PR_LOG_DEBUG, ("%04x %04x %04x", sfp.wgBlank, sfp.wgDefault, sfp.wgInvalid));
-            PR_LOG(gFontLog, PR_LOG_DEBUG, ("glyph%d - 0x%04x", i, mGlyphs[i]));
-            PR_LOG(gFontLog, PR_LOG_DEBUG, ("%04x  --  %04x -- %04x", ScriptProperties()->fInvalidGlyph, mScriptItem->a.fNoGlyphIndex, mAttr[i].fZeroWidth));
-#endif
-        }
-        return PR_FALSE;
-    }
-
     HRESULT Place() {
         HRESULT rv;
 
         mOffsets = (GOFFSET *)malloc(mNumGlyphs * sizeof(GOFFSET));
         mAdvances = (int *)malloc(mNumGlyphs * sizeof(int));
 
         HDC placeDC = nsnull;
 
@@ -1281,48 +1208,41 @@ TRY_AGAIN_HOPE_FOR_THE_BEST_2:
                             AppendPrefFonts(langGroup);
                         }
                     }
                 }
             }
             goto TRY_AGAIN_HOPE_FOR_THE_BEST_2;
         } else if (!mTriedOtherFonts) {
             mTriedOtherFonts = PR_TRUE;
-            nsString fonts;
             gfxWindowsPlatform *platform = gfxWindowsPlatform::GetPlatform();
 
             if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG)) {
                 PR_LOG(gFontLog, PR_LOG_DEBUG, ("Looking for other fonts to support the string:"));
                 for (PRUint32 la = 0; la < mLength; la++) {
                     PRUint32 ch = mString[la];
 
                     if ((la+1 < mLength) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(mString[la+1])) {
                         la++;
                         ch = SURROGATE_TO_UCS4(ch, mString[la]);
                     }
 
                     PR_LOG(gFontLog, PR_LOG_DEBUG, (" - 0x%04x", ch));
                 }
             }
-            
-            platform->FindOtherFonts(mString, mLength,
-                                     nsPromiseFlatCString(mGroup->GetStyle()->langGroup).get(),
-                                     nsPromiseFlatCString(mGroup->GetGenericFamily()).get(),
-                                     fonts);
-            if (!fonts.IsEmpty()) {
+
+            nsRefPtr<FontEntry> newFont = platform->FindFontForString(mString, mLength, mFonts[0]);
+                                                                
+            if (newFont) {
                 if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
-                    PR_LOG(gFontLog, PR_LOG_DEBUG, ("Got back: %s", NS_LossyConvertUTF16toASCII(fonts).get()));
-                gfxFontGroup::ForEachFont(fonts, EmptyCString(), UniscribeItem::AddFontCallback, this);
+                    PR_LOG(gFontLog, PR_LOG_DEBUG, ("Got back: %s", NS_LossyConvertUTF16toASCII(newFont->mName).get()));
+
+                AddFontCallback(newFont->mName, EmptyCString(), this);
             }
             goto TRY_AGAIN_HOPE_FOR_THE_BEST_2;
-        } else {
-            // const SCRIPT_PROPERTIES *sp = item->ScriptProperties();
-            // We should try to look up the font based on sp.langgroup
-            // if it isn't ambiguious.  Would save us time over doing
-            // character by character lookups at least for pref fonts
         }
 
         return nsnull;
     }
 
     void ResetFontIndex() {
         mFontIndex = 0;
     }
@@ -1348,17 +1268,16 @@ TRY_AGAIN_HOPE_FOR_THE_BEST_2:
         cairo_set_font_face(cr, mCurrentFont->CairoFontFace());
         cairo_set_font_size(cr, mCurrentFont->GetAdjustedSize());
 
         cairo_win32_scaled_font_select_font(mCurrentFont->CairoScaledFont(), mDC);
 
         mFontSelected = PR_TRUE;
     }
 
-
 private:
     static PRInt32 GetCJKLangGroupIndex(const char *aLangGroup) {
         PRInt32 i;
         for (i = 0; i < COUNT_OF_CJK_LANG_GROUP; i++) {
             if (!PL_strcasecmp(aLangGroup, sCJKLangGroup[i]))
                 return i;
         }
         return -1;
@@ -1436,16 +1355,17 @@ private:
         mAlternativeString = (PRUnichar *)malloc(mLength * sizeof(PRUnichar));
         memcpy((void *)mAlternativeString, (const void *)mString,
                mLength * sizeof(PRUnichar));
         for (PRUint32 i = 0; i < mLength; i++) {
             if (NS_IS_HIGH_SURROGATE(mString[i]) || NS_IS_LOW_SURROGATE(mString[i]))
                 mAlternativeString[i] = PRUnichar(0xFFFD);
         }
     }
+
 private:
     nsRefPtr<gfxContext> mContext;
     HDC mDC;
 
     SCRIPT_ITEM *mScriptItem;
     WORD mScript;
 
     const PRUnichar *mString;
@@ -1476,17 +1396,16 @@ private:
     PRPackedBool mFontSelected;
 };
 
 class Uniscribe
 {
 public:
     Uniscribe(gfxContext *aContext, HDC aDC, const PRUnichar *aString, PRUint32 aLength, PRBool aIsRTL) :
         mContext(aContext), mDC(aDC), mString(aString), mLength(aLength), mIsRTL(aIsRTL),
-        mIsComplex(ScriptIsComplex(aString, aLength, SIC_COMPLEX) == S_OK),
         mItems(nsnull) {
     }
     ~Uniscribe() {
         if (mItems)
             free(mItems);
     }
 
     void Init() {
@@ -1536,120 +1455,105 @@ public:
     }
 
 private:
     nsRefPtr<gfxContext> mContext;
     HDC mDC;
     const PRUnichar *mString;
     const PRUint32 mLength;
     const PRBool mIsRTL;
-    // XXX not used
-    const PRBool mIsComplex;
 
     SCRIPT_CONTROL mControl;
     SCRIPT_STATE   mState;
     SCRIPT_ITEM   *mItems;
     int mNumItems;
 };
 
-
 void
 gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun, const PRUnichar *aString,
-                                         PRUint32 aLength)
+                                          PRUint32 aLength)
 {
     nsRefPtr<gfxASurface> surf = aContext->CurrentSurface();
     HDC aDC = GetDCFromSurface(surf);
     NS_ASSERTION(aDC, "No DC");
  
     const PRBool isRTL = aRun->IsRightToLeft();
 
     HRESULT rv;
 
     Uniscribe us(aContext, aDC, aString, aLength, isRTL);
 
     /* itemize the string */
     int numItems = us.Itemize();
 
-    for (int i=0; i < numItems; ++i) {
+    for (int i = 0; i < numItems; ++i) {
         PRUint32 fontIndex = 0;
 
         SaveDC(aDC);
 
         UniscribeItem *item = us.GetItem(i, this);
 
         int giveUp = PR_FALSE;
 
         // Perform font substitution
         while (PR_TRUE) {
             nsRefPtr<gfxWindowsFont> font = item->GetNextFont();
 
             if (font) {
                 /* set the curret font on the item */
                 item->SetCurrentFont(font);
-
+ 
                 if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
                     PR_LOG(gFontLog, PR_LOG_DEBUG, ("trying: %s", NS_LossyConvertUTF16toASCII(font->GetName()).get()));
-
-                PRBool cmapHasGlyphs = PR_FALSE; // false means "maybe" here
-
-                if (!giveUp && !(aRun->GetFlags() & TEXT_HAS_SURROGATES)) {
-                    if (item->IsMissingGlyphsCMap())
-                        continue;
-                    else
-                        cmapHasGlyphs = PR_TRUE;
+ 
+                if (!giveUp && item->IsMissingGlyphsCMap()) {
+                    PR_LOG(gFontLog, PR_LOG_DEBUG, (" - missing glyphs in cmap"));
+                    continue;
                 }
-
+ 
+SCRIPT_SHAPE:
                 rv = item->Shape();
-
+ 
                 if (giveUp) {
                     if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
                         PR_LOG(gFontLog, PR_LOG_DEBUG, ("%s - gave up", NS_LossyConvertUTF16toASCII(font->GetName()).get()));
                     goto SCRIPT_PLACE;
                 }
-
-                if (FAILED(rv))
-                    continue;
-
-                if (!cmapHasGlyphs && item->IsMissingGlyphs())
-                    continue;
+ 
+                if (FAILED(rv)) {
+                    PR_LOG(gFontLog, PR_LOG_DEBUG, (" - shaping failed"));
+                    // we know we have the glyphs to display this font already
+                    // so Uniscribe just doesn't know how to shape the script.
+                    // Render the glyphs without shaping.
+                    item->DisableShaping();
+                    goto SCRIPT_SHAPE;
+                }
 
             } else {
-#if 0
-                /* code to try all the fonts again without shaping on.
-                   in general, if we couldn't shape we should probably just give up */
-                if (item->ShapingEnabled()) {
-                    item->DisableShaping();
-                    item->ResetFontIndex();
-                    continue;
-                }
-#endif
                 giveUp = PR_TRUE;
                 item->DisableShaping();
                 item->ResetFontIndex();
                 continue;
             }
-
+ 
             if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
                 PR_LOG(gFontLog, PR_LOG_DEBUG, ("%s - worked", NS_LossyConvertUTF16toASCII(font->GetName()).get()));
-
+ 
 SCRIPT_PLACE:
             NS_ASSERTION(SUCCEEDED(rv), "Failed to shape -- we should never hit this");
 
-#ifdef DEBUG_pavlov
-            item->Break();
-#endif
-
             rv = item->Place();
             if (FAILED(rv)) {
                 if (PR_LOG_TEST(gFontLog, PR_LOG_WARNING))
                     PR_LOG(gFontLog, PR_LOG_WARNING, ("Failed to place with %s", NS_LossyConvertUTF16toASCII(font->GetName()).get()));
                 continue;
             }
 
             break;
         }
 
         item->SaveGlyphs(aRun);
+
         delete item;
 
         RestoreDC(aDC, -1);
     }
 }
--- a/gfx/thebes/src/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/src/gfxWindowsPlatform.cpp
@@ -49,20 +49,31 @@
 #include "nsServiceManagerUtils.h"
 
 #include "nsIWindowsRegKey.h"
 
 #include "gfxWindowsFonts.h"
 
 #include <string>
 
+//#define DEBUG_CMAP_SIZE 1
+
+/* Define this if we want to update the unicode range bitsets based
+ * on the actual characters a font supports.
+ *
+ * Doing this can result in very large lists of fonts being returned.
+ * Not doing this can let us prioritize fonts that do have the bit set
+ * as they are more likely to provide better glyphs (in theory).
+ */
+//#define UPDATE_RANGES
+
+
 gfxWindowsPlatform::gfxWindowsPlatform()
 {
     mFonts.Init(200);
-    mFontWeights.Init(200);
     mFontAliases.Init(20);
     mFontSubstitutes.Init(50);
     UpdateFontList();
 }
 
 gfxWindowsPlatform::~gfxWindowsPlatform()
 {
 }
@@ -98,23 +109,21 @@ gfxWindowsPlatform::FontEnumProc(const E
     if (!thisp->mFonts.Get(name, &fe)) {
         fe = new FontEntry(nsDependentString(logFont.lfFaceName), (PRUint16)fontType);
         thisp->mFonts.Put(name, fe);
     }
 
     // mark the charset bit
     fe->mCharset[metrics.tmCharSet] = 1;
 
-    // put this in the weight table
-    nsRefPtr<WeightTable> wt;
-    if (!thisp->mFontWeights.Get(name, &wt)) {
-        wt = new WeightTable();
-        wt->SetWeight(PR_MAX(1, PR_MIN(9, metrics.tmWeight / 100)), PR_TRUE);
-        thisp->mFontWeights.Put(name, wt);
-    }
+    // put the default weight in the weight table
+    fe->mWeightTable.SetWeight(PR_MAX(1, PR_MIN(9, metrics.tmWeight / 100)), PR_TRUE);
+
+    // store the default font weight
+    fe->mDefaultWeight = metrics.tmWeight;
 
     fe->mFamily = logFont.lfPitchAndFamily & 0xF0;
     fe->mPitch = logFont.lfPitchAndFamily & 0x0F;
 
     if (nmetrics->ntmFontSig.fsUsb[0] == 0x00000000 &&
         nmetrics->ntmFontSig.fsUsb[1] == 0x00000000 &&
         nmetrics->ntmFontSig.fsUsb[2] == 0x00000000 &&
         nmetrics->ntmFontSig.fsUsb[3] == 0x00000000) {
@@ -131,16 +140,237 @@ gfxWindowsPlatform::FontEnumProc(const E
                 fe->mUnicodeRanges[x++] = (range & (1 << k)) != 0;
             }
         }
     }
 
     return 1;
 }
 
+static inline PRUint16
+ReadShortAt(const PRUint8 *aBuf, PRUint32 aIndex)
+{
+    return (aBuf[aIndex] << 8) | aBuf[aIndex + 1];
+}
+
+static inline PRUint32
+ReadLongAt(const PRUint8 *aBuf, PRUint32 aIndex)
+{
+    return ((aBuf[aIndex] << 24) | (aBuf[aIndex + 1] << 16) | (aBuf[aIndex + 2] << 8) | (aBuf[aIndex + 3]));
+}
+
+static nsresult
+ReadCMAPTableFormat12(PRUint8 *aBuf, PRInt32 len, FontEntry *aFontEntry) 
+{
+    enum {
+        OffsetFormat = 0,
+        OffsetReserved = 2,
+        OffsetTableLength = 4,
+        OffsetLanguage = 8,
+        OffsetNumberGroups = 12,
+        OffsetGroups = 16,
+
+        SizeOfGroup = 12,
+
+        GroupOffsetStartCode = 0,
+        GroupOffsetEndCode = 4
+    };
+
+    NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 12, NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetReserved) == 0, NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(ReadLongAt(aBuf, OffsetTableLength) != 0, NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(ReadLongAt(aBuf, OffsetLanguage) == 0, NS_ERROR_FAILURE);
+
+    const PRUint32 numGroups  = ReadLongAt(aBuf, OffsetNumberGroups);
+    const PRUint8 *groups = aBuf + OffsetGroups;
+    for (PRUint32 i = 0; i < numGroups; i++, groups += SizeOfGroup) {
+        const PRUint32 startCharCode = ReadLongAt(groups, GroupOffsetStartCode);
+        const PRUint32 endCharCode = ReadLongAt(groups, GroupOffsetEndCode);
+        for (PRUint32 c = startCharCode; c <= endCharCode; ++c) {
+            // XXX we should use a range setting functions on gfxSparseBitset
+            // which could be a lot faster
+            aFontEntry->mCharacterMap.set(c);
+#ifdef UPDATE_RANGES
+            PRUint16 b = CharRangeBit(c);
+            if (b != NO_RANGE_FOUND)
+                aFontEntry->mUnicodeRanges.set(b, true);
+#endif
+        }
+    }
+
+    return NS_OK;
+}
+
+static nsresult 
+ReadCMAPTableFormat4(PRUint8 *aBuf, PRInt32 aLength, FontEntry *aFontEntry)
+{
+    enum {
+        OffsetFormat = 0,
+        OffsetLength = 2,
+        OffsetLanguage = 4,
+        OffsetSegCountX2 = 6,
+    };
+
+    NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 4, NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetLength) != 0, NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetLanguage) == 0, NS_ERROR_FAILURE);
+
+    PRUint16 segCountX2 = ReadShortAt(aBuf, OffsetSegCountX2);
+    const PRUint8 *endCounts = aBuf + 14;
+    const PRUint8 *startCounts = endCounts + segCountX2 + 2;
+    const PRUint8 *idDeltas = startCounts + segCountX2;
+    const PRUint8 *idRangeOffsets = idDeltas + segCountX2;
+    for (PRUint16 i = 0; i < segCountX2; i += 2) {
+        const PRUint16 endCount = ReadShortAt(endCounts, i);
+        const PRUint16 startCount = ReadShortAt(startCounts, i);
+        const PRUint16 idRangeOffset = ReadShortAt(idRangeOffsets, i);
+        if (idRangeOffset == 0) {
+            for (PRUint32 c = startCount; c <= endCount; c++) {
+                aFontEntry->mCharacterMap.set(c);
+#ifdef UPDATE_RANGES
+                PRUint16 b = CharRangeBit(c);
+                if (b != NO_RANGE_FOUND)
+                    aFontEntry->mUnicodeRanges.set(b, true);
+#endif
+            }
+        } else {
+            const PRUint8 *gdata = idRangeOffsets + i + idRangeOffset;
+            for (PRUint16 c = startCount; c <= endCount; ++c, gdata += 2) {
+                // make sure we have a glyph
+                if (PRUint16 g = ReadShortAt(gdata, 0)) {
+                    aFontEntry->mCharacterMap.set(c);
+#ifdef UPDATE_RANGES
+                    PRUint16 b = CharRangeBit(c);
+                    if (b != NO_RANGE_FOUND)
+                        aFontEntry->mUnicodeRanges.set(b, true);
+#endif
+                }
+            }
+        }
+    }
+
+    return NS_OK;
+}
+
+static nsresult
+ReadCMAP(HDC hdc, FontEntry *aFontEntry)
+{
+    const PRUint32 kCMAP = (('c') | ('m' << 8) | ('a' << 16) | ('p' << 24));
+
+    DWORD len = GetFontData(hdc, kCMAP, 0, nsnull, 0);
+    NS_ENSURE_TRUE(len != GDI_ERROR && len != 0, NS_ERROR_FAILURE);
+
+    nsAutoTArray<PRUint8,16384> buffer;
+    if (!buffer.AppendElements(len))
+        return NS_ERROR_OUT_OF_MEMORY;
+    PRUint8 *buf = buffer.Elements();
+
+    DWORD newLen = GetFontData(hdc, kCMAP, 0, buf, len);
+    NS_ENSURE_TRUE(newLen == len, NS_ERROR_FAILURE);
+
+    enum {
+        OffsetVersion = 0,
+        OffsetNumTables = 2,
+        SizeOfHeader = 4,
+
+        TableOffsetPlatformID = 0,
+        TableOffsetEncodingID = 2,
+        TableOffsetOffset = 4,
+        SizeOfTable = 8,
+
+        SubtableOffsetFormat = 0,
+    };
+    enum {
+        PlatformIDMicrosoft = 3
+    };
+    enum {
+        EncodingIDMicrosoft = 1,
+        EncodingIDUCS4 = 10
+    };
+
+    PRUint16 version = ReadShortAt(buf, OffsetVersion);
+    PRUint16 numTables = ReadShortAt(buf, OffsetNumTables);
+
+    // save the format and offset we want here
+    PRUint32 keepOffset;
+    PRUint32 keepFormat;
+
+    PRUint8 *table = buf + SizeOfHeader;
+    for (PRUint16 i = 0; i < numTables; ++i, table += SizeOfTable) {
+        const PRUint16 platformID = ReadShortAt(table, TableOffsetPlatformID);
+        if (platformID != PlatformIDMicrosoft)
+            continue;
+
+        const PRUint16 encodingID = ReadShortAt(table, TableOffsetEncodingID);
+        const PRUint32 offset = ReadLongAt(table, TableOffsetOffset);
+
+        const PRUint8 *subtable = buf + offset;
+        const PRUint16 format = ReadShortAt(subtable, SubtableOffsetFormat);
+
+        if (format == 4 && encodingID == EncodingIDMicrosoft) {
+            keepFormat = format;
+            keepOffset = offset;
+        }
+        else if (format == 12 && encodingID == EncodingIDUCS4) {
+            keepFormat = format;
+            keepOffset = offset;
+            break; // we don't want to try anything else when this format is available.
+        }
+    }
+
+    nsresult rv = NS_ERROR_FAILURE;
+
+    if (keepFormat == 12)
+        rv = ReadCMAPTableFormat12(buf + keepOffset, len - keepOffset, aFontEntry);
+    else if (keepFormat == 4)
+        rv = ReadCMAPTableFormat4(buf + keepOffset, len - keepOffset, aFontEntry);
+
+    return rv;
+}
+
+PLDHashOperator PR_CALLBACK
+gfxWindowsPlatform::FontGetCMapDataProc(nsStringHashKey::KeyType aKey,
+                                        nsRefPtr<FontEntry>& aFontEntry,
+                                        void* userArg)
+{
+    if (aFontEntry->IsCrappyFont())
+        return PL_DHASH_NEXT;
+
+    HDC hdc = GetDC(nsnull);
+
+    LOGFONTW logFont;
+    memset(&logFont, 0, sizeof(LOGFONTW));
+    logFont.lfCharSet = DEFAULT_CHARSET;
+    logFont.lfPitchAndFamily = 0;
+    PRUint32 l = PR_MIN(aFontEntry->mName.Length(), LF_FACESIZE - 1);
+    memcpy(logFont.lfFaceName,
+           nsPromiseFlatString(aFontEntry->mName).get(),
+           l * sizeof(PRUnichar));
+    logFont.lfFaceName[l] = 0;
+
+    HFONT font = CreateFontIndirectW(&logFont);
+
+    if (font) {
+        HFONT oldFont = (HFONT)SelectObject(hdc, font);
+
+        nsresult rv = ReadCMAP(hdc, aFontEntry);
+
+        if (NS_FAILED(rv))
+            aFontEntry->mUnicodeFont = PR_FALSE;
+
+        SelectObject(hdc, oldFont);
+        DeleteObject(font);
+    }
+
+    ReleaseDC(nsnull, hdc);
+
+    return PL_DHASH_NEXT;
+}
+
+
 struct FontListData {
     FontListData(const nsACString& aLangGroup, const nsACString& aGenericFamily, nsStringArray& aListOfFonts) :
         mLangGroup(aLangGroup), mGenericFamily(aGenericFamily), mStringArray(aListOfFonts) {}
     const nsACString& mLangGroup;
     const nsACString& mGenericFamily;
     nsStringArray& mStringArray;
 };
 
@@ -202,27 +432,27 @@ gfxWindowsPlatform::UpdateFontList()
     logFont.lfFaceName[0] = 0;
     logFont.lfPitchAndFamily = 0;
 
     // Use the screen DC here.. should we use something else for printing?
     HDC dc = ::GetDC(nsnull);
     EnumFontFamiliesExW(dc, &logFont, (FONTENUMPROCW)gfxWindowsPlatform::FontEnumProc, (LPARAM)this, 0);
     ::ReleaseDC(nsnull, dc);
 
+    // Update all the fonts cmaps
+    mFonts.Enumerate(gfxWindowsPlatform::FontGetCMapDataProc, nsnull);
+
     // Create the list of FontSubstitutes
-    nsCOMPtr<nsIWindowsRegKey> regKey =
-        do_CreateInstance("@mozilla.org/windows-registry-key;1");
+    nsCOMPtr<nsIWindowsRegKey> regKey = do_CreateInstance("@mozilla.org/windows-registry-key;1");
     if (!regKey)
         return NS_ERROR_FAILURE;
-     NS_NAMED_LITERAL_STRING(kFontSubstitutesKey,
-          "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes");
+     NS_NAMED_LITERAL_STRING(kFontSubstitutesKey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes");
 
-    nsresult rv =
-        regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
-                     kFontSubstitutesKey, nsIWindowsRegKey::ACCESS_READ);
+    nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
+                               kFontSubstitutesKey, nsIWindowsRegKey::ACCESS_READ);
     if (NS_FAILED(rv))
         return rv;
 
     PRUint32 count;
     rv = regKey->GetValueCount(&count);
     if (NS_FAILED(rv) || count == 0)
         return rv;
     for (PRUint32 i = 0; i < count; i++) {
@@ -346,165 +576,111 @@ gfxWindowsPlatform::FontResolveProc(cons
     rData->mFoundCount++;
     rData->mCaller->mFontAliases.Put(*(rData->mFontName), fe);
 
     return (rData->mCallback)(name, rData->mClosure);
 
     // XXX If the font has font link, we should add the linked font.
 }
 
-struct FontMatch {
-    FontMatch() : rank(0) {}
-    PRBool operator <(const FontMatch& other) const { return (rank < other.rank); }
-    PRBool operator >(const FontMatch& other) const { return (rank > other.rank); }
-    PRBool operator ==(const FontMatch& other) const { return (rank == other.rank); }
-    nsRefPtr<FontEntry> fontEntry;
-    PRInt8 rank;
-};
-
 struct FontSearch {
-    FontSearch(PRUnichar aCh, PRUint8 aRange, const char *aLangGroup, const char *aFamily) :
-        ch(aCh), langGroup(aLangGroup), family(aFamily), range(aRange), highestRank(0), fontMatches(25) {
-    }
-    PRBool RankIsOK(PRUint32 rank) {
-        return (rank >= (highestRank / 2) + 1);
+    FontSearch(const PRUnichar *aString, PRUint32 aLength, gfxWindowsFont *aFont) :
+        string(aString), length(aLength), fontToMatch(aFont), matchRank(0) {
     }
-    const PRUint32 ch;
-    const char *langGroup;
-    const char *family;
-    const PRUint8 range;
-    PRInt8 highestRank;
-    nsTArray<FontMatch> fontMatches;
+    const PRUnichar *string;
+    const PRUint32 length;
+    nsRefPtr<gfxWindowsFont> fontToMatch;
+    PRInt32 matchRank;
+    nsRefPtr<FontEntry> bestMatch;
 };
 
 PLDHashOperator PR_CALLBACK
-gfxWindowsPlatform::FindFontForChar(nsStringHashKey::KeyType aKey,
-                                    nsRefPtr<FontEntry>& aFontEntry,
-                                    void* userArg)
+gfxWindowsPlatform::FindFontForStringProc(nsStringHashKey::KeyType aKey,
+                                          nsRefPtr<FontEntry>& aFontEntry,
+                                          void* userArg)
 {
     // bitmap fonts suck
     if (aFontEntry->IsCrappyFont())
         return PL_DHASH_NEXT;
 
     FontSearch *data = (FontSearch*)userArg;
-    FontMatch fm;
-    if (aFontEntry->SupportsRange(data->range))
-        fm.rank += 20;
+
+    PRInt32 rank = 0;
 
-    if (aFontEntry->SupportsLangGroup(nsDependentCString(data->langGroup)))
-        fm.rank += 10;
+    for (PRUint32 i = 0; i < data->length; ++i) {
+        PRUint32 ch = data->string[i];
 
-    if (data->family && aFontEntry->MatchesGenericFamily(nsDependentCString(data->family)))
-        fm.rank += 5;
+        if ((i+1 < data->length) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(data->string[i+1])) {
+            i++;
+            ch = SURROGATE_TO_UCS4(ch, data->string[i]);
+        }
 
-    // XXX this code doesn't really work like I would hope
-    // we really just want to avoid non-unicode fonts.. i.e. wingdings, etc
-    // find something better to replace it with
-    /* rank symbol fonts lower than other stuff -- this might be a bad idea, but should
-     * avoid things like wingdings showing up while rendering hindi scripts
-     */
-    //    if (aFontEntry->SupportsLangGroup(NS_LITERAL_CSTRING("x-symbol")))
-    //        fm.rank -= 5;
+        if (aFontEntry->mCharacterMap.test(ch)) {
+            rank += 20;
 
-    /* This will allow us to cut out some of the fonts, but not all
-     * since some might get in early before we find the real highest rank.
-     */
-    if (fm.rank > data->highestRank)
-        data->highestRank = fm.rank;
+            // fonts that claim to support the range are more
+            // likely to be "better fonts" than ones that don't... (in theory)
+            if (aFontEntry->SupportsRange(CharRangeBit(ch)))
+                rank += 1;
+        }
+    }
 
-    if (!data->RankIsOK(fm.rank))
+    // if we didn't match any characters don't bother wasting more time.
+    if (rank == 0)
         return PL_DHASH_NEXT;
 
-    if (fm.rank > 0) {
-        fm.fontEntry = aFontEntry;
-        data->fontMatches.AppendElement(fm);
+
+    if (aFontEntry->SupportsLangGroup(data->fontToMatch->GetStyle()->langGroup))
+        rank += 10;
+
+    if (data->fontToMatch->GetFontEntry()->mFamily == aFontEntry->mFamily)
+        rank += 5;
+    if (data->fontToMatch->GetFontEntry()->mFamily == aFontEntry->mPitch)
+        rank += 5;
+
+    /* weight */
+    PRInt8 baseWeight, weightDistance;
+    data->fontToMatch->GetStyle()->ComputeWeightAndOffset(&baseWeight, &weightDistance);
+    PRUint16 targetWeight = (baseWeight * 100) + (weightDistance * 100);
+    if (targetWeight == aFontEntry->mDefaultWeight)
+        rank += 5;
+
+    if (data->matchRank == 0 || rank > data->matchRank) {
+        data->bestMatch = aFontEntry;
+        data->matchRank = rank;
     }
 
     return PL_DHASH_NEXT;
 }
 
-void
-gfxWindowsPlatform::FindOtherFonts(const PRUnichar* aString, PRUint32 aLength, const char *aLangGroup, const char *aGeneric, nsString& fonts)
-{
-    fonts.Truncate();
-
-    PRBool surrogates = PR_FALSE;
-
-    std::bitset<128> ranges(0);
-
-    for (PRUint32 z = 0; z < aLength; ++z) {
-        PRUint32 ch = aString[z];
-
-        if ((z+1 < aLength) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(aString[z+1])) {
-            z++;
-            ch = SURROGATE_TO_UCS4(ch, aString[z]);
-            surrogates = PR_TRUE;
-        }
-
-        PRUint8 range = CharRangeBit(ch);
-        if (range != NO_RANGE_FOUND && !ranges[range]) {
-            FontSearch data(ch, CharRangeBit(ch), aLangGroup, aGeneric);
-
-            mFonts.Enumerate(gfxWindowsPlatform::FindFontForChar, &data);
-
-            data.fontMatches.Sort();
 
-            PRUint32 nmatches = data.fontMatches.Length();
-            if (nmatches > 0) {
-                //printf("%d matches for 0x%04x\n", nmatches, ch);
-                for (PRUint32 i = nmatches - 1; i > 0; i--) {
-                    const FontMatch& fm = data.fontMatches[i];
-                    if (data.RankIsOK(fm.rank)) {
-                        if (!fonts.IsEmpty())
-                            fonts.AppendLiteral(", ");
-                        fonts.Append(fm.fontEntry->mName);
-                    }
-                }
-            }
-            ranges[range] = PR_TRUE;
-        }
-    }
-
-
-    if (surrogates) {
-        // append fonts that support surrogates on to the list
-        FontSearch data(0xd801, 57, aLangGroup, aGeneric);
-
-        mFonts.Enumerate(gfxWindowsPlatform::FindFontForChar, &data);
-
-        data.fontMatches.Sort();
+FontEntry *
+gfxWindowsPlatform::FindFontForString(const PRUnichar *aString, PRUint32 aLength, gfxWindowsFont *aFont)
+{
+    FontSearch data(aString, aLength, aFont);
 
-        PRUint32 nmatches = data.fontMatches.Length();
-        if (nmatches > 0) {
-            for (PRUint32 i = nmatches - 1; i > 0; i--) {
-                const FontMatch& fm = data.fontMatches[i];
-                if (data.RankIsOK(fm.rank)) {
-                    if (!fonts.IsEmpty())
-                        fonts.AppendLiteral(", ");
-                    fonts.Append(fm.fontEntry->mName);
-                }
-            }
-        }
-    }
-}
+    // find fonts that support the character
+    mFonts.Enumerate(gfxWindowsPlatform::FindFontForStringProc, &data);
 
-WeightTable *
-gfxWindowsPlatform::GetFontWeightTable(const nsAString& aName)
-{
-    nsRefPtr<WeightTable> wt;
-    if (!mFontWeights.Get(aName, &wt)) {
-        return nsnull;
-    }
-    return wt;
-}
-
-void
-gfxWindowsPlatform::PutFontWeightTable(const nsAString& aName, WeightTable *aWeightTable)
-{
-    mFontWeights.Put(aName, aWeightTable);
+    return data.bestMatch;
 }
 
 gfxFontGroup *
 gfxWindowsPlatform::CreateFontGroup(const nsAString &aFamilies,
                                     const gfxFontStyle *aStyle)
 {
     return new gfxWindowsFontGroup(aFamilies, aStyle);
 }
+
+FontEntry *
+gfxWindowsPlatform::FindFontEntry(const nsAString& aName)
+{
+    nsString name(aName);
+    ToLowerCase(name);
+
+    nsRefPtr<FontEntry> fe;
+    if (!mFonts.Get(name, &fe) &&
+        !mFontSubstitutes.Get(name, &fe) &&
+        !mFontAliases.Get(name, &fe)) {
+        return nsnull;
+    }
+    return fe.get();
+}