summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxDWriteFontList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/thebes/gfxDWriteFontList.cpp')
-rw-r--r--gfx/thebes/gfxDWriteFontList.cpp1838
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