diff --git a/mozilla/gfx/thebes/public/gfxPlatform.h b/mozilla/gfx/thebes/public/gfxPlatform.h index 5715831758d..eb863395868 100644 --- a/mozilla/gfx/thebes/public/gfxPlatform.h +++ b/mozilla/gfx/thebes/public/gfxPlatform.h @@ -124,7 +124,7 @@ public: /** * 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 */ diff --git a/mozilla/gfx/thebes/public/gfxWindowsFonts.h b/mozilla/gfx/thebes/public/gfxWindowsFonts.h index 61565e6a7dd..919b25fd626 100644 --- a/mozilla/gfx/thebes/public/gfxWindowsFonts.h +++ b/mozilla/gfx/thebes/public/gfxWindowsFonts.h @@ -60,45 +60,6 @@ #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 * @@ -279,6 +240,58 @@ static PRUint8 CharRangeBit(PRUint32 ch) { 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 *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 > 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. @@ -291,8 +304,8 @@ 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) { } @@ -385,10 +398,32 @@ public: 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; @@ -396,6 +431,10 @@ public: std::bitset<256> mCharset; std::bitset<128> mUnicodeRanges; + + WeightTable mWeightTable; + + gfxSparseBitSet mCharacterMap; }; @@ -429,6 +468,8 @@ public: return mSpaceGlyph; }; + FontEntry *GetFontEntry() { return mFontEntry; } + protected: HFONT MakeHFONT(); cairo_font_face_t *MakeCairoFontFace(); @@ -452,7 +493,7 @@ private: LOGFONTW mLogFont; - nsRefPtr mWeightTable; + nsRefPtr mFontEntry; virtual void SetupCairoFont(cairo_t *aCR); }; diff --git a/mozilla/gfx/thebes/public/gfxWindowsPlatform.h b/mozilla/gfx/thebes/public/gfxWindowsPlatform.h index c1348ed933b..57f966fed6a 100644 --- a/mozilla/gfx/thebes/public/gfxWindowsPlatform.h +++ b/mozilla/gfx/thebes/public/gfxWindowsPlatform.h @@ -72,11 +72,17 @@ public: 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(); @@ -85,6 +91,10 @@ private: const NEWTEXTMETRICEXW *metrics, DWORD fontType, LPARAM data); + static PLDHashOperator PR_CALLBACK FontGetCMapDataProc(nsStringHashKey::KeyType aKey, + nsRefPtr& aFontEntry, + void* userArg); + static int CALLBACK FontResolveProc(const ENUMLOGFONTEXW *lpelfe, const NEWTEXTMETRICEXW *metrics, DWORD fontType, LPARAM data); @@ -93,12 +103,11 @@ private: nsRefPtr& aData, void* userArg); - static PLDHashOperator PR_CALLBACK FindFontForChar(nsStringHashKey::KeyType aKey, - nsRefPtr& aFontEntry, - void* userArg); + static PLDHashOperator PR_CALLBACK FindFontForStringProc(nsStringHashKey::KeyType aKey, + nsRefPtr& aFontEntry, + void* userArg); nsDataHashtable > mFonts; - nsDataHashtable > mFontWeights; nsDataHashtable > mFontAliases; nsDataHashtable > mFontSubstitutes; nsStringArray mNonExistingFonts; diff --git a/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp b/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp index 399745cb256..3ac48990264 100644 --- a/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp +++ b/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp @@ -97,6 +97,9 @@ gfxWindowsFont::gfxWindowsFont(const nsAString& aName, const gfxFontStyle *aFont 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() @@ -175,19 +178,6 @@ 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); @@ -198,10 +188,10 @@ gfxWindowsFont::MakeHFONT() 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; @@ -216,7 +206,7 @@ gfxWindowsFont::MakeHFONT() GetTextMetrics(dc, &metrics); PRBool hasWeight = (metrics.tmWeight == tryWeight); - mWeightTable->SetWeight(i, hasWeight); + mFontEntry->mWeightTable.SetWeight(i, hasWeight); if (hasWeight) { chosenWeight = i * 100; k++; @@ -328,7 +318,7 @@ gfxWindowsFont::ComputeMetrics() 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); @@ -674,7 +664,7 @@ SetupTextRunFromGlyphs(gfxTextRun *aRun, WCHAR *aGlyphs, HDC aDC, PRUint32 length = aRun->GetLength(); if (IsAnyGlyphMissing(aGlyphs, length)) return PR_FALSE; - + SIZE size; nsAutoTArray partialWidthArray; if (!partialWidthArray.AppendElements(length)) @@ -936,11 +926,6 @@ public: 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) { @@ -955,53 +940,26 @@ public: if (aName.Equals(item->mFonts[i]->GetName())) return PR_TRUE; - nsRefPtr font = - GetOrMakeFont(aName, item->mGroup->GetStyle()); + nsRefPtr 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, @@ -1023,10 +981,6 @@ public: continue; } -#if 0 // debugging only - if (rv != USP_E_SCRIPT_NOT_IN_FONT && !shapeDC) - printf("skipped select-shape %d\n", rv); -#endif return rv; } } @@ -1043,30 +997,19 @@ public: 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); - - if (rv == E_PENDING) { - SelectFont(); - cmapDC = mDC; - continue; + nsRefPtr 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 (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; + if (!fe->mCharacterMap.test(ch)) + return PR_TRUE; } + + return PR_FALSE; } PRBool IsGlyphMissing(SCRIPT_FONTPROPERTIES *aSFP, PRUint32 aGlyphIndex) { @@ -1075,22 +1018,6 @@ public: 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; @@ -1286,7 +1213,6 @@ TRY_AGAIN_HOPE_FOR_THE_BEST_2: 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)) { @@ -1302,22 +1228,16 @@ TRY_AGAIN_HOPE_FOR_THE_BEST_2: 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 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; @@ -1353,7 +1273,6 @@ TRY_AGAIN_HOPE_FOR_THE_BEST_2: mFontSelected = PR_TRUE; } - private: static PRInt32 GetCJKLangGroupIndex(const char *aLangGroup) { PRInt32 i; @@ -1441,6 +1360,7 @@ private: mAlternativeString[i] = PRUnichar(0xFFFD); } } + private: nsRefPtr mContext; HDC mDC; @@ -1481,7 +1401,6 @@ 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() { @@ -1541,8 +1460,6 @@ private: const PRUnichar *mString; const PRUint32 mLength; const PRBool mIsRTL; - // XXX not used - const PRBool mIsComplex; SCRIPT_CONTROL mControl; SCRIPT_STATE mState; @@ -1550,10 +1467,9 @@ private: int mNumItems; }; - void gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun, const PRUnichar *aString, - PRUint32 aLength) + PRUint32 aLength) { nsRefPtr surf = aContext->CurrentSurface(); HDC aDC = GetDCFromSurface(surf); @@ -1568,7 +1484,7 @@ gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun /* 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); @@ -1584,59 +1500,46 @@ gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun 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)) @@ -1648,6 +1551,7 @@ SCRIPT_PLACE: } item->SaveGlyphs(aRun); + delete item; RestoreDC(aDC, -1); diff --git a/mozilla/gfx/thebes/src/gfxWindowsPlatform.cpp b/mozilla/gfx/thebes/src/gfxWindowsPlatform.cpp index 44c0ed9bf2e..5c12b0c860d 100644 --- a/mozilla/gfx/thebes/src/gfxWindowsPlatform.cpp +++ b/mozilla/gfx/thebes/src/gfxWindowsPlatform.cpp @@ -54,10 +54,21 @@ #include +//#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(); @@ -103,13 +114,11 @@ gfxWindowsPlatform::FontEnumProc(const ENUMLOGFONTEXW *lpelfe, // mark the charset bit fe->mCharset[metrics.tmCharSet] = 1; - // put this in the weight table - nsRefPtr 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; @@ -136,6 +145,227 @@ gfxWindowsPlatform::FontEnumProc(const ENUMLOGFONTEXW *lpelfe, 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 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& 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) {} @@ -207,17 +437,17 @@ gfxWindowsPlatform::UpdateFontList() 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 regKey = - do_CreateInstance("@mozilla.org/windows-registry-key;1"); + nsCOMPtr 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; @@ -351,155 +581,86 @@ gfxWindowsPlatform::FontResolveProc(const ENUMLOGFONTEXW *lpelfe, // 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; - 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) { + FontSearch(const PRUnichar *aString, PRUint32 aLength, gfxWindowsFont *aFont) : + string(aString), length(aLength), fontToMatch(aFont), matchRank(0) { } - PRBool RankIsOK(PRUint32 rank) { - return (rank >= (highestRank / 2) + 1); - } - const PRUint32 ch; - const char *langGroup; - const char *family; - const PRUint8 range; - PRInt8 highestRank; - nsTArray fontMatches; + const PRUnichar *string; + const PRUint32 length; + nsRefPtr fontToMatch; + PRInt32 matchRank; + nsRefPtr bestMatch; }; PLDHashOperator PR_CALLBACK -gfxWindowsPlatform::FindFontForChar(nsStringHashKey::KeyType aKey, - nsRefPtr& aFontEntry, - void* userArg) +gfxWindowsPlatform::FindFontForStringProc(nsStringHashKey::KeyType aKey, + nsRefPtr& 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; - if (aFontEntry->SupportsLangGroup(nsDependentCString(data->langGroup))) - fm.rank += 10; + PRInt32 rank = 0; - if (data->family && aFontEntry->MatchesGenericFamily(nsDependentCString(data->family))) - fm.rank += 5; + for (PRUint32 i = 0; i < data->length; ++i) { + PRUint32 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 ((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]); + } - /* 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; + if (aFontEntry->mCharacterMap.test(ch)) { + rank += 20; - if (!data->RankIsOK(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 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) + +FontEntry * +gfxWindowsPlatform::FindFontForString(const PRUnichar *aString, PRUint32 aLength, gfxWindowsFont *aFont) { - fonts.Truncate(); + FontSearch data(aString, aLength, aFont); - PRBool surrogates = PR_FALSE; + // find fonts that support the character + mFonts.Enumerate(gfxWindowsPlatform::FindFontForStringProc, &data); - 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(); - - 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); - } - } - } - } -} - -WeightTable * -gfxWindowsPlatform::GetFontWeightTable(const nsAString& aName) -{ - nsRefPtr 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 * @@ -508,3 +669,18 @@ gfxWindowsPlatform::CreateFontGroup(const nsAString &aFamilies, { return new gfxWindowsFontGroup(aFamilies, aStyle); } + +FontEntry * +gfxWindowsPlatform::FindFontEntry(const nsAString& aName) +{ + nsString name(aName); + ToLowerCase(name); + + nsRefPtr fe; + if (!mFonts.Get(name, &fe) && + !mFontSubstitutes.Get(name, &fe) && + !mFontAliases.Get(name, &fe)) { + return nsnull; + } + return fe.get(); +}