diff options
Diffstat (limited to 'gfx/thebes/gfxDWriteFontList.cpp')
-rw-r--r-- | gfx/thebes/gfxDWriteFontList.cpp | 1838 |
1 files changed, 1838 insertions, 0 deletions
diff --git a/gfx/thebes/gfxDWriteFontList.cpp b/gfx/thebes/gfxDWriteFontList.cpp new file mode 100644 index 000000000..d159fddb1 --- /dev/null +++ b/gfx/thebes/gfxDWriteFontList.cpp @@ -0,0 +1,1838 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/MemoryReporting.h" + +#include "gfxDWriteFontList.h" +#include "gfxDWriteFonts.h" +#include "nsUnicharUtils.h" +#include "nsILocaleService.h" +#include "nsServiceManagerUtils.h" +#include "nsCharSeparatedTokenizer.h" +#include "mozilla/Preferences.h" +#include "mozilla/Sprintf.h" +#include "mozilla/Telemetry.h" +#include "nsDirectoryServiceUtils.h" +#include "nsDirectoryServiceDefs.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsISimpleEnumerator.h" + +#include "gfxGDIFontList.h" + +#include "nsIWindowsRegKey.h" + +#include "harfbuzz/hb.h" + +using namespace mozilla; + +#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ + LogLevel::Debug, args) +#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \ + gfxPlatform::GetLog(eGfxLog_fontlist), \ + LogLevel::Debug) + +#define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \ + LogLevel::Debug, args) +#define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \ + gfxPlatform::GetLog(eGfxLog_fontinit), \ + LogLevel::Debug) + +#define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \ + gfxPlatform::GetLog(eGfxLog_cmapdata), \ + LogLevel::Debug) + +static __inline void +BuildKeyNameFromFontName(nsAString &aName) +{ + if (aName.Length() >= LF_FACESIZE) + aName.Truncate(LF_FACESIZE - 1); + ToLowerCase(aName); +} + +//////////////////////////////////////////////////////////////////////////////// +// gfxDWriteFontFamily + +gfxDWriteFontFamily::~gfxDWriteFontFamily() +{ +} + +static HRESULT +GetDirectWriteFontName(IDWriteFont *aFont, nsAString& aFontName) +{ + HRESULT hr; + + RefPtr<IDWriteLocalizedStrings> names; + hr = aFont->GetFaceNames(getter_AddRefs(names)); + if (FAILED(hr)) { + return hr; + } + + BOOL exists; + AutoTArray<wchar_t,32> faceName; + UINT32 englishIdx = 0; + hr = names->FindLocaleName(L"en-us", &englishIdx, &exists); + if (FAILED(hr)) { + return hr; + } + + if (!exists) { + // No english found, use whatever is first in the list. + englishIdx = 0; + } + UINT32 length; + hr = names->GetStringLength(englishIdx, &length); + if (FAILED(hr)) { + return hr; + } + faceName.SetLength(length + 1); + hr = names->GetString(englishIdx, faceName.Elements(), length + 1); + if (FAILED(hr)) { + return hr; + } + + aFontName.Assign(faceName.Elements()); + return S_OK; +} + +#define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_FULL_NAME +#define PSNAME_ID DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME + +// for use in reading postscript or fullname +static HRESULT +GetDirectWriteFaceName(IDWriteFont *aFont, + DWRITE_INFORMATIONAL_STRING_ID aWhichName, + nsAString& aFontName) +{ + HRESULT hr; + + BOOL exists; + RefPtr<IDWriteLocalizedStrings> infostrings; + hr = aFont->GetInformationalStrings(aWhichName, getter_AddRefs(infostrings), &exists); + if (FAILED(hr) || !exists) { + return E_FAIL; + } + + AutoTArray<wchar_t,32> faceName; + UINT32 englishIdx = 0; + hr = infostrings->FindLocaleName(L"en-us", &englishIdx, &exists); + if (FAILED(hr)) { + return hr; + } + + if (!exists) { + // No english found, use whatever is first in the list. + englishIdx = 0; + } + UINT32 length; + hr = infostrings->GetStringLength(englishIdx, &length); + if (FAILED(hr)) { + return hr; + } + faceName.SetLength(length + 1); + hr = infostrings->GetString(englishIdx, faceName.Elements(), length + 1); + if (FAILED(hr)) { + return hr; + } + + aFontName.Assign(faceName.Elements()); + return S_OK; +} + +void +gfxDWriteFontFamily::FindStyleVariations(FontInfoData *aFontInfoData) +{ + HRESULT hr; + if (mHasStyles) { + return; + } + mHasStyles = true; + + gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList(); + + bool skipFaceNames = mFaceNamesInitialized || + !fp->NeedFullnamePostscriptNames(); + bool fontInfoShouldHaveFaceNames = !mFaceNamesInitialized && + fp->NeedFullnamePostscriptNames() && + aFontInfoData; + + for (UINT32 i = 0; i < mDWFamily->GetFontCount(); i++) { + RefPtr<IDWriteFont> font; + hr = mDWFamily->GetFont(i, getter_AddRefs(font)); + if (FAILED(hr)) { + // This should never happen. + NS_WARNING("Failed to get existing font from family."); + continue; + } + + if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) { + // We don't want these in the font list; we'll apply simulations + // on the fly when appropriate. + continue; + } + + // name + nsString fullID(mName); + nsAutoString faceName; + hr = GetDirectWriteFontName(font, faceName); + if (FAILED(hr)) { + continue; + } + fullID.Append(' '); + fullID.Append(faceName); + + gfxDWriteFontEntry *fe = new gfxDWriteFontEntry(fullID, font); + fe->SetForceGDIClassic(mForceGDIClassic); + AddFontEntry(fe); + + // postscript/fullname if needed + nsAutoString psname, fullname; + if (fontInfoShouldHaveFaceNames) { + aFontInfoData->GetFaceNames(fe->Name(), fullname, psname); + if (!fullname.IsEmpty()) { + fp->AddFullname(fe, fullname); + } + if (!psname.IsEmpty()) { + fp->AddPostscriptName(fe, psname); + } + } else if (!skipFaceNames) { + hr = GetDirectWriteFaceName(font, PSNAME_ID, psname); + if (FAILED(hr)) { + skipFaceNames = true; + } else if (psname.Length() > 0) { + fp->AddPostscriptName(fe, psname); + } + + hr = GetDirectWriteFaceName(font, FULLNAME_ID, fullname); + if (FAILED(hr)) { + skipFaceNames = true; + } else if (fullname.Length() > 0) { + fp->AddFullname(fe, fullname); + } + } + + if (LOG_FONTLIST_ENABLED()) { + LOG_FONTLIST(("(fontlist) added (%s) to family (%s)" + " with style: %s weight: %d stretch: %d psname: %s fullname: %s", + NS_ConvertUTF16toUTF8(fe->Name()).get(), + NS_ConvertUTF16toUTF8(Name()).get(), + (fe->IsItalic()) ? + "italic" : (fe->IsOblique() ? "oblique" : "normal"), + fe->Weight(), fe->Stretch(), + NS_ConvertUTF16toUTF8(psname).get(), + NS_ConvertUTF16toUTF8(fullname).get())); + } + } + + // assume that if no error, all postscript/fullnames were initialized + if (!skipFaceNames) { + mFaceNamesInitialized = true; + } + + if (!mAvailableFonts.Length()) { + NS_WARNING("Family with no font faces in it."); + } + + if (mIsBadUnderlineFamily) { + SetBadUnderlineFonts(); + } +} + +void +gfxDWriteFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList, + bool aNeedFullnamePostscriptNames, + FontInfoData *aFontInfoData) +{ + // if all needed names have already been read, skip + if (mOtherFamilyNamesInitialized && + (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) { + return; + } + + // If we've been passed a FontInfoData, we skip the DWrite implementation + // here and fall back to the generic code which will use that info. + if (!aFontInfoData) { + // DirectWrite version of this will try to read + // postscript/fullnames via DirectWrite API + FindStyleVariations(); + } + + // fallback to looking up via name table + if (!mOtherFamilyNamesInitialized || !mFaceNamesInitialized) { + gfxFontFamily::ReadFaceNames(aPlatformFontList, + aNeedFullnamePostscriptNames, + aFontInfoData); + } +} + +void +gfxDWriteFontFamily::LocalizedName(nsAString &aLocalizedName) +{ + aLocalizedName.AssignLiteral("Unknown Font"); + HRESULT hr; + nsresult rv; + nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID, + &rv); + nsCOMPtr<nsILocale> locale; + rv = ls->GetApplicationLocale(getter_AddRefs(locale)); + nsString localeName; + if (NS_SUCCEEDED(rv)) { + rv = locale->GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), + localeName); + } + if (NS_FAILED(rv)) { + localeName.AssignLiteral("en-us"); + } + + RefPtr<IDWriteLocalizedStrings> names; + + hr = mDWFamily->GetFamilyNames(getter_AddRefs(names)); + if (FAILED(hr)) { + return; + } + UINT32 idx = 0; + BOOL exists; + hr = names->FindLocaleName(localeName.get(), + &idx, + &exists); + if (FAILED(hr)) { + return; + } + if (!exists) { + // Use english is localized is not found. + hr = names->FindLocaleName(L"en-us", &idx, &exists); + if (FAILED(hr)) { + return; + } + if (!exists) { + // Use 0 index if english is not found. + idx = 0; + } + } + AutoTArray<WCHAR, 32> famName; + UINT32 length; + + hr = names->GetStringLength(idx, &length); + if (FAILED(hr)) { + return; + } + + if (!famName.SetLength(length + 1, fallible)) { + // Eeep - running out of memory. Unlikely to end well. + return; + } + + hr = names->GetString(idx, famName.Elements(), length + 1); + if (FAILED(hr)) { + return; + } + + aLocalizedName = nsDependentString(famName.Elements()); +} + +void +gfxDWriteFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const +{ + gfxFontFamily::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); + // TODO: + // This doesn't currently account for |mDWFamily| +} + +void +gfxDWriteFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const +{ + aSizes->mFontListSize += aMallocSizeOf(this); + AddSizeOfExcludingThis(aMallocSizeOf, aSizes); +} + +//////////////////////////////////////////////////////////////////////////////// +// gfxDWriteFontEntry + +gfxDWriteFontEntry::~gfxDWriteFontEntry() +{ +} + +bool +gfxDWriteFontEntry::IsSymbolFont() +{ + if (mFont) { + return mFont->IsSymbolFont(); + } else { + return false; + } +} + +static bool +UsingArabicOrHebrewScriptSystemLocale() +{ + LANGID langid = PRIMARYLANGID(::GetSystemDefaultLangID()); + switch (langid) { + case LANG_ARABIC: + case LANG_DARI: + case LANG_PASHTO: + case LANG_PERSIAN: + case LANG_SINDHI: + case LANG_UIGHUR: + case LANG_URDU: + case LANG_HEBREW: + return true; + default: + return false; + } +} + +nsresult +gfxDWriteFontEntry::CopyFontTable(uint32_t aTableTag, + nsTArray<uint8_t> &aBuffer) +{ + gfxDWriteFontList *pFontList = gfxDWriteFontList::PlatformFontList(); + const uint32_t tagBE = NativeEndian::swapToBigEndian(aTableTag); + + // Don't use GDI table loading for symbol fonts or for + // italic fonts in Arabic-script system locales because of + // potential cmap discrepancies, see bug 629386. + // Ditto for Hebrew, bug 837498. + if (mFont && pFontList->UseGDIFontTableAccess() && + !(mStyle && UsingArabicOrHebrewScriptSystemLocale()) && + !mFont->IsSymbolFont()) + { + LOGFONTW logfont = { 0 }; + if (InitLogFont(mFont, &logfont)) { + AutoDC dc; + AutoSelectFont font(dc.GetDC(), &logfont); + if (font.IsValid()) { + uint32_t tableSize = + ::GetFontData(dc.GetDC(), tagBE, 0, nullptr, 0); + if (tableSize != GDI_ERROR) { + if (aBuffer.SetLength(tableSize, fallible)) { + ::GetFontData(dc.GetDC(), tagBE, 0, + aBuffer.Elements(), aBuffer.Length()); + return NS_OK; + } + return NS_ERROR_OUT_OF_MEMORY; + } + } + } + } + + RefPtr<IDWriteFontFace> fontFace; + nsresult rv = CreateFontFace(getter_AddRefs(fontFace)); + if (NS_FAILED(rv)) { + return rv; + } + + uint8_t *tableData; + uint32_t len; + void *tableContext = nullptr; + BOOL exists; + HRESULT hr = + fontFace->TryGetFontTable(tagBE, (const void**)&tableData, &len, + &tableContext, &exists); + if (FAILED(hr) || !exists) { + return NS_ERROR_FAILURE; + } + + if (aBuffer.SetLength(len, fallible)) { + memcpy(aBuffer.Elements(), tableData, len); + rv = NS_OK; + } else { + rv = NS_ERROR_OUT_OF_MEMORY; + } + + if (tableContext) { + fontFace->ReleaseFontTable(&tableContext); + } + + return rv; +} + +// Access to font tables packaged in hb_blob_t form + +// object attached to the Harfbuzz blob, used to release +// the table when the blob is destroyed +class FontTableRec { +public: + FontTableRec(IDWriteFontFace *aFontFace, void *aContext) + : mFontFace(aFontFace), mContext(aContext) + { + MOZ_COUNT_CTOR(FontTableRec); + } + + ~FontTableRec() { + MOZ_COUNT_DTOR(FontTableRec); + mFontFace->ReleaseFontTable(mContext); + } + +private: + RefPtr<IDWriteFontFace> mFontFace; + void *mContext; +}; + +static void +DestroyBlobFunc(void* aUserData) +{ + FontTableRec *ftr = static_cast<FontTableRec*>(aUserData); + delete ftr; +} + +hb_blob_t * +gfxDWriteFontEntry::GetFontTable(uint32_t aTag) +{ + // try to avoid potentially expensive DWrite call if we haven't actually + // created the font face yet, by using the gfxFontEntry method that will + // use CopyFontTable and then cache the data + if (!mFontFace) { + return gfxFontEntry::GetFontTable(aTag); + } + + const void *data; + UINT32 size; + void *context; + BOOL exists; + HRESULT hr = mFontFace->TryGetFontTable(NativeEndian::swapToBigEndian(aTag), + &data, &size, &context, &exists); + if (SUCCEEDED(hr) && exists) { + FontTableRec *ftr = new FontTableRec(mFontFace, context); + return hb_blob_create(static_cast<const char*>(data), size, + HB_MEMORY_MODE_READONLY, + ftr, DestroyBlobFunc); + } + + return nullptr; +} + +nsresult +gfxDWriteFontEntry::ReadCMAP(FontInfoData *aFontInfoData) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); + + // attempt this once, if errors occur leave a blank cmap + if (mCharacterMap) { + return NS_OK; + } + + RefPtr<gfxCharacterMap> charmap; + nsresult rv; + bool symbolFont; + + if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, + mUVSOffset, + symbolFont))) { + rv = NS_OK; + } else { + uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); + charmap = new gfxCharacterMap(); + AutoTable cmapTable(this, kCMAP); + + if (cmapTable) { + bool unicodeFont = false, symbolFont = false; // currently ignored + uint32_t cmapLen; + const uint8_t* cmapData = + reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable, + &cmapLen)); + rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, + *charmap, mUVSOffset, + unicodeFont, symbolFont); + } else { + rv = NS_ERROR_NOT_AVAILABLE; + } + } + + mHasCmapTable = NS_SUCCEEDED(rv); + if (mHasCmapTable) { + // Bug 969504: exclude U+25B6 from Segoe UI family, because it's used + // by sites to represent a "Play" icon, but the glyph in Segoe UI Light + // and Semibold on Windows 7 is too thin. (Ditto for leftward U+25C0.) + // Fallback to Segoe UI Symbol is preferred. + if (FamilyName().EqualsLiteral("Segoe UI")) { + charmap->clear(0x25b6); + charmap->clear(0x25c0); + } + gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); + mCharacterMap = pfl->FindCharMap(charmap); + } else { + // if error occurred, initialize to null cmap + mCharacterMap = new gfxCharacterMap(); + } + + LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n", + NS_ConvertUTF16toUTF8(mName).get(), + charmap->SizeOfIncludingThis(moz_malloc_size_of), + charmap->mHash, mCharacterMap == charmap ? " new" : "")); + if (LOG_CMAPDATA_ENABLED()) { + char prefix[256]; + SprintfLiteral(prefix, "(cmapdata) name: %.220s", + NS_ConvertUTF16toUTF8(mName).get()); + charmap->Dump(prefix, eGfxLog_cmapdata); + } + + return rv; +} + +gfxFont * +gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, + bool aNeedsBold) +{ + return new gfxDWriteFont(this, aFontStyle, aNeedsBold); +} + +nsresult +gfxDWriteFontEntry::CreateFontFace(IDWriteFontFace **aFontFace, + DWRITE_FONT_SIMULATIONS aSimulations) +{ + // initialize mFontFace if this hasn't been done before + if (!mFontFace) { + HRESULT hr; + if (mFont) { + hr = mFont->CreateFontFace(getter_AddRefs(mFontFace)); + } else if (mFontFile) { + IDWriteFontFile *fontFile = mFontFile.get(); + hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()-> + CreateFontFace(mFaceType, + 1, + &fontFile, + 0, + DWRITE_FONT_SIMULATIONS_NONE, + getter_AddRefs(mFontFace)); + } else { + NS_NOTREACHED("invalid font entry"); + return NS_ERROR_FAILURE; + } + if (FAILED(hr)) { + return NS_ERROR_FAILURE; + } + } + + // check whether we need to add a DWrite simulated style + if ((aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) && + !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD)) { + // if so, we need to return not mFontFace itself but a version that + // has the Bold simulation - unfortunately, DWrite doesn't provide + // a simple API for this + UINT32 numberOfFiles = 0; + if (FAILED(mFontFace->GetFiles(&numberOfFiles, nullptr))) { + return NS_ERROR_FAILURE; + } + AutoTArray<IDWriteFontFile*,1> files; + files.AppendElements(numberOfFiles); + if (FAILED(mFontFace->GetFiles(&numberOfFiles, files.Elements()))) { + return NS_ERROR_FAILURE; + } + HRESULT hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()-> + CreateFontFace(mFontFace->GetType(), + numberOfFiles, + files.Elements(), + mFontFace->GetIndex(), + aSimulations, + aFontFace); + for (UINT32 i = 0; i < numberOfFiles; ++i) { + files[i]->Release(); + } + return FAILED(hr) ? NS_ERROR_FAILURE : NS_OK; + } + + // no simulation: we can just add a reference to mFontFace and return that + *aFontFace = mFontFace; + (*aFontFace)->AddRef(); + return NS_OK; +} + +bool +gfxDWriteFontEntry::InitLogFont(IDWriteFont *aFont, LOGFONTW *aLogFont) +{ + HRESULT hr; + + BOOL isInSystemCollection; + IDWriteGdiInterop *gdi = + gfxDWriteFontList::PlatformFontList()->GetGDIInterop(); + hr = gdi->ConvertFontToLOGFONT(aFont, aLogFont, &isInSystemCollection); + // If the font is not in the system collection, GDI will be unable to + // select it and load its tables, so we return false here to indicate + // failure, and let CopyFontTable fall back to DWrite native methods. + return (SUCCEEDED(hr) && isInSystemCollection); +} + +bool +gfxDWriteFontEntry::IsCJKFont() +{ + if (mIsCJK != UNINITIALIZED_VALUE) { + return mIsCJK; + } + + mIsCJK = false; + + const uint32_t kOS2Tag = TRUETYPE_TAG('O','S','/','2'); + AutoTArray<uint8_t, 128> buffer; + if (CopyFontTable(kOS2Tag, buffer) != NS_OK) { + return mIsCJK; + } + + // ulCodePageRange bit definitions for the CJK codepages, + // from http://www.microsoft.com/typography/otspec/os2.htm#cpr + const uint32_t CJK_CODEPAGE_BITS = + (1 << 17) | // codepage 932 - JIS/Japan + (1 << 18) | // codepage 936 - Chinese (simplified) + (1 << 19) | // codepage 949 - Korean Wansung + (1 << 20) | // codepage 950 - Chinese (traditional) + (1 << 21); // codepage 1361 - Korean Johab + + if (buffer.Length() >= offsetof(OS2Table, sxHeight)) { + const OS2Table* os2 = + reinterpret_cast<const OS2Table*>(buffer.Elements()); + if ((uint32_t(os2->codePageRange1) & CJK_CODEPAGE_BITS) != 0) { + mIsCJK = true; + } + } + + return mIsCJK; +} + +void +gfxDWriteFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const +{ + gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); + // TODO: + // This doesn't currently account for the |mFont| and |mFontFile| members +} + +void +gfxDWriteFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const +{ + aSizes->mFontListSize += aMallocSizeOf(this); + AddSizeOfExcludingThis(aMallocSizeOf, aSizes); +} + +//////////////////////////////////////////////////////////////////////////////// +// gfxDWriteFontList + +gfxDWriteFontList::gfxDWriteFontList() + : mForceGDIClassicMaxFontSize(0.0) +{ +} + +// bug 602792 - CJK systems default to large CJK fonts which cause excessive +// I/O strain during cold startup due to dwrite caching bugs. Default to +// Arial to avoid this. + +gfxFontFamily * +gfxDWriteFontList::GetDefaultFontForPlatform(const gfxFontStyle *aStyle) +{ + nsAutoString resolvedName; + + // try Arial first + gfxFontFamily *ff; + if ((ff = FindFamily(NS_LITERAL_STRING("Arial")))) { + return ff; + } + + // otherwise, use local default + NONCLIENTMETRICSW ncm; + ncm.cbSize = sizeof(ncm); + BOOL status = ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, + sizeof(ncm), &ncm, 0); + + if (status) { + ff = FindFamily(nsDependentString(ncm.lfMessageFont.lfFaceName)); + if (ff) { + return ff; + } + } + + return nullptr; +} + +gfxFontEntry * +gfxDWriteFontList::LookupLocalFont(const nsAString& aFontName, + uint16_t aWeight, + int16_t aStretch, + uint8_t aStyle) +{ + gfxFontEntry *lookup; + + lookup = LookupInFaceNameLists(aFontName); + if (!lookup) { + return nullptr; + } + + gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup); + gfxDWriteFontEntry *fe = + new gfxDWriteFontEntry(lookup->Name(), + dwriteLookup->mFont, + aWeight, + aStretch, + aStyle); + fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic()); + return fe; +} + +gfxFontEntry * +gfxDWriteFontList::MakePlatformFont(const nsAString& aFontName, + uint16_t aWeight, + int16_t aStretch, + uint8_t aStyle, + const uint8_t* aFontData, + uint32_t aLength) +{ + nsresult rv; + nsAutoString uniqueName; + rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName); + if (NS_FAILED(rv)) { + free((void*)aFontData); + return nullptr; + } + + FallibleTArray<uint8_t> newFontData; + + rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData); + free((void*)aFontData); + + if (NS_FAILED(rv)) { + return nullptr; + } + + RefPtr<IDWriteFontFileStream> fontFileStream; + RefPtr<IDWriteFontFile> fontFile; + HRESULT hr = + gfxDWriteFontFileLoader::CreateCustomFontFile(newFontData, + getter_AddRefs(fontFile), + getter_AddRefs(fontFileStream)); + + if (FAILED(hr)) { + NS_WARNING("Failed to create custom font file reference."); + return nullptr; + } + + BOOL isSupported; + DWRITE_FONT_FILE_TYPE fileType; + UINT32 numFaces; + + gfxDWriteFontEntry *entry = + new gfxDWriteFontEntry(uniqueName, + fontFile, + fontFileStream, + aWeight, + aStretch, + aStyle); + + fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces); + if (!isSupported || numFaces > 1) { + // We don't know how to deal with 0 faces either. + delete entry; + return nullptr; + } + + return entry; +} + +enum DWriteInitError { + errGDIInterop = 1, + errSystemFontCollection = 2, + errNoFonts = 3 +}; + +nsresult +gfxDWriteFontList::InitFontListForPlatform() +{ + LARGE_INTEGER frequency; // ticks per second + LARGE_INTEGER t1, t2, t3, t4, t5; // ticks + double elapsedTime, upTime; + char nowTime[256], nowDate[256]; + + if (LOG_FONTINIT_ENABLED()) { + GetTimeFormatA(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT, + nullptr, nullptr, nowTime, 256); + GetDateFormatA(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256); + upTime = (double) GetTickCount(); + } + QueryPerformanceFrequency(&frequency); + QueryPerformanceCounter(&t1); // start + + HRESULT hr; + mGDIFontTableAccess = + Preferences::GetBool("gfx.font_rendering.directwrite.use_gdi_table_loading", + false); + + mFontSubstitutes.Clear(); + mNonExistingFonts.Clear(); + + hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()-> + GetGdiInterop(getter_AddRefs(mGDIInterop)); + if (FAILED(hr)) { + Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM, + uint32_t(errGDIInterop)); + return NS_ERROR_FAILURE; + } + + QueryPerformanceCounter(&t2); // base-class/interop initialization + + RefPtr<IDWriteFactory> factory = + gfxWindowsPlatform::GetPlatform()->GetDWriteFactory(); + + hr = factory->GetSystemFontCollection(getter_AddRefs(mSystemFonts)); + NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!"); + + if (FAILED(hr)) { + Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM, + uint32_t(errSystemFontCollection)); + return NS_ERROR_FAILURE; + } + + QueryPerformanceCounter(&t3); // system font collection + + GetFontsFromCollection(mSystemFonts); + + // if no fonts found, something is out of whack, bail and use GDI backend + NS_ASSERTION(mFontFamilies.Count() != 0, + "no fonts found in the system fontlist -- holy crap batman!"); + if (mFontFamilies.Count() == 0) { + Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM, + uint32_t(errNoFonts)); + return NS_ERROR_FAILURE; + } + + QueryPerformanceCounter(&t4); // iterate over system fonts + +#ifdef MOZ_BUNDLED_FONTS + mBundledFonts = CreateBundledFontsCollection(factory); + if (mBundledFonts) { + GetFontsFromCollection(mBundledFonts); + } +#endif + + mOtherFamilyNamesInitialized = true; + GetFontSubstitutes(); + + // bug 642093 - DirectWrite does not support old bitmap (.fon) + // font files, but a few of these such as "Courier" and "MS Sans Serif" + // are frequently specified in shoddy CSS, without appropriate fallbacks. + // By mapping these to TrueType equivalents, we provide better consistency + // with both pre-DW systems and with IE9, which appears to do the same. + GetDirectWriteSubstitutes(); + + // bug 551313 - DirectWrite creates a Gill Sans family out of + // poorly named members of the Gill Sans MT family containing + // only Ultra Bold weights. This causes big problems for pages + // using Gill Sans which is usually only available on OSX + + nsAutoString nameGillSans(L"Gill Sans"); + nsAutoString nameGillSansMT(L"Gill Sans MT"); + BuildKeyNameFromFontName(nameGillSans); + BuildKeyNameFromFontName(nameGillSansMT); + + gfxFontFamily *gillSansFamily = mFontFamilies.GetWeak(nameGillSans); + gfxFontFamily *gillSansMTFamily = mFontFamilies.GetWeak(nameGillSansMT); + + if (gillSansFamily && gillSansMTFamily) { + gillSansFamily->FindStyleVariations(); + nsTArray<RefPtr<gfxFontEntry> >& faces = gillSansFamily->GetFontList(); + uint32_t i; + + bool allUltraBold = true; + for (i = 0; i < faces.Length(); i++) { + // does the face have 'Ultra Bold' in the name? + if (faces[i]->Name().Find(NS_LITERAL_STRING("Ultra Bold")) == -1) { + allUltraBold = false; + break; + } + } + + // if all the Gill Sans faces are Ultra Bold ==> move faces + // for Gill Sans into Gill Sans MT family + if (allUltraBold) { + + // add faces to Gill Sans MT + for (i = 0; i < faces.Length(); i++) { + // change the entry's family name to match its adoptive family + faces[i]->mFamilyName = gillSansMTFamily->Name(); + gillSansMTFamily->AddFontEntry(faces[i]); + + if (LOG_FONTLIST_ENABLED()) { + gfxFontEntry *fe = faces[i]; + LOG_FONTLIST(("(fontlist) moved (%s) to family (%s)" + " with style: %s weight: %d stretch: %d", + NS_ConvertUTF16toUTF8(fe->Name()).get(), + NS_ConvertUTF16toUTF8(gillSansMTFamily->Name()).get(), + (fe->IsItalic()) ? + "italic" : (fe->IsOblique() ? "oblique" : "normal"), + fe->Weight(), fe->Stretch())); + } + } + + // remove Gills Sans + mFontFamilies.Remove(nameGillSans); + } + } + + nsAdoptingCString classicFamilies = + Preferences::GetCString("gfx.font_rendering.cleartype_params.force_gdi_classic_for_families"); + if (classicFamilies) { + nsCCharSeparatedTokenizer tokenizer(classicFamilies, ','); + while (tokenizer.hasMoreTokens()) { + NS_ConvertUTF8toUTF16 name(tokenizer.nextToken()); + BuildKeyNameFromFontName(name); + gfxFontFamily *family = mFontFamilies.GetWeak(name); + if (family) { + static_cast<gfxDWriteFontFamily*>(family)->SetForceGDIClassic(true); + } + } + } + mForceGDIClassicMaxFontSize = + Preferences::GetInt("gfx.font_rendering.cleartype_params.force_gdi_classic_max_size", + mForceGDIClassicMaxFontSize); + + GetPrefsAndStartLoader(); + + QueryPerformanceCounter(&t5); // misc initialization + + if (LOG_FONTINIT_ENABLED()) { + // determine dwrite version + nsAutoString dwriteVers; + gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers); + LOG_FONTINIT(("(fontinit) Start: %s %s\n", nowDate, nowTime)); + LOG_FONTINIT(("(fontinit) Uptime: %9.3f s\n", upTime/1000)); + LOG_FONTINIT(("(fontinit) dwrite version: %s\n", + NS_ConvertUTF16toUTF8(dwriteVers).get())); + } + + elapsedTime = (t5.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart; + Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_TOTAL, elapsedTime); + Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COUNT, + mSystemFonts->GetFontFamilyCount()); + LOG_FONTINIT(( + "(fontinit) Total time in InitFontList: %9.3f ms (families: %d, %s)\n", + elapsedTime, mSystemFonts->GetFontFamilyCount(), + (mGDIFontTableAccess ? "gdi table access" : "dwrite table access"))); + + elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart; + LOG_FONTINIT(("(fontinit) --- base/interop obj initialization init: %9.3f ms\n", elapsedTime)); + + elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart; + Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COLLECT, elapsedTime); + LOG_FONTINIT(("(fontinit) --- GetSystemFontCollection: %9.3f ms\n", elapsedTime)); + + elapsedTime = (t4.QuadPart - t3.QuadPart) * 1000.0 / frequency.QuadPart; + LOG_FONTINIT(("(fontinit) --- iterate over families: %9.3f ms\n", elapsedTime)); + + elapsedTime = (t5.QuadPart - t4.QuadPart) * 1000.0 / frequency.QuadPart; + LOG_FONTINIT(("(fontinit) --- misc initialization: %9.3f ms\n", elapsedTime)); + + return NS_OK; +} + +void +gfxDWriteFontList::GetFontsFromCollection(IDWriteFontCollection* aCollection) +{ + for (UINT32 i = 0; i < aCollection->GetFontFamilyCount(); i++) { + RefPtr<IDWriteFontFamily> family; + aCollection->GetFontFamily(i, getter_AddRefs(family)); + + RefPtr<IDWriteLocalizedStrings> names; + HRESULT hr = family->GetFamilyNames(getter_AddRefs(names)); + if (FAILED(hr)) { + continue; + } + + UINT32 englishIdx = 0; + + BOOL exists; + hr = names->FindLocaleName(L"en-us", &englishIdx, &exists); + if (FAILED(hr)) { + continue; + } + if (!exists) { + // Use 0 index if english is not found. + englishIdx = 0; + } + + AutoTArray<WCHAR, 32> enName; + UINT32 length; + + hr = names->GetStringLength(englishIdx, &length); + if (FAILED(hr)) { + continue; + } + + if (!enName.SetLength(length + 1, fallible)) { + // Eeep - running out of memory. Unlikely to end well. + continue; + } + + hr = names->GetString(englishIdx, enName.Elements(), length + 1); + if (FAILED(hr)) { + continue; + } + + nsAutoString name(enName.Elements()); + BuildKeyNameFromFontName(name); + + RefPtr<gfxFontFamily> fam; + + if (mFontFamilies.GetWeak(name)) { + continue; + } + + nsDependentString familyName(enName.Elements()); + + fam = new gfxDWriteFontFamily(familyName, family); + if (!fam) { + continue; + } + + if (mBadUnderlineFamilyNames.Contains(name)) { + fam->SetBadUnderlineFamily(); + } + mFontFamilies.Put(name, fam); + + // now add other family name localizations, if present + uint32_t nameCount = names->GetCount(); + uint32_t nameIndex; + + for (nameIndex = 0; nameIndex < nameCount; nameIndex++) { + UINT32 nameLen; + AutoTArray<WCHAR, 32> localizedName; + + // only add other names + if (nameIndex == englishIdx) { + continue; + } + + hr = names->GetStringLength(nameIndex, &nameLen); + if (FAILED(hr)) { + continue; + } + + if (!localizedName.SetLength(nameLen + 1, fallible)) { + continue; + } + + hr = names->GetString(nameIndex, localizedName.Elements(), + nameLen + 1); + if (FAILED(hr)) { + continue; + } + + nsDependentString locName(localizedName.Elements()); + + if (!familyName.Equals(locName)) { + AddOtherFamilyName(fam, locName); + } + + } + + // at this point, all family names have been read in + fam->SetOtherFamilyNamesInitialized(); + } +} + +static void +RemoveCharsetFromFontSubstitute(nsAString &aName) +{ + int32_t comma = aName.FindChar(char16_t(',')); + if (comma >= 0) + aName.Truncate(comma); +} + +#define MAX_VALUE_NAME 512 +#define MAX_VALUE_DATA 512 + +nsresult +gfxDWriteFontList::GetFontSubstitutes() +{ + HKEY hKey; + DWORD i, rv, lenAlias, lenActual, valueType; + WCHAR aliasName[MAX_VALUE_NAME]; + WCHAR actualName[MAX_VALUE_DATA]; + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", + 0, KEY_READ, &hKey) != ERROR_SUCCESS) + { + return NS_ERROR_FAILURE; + } + + for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) { + aliasName[0] = 0; + lenAlias = ArrayLength(aliasName); + actualName[0] = 0; + lenActual = sizeof(actualName); + rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType, + (LPBYTE)actualName, &lenActual); + + if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) { + continue; + } + + if (aliasName[0] == WCHAR('@')) { + continue; + } + + nsAutoString substituteName((char16_t*) aliasName); + nsAutoString actualFontName((char16_t*) actualName); + RemoveCharsetFromFontSubstitute(substituteName); + BuildKeyNameFromFontName(substituteName); + RemoveCharsetFromFontSubstitute(actualFontName); + BuildKeyNameFromFontName(actualFontName); + gfxFontFamily *ff; + if (!actualFontName.IsEmpty() && + (ff = mFontFamilies.GetWeak(actualFontName))) { + mFontSubstitutes.Put(substituteName, ff); + } else { + mNonExistingFonts.AppendElement(substituteName); + } + } + return NS_OK; +} + +struct FontSubstitution { + const WCHAR* aliasName; + const WCHAR* actualName; +}; + +static const FontSubstitution sDirectWriteSubs[] = { + { L"MS Sans Serif", L"Microsoft Sans Serif" }, + { L"MS Serif", L"Times New Roman" }, + { L"Courier", L"Courier New" }, + { L"Small Fonts", L"Arial" }, + { L"Roman", L"Times New Roman" }, + { L"Script", L"Mistral" } +}; + +void +gfxDWriteFontList::GetDirectWriteSubstitutes() +{ + for (uint32_t i = 0; i < ArrayLength(sDirectWriteSubs); ++i) { + const FontSubstitution& sub(sDirectWriteSubs[i]); + nsAutoString substituteName((char16_t*)sub.aliasName); + BuildKeyNameFromFontName(substituteName); + if (nullptr != mFontFamilies.GetWeak(substituteName)) { + // don't do the substitution if user actually has a usable font + // with this name installed + continue; + } + nsAutoString actualFontName((char16_t*)sub.actualName); + BuildKeyNameFromFontName(actualFontName); + gfxFontFamily *ff; + if (nullptr != (ff = mFontFamilies.GetWeak(actualFontName))) { + mFontSubstitutes.Put(substituteName, ff); + } else { + mNonExistingFonts.AppendElement(substituteName); + } + } +} + +bool +gfxDWriteFontList::GetStandardFamilyName(const nsAString& aFontName, + nsAString& aFamilyName) +{ + gfxFontFamily *family = FindFamily(aFontName); + if (family) { + family->LocalizedName(aFamilyName); + return true; + } + + return false; +} + +bool +gfxDWriteFontList::FindAndAddFamilies(const nsAString& aFamily, + nsTArray<gfxFontFamily*>* aOutput, + gfxFontStyle* aStyle, + gfxFloat aDevToCssSize) +{ + nsAutoString keyName(aFamily); + BuildKeyNameFromFontName(keyName); + + gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName); + if (ff) { + aOutput->AppendElement(ff); + return true; + } + + if (mNonExistingFonts.Contains(keyName)) { + return false; + } + + return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle, + aDevToCssSize); +} + +void +gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const +{ + gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); + + aSizes->mFontListSize += + SizeOfFontFamilyTableExcludingThis(mFontSubstitutes, aMallocSizeOf); + + aSizes->mFontListSize += + mNonExistingFonts.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) { + aSizes->mFontListSize += + mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf); + } +} + +void +gfxDWriteFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const +{ + aSizes->mFontListSize += aMallocSizeOf(this); + AddSizeOfExcludingThis(aMallocSizeOf, aSizes); +} + +static HRESULT GetFamilyName(IDWriteFont *aFont, nsString& aFamilyName) +{ + HRESULT hr; + RefPtr<IDWriteFontFamily> family; + + // clean out previous value + aFamilyName.Truncate(); + + hr = aFont->GetFontFamily(getter_AddRefs(family)); + if (FAILED(hr)) { + return hr; + } + + RefPtr<IDWriteLocalizedStrings> familyNames; + + hr = family->GetFamilyNames(getter_AddRefs(familyNames)); + if (FAILED(hr)) { + return hr; + } + + UINT32 index = 0; + BOOL exists = false; + + hr = familyNames->FindLocaleName(L"en-us", &index, &exists); + if (FAILED(hr)) { + return hr; + } + + // If the specified locale doesn't exist, select the first on the list. + if (!exists) { + index = 0; + } + + AutoTArray<WCHAR, 32> name; + UINT32 length; + + hr = familyNames->GetStringLength(index, &length); + if (FAILED(hr)) { + return hr; + } + + if (!name.SetLength(length + 1, fallible)) { + return E_FAIL; + } + hr = familyNames->GetString(index, name.Elements(), length + 1); + if (FAILED(hr)) { + return hr; + } + + aFamilyName.Assign(name.Elements()); + return S_OK; +} + +// bug 705594 - the method below doesn't actually do any "drawing", it's only +// used to invoke the DirectWrite layout engine to determine the fallback font +// for a given character. + +IFACEMETHODIMP DWriteFontFallbackRenderer::DrawGlyphRun( + void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_MEASURING_MODE measuringMode, + DWRITE_GLYPH_RUN const* glyphRun, + DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, + IUnknown* clientDrawingEffect + ) +{ + if (!mSystemFonts) { + return E_FAIL; + } + + HRESULT hr = S_OK; + + RefPtr<IDWriteFont> font; + hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace, + getter_AddRefs(font)); + if (FAILED(hr)) { + return hr; + } + + // copy the family name + hr = GetFamilyName(font, mFamilyName); + if (FAILED(hr)) { + return hr; + } + + // Arial is used as the default fallback font + // so if it matches ==> no font found + if (mFamilyName.EqualsLiteral("Arial")) { + mFamilyName.Truncate(); + return E_FAIL; + } + return hr; +} + +gfxFontEntry* +gfxDWriteFontList::PlatformGlobalFontFallback(const uint32_t aCh, + Script aRunScript, + const gfxFontStyle* aMatchStyle, + gfxFontFamily** aMatchedFamily) +{ + HRESULT hr; + + RefPtr<IDWriteFactory> dwFactory = + gfxWindowsPlatform::GetPlatform()->GetDWriteFactory(); + if (!dwFactory) { + return nullptr; + } + + // initialize fallback renderer + if (!mFallbackRenderer) { + mFallbackRenderer = new DWriteFontFallbackRenderer(dwFactory); + } + + // initialize text format + if (!mFallbackFormat) { + hr = dwFactory->CreateTextFormat(L"Arial", nullptr, + DWRITE_FONT_WEIGHT_REGULAR, + DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + 72.0f, L"en-us", + getter_AddRefs(mFallbackFormat)); + if (FAILED(hr)) { + return nullptr; + } + } + + // set up string with fallback character + wchar_t str[16]; + uint32_t strLen; + + if (IS_IN_BMP(aCh)) { + str[0] = static_cast<wchar_t> (aCh); + str[1] = 0; + strLen = 1; + } else { + str[0] = static_cast<wchar_t> (H_SURROGATE(aCh)); + str[1] = static_cast<wchar_t> (L_SURROGATE(aCh)); + str[2] = 0; + strLen = 2; + } + + // set up layout + RefPtr<IDWriteTextLayout> fallbackLayout; + + hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat, + 200.0f, 200.0f, + getter_AddRefs(fallbackLayout)); + if (FAILED(hr)) { + return nullptr; + } + + // call the draw method to invoke the DirectWrite layout functions + // which determine the fallback font + hr = fallbackLayout->Draw(nullptr, mFallbackRenderer, 50.0f, 50.0f); + if (FAILED(hr)) { + return nullptr; + } + + gfxFontFamily *family = FindFamily(mFallbackRenderer->FallbackFamilyName()); + if (family) { + gfxFontEntry *fontEntry; + bool needsBold; // ignored in the system fallback case + fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold); + if (fontEntry && fontEntry->HasCharacter(aCh)) { + *aMatchedFamily = family; + return fontEntry; + } + Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, true); + } + + return nullptr; +} + +// used to load system-wide font info on off-main thread +class DirectWriteFontInfo : public FontInfoData { +public: + DirectWriteFontInfo(bool aLoadOtherNames, + bool aLoadFaceNames, + bool aLoadCmaps, + IDWriteFontCollection* aSystemFonts +#ifdef MOZ_BUNDLED_FONTS + , IDWriteFontCollection* aBundledFonts +#endif + ) : + FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) + , mSystemFonts(aSystemFonts) +#ifdef MOZ_BUNDLED_FONTS + , mBundledFonts(aBundledFonts) +#endif + {} + + virtual ~DirectWriteFontInfo() {} + + // loads font data for all members of a given family + virtual void LoadFontFamilyData(const nsAString& aFamilyName); + +private: + RefPtr<IDWriteFontCollection> mSystemFonts; +#ifdef MOZ_BUNDLED_FONTS + RefPtr<IDWriteFontCollection> mBundledFonts; +#endif +}; + +void +DirectWriteFontInfo::LoadFontFamilyData(const nsAString& aFamilyName) +{ + // lookup the family + AutoTArray<wchar_t, 32> famName; + + uint32_t len = aFamilyName.Length(); + if(!famName.SetLength(len + 1, fallible)) { + return; + } + memcpy(famName.Elements(), aFamilyName.BeginReading(), len * sizeof(char16_t)); + famName[len] = 0; + + HRESULT hr; + BOOL exists = false; + + uint32_t index; + RefPtr<IDWriteFontFamily> family; + hr = mSystemFonts->FindFamilyName(famName.Elements(), &index, &exists); + if (SUCCEEDED(hr) && exists) { + mSystemFonts->GetFontFamily(index, getter_AddRefs(family)); + if (!family) { + return; + } + } + +#ifdef MOZ_BUNDLED_FONTS + if (!family && mBundledFonts) { + hr = mBundledFonts->FindFamilyName(famName.Elements(), &index, &exists); + if (SUCCEEDED(hr) && exists) { + mBundledFonts->GetFontFamily(index, getter_AddRefs(family)); + } + } +#endif + + if (!family) { + return; + } + + // later versions of DirectWrite support querying the fullname/psname + bool loadFaceNamesUsingDirectWrite = mLoadFaceNames; + + for (uint32_t i = 0; i < family->GetFontCount(); i++) { + // get the font + RefPtr<IDWriteFont> dwFont; + hr = family->GetFont(i, getter_AddRefs(dwFont)); + if (FAILED(hr)) { + // This should never happen. + NS_WARNING("Failed to get existing font from family."); + continue; + } + + if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) { + // We don't want these in the font list; we'll apply simulations + // on the fly when appropriate. + continue; + } + + mLoadStats.fonts++; + + // get the name of the face + nsString fullID(aFamilyName); + nsAutoString fontName; + hr = GetDirectWriteFontName(dwFont, fontName); + if (FAILED(hr)) { + continue; + } + fullID.Append(' '); + fullID.Append(fontName); + + FontFaceData fontData; + bool haveData = true; + RefPtr<IDWriteFontFace> dwFontFace; + + if (mLoadFaceNames) { + // try to load using DirectWrite first + if (loadFaceNamesUsingDirectWrite) { + hr = GetDirectWriteFaceName(dwFont, PSNAME_ID, fontData.mPostscriptName); + if (FAILED(hr)) { + loadFaceNamesUsingDirectWrite = false; + } + hr = GetDirectWriteFaceName(dwFont, FULLNAME_ID, fontData.mFullName); + if (FAILED(hr)) { + loadFaceNamesUsingDirectWrite = false; + } + } + + // if DirectWrite read fails, load directly from name table + if (!loadFaceNamesUsingDirectWrite) { + hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace)); + if (SUCCEEDED(hr)) { + uint32_t kNAME = + NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e')); + const char *nameData; + BOOL exists; + void* ctx; + uint32_t nameSize; + + hr = dwFontFace->TryGetFontTable( + kNAME, + (const void**)&nameData, &nameSize, &ctx, &exists); + + if (SUCCEEDED(hr) && nameData && nameSize > 0) { + gfxFontUtils::ReadCanonicalName(nameData, nameSize, + gfxFontUtils::NAME_ID_FULL, + fontData.mFullName); + gfxFontUtils::ReadCanonicalName(nameData, nameSize, + gfxFontUtils::NAME_ID_POSTSCRIPT, + fontData.mPostscriptName); + dwFontFace->ReleaseFontTable(ctx); + } + } + } + + haveData = !fontData.mPostscriptName.IsEmpty() || + !fontData.mFullName.IsEmpty(); + if (haveData) { + mLoadStats.facenames++; + } + } + + // cmaps + if (mLoadCmaps) { + if (!dwFontFace) { + hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace)); + if (!SUCCEEDED(hr)) { + continue; + } + } + + uint32_t kCMAP = + NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p')); + const uint8_t *cmapData; + BOOL exists; + void* ctx; + uint32_t cmapSize; + + hr = dwFontFace->TryGetFontTable(kCMAP, + (const void**)&cmapData, &cmapSize, &ctx, &exists); + + if (SUCCEEDED(hr)) { + bool cmapLoaded = false; + bool unicodeFont = false, symbolFont = false; + RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(); + uint32_t offset; + + if (cmapData && + cmapSize > 0 && + NS_SUCCEEDED( + gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap, + offset, unicodeFont, symbolFont))) { + fontData.mCharacterMap = charmap; + fontData.mUVSOffset = offset; + fontData.mSymbolFont = symbolFont; + cmapLoaded = true; + mLoadStats.cmaps++; + } + dwFontFace->ReleaseFontTable(ctx); + haveData = haveData || cmapLoaded; + } + } + + // if have data, load + if (haveData) { + mFontFaceData.Put(fullID, fontData); + } + } +} + +already_AddRefed<FontInfoData> +gfxDWriteFontList::CreateFontInfoData() +{ + bool loadCmaps = !UsesSystemFallback() || + gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); + + RefPtr<DirectWriteFontInfo> fi = + new DirectWriteFontInfo(false, NeedFullnamePostscriptNames(), loadCmaps, + mSystemFonts +#ifdef MOZ_BUNDLED_FONTS + , mBundledFonts +#endif + ); + + return fi.forget(); +} + + +#ifdef MOZ_BUNDLED_FONTS + +#define IMPL_QI_FOR_DWRITE(_interface) \ + public: \ + IFACEMETHOD(QueryInterface) (IID const& riid, void** ppvObject) \ + { \ + if (__uuidof(_interface) == riid) { \ + *ppvObject = this; \ + } else if (__uuidof(IUnknown) == riid) { \ + *ppvObject = this; \ + } else { \ + *ppvObject = nullptr; \ + return E_NOINTERFACE; \ + } \ + this->AddRef(); \ + return S_OK; \ + } + +class BundledFontFileEnumerator + : public IDWriteFontFileEnumerator +{ + IMPL_QI_FOR_DWRITE(IDWriteFontFileEnumerator) + + NS_INLINE_DECL_REFCOUNTING(BundledFontFileEnumerator) + +public: + BundledFontFileEnumerator(IDWriteFactory *aFactory, + nsIFile *aFontDir); + + IFACEMETHODIMP MoveNext(BOOL * hasCurrentFile); + + IFACEMETHODIMP GetCurrentFontFile(IDWriteFontFile ** fontFile); + +private: + BundledFontFileEnumerator() = delete; + BundledFontFileEnumerator(const BundledFontFileEnumerator&) = delete; + BundledFontFileEnumerator& operator=(const BundledFontFileEnumerator&) = delete; + virtual ~BundledFontFileEnumerator() {} + + RefPtr<IDWriteFactory> mFactory; + + nsCOMPtr<nsIFile> mFontDir; + nsCOMPtr<nsISimpleEnumerator> mEntries; + nsCOMPtr<nsISupports> mCurrent; +}; + +BundledFontFileEnumerator::BundledFontFileEnumerator(IDWriteFactory *aFactory, + nsIFile *aFontDir) + : mFactory(aFactory) + , mFontDir(aFontDir) +{ + mFontDir->GetDirectoryEntries(getter_AddRefs(mEntries)); +} + +IFACEMETHODIMP +BundledFontFileEnumerator::MoveNext(BOOL * aHasCurrentFile) +{ + bool hasMore = false; + if (mEntries) { + if (NS_SUCCEEDED(mEntries->HasMoreElements(&hasMore)) && hasMore) { + if (NS_SUCCEEDED(mEntries->GetNext(getter_AddRefs(mCurrent)))) { + hasMore = true; + } + } + } + *aHasCurrentFile = hasMore; + return S_OK; +} + +IFACEMETHODIMP +BundledFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile ** aFontFile) +{ + nsCOMPtr<nsIFile> file = do_QueryInterface(mCurrent); + if (!file) { + return E_FAIL; + } + nsString path; + if (NS_FAILED(file->GetPath(path))) { + return E_FAIL; + } + return mFactory->CreateFontFileReference((const WCHAR*)path.get(), + nullptr, aFontFile); +} + +class BundledFontLoader + : public IDWriteFontCollectionLoader +{ + IMPL_QI_FOR_DWRITE(IDWriteFontCollectionLoader) + + NS_INLINE_DECL_REFCOUNTING(BundledFontLoader) + +public: + BundledFontLoader() + { + } + + IFACEMETHODIMP CreateEnumeratorFromKey( + IDWriteFactory *aFactory, + const void *aCollectionKey, + UINT32 aCollectionKeySize, + IDWriteFontFileEnumerator **aFontFileEnumerator); + +private: + BundledFontLoader(const BundledFontLoader&) = delete; + BundledFontLoader& operator=(const BundledFontLoader&) = delete; + virtual ~BundledFontLoader() { } +}; + +IFACEMETHODIMP +BundledFontLoader::CreateEnumeratorFromKey( + IDWriteFactory *aFactory, + const void *aCollectionKey, + UINT32 aCollectionKeySize, + IDWriteFontFileEnumerator **aFontFileEnumerator) +{ + nsIFile *fontDir = *(nsIFile**)aCollectionKey; + *aFontFileEnumerator = new BundledFontFileEnumerator(aFactory, fontDir); + NS_ADDREF(*aFontFileEnumerator); + return S_OK; +} + +already_AddRefed<IDWriteFontCollection> +gfxDWriteFontList::CreateBundledFontsCollection(IDWriteFactory* aFactory) +{ + nsCOMPtr<nsIFile> localDir; + nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir)); + if (NS_FAILED(rv)) { + return nullptr; + } + if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) { + return nullptr; + } + bool isDir; + if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) { + return nullptr; + } + + RefPtr<BundledFontLoader> loader = new BundledFontLoader(); + if (FAILED(aFactory->RegisterFontCollectionLoader(loader))) { + return nullptr; + } + + const void *key = localDir.get(); + RefPtr<IDWriteFontCollection> collection; + HRESULT hr = + aFactory->CreateCustomFontCollection(loader, &key, sizeof(key), + getter_AddRefs(collection)); + + aFactory->UnregisterFontCollectionLoader(loader); + + if (FAILED(hr)) { + return nullptr; + } else { + return collection.forget(); + } +} + +#endif |