michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG /* Allow logging in the release build */ michael@0: #endif /* MOZ_LOGGING */ michael@0: michael@0: #include "gfxDWriteFontList.h" michael@0: #include "gfxDWriteFonts.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsILocaleService.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsCharSeparatedTokenizer.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Telemetry.h" michael@0: michael@0: #include "gfxGDIFontList.h" michael@0: michael@0: #include "nsIWindowsRegKey.h" michael@0: michael@0: #include "harfbuzz/hb.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ michael@0: PR_LOG_DEBUG, args) michael@0: #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \ michael@0: gfxPlatform::GetLog(eGfxLog_fontlist), \ michael@0: PR_LOG_DEBUG) michael@0: michael@0: #define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \ michael@0: PR_LOG_DEBUG, args) michael@0: #define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \ michael@0: gfxPlatform::GetLog(eGfxLog_fontinit), \ michael@0: PR_LOG_DEBUG) michael@0: michael@0: #define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \ michael@0: gfxPlatform::GetLog(eGfxLog_cmapdata), \ michael@0: PR_LOG_DEBUG) michael@0: michael@0: static __inline void michael@0: BuildKeyNameFromFontName(nsAString &aName) michael@0: { michael@0: if (aName.Length() >= LF_FACESIZE) michael@0: aName.Truncate(LF_FACESIZE - 1); michael@0: ToLowerCase(aName); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // gfxDWriteFontFamily michael@0: michael@0: gfxDWriteFontFamily::~gfxDWriteFontFamily() michael@0: { michael@0: } michael@0: michael@0: static HRESULT michael@0: GetDirectWriteFontName(IDWriteFont *aFont, nsAString& aFontName) michael@0: { michael@0: HRESULT hr; michael@0: michael@0: nsRefPtr names; michael@0: hr = aFont->GetFaceNames(getter_AddRefs(names)); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: BOOL exists; michael@0: nsAutoTArray faceName; michael@0: UINT32 englishIdx = 0; michael@0: hr = names->FindLocaleName(L"en-us", &englishIdx, &exists); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: if (!exists) { michael@0: // No english found, use whatever is first in the list. michael@0: englishIdx = 0; michael@0: } michael@0: UINT32 length; michael@0: hr = names->GetStringLength(englishIdx, &length); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: faceName.SetLength(length + 1); michael@0: hr = names->GetString(englishIdx, faceName.Elements(), length + 1); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: aFontName.Assign(faceName.Elements()); michael@0: return S_OK; michael@0: } michael@0: michael@0: // These strings are only defined in Win SDK 8+, so use #ifdef for now michael@0: #if MOZ_WINSDK_TARGETVER > 0x08000000 michael@0: #define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_FULL_NAME michael@0: #define PSNAME_ID DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME michael@0: #else michael@0: #define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_ID(DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT + 1) michael@0: #define PSNAME_ID DWRITE_INFORMATIONAL_STRING_ID(DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT + 2) michael@0: #endif michael@0: michael@0: // for use in reading postscript or fullname michael@0: static HRESULT michael@0: GetDirectWriteFaceName(IDWriteFont *aFont, michael@0: DWRITE_INFORMATIONAL_STRING_ID aWhichName, michael@0: nsAString& aFontName) michael@0: { michael@0: HRESULT hr; michael@0: michael@0: BOOL exists; michael@0: nsRefPtr infostrings; michael@0: hr = aFont->GetInformationalStrings(aWhichName, getter_AddRefs(infostrings), &exists); michael@0: if (FAILED(hr) || !exists) { michael@0: return E_FAIL; michael@0: } michael@0: michael@0: nsAutoTArray faceName; michael@0: UINT32 englishIdx = 0; michael@0: hr = infostrings->FindLocaleName(L"en-us", &englishIdx, &exists); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: if (!exists) { michael@0: // No english found, use whatever is first in the list. michael@0: englishIdx = 0; michael@0: } michael@0: UINT32 length; michael@0: hr = infostrings->GetStringLength(englishIdx, &length); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: faceName.SetLength(length + 1); michael@0: hr = infostrings->GetString(englishIdx, faceName.Elements(), length + 1); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: aFontName.Assign(faceName.Elements()); michael@0: return S_OK; michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFontFamily::FindStyleVariations(FontInfoData *aFontInfoData) michael@0: { michael@0: HRESULT hr; michael@0: if (mHasStyles) { michael@0: return; michael@0: } michael@0: mHasStyles = true; michael@0: michael@0: gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList(); michael@0: michael@0: bool skipFaceNames = mFaceNamesInitialized || michael@0: !fp->NeedFullnamePostscriptNames(); michael@0: bool fontInfoShouldHaveFaceNames = !mFaceNamesInitialized && michael@0: fp->NeedFullnamePostscriptNames() && michael@0: aFontInfoData; michael@0: michael@0: for (UINT32 i = 0; i < mDWFamily->GetFontCount(); i++) { michael@0: nsRefPtr font; michael@0: hr = mDWFamily->GetFont(i, getter_AddRefs(font)); michael@0: if (FAILED(hr)) { michael@0: // This should never happen. michael@0: NS_WARNING("Failed to get existing font from family."); michael@0: continue; michael@0: } michael@0: michael@0: if (font->GetSimulations() & DWRITE_FONT_SIMULATIONS_OBLIQUE) { michael@0: // We don't want these. michael@0: continue; michael@0: } michael@0: michael@0: // name michael@0: nsString fullID(mName); michael@0: nsAutoString faceName; michael@0: hr = GetDirectWriteFontName(font, faceName); michael@0: if (FAILED(hr)) { michael@0: continue; michael@0: } michael@0: fullID.Append(NS_LITERAL_STRING(" ")); michael@0: fullID.Append(faceName); michael@0: michael@0: gfxDWriteFontEntry *fe = new gfxDWriteFontEntry(fullID, font); michael@0: fe->SetForceGDIClassic(mForceGDIClassic); michael@0: AddFontEntry(fe); michael@0: michael@0: // postscript/fullname if needed michael@0: nsAutoString psname, fullname; michael@0: if (fontInfoShouldHaveFaceNames) { michael@0: aFontInfoData->GetFaceNames(fe->Name(), fullname, psname); michael@0: if (!fullname.IsEmpty()) { michael@0: fp->AddFullname(fe, fullname); michael@0: } michael@0: if (!psname.IsEmpty()) { michael@0: fp->AddPostscriptName(fe, psname); michael@0: } michael@0: } else if (!skipFaceNames) { michael@0: hr = GetDirectWriteFaceName(font, PSNAME_ID, psname); michael@0: if (FAILED(hr)) { michael@0: skipFaceNames = true; michael@0: } else if (psname.Length() > 0) { michael@0: fp->AddPostscriptName(fe, psname); michael@0: } michael@0: michael@0: hr = GetDirectWriteFaceName(font, FULLNAME_ID, fullname); michael@0: if (FAILED(hr)) { michael@0: skipFaceNames = true; michael@0: } else if (fullname.Length() > 0) { michael@0: fp->AddFullname(fe, fullname); michael@0: } michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (LOG_FONTLIST_ENABLED()) { michael@0: LOG_FONTLIST(("(fontlist) added (%s) to family (%s)" michael@0: " with style: %s weight: %d stretch: %d psname: %s fullname: %s", michael@0: NS_ConvertUTF16toUTF8(fe->Name()).get(), michael@0: NS_ConvertUTF16toUTF8(Name()).get(), michael@0: (fe->IsItalic()) ? "italic" : "normal", michael@0: fe->Weight(), fe->Stretch(), michael@0: NS_ConvertUTF16toUTF8(psname).get(), michael@0: NS_ConvertUTF16toUTF8(fullname).get())); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: // assume that if no error, all postscript/fullnames were initialized michael@0: if (!skipFaceNames) { michael@0: mFaceNamesInitialized = true; michael@0: } michael@0: michael@0: if (!mAvailableFonts.Length()) { michael@0: NS_WARNING("Family with no font faces in it."); michael@0: } michael@0: michael@0: if (mIsBadUnderlineFamily) { michael@0: SetBadUnderlineFonts(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList, michael@0: bool aNeedFullnamePostscriptNames) michael@0: { michael@0: // if all needed names have already been read, skip michael@0: if (mOtherFamilyNamesInitialized && michael@0: (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) { michael@0: return; michael@0: } michael@0: michael@0: // DirectWrite version of this will try to read michael@0: // postscript/fullnames via DirectWrite API michael@0: FindStyleVariations(); michael@0: michael@0: // fallback to looking up via name table michael@0: if (!mOtherFamilyNamesInitialized || !mFaceNamesInitialized) { michael@0: gfxFontFamily::ReadFaceNames(aPlatformFontList, michael@0: aNeedFullnamePostscriptNames); michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFontFamily::LocalizedName(nsAString &aLocalizedName) michael@0: { michael@0: aLocalizedName.AssignLiteral("Unknown Font"); michael@0: HRESULT hr; michael@0: nsresult rv; michael@0: nsCOMPtr ls = do_GetService(NS_LOCALESERVICE_CONTRACTID, michael@0: &rv); michael@0: nsCOMPtr locale; michael@0: rv = ls->GetApplicationLocale(getter_AddRefs(locale)); michael@0: nsString localeName; michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = locale->GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), michael@0: localeName); michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: localeName.AssignLiteral("en-us"); michael@0: } michael@0: michael@0: nsRefPtr names; michael@0: michael@0: hr = mDWFamily->GetFamilyNames(getter_AddRefs(names)); michael@0: if (FAILED(hr)) { michael@0: return; michael@0: } michael@0: UINT32 idx = 0; michael@0: BOOL exists; michael@0: hr = names->FindLocaleName(localeName.get(), michael@0: &idx, michael@0: &exists); michael@0: if (FAILED(hr)) { michael@0: return; michael@0: } michael@0: if (!exists) { michael@0: // Use english is localized is not found. michael@0: hr = names->FindLocaleName(L"en-us", &idx, &exists); michael@0: if (FAILED(hr)) { michael@0: return; michael@0: } michael@0: if (!exists) { michael@0: // Use 0 index if english is not found. michael@0: idx = 0; michael@0: } michael@0: } michael@0: AutoFallibleTArray famName; michael@0: UINT32 length; michael@0: michael@0: hr = names->GetStringLength(idx, &length); michael@0: if (FAILED(hr)) { michael@0: return; michael@0: } michael@0: michael@0: if (!famName.SetLength(length + 1)) { michael@0: // Eeep - running out of memory. Unlikely to end well. michael@0: return; michael@0: } michael@0: michael@0: hr = names->GetString(idx, famName.Elements(), length + 1); michael@0: if (FAILED(hr)) { michael@0: return; michael@0: } michael@0: michael@0: aLocalizedName = nsDependentString(famName.Elements()); michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: gfxFontFamily::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: // TODO: michael@0: // This doesn't currently account for |mDWFamily| michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: aSizes->mFontListSize += aMallocSizeOf(this); michael@0: AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // gfxDWriteFontEntry michael@0: michael@0: gfxDWriteFontEntry::~gfxDWriteFontEntry() michael@0: { michael@0: } michael@0: michael@0: bool michael@0: gfxDWriteFontEntry::IsSymbolFont() michael@0: { michael@0: if (mFont) { michael@0: return mFont->IsSymbolFont(); michael@0: } else { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: UsingArabicOrHebrewScriptSystemLocale() michael@0: { michael@0: LANGID langid = PRIMARYLANGID(::GetSystemDefaultLangID()); michael@0: switch (langid) { michael@0: case LANG_ARABIC: michael@0: case LANG_DARI: michael@0: case LANG_PASHTO: michael@0: case LANG_PERSIAN: michael@0: case LANG_SINDHI: michael@0: case LANG_UIGHUR: michael@0: case LANG_URDU: michael@0: case LANG_HEBREW: michael@0: return true; michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: gfxDWriteFontEntry::CopyFontTable(uint32_t aTableTag, michael@0: FallibleTArray &aBuffer) michael@0: { michael@0: gfxDWriteFontList *pFontList = gfxDWriteFontList::PlatformFontList(); michael@0: michael@0: // Don't use GDI table loading for symbol fonts or for michael@0: // italic fonts in Arabic-script system locales because of michael@0: // potential cmap discrepancies, see bug 629386. michael@0: // Ditto for Hebrew, bug 837498. michael@0: if (mFont && pFontList->UseGDIFontTableAccess() && michael@0: !(mItalic && UsingArabicOrHebrewScriptSystemLocale()) && michael@0: !mFont->IsSymbolFont()) michael@0: { michael@0: LOGFONTW logfont = { 0 }; michael@0: if (!InitLogFont(mFont, &logfont)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: AutoDC dc; michael@0: AutoSelectFont font(dc.GetDC(), &logfont); michael@0: if (font.IsValid()) { michael@0: uint32_t tableSize = michael@0: ::GetFontData(dc.GetDC(), michael@0: NativeEndian::swapToBigEndian(aTableTag), 0, michael@0: nullptr, 0); michael@0: if (tableSize != GDI_ERROR) { michael@0: if (aBuffer.SetLength(tableSize)) { michael@0: ::GetFontData(dc.GetDC(), michael@0: NativeEndian::swapToBigEndian(aTableTag), 0, michael@0: aBuffer.Elements(), aBuffer.Length()); michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRefPtr fontFace; michael@0: nsresult rv = CreateFontFace(getter_AddRefs(fontFace)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: uint8_t *tableData; michael@0: uint32_t len; michael@0: void *tableContext = nullptr; michael@0: BOOL exists; michael@0: HRESULT hr = michael@0: fontFace->TryGetFontTable(NativeEndian::swapToBigEndian(aTableTag), michael@0: (const void**)&tableData, &len, michael@0: &tableContext, &exists); michael@0: if (FAILED(hr) || !exists) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (aBuffer.SetLength(len)) { michael@0: memcpy(aBuffer.Elements(), tableData, len); michael@0: rv = NS_OK; michael@0: } else { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (tableContext) { michael@0: fontFace->ReleaseFontTable(&tableContext); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // Access to font tables packaged in hb_blob_t form michael@0: michael@0: // object attached to the Harfbuzz blob, used to release michael@0: // the table when the blob is destroyed michael@0: class FontTableRec { michael@0: public: michael@0: FontTableRec(IDWriteFontFace *aFontFace, void *aContext) michael@0: : mFontFace(aFontFace), mContext(aContext) michael@0: { } michael@0: michael@0: ~FontTableRec() { michael@0: mFontFace->ReleaseFontTable(mContext); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFontFace; michael@0: void *mContext; michael@0: }; michael@0: michael@0: static void michael@0: DestroyBlobFunc(void* aUserData) michael@0: { michael@0: FontTableRec *ftr = static_cast(aUserData); michael@0: delete ftr; michael@0: } michael@0: michael@0: hb_blob_t * michael@0: gfxDWriteFontEntry::GetFontTable(uint32_t aTag) michael@0: { michael@0: // try to avoid potentially expensive DWrite call if we haven't actually michael@0: // created the font face yet, by using the gfxFontEntry method that will michael@0: // use CopyFontTable and then cache the data michael@0: if (!mFontFace) { michael@0: return gfxFontEntry::GetFontTable(aTag); michael@0: } michael@0: michael@0: const void *data; michael@0: UINT32 size; michael@0: void *context; michael@0: BOOL exists; michael@0: HRESULT hr = mFontFace->TryGetFontTable(NativeEndian::swapToBigEndian(aTag), michael@0: &data, &size, &context, &exists); michael@0: if (SUCCEEDED(hr) && exists) { michael@0: FontTableRec *ftr = new FontTableRec(mFontFace, context); michael@0: return hb_blob_create(static_cast(data), size, michael@0: HB_MEMORY_MODE_READONLY, michael@0: ftr, DestroyBlobFunc); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: gfxDWriteFontEntry::ReadCMAP(FontInfoData *aFontInfoData) michael@0: { michael@0: // attempt this once, if errors occur leave a blank cmap michael@0: if (mCharacterMap) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr charmap; michael@0: nsresult rv; michael@0: bool symbolFont; michael@0: michael@0: if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, michael@0: mUVSOffset, michael@0: symbolFont))) { michael@0: rv = NS_OK; michael@0: } else { michael@0: uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); michael@0: charmap = new gfxCharacterMap(); michael@0: AutoTable cmapTable(this, kCMAP); michael@0: michael@0: if (cmapTable) { michael@0: bool unicodeFont = false, symbolFont = false; // currently ignored michael@0: uint32_t cmapLen; michael@0: const uint8_t* cmapData = michael@0: reinterpret_cast(hb_blob_get_data(cmapTable, michael@0: &cmapLen)); michael@0: rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, michael@0: *charmap, mUVSOffset, michael@0: unicodeFont, symbolFont); michael@0: } else { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: } michael@0: michael@0: mHasCmapTable = NS_SUCCEEDED(rv); michael@0: if (mHasCmapTable) { michael@0: // Bug 969504: exclude U+25B6 from Segoe UI family, because it's used michael@0: // by sites to represent a "Play" icon, but the glyph in Segoe UI Light michael@0: // and Semibold on Windows 7 is too thin. (Ditto for leftward U+25C0.) michael@0: // Fallback to Segoe UI Symbol is preferred. michael@0: if (FamilyName().EqualsLiteral("Segoe UI")) { michael@0: charmap->clear(0x25b6); michael@0: charmap->clear(0x25c0); michael@0: } michael@0: gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); michael@0: mCharacterMap = pfl->FindCharMap(charmap); michael@0: } else { michael@0: // if error occurred, initialize to null cmap michael@0: mCharacterMap = new gfxCharacterMap(); michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n", michael@0: NS_ConvertUTF16toUTF8(mName).get(), michael@0: charmap->SizeOfIncludingThis(moz_malloc_size_of), michael@0: charmap->mHash, mCharacterMap == charmap ? " new" : "")); michael@0: if (LOG_CMAPDATA_ENABLED()) { michael@0: char prefix[256]; michael@0: sprintf(prefix, "(cmapdata) name: %.220s", michael@0: NS_ConvertUTF16toUTF8(mName).get()); michael@0: charmap->Dump(prefix, eGfxLog_cmapdata); michael@0: } michael@0: #endif michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: gfxFont * michael@0: gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, michael@0: bool aNeedsBold) michael@0: { michael@0: return new gfxDWriteFont(this, aFontStyle, aNeedsBold); michael@0: } michael@0: michael@0: nsresult michael@0: gfxDWriteFontEntry::CreateFontFace(IDWriteFontFace **aFontFace, michael@0: DWRITE_FONT_SIMULATIONS aSimulations) michael@0: { michael@0: // initialize mFontFace if this hasn't been done before michael@0: if (!mFontFace) { michael@0: HRESULT hr; michael@0: if (mFont) { michael@0: hr = mFont->CreateFontFace(getter_AddRefs(mFontFace)); michael@0: } else if (mFontFile) { michael@0: IDWriteFontFile *fontFile = mFontFile.get(); michael@0: hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()-> michael@0: CreateFontFace(mFaceType, michael@0: 1, michael@0: &fontFile, michael@0: 0, michael@0: DWRITE_FONT_SIMULATIONS_NONE, michael@0: getter_AddRefs(mFontFace)); michael@0: } else { michael@0: NS_NOTREACHED("invalid font entry"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: if (FAILED(hr)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: // check whether we need to add a DWrite simulated style michael@0: if ((aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) && michael@0: !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD)) { michael@0: // if so, we need to return not mFontFace itself but a version that michael@0: // has the Bold simulation - unfortunately, DWrite doesn't provide michael@0: // a simple API for this michael@0: UINT32 numberOfFiles = 0; michael@0: if (FAILED(mFontFace->GetFiles(&numberOfFiles, nullptr))) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: nsAutoTArray files; michael@0: files.AppendElements(numberOfFiles); michael@0: if (FAILED(mFontFace->GetFiles(&numberOfFiles, files.Elements()))) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: HRESULT hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()-> michael@0: CreateFontFace(mFontFace->GetType(), michael@0: numberOfFiles, michael@0: files.Elements(), michael@0: mFontFace->GetIndex(), michael@0: aSimulations, michael@0: aFontFace); michael@0: for (UINT32 i = 0; i < numberOfFiles; ++i) { michael@0: files[i]->Release(); michael@0: } michael@0: return FAILED(hr) ? NS_ERROR_FAILURE : NS_OK; michael@0: } michael@0: michael@0: // no simulation: we can just add a reference to mFontFace and return that michael@0: *aFontFace = mFontFace; michael@0: (*aFontFace)->AddRef(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: gfxDWriteFontEntry::InitLogFont(IDWriteFont *aFont, LOGFONTW *aLogFont) michael@0: { michael@0: HRESULT hr; michael@0: michael@0: BOOL isInSystemCollection; michael@0: IDWriteGdiInterop *gdi = michael@0: gfxDWriteFontList::PlatformFontList()->GetGDIInterop(); michael@0: hr = gdi->ConvertFontToLOGFONT(aFont, aLogFont, &isInSystemCollection); michael@0: return (FAILED(hr) ? false : true); michael@0: } michael@0: michael@0: bool michael@0: gfxDWriteFontEntry::IsCJKFont() michael@0: { michael@0: if (mIsCJK != UNINITIALIZED_VALUE) { michael@0: return mIsCJK; michael@0: } michael@0: michael@0: mIsCJK = false; michael@0: michael@0: const uint32_t kOS2Tag = TRUETYPE_TAG('O','S','/','2'); michael@0: AutoFallibleTArray buffer; michael@0: if (CopyFontTable(kOS2Tag, buffer) != NS_OK) { michael@0: return mIsCJK; michael@0: } michael@0: michael@0: // ulCodePageRange bit definitions for the CJK codepages, michael@0: // from http://www.microsoft.com/typography/otspec/os2.htm#cpr michael@0: const uint32_t CJK_CODEPAGE_BITS = michael@0: (1 << 17) | // codepage 932 - JIS/Japan michael@0: (1 << 18) | // codepage 936 - Chinese (simplified) michael@0: (1 << 19) | // codepage 949 - Korean Wansung michael@0: (1 << 20) | // codepage 950 - Chinese (traditional) michael@0: (1 << 21); // codepage 1361 - Korean Johab michael@0: michael@0: if (buffer.Length() >= offsetof(OS2Table, sxHeight)) { michael@0: const OS2Table* os2 = michael@0: reinterpret_cast(buffer.Elements()); michael@0: if ((uint32_t(os2->codePageRange1) & CJK_CODEPAGE_BITS) != 0) { michael@0: mIsCJK = true; michael@0: } michael@0: } michael@0: michael@0: return mIsCJK; michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: // TODO: michael@0: // This doesn't currently account for the |mFont| and |mFontFile| members michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: aSizes->mFontListSize += aMallocSizeOf(this); michael@0: AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // gfxDWriteFontList michael@0: michael@0: gfxDWriteFontList::gfxDWriteFontList() michael@0: : mInitialized(false), mForceGDIClassicMaxFontSize(0.0) michael@0: { michael@0: } michael@0: michael@0: // bug 602792 - CJK systems default to large CJK fonts which cause excessive michael@0: // I/O strain during cold startup due to dwrite caching bugs. Default to michael@0: // Arial to avoid this. michael@0: michael@0: gfxFontFamily * michael@0: gfxDWriteFontList::GetDefaultFont(const gfxFontStyle *aStyle) michael@0: { michael@0: nsAutoString resolvedName; michael@0: michael@0: // try Arial first michael@0: if (ResolveFontName(NS_LITERAL_STRING("Arial"), resolvedName)) { michael@0: return FindFamily(resolvedName); michael@0: } michael@0: michael@0: // otherwise, use local default michael@0: NONCLIENTMETRICSW ncm; michael@0: ncm.cbSize = sizeof(ncm); michael@0: BOOL status = ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, michael@0: sizeof(ncm), &ncm, 0); michael@0: if (status) { michael@0: if (ResolveFontName(nsDependentString(ncm.lfMessageFont.lfFaceName), michael@0: resolvedName)) { michael@0: return FindFamily(resolvedName); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: gfxFontEntry * michael@0: gfxDWriteFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, michael@0: const nsAString& aFullname) michael@0: { michael@0: gfxFontEntry *lookup; michael@0: michael@0: lookup = LookupInFaceNameLists(aFullname); michael@0: if (!lookup) { michael@0: return nullptr; michael@0: } michael@0: michael@0: gfxDWriteFontEntry* dwriteLookup = static_cast(lookup); michael@0: gfxDWriteFontEntry *fe = michael@0: new gfxDWriteFontEntry(lookup->Name(), michael@0: dwriteLookup->mFont, michael@0: aProxyEntry->Weight(), michael@0: aProxyEntry->Stretch(), michael@0: aProxyEntry->IsItalic()); michael@0: fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic()); michael@0: return fe; michael@0: } michael@0: michael@0: gfxFontEntry * michael@0: gfxDWriteFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, michael@0: const uint8_t *aFontData, michael@0: uint32_t aLength) michael@0: { michael@0: nsresult rv; michael@0: nsAutoString uniqueName; michael@0: rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName); michael@0: if (NS_FAILED(rv)) { michael@0: NS_Free((void*)aFontData); michael@0: return nullptr; michael@0: } michael@0: michael@0: FallibleTArray newFontData; michael@0: michael@0: rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData); michael@0: NS_Free((void*)aFontData); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr fontFile; michael@0: HRESULT hr; michael@0: michael@0: /** michael@0: * We pass in a pointer to a structure containing a pointer to the array michael@0: * containing the font data and a unique identifier. DWrite will michael@0: * internally copy what is at that pointer, and pass that to michael@0: * CreateStreamFromKey. The array will be empty when the function michael@0: * succesfully returns since it swaps out the data. michael@0: */ michael@0: ffReferenceKey key; michael@0: key.mArray = &newFontData; michael@0: nsCOMPtr uuidgen = michael@0: do_GetService("@mozilla.org/uuid-generator;1"); michael@0: if (!uuidgen) { michael@0: return nullptr; michael@0: } michael@0: michael@0: rv = uuidgen->GenerateUUIDInPlace(&key.mGUID); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()-> michael@0: CreateCustomFontFileReference(&key, michael@0: sizeof(key), michael@0: gfxDWriteFontFileLoader::Instance(), michael@0: getter_AddRefs(fontFile)); michael@0: michael@0: if (FAILED(hr)) { michael@0: NS_WARNING("Failed to create custom font file reference."); michael@0: return nullptr; michael@0: } michael@0: michael@0: BOOL isSupported; michael@0: DWRITE_FONT_FILE_TYPE fileType; michael@0: UINT32 numFaces; michael@0: michael@0: gfxDWriteFontEntry *entry = michael@0: new gfxDWriteFontEntry(uniqueName, michael@0: fontFile, michael@0: aProxyEntry->Weight(), michael@0: aProxyEntry->Stretch(), michael@0: aProxyEntry->IsItalic()); michael@0: michael@0: fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces); michael@0: if (!isSupported || numFaces > 1) { michael@0: // We don't know how to deal with 0 faces either. michael@0: delete entry; michael@0: return nullptr; michael@0: } michael@0: michael@0: return entry; michael@0: } michael@0: michael@0: #ifdef DEBUG_DWRITE_STARTUP michael@0: michael@0: #define LOGREGISTRY(msg) LogRegistryEvent(msg) michael@0: michael@0: // for use when monitoring process michael@0: static void LogRegistryEvent(const wchar_t *msg) michael@0: { michael@0: HKEY dummyKey; michael@0: HRESULT hr; michael@0: wchar_t buf[512]; michael@0: michael@0: wsprintfW(buf, L" log %s", msg); michael@0: hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &dummyKey); michael@0: if (SUCCEEDED(hr)) { michael@0: RegCloseKey(dummyKey); michael@0: } michael@0: } michael@0: #else michael@0: michael@0: #define LOGREGISTRY(msg) michael@0: michael@0: #endif michael@0: michael@0: nsresult michael@0: gfxDWriteFontList::InitFontList() michael@0: { michael@0: LOGREGISTRY(L"InitFontList start"); michael@0: michael@0: mInitialized = false; michael@0: michael@0: LARGE_INTEGER frequency; // ticks per second michael@0: LARGE_INTEGER t1, t2, t3; // ticks michael@0: double elapsedTime, upTime; michael@0: char nowTime[256], nowDate[256]; michael@0: michael@0: if (LOG_FONTINIT_ENABLED()) { michael@0: GetTimeFormat(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT, michael@0: nullptr, nullptr, nowTime, 256); michael@0: GetDateFormat(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256); michael@0: } michael@0: upTime = (double) GetTickCount(); michael@0: QueryPerformanceFrequency(&frequency); michael@0: QueryPerformanceCounter(&t1); michael@0: michael@0: HRESULT hr; michael@0: gfxFontCache *fc = gfxFontCache::GetCache(); michael@0: if (fc) { michael@0: fc->AgeAllGenerations(); michael@0: } michael@0: michael@0: mGDIFontTableAccess = Preferences::GetBool("gfx.font_rendering.directwrite.use_gdi_table_loading", false); michael@0: michael@0: gfxPlatformFontList::InitFontList(); michael@0: michael@0: mFontSubstitutes.Clear(); michael@0: mNonExistingFonts.Clear(); michael@0: michael@0: QueryPerformanceCounter(&t2); michael@0: michael@0: hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()-> michael@0: GetGdiInterop(getter_AddRefs(mGDIInterop)); michael@0: if (FAILED(hr)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: LOGREGISTRY(L"InitFontList end"); michael@0: michael@0: QueryPerformanceCounter(&t3); michael@0: michael@0: if (LOG_FONTINIT_ENABLED()) { michael@0: // determine dwrite version michael@0: nsAutoString dwriteVers; michael@0: gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers); michael@0: LOG_FONTINIT(("InitFontList\n")); michael@0: LOG_FONTINIT(("Start: %s %s\n", nowDate, nowTime)); michael@0: LOG_FONTINIT(("Uptime: %9.3f s\n", upTime/1000)); michael@0: LOG_FONTINIT(("dwrite version: %s\n", michael@0: NS_ConvertUTF16toUTF8(dwriteVers).get())); michael@0: } michael@0: michael@0: elapsedTime = (t3.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart; michael@0: Telemetry::Accumulate(Telemetry::DWRITEFONT_INITFONTLIST_TOTAL, elapsedTime); michael@0: LOG_FONTINIT(("Total time in InitFontList: %9.3f ms\n", elapsedTime)); michael@0: elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart; michael@0: Telemetry::Accumulate(Telemetry::DWRITEFONT_INITFONTLIST_INIT, elapsedTime); michael@0: LOG_FONTINIT((" --- gfxPlatformFontList init: %9.3f ms\n", elapsedTime)); michael@0: elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart; michael@0: Telemetry::Accumulate(Telemetry::DWRITEFONT_INITFONTLIST_GDI, elapsedTime); michael@0: LOG_FONTINIT((" --- GdiInterop object: %9.3f ms\n", elapsedTime)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: gfxDWriteFontList::DelayedInitFontList() michael@0: { michael@0: LOGREGISTRY(L"DelayedInitFontList start"); michael@0: michael@0: LARGE_INTEGER frequency; // ticks per second michael@0: LARGE_INTEGER t1, t2, t3; // ticks michael@0: double elapsedTime, upTime; michael@0: char nowTime[256], nowDate[256]; michael@0: michael@0: if (LOG_FONTINIT_ENABLED()) { michael@0: GetTimeFormat(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT, michael@0: nullptr, nullptr, nowTime, 256); michael@0: GetDateFormat(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256); michael@0: } michael@0: michael@0: upTime = (double) GetTickCount(); michael@0: QueryPerformanceFrequency(&frequency); michael@0: QueryPerformanceCounter(&t1); michael@0: michael@0: HRESULT hr; michael@0: michael@0: LOGREGISTRY(L"calling GetSystemFontCollection"); michael@0: nsRefPtr systemFonts; michael@0: hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()-> michael@0: GetSystemFontCollection(getter_AddRefs(systemFonts)); michael@0: NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!"); michael@0: LOGREGISTRY(L"GetSystemFontCollection done"); michael@0: michael@0: if (FAILED(hr)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: QueryPerformanceCounter(&t2); michael@0: michael@0: for (UINT32 i = 0; i < systemFonts->GetFontFamilyCount(); i++) { michael@0: nsRefPtr family; michael@0: systemFonts->GetFontFamily(i, getter_AddRefs(family)); michael@0: michael@0: nsRefPtr names; michael@0: hr = family->GetFamilyNames(getter_AddRefs(names)); michael@0: if (FAILED(hr)) { michael@0: continue; michael@0: } michael@0: michael@0: UINT32 englishIdx = 0; michael@0: michael@0: BOOL exists; michael@0: hr = names->FindLocaleName(L"en-us", &englishIdx, &exists); michael@0: if (FAILED(hr)) { michael@0: continue; michael@0: } michael@0: if (!exists) { michael@0: // Use 0 index if english is not found. michael@0: englishIdx = 0; michael@0: } michael@0: michael@0: AutoFallibleTArray enName; michael@0: UINT32 length; michael@0: michael@0: hr = names->GetStringLength(englishIdx, &length); michael@0: if (FAILED(hr)) { michael@0: continue; michael@0: } michael@0: michael@0: if (!enName.SetLength(length + 1)) { michael@0: // Eeep - running out of memory. Unlikely to end well. michael@0: continue; michael@0: } michael@0: michael@0: hr = names->GetString(englishIdx, enName.Elements(), length + 1); michael@0: if (FAILED(hr)) { michael@0: continue; michael@0: } michael@0: michael@0: nsAutoString name(enName.Elements()); michael@0: BuildKeyNameFromFontName(name); michael@0: michael@0: nsRefPtr fam; michael@0: michael@0: if (mFontFamilies.GetWeak(name)) { michael@0: continue; michael@0: } michael@0: michael@0: nsDependentString familyName(enName.Elements()); michael@0: michael@0: fam = new gfxDWriteFontFamily(familyName, family); michael@0: if (!fam) { michael@0: continue; michael@0: } michael@0: michael@0: if (mBadUnderlineFamilyNames.Contains(name)) { michael@0: fam->SetBadUnderlineFamily(); michael@0: } michael@0: mFontFamilies.Put(name, fam); michael@0: michael@0: // now add other family name localizations, if present michael@0: uint32_t nameCount = names->GetCount(); michael@0: uint32_t nameIndex; michael@0: michael@0: for (nameIndex = 0; nameIndex < nameCount; nameIndex++) { michael@0: UINT32 nameLen; michael@0: AutoFallibleTArray localizedName; michael@0: michael@0: // only add other names michael@0: if (nameIndex == englishIdx) { michael@0: continue; michael@0: } michael@0: michael@0: hr = names->GetStringLength(nameIndex, &nameLen); michael@0: if (FAILED(hr)) { michael@0: continue; michael@0: } michael@0: michael@0: if (!localizedName.SetLength(nameLen + 1)) { michael@0: continue; michael@0: } michael@0: michael@0: hr = names->GetString(nameIndex, localizedName.Elements(), michael@0: nameLen + 1); michael@0: if (FAILED(hr)) { michael@0: continue; michael@0: } michael@0: michael@0: nsDependentString locName(localizedName.Elements()); michael@0: michael@0: if (!familyName.Equals(locName)) { michael@0: AddOtherFamilyName(fam, locName); michael@0: } michael@0: michael@0: } michael@0: michael@0: // at this point, all family names have been read in michael@0: fam->SetOtherFamilyNamesInitialized(); michael@0: } michael@0: michael@0: mOtherFamilyNamesInitialized = true; michael@0: GetFontSubstitutes(); michael@0: michael@0: // bug 642093 - DirectWrite does not support old bitmap (.fon) michael@0: // font files, but a few of these such as "Courier" and "MS Sans Serif" michael@0: // are frequently specified in shoddy CSS, without appropriate fallbacks. michael@0: // By mapping these to TrueType equivalents, we provide better consistency michael@0: // with both pre-DW systems and with IE9, which appears to do the same. michael@0: GetDirectWriteSubstitutes(); michael@0: michael@0: // bug 551313 - DirectWrite creates a Gill Sans family out of michael@0: // poorly named members of the Gill Sans MT family containing michael@0: // only Ultra Bold weights. This causes big problems for pages michael@0: // using Gill Sans which is usually only available on OSX michael@0: michael@0: nsAutoString nameGillSans(L"Gill Sans"); michael@0: nsAutoString nameGillSansMT(L"Gill Sans MT"); michael@0: BuildKeyNameFromFontName(nameGillSans); michael@0: BuildKeyNameFromFontName(nameGillSansMT); michael@0: michael@0: gfxFontFamily *gillSansFamily = mFontFamilies.GetWeak(nameGillSans); michael@0: gfxFontFamily *gillSansMTFamily = mFontFamilies.GetWeak(nameGillSansMT); michael@0: michael@0: if (gillSansFamily && gillSansMTFamily) { michael@0: gillSansFamily->FindStyleVariations(); michael@0: nsTArray >& faces = gillSansFamily->GetFontList(); michael@0: uint32_t i; michael@0: michael@0: bool allUltraBold = true; michael@0: for (i = 0; i < faces.Length(); i++) { michael@0: // does the face have 'Ultra Bold' in the name? michael@0: if (faces[i]->Name().Find(NS_LITERAL_STRING("Ultra Bold")) == -1) { michael@0: allUltraBold = false; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // if all the Gill Sans faces are Ultra Bold ==> move faces michael@0: // for Gill Sans into Gill Sans MT family michael@0: if (allUltraBold) { michael@0: michael@0: // add faces to Gill Sans MT michael@0: for (i = 0; i < faces.Length(); i++) { michael@0: gillSansMTFamily->AddFontEntry(faces[i]); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (LOG_FONTLIST_ENABLED()) { michael@0: gfxFontEntry *fe = faces[i]; michael@0: LOG_FONTLIST(("(fontlist) moved (%s) to family (%s)" michael@0: " with style: %s weight: %d stretch: %d", michael@0: NS_ConvertUTF16toUTF8(fe->Name()).get(), michael@0: NS_ConvertUTF16toUTF8(gillSansMTFamily->Name()).get(), michael@0: (fe->IsItalic()) ? "italic" : "normal", michael@0: fe->Weight(), fe->Stretch())); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: // remove Gills Sans michael@0: mFontFamilies.Remove(nameGillSans); michael@0: } michael@0: } michael@0: michael@0: nsAdoptingCString classicFamilies = michael@0: Preferences::GetCString("gfx.font_rendering.cleartype_params.force_gdi_classic_for_families"); michael@0: if (classicFamilies) { michael@0: nsCCharSeparatedTokenizer tokenizer(classicFamilies, ','); michael@0: while (tokenizer.hasMoreTokens()) { michael@0: NS_ConvertUTF8toUTF16 name(tokenizer.nextToken()); michael@0: BuildKeyNameFromFontName(name); michael@0: gfxFontFamily *family = mFontFamilies.GetWeak(name); michael@0: if (family) { michael@0: static_cast(family)->SetForceGDIClassic(true); michael@0: } michael@0: } michael@0: } michael@0: mForceGDIClassicMaxFontSize = michael@0: Preferences::GetInt("gfx.font_rendering.cleartype_params.force_gdi_classic_max_size", michael@0: mForceGDIClassicMaxFontSize); michael@0: michael@0: GetPrefsAndStartLoader(); michael@0: michael@0: LOGREGISTRY(L"DelayedInitFontList end"); michael@0: michael@0: QueryPerformanceCounter(&t3); michael@0: michael@0: if (LOG_FONTINIT_ENABLED()) { michael@0: // determine dwrite version michael@0: nsAutoString dwriteVers; michael@0: gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers); michael@0: LOG_FONTINIT(("DelayedInitFontList\n")); michael@0: LOG_FONTINIT(("Start: %s %s\n", nowDate, nowTime)); michael@0: LOG_FONTINIT(("Uptime: %9.3f s\n", upTime/1000)); michael@0: LOG_FONTINIT(("dwrite version: %s\n", michael@0: NS_ConvertUTF16toUTF8(dwriteVers).get())); michael@0: } michael@0: michael@0: elapsedTime = (t3.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart; michael@0: Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_TOTAL, elapsedTime); michael@0: Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COUNT, michael@0: systemFonts->GetFontFamilyCount()); michael@0: Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_GDI_TABLE, mGDIFontTableAccess); michael@0: LOG_FONTINIT(( michael@0: "Total time in DelayedInitFontList: %9.3f ms (families: %d, %s)\n", michael@0: elapsedTime, systemFonts->GetFontFamilyCount(), michael@0: (mGDIFontTableAccess ? "gdi table access" : "dwrite table access"))); michael@0: michael@0: elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart; michael@0: Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COLLECT, elapsedTime); michael@0: LOG_FONTINIT((" --- GetSystemFontCollection: %9.3f ms\n", elapsedTime)); michael@0: michael@0: elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart; michael@0: Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_ITERATE, elapsedTime); michael@0: LOG_FONTINIT((" --- iterate over families: %9.3f ms\n", elapsedTime)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: RemoveCharsetFromFontSubstitute(nsAString &aName) michael@0: { michael@0: int32_t comma = aName.FindChar(char16_t(',')); michael@0: if (comma >= 0) michael@0: aName.Truncate(comma); michael@0: } michael@0: michael@0: #define MAX_VALUE_NAME 512 michael@0: #define MAX_VALUE_DATA 512 michael@0: michael@0: nsresult michael@0: gfxDWriteFontList::GetFontSubstitutes() michael@0: { michael@0: HKEY hKey; michael@0: DWORD i, rv, lenAlias, lenActual, valueType; michael@0: WCHAR aliasName[MAX_VALUE_NAME]; michael@0: WCHAR actualName[MAX_VALUE_DATA]; michael@0: michael@0: if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, michael@0: L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", michael@0: 0, KEY_READ, &hKey) != ERROR_SUCCESS) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) { michael@0: aliasName[0] = 0; michael@0: lenAlias = ArrayLength(aliasName); michael@0: actualName[0] = 0; michael@0: lenActual = sizeof(actualName); michael@0: rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType, michael@0: (LPBYTE)actualName, &lenActual); michael@0: michael@0: if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) { michael@0: continue; michael@0: } michael@0: michael@0: if (aliasName[0] == WCHAR('@')) { michael@0: continue; michael@0: } michael@0: michael@0: nsAutoString substituteName((char16_t*) aliasName); michael@0: nsAutoString actualFontName((char16_t*) actualName); michael@0: RemoveCharsetFromFontSubstitute(substituteName); michael@0: BuildKeyNameFromFontName(substituteName); michael@0: RemoveCharsetFromFontSubstitute(actualFontName); michael@0: BuildKeyNameFromFontName(actualFontName); michael@0: gfxFontFamily *ff; michael@0: if (!actualFontName.IsEmpty() && michael@0: (ff = mFontFamilies.GetWeak(actualFontName))) { michael@0: mFontSubstitutes.Put(substituteName, ff); michael@0: } else { michael@0: mNonExistingFonts.AppendElement(substituteName); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: struct FontSubstitution { michael@0: const WCHAR* aliasName; michael@0: const WCHAR* actualName; michael@0: }; michael@0: michael@0: static const FontSubstitution sDirectWriteSubs[] = { michael@0: { L"MS Sans Serif", L"Microsoft Sans Serif" }, michael@0: { L"MS Serif", L"Times New Roman" }, michael@0: { L"Courier", L"Courier New" }, michael@0: { L"Small Fonts", L"Arial" }, michael@0: { L"Roman", L"Times New Roman" }, michael@0: { L"Script", L"Mistral" } michael@0: }; michael@0: michael@0: void michael@0: gfxDWriteFontList::GetDirectWriteSubstitutes() michael@0: { michael@0: for (uint32_t i = 0; i < ArrayLength(sDirectWriteSubs); ++i) { michael@0: const FontSubstitution& sub(sDirectWriteSubs[i]); michael@0: nsAutoString substituteName((char16_t*)sub.aliasName); michael@0: BuildKeyNameFromFontName(substituteName); michael@0: if (nullptr != mFontFamilies.GetWeak(substituteName)) { michael@0: // don't do the substitution if user actually has a usable font michael@0: // with this name installed michael@0: continue; michael@0: } michael@0: nsAutoString actualFontName((char16_t*)sub.actualName); michael@0: BuildKeyNameFromFontName(actualFontName); michael@0: gfxFontFamily *ff; michael@0: if (nullptr != (ff = mFontFamilies.GetWeak(actualFontName))) { michael@0: mFontSubstitutes.Put(substituteName, ff); michael@0: } else { michael@0: mNonExistingFonts.AppendElement(substituteName); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: gfxDWriteFontList::GetStandardFamilyName(const nsAString& aFontName, michael@0: nsAString& aFamilyName) michael@0: { michael@0: gfxFontFamily *family = FindFamily(aFontName); michael@0: if (family) { michael@0: family->LocalizedName(aFamilyName); michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: gfxFontFamily* gfxDWriteFontList::FindFamily(const nsAString& aFamily) michael@0: { michael@0: if (!mInitialized) { michael@0: mInitialized = true; michael@0: DelayedInitFontList(); michael@0: } michael@0: michael@0: return gfxPlatformFontList::FindFamily(aFamily); michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFontList::GetFontFamilyList(nsTArray >& aFamilyArray) michael@0: { michael@0: if (!mInitialized) { michael@0: mInitialized = true; michael@0: DelayedInitFontList(); michael@0: } michael@0: michael@0: return gfxPlatformFontList::GetFontFamilyList(aFamilyArray); michael@0: } michael@0: michael@0: bool michael@0: gfxDWriteFontList::ResolveFontName(const nsAString& aFontName, michael@0: nsAString& aResolvedFontName) michael@0: { michael@0: if (!mInitialized) { michael@0: mInitialized = true; michael@0: DelayedInitFontList(); michael@0: } michael@0: michael@0: nsAutoString keyName(aFontName); michael@0: BuildKeyNameFromFontName(keyName); michael@0: michael@0: gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName); michael@0: if (ff) { michael@0: aResolvedFontName = ff->Name(); michael@0: return true; michael@0: } michael@0: michael@0: if (mNonExistingFonts.Contains(keyName)) { michael@0: return false; michael@0: } michael@0: michael@0: return gfxPlatformFontList::ResolveFontName(aFontName, aResolvedFontName); michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: michael@0: aSizes->mFontListSize += michael@0: mFontSubstitutes.SizeOfExcludingThis(SizeOfFamilyNameEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: michael@0: aSizes->mFontListSize += michael@0: mNonExistingFonts.SizeOfExcludingThis(aMallocSizeOf); michael@0: for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) { michael@0: aSizes->mFontListSize += michael@0: mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf); michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: aSizes->mFontListSize += aMallocSizeOf(this); michael@0: AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: } michael@0: michael@0: static HRESULT GetFamilyName(IDWriteFont *aFont, nsString& aFamilyName) michael@0: { michael@0: HRESULT hr; michael@0: nsRefPtr family; michael@0: michael@0: // clean out previous value michael@0: aFamilyName.Truncate(); michael@0: michael@0: hr = aFont->GetFontFamily(getter_AddRefs(family)); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: nsRefPtr familyNames; michael@0: michael@0: hr = family->GetFamilyNames(getter_AddRefs(familyNames)); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: UINT32 index = 0; michael@0: BOOL exists = false; michael@0: michael@0: hr = familyNames->FindLocaleName(L"en-us", &index, &exists); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: // If the specified locale doesn't exist, select the first on the list. michael@0: if (!exists) { michael@0: index = 0; michael@0: } michael@0: michael@0: AutoFallibleTArray name; michael@0: UINT32 length; michael@0: michael@0: hr = familyNames->GetStringLength(index, &length); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: if (!name.SetLength(length + 1)) { michael@0: return E_FAIL; michael@0: } michael@0: hr = familyNames->GetString(index, name.Elements(), length + 1); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: aFamilyName.Assign(name.Elements()); michael@0: return S_OK; michael@0: } michael@0: michael@0: // bug 705594 - the method below doesn't actually do any "drawing", it's only michael@0: // used to invoke the DirectWrite layout engine to determine the fallback font michael@0: // for a given character. michael@0: michael@0: IFACEMETHODIMP FontFallbackRenderer::DrawGlyphRun( michael@0: void* clientDrawingContext, michael@0: FLOAT baselineOriginX, michael@0: FLOAT baselineOriginY, michael@0: DWRITE_MEASURING_MODE measuringMode, michael@0: DWRITE_GLYPH_RUN const* glyphRun, michael@0: DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, michael@0: IUnknown* clientDrawingEffect michael@0: ) michael@0: { michael@0: if (!mSystemFonts) { michael@0: return E_FAIL; michael@0: } michael@0: michael@0: HRESULT hr = S_OK; michael@0: michael@0: nsRefPtr font; michael@0: hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace, michael@0: getter_AddRefs(font)); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: // copy the family name michael@0: hr = GetFamilyName(font, mFamilyName); michael@0: if (FAILED(hr)) { michael@0: return hr; michael@0: } michael@0: michael@0: // Arial is used as the default fallback font michael@0: // so if it matches ==> no font found michael@0: if (mFamilyName.EqualsLiteral("Arial")) { michael@0: mFamilyName.Truncate(); michael@0: return E_FAIL; michael@0: } michael@0: return hr; michael@0: } michael@0: michael@0: gfxFontEntry* michael@0: gfxDWriteFontList::GlobalFontFallback(const uint32_t aCh, michael@0: int32_t aRunScript, michael@0: const gfxFontStyle* aMatchStyle, michael@0: uint32_t& aCmapCount, michael@0: gfxFontFamily** aMatchedFamily) michael@0: { michael@0: bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); michael@0: michael@0: if (useCmaps) { michael@0: return gfxPlatformFontList::GlobalFontFallback(aCh, michael@0: aRunScript, michael@0: aMatchStyle, michael@0: aCmapCount, michael@0: aMatchedFamily); michael@0: } michael@0: michael@0: HRESULT hr; michael@0: michael@0: nsRefPtr dwFactory = michael@0: gfxWindowsPlatform::GetPlatform()->GetDWriteFactory(); michael@0: if (!dwFactory) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // initialize fallback renderer michael@0: if (!mFallbackRenderer) { michael@0: mFallbackRenderer = new FontFallbackRenderer(dwFactory); michael@0: } michael@0: michael@0: // initialize text format michael@0: if (!mFallbackFormat) { michael@0: hr = dwFactory->CreateTextFormat(L"Arial", nullptr, michael@0: DWRITE_FONT_WEIGHT_REGULAR, michael@0: DWRITE_FONT_STYLE_NORMAL, michael@0: DWRITE_FONT_STRETCH_NORMAL, michael@0: 72.0f, L"en-us", michael@0: getter_AddRefs(mFallbackFormat)); michael@0: if (FAILED(hr)) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: // set up string with fallback character michael@0: wchar_t str[16]; michael@0: uint32_t strLen; michael@0: michael@0: if (IS_IN_BMP(aCh)) { michael@0: str[0] = static_cast (aCh); michael@0: str[1] = 0; michael@0: strLen = 1; michael@0: } else { michael@0: str[0] = static_cast (H_SURROGATE(aCh)); michael@0: str[1] = static_cast (L_SURROGATE(aCh)); michael@0: str[2] = 0; michael@0: strLen = 2; michael@0: } michael@0: michael@0: // set up layout michael@0: nsRefPtr fallbackLayout; michael@0: michael@0: hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat, michael@0: 200.0f, 200.0f, michael@0: getter_AddRefs(fallbackLayout)); michael@0: if (FAILED(hr)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // call the draw method to invoke the DirectWrite layout functions michael@0: // which determine the fallback font michael@0: hr = fallbackLayout->Draw(nullptr, mFallbackRenderer, 50.0f, 50.0f); michael@0: if (FAILED(hr)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: gfxFontFamily *family = FindFamily(mFallbackRenderer->FallbackFamilyName()); michael@0: if (family) { michael@0: gfxFontEntry *fontEntry; michael@0: bool needsBold; // ignored in the system fallback case michael@0: fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold); michael@0: if (fontEntry && fontEntry->TestCharacterMap(aCh)) { michael@0: *aMatchedFamily = family; michael@0: return fontEntry; michael@0: } michael@0: Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, true); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: // used to load system-wide font info on off-main thread michael@0: class DirectWriteFontInfo : public FontInfoData { michael@0: public: michael@0: DirectWriteFontInfo(bool aLoadOtherNames, michael@0: bool aLoadFaceNames, michael@0: bool aLoadCmaps) : michael@0: FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) michael@0: {} michael@0: michael@0: virtual ~DirectWriteFontInfo() {} michael@0: michael@0: // loads font data for all members of a given family michael@0: virtual void LoadFontFamilyData(const nsAString& aFamilyName); michael@0: michael@0: nsRefPtr mSystemFonts; michael@0: }; michael@0: michael@0: void michael@0: DirectWriteFontInfo::LoadFontFamilyData(const nsAString& aFamilyName) michael@0: { michael@0: // lookup the family michael@0: nsAutoTArray famName; michael@0: michael@0: uint32_t len = aFamilyName.Length(); michael@0: famName.SetLength(len + 1); michael@0: memcpy(famName.Elements(), aFamilyName.BeginReading(), len * sizeof(char16_t)); michael@0: famName[len] = 0; michael@0: michael@0: HRESULT hr; michael@0: BOOL exists = false; michael@0: michael@0: uint32_t index; michael@0: hr = mSystemFonts->FindFamilyName(famName.Elements(), &index, &exists); michael@0: if (FAILED(hr) || !exists) { michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr family; michael@0: mSystemFonts->GetFontFamily(index, getter_AddRefs(family)); michael@0: if (!family) { michael@0: return; michael@0: } michael@0: michael@0: // later versions of DirectWrite support querying the fullname/psname michael@0: bool loadFaceNamesUsingDirectWrite = mLoadFaceNames; michael@0: michael@0: for (uint32_t i = 0; i < family->GetFontCount(); i++) { michael@0: // get the font michael@0: nsRefPtr dwFont; michael@0: hr = family->GetFont(i, getter_AddRefs(dwFont)); michael@0: if (FAILED(hr)) { michael@0: // This should never happen. michael@0: NS_WARNING("Failed to get existing font from family."); michael@0: continue; michael@0: } michael@0: michael@0: if (dwFont->GetSimulations() & DWRITE_FONT_SIMULATIONS_OBLIQUE) { michael@0: // We don't want these. michael@0: continue; michael@0: } michael@0: michael@0: mLoadStats.fonts++; michael@0: michael@0: // get the name of the face michael@0: nsString fullID(aFamilyName); michael@0: nsAutoString fontName; michael@0: hr = GetDirectWriteFontName(dwFont, fontName); michael@0: if (FAILED(hr)) { michael@0: continue; michael@0: } michael@0: fullID.Append(NS_LITERAL_STRING(" ")); michael@0: fullID.Append(fontName); michael@0: michael@0: FontFaceData fontData; michael@0: bool haveData = true; michael@0: nsRefPtr dwFontFace; michael@0: michael@0: if (mLoadFaceNames) { michael@0: // try to load using DirectWrite first michael@0: if (loadFaceNamesUsingDirectWrite) { michael@0: hr = GetDirectWriteFaceName(dwFont, PSNAME_ID, fontData.mPostscriptName); michael@0: if (FAILED(hr)) { michael@0: loadFaceNamesUsingDirectWrite = false; michael@0: } michael@0: hr = GetDirectWriteFaceName(dwFont, FULLNAME_ID, fontData.mFullName); michael@0: if (FAILED(hr)) { michael@0: loadFaceNamesUsingDirectWrite = false; michael@0: } michael@0: } michael@0: michael@0: // if DirectWrite read fails, load directly from name table michael@0: if (!loadFaceNamesUsingDirectWrite) { michael@0: hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace)); michael@0: if (SUCCEEDED(hr)) { michael@0: uint32_t kNAME = michael@0: NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e')); michael@0: const char *nameData; michael@0: BOOL exists; michael@0: void* ctx; michael@0: uint32_t nameSize; michael@0: michael@0: hr = dwFontFace->TryGetFontTable( michael@0: kNAME, michael@0: (const void**)&nameData, &nameSize, &ctx, &exists); michael@0: michael@0: if (SUCCEEDED(hr) && nameData && nameSize > 0) { michael@0: gfxFontUtils::ReadCanonicalName(nameData, nameSize, michael@0: gfxFontUtils::NAME_ID_FULL, michael@0: fontData.mFullName); michael@0: gfxFontUtils::ReadCanonicalName(nameData, nameSize, michael@0: gfxFontUtils::NAME_ID_POSTSCRIPT, michael@0: fontData.mPostscriptName); michael@0: dwFontFace->ReleaseFontTable(ctx); michael@0: } michael@0: } michael@0: } michael@0: michael@0: haveData = !fontData.mPostscriptName.IsEmpty() || michael@0: !fontData.mFullName.IsEmpty(); michael@0: if (haveData) { michael@0: mLoadStats.facenames++; michael@0: } michael@0: } michael@0: michael@0: // cmaps michael@0: if (mLoadCmaps) { michael@0: if (!dwFontFace) { michael@0: hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace)); michael@0: if (!SUCCEEDED(hr)) { michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: uint32_t kCMAP = michael@0: NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p')); michael@0: const uint8_t *cmapData; michael@0: BOOL exists; michael@0: void* ctx; michael@0: uint32_t cmapSize; michael@0: michael@0: hr = dwFontFace->TryGetFontTable(kCMAP, michael@0: (const void**)&cmapData, &cmapSize, &ctx, &exists); michael@0: michael@0: if (SUCCEEDED(hr)) { michael@0: bool cmapLoaded = false; michael@0: bool unicodeFont = false, symbolFont = false; michael@0: nsRefPtr charmap = new gfxCharacterMap(); michael@0: uint32_t offset; michael@0: michael@0: if (cmapData && michael@0: cmapSize > 0 && michael@0: NS_SUCCEEDED( michael@0: gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap, michael@0: offset, unicodeFont, symbolFont))) { michael@0: fontData.mCharacterMap = charmap; michael@0: fontData.mUVSOffset = offset; michael@0: fontData.mSymbolFont = symbolFont; michael@0: cmapLoaded = true; michael@0: mLoadStats.cmaps++; michael@0: } michael@0: dwFontFace->ReleaseFontTable(ctx); michael@0: haveData = haveData || cmapLoaded; michael@0: } michael@0: } michael@0: michael@0: // if have data, load michael@0: if (haveData) { michael@0: mFontFaceData.Put(fullID, fontData); michael@0: } michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: gfxDWriteFontList::CreateFontInfoData() michael@0: { michael@0: bool loadCmaps = !UsesSystemFallback() || michael@0: gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); michael@0: michael@0: nsRefPtr fi = michael@0: new DirectWriteFontInfo(false, NeedFullnamePostscriptNames(), loadCmaps); michael@0: gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()-> michael@0: GetSystemFontCollection(getter_AddRefs(fi->mSystemFonts)); michael@0: michael@0: return fi.forget(); michael@0: }