summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxDWriteFontList.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/thebes/gfxDWriteFontList.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/thebes/gfxDWriteFontList.cpp')
-rw-r--r--gfx/thebes/gfxDWriteFontList.cpp2623
1 files changed, 2623 insertions, 0 deletions
diff --git a/gfx/thebes/gfxDWriteFontList.cpp b/gfx/thebes/gfxDWriteFontList.cpp
new file mode 100644
index 0000000000..d31ba05deb
--- /dev/null
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -0,0 +1,2623 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/FontPropertyTypes.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/intl/OSPreferences.h"
+
+#include "gfxDWriteFontList.h"
+#include "gfxDWriteFonts.h"
+#include "nsUnicharUtils.h"
+#include "nsPresContext.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/WindowsProcessMitigations.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+
+#include "gfxGDIFontList.h"
+#include "gfxRect.h"
+#include "SharedFontList-impl.h"
+
+#include "harfbuzz/hb.h"
+
+#include "StandardFonts-win10.inc"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using mozilla::intl::OSPreferences;
+
+#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(nsACString& aName) {
+ ToLowerCase(aName);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// gfxDWriteFontFamily
+
+gfxDWriteFontFamily::~gfxDWriteFontFamily() {}
+
+static bool GetNameAsUtf8(nsACString& aName, IDWriteLocalizedStrings* aStrings,
+ UINT32 aIndex) {
+ AutoTArray<WCHAR, 32> name;
+ UINT32 length;
+ HRESULT hr = aStrings->GetStringLength(aIndex, &length);
+ if (FAILED(hr)) {
+ return false;
+ }
+ if (!name.SetLength(length + 1, fallible)) {
+ return false;
+ }
+ hr = aStrings->GetString(aIndex, name.Elements(), length + 1);
+ if (FAILED(hr)) {
+ return false;
+ }
+ aName.Truncate();
+ AppendUTF16toUTF8(
+ Substring(reinterpret_cast<const char16_t*>(name.Elements()),
+ name.Length() - 1),
+ aName);
+ return true;
+}
+
+static bool GetEnglishOrFirstName(nsACString& aName,
+ IDWriteLocalizedStrings* aStrings) {
+ UINT32 englishIdx = 0;
+ BOOL exists;
+ HRESULT hr = aStrings->FindLocaleName(L"en-us", &englishIdx, &exists);
+ if (FAILED(hr) || !exists) {
+ // Use 0 index if english is not found.
+ englishIdx = 0;
+ }
+ return GetNameAsUtf8(aName, aStrings, englishIdx);
+}
+
+static HRESULT GetDirectWriteFontName(IDWriteFont* aFont,
+ nsACString& aFontName) {
+ HRESULT hr;
+
+ RefPtr<IDWriteLocalizedStrings> names;
+ hr = aFont->GetFaceNames(getter_AddRefs(names));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ if (!GetEnglishOrFirstName(aFontName, names)) {
+ return E_FAIL;
+ }
+
+ 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,
+ nsACString& aFontName) {
+ HRESULT hr;
+
+ BOOL exists;
+ RefPtr<IDWriteLocalizedStrings> infostrings;
+ hr = aFont->GetInformationalStrings(aWhichName, getter_AddRefs(infostrings),
+ &exists);
+ if (FAILED(hr) || !exists) {
+ return E_FAIL;
+ }
+
+ if (!GetEnglishOrFirstName(aFontName, infostrings)) {
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+void gfxDWriteFontFamily::FindStyleVariationsLocked(
+ 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
+ nsCString fullID(mName);
+ nsAutoCString faceName;
+ hr = GetDirectWriteFontName(font, faceName);
+ if (FAILED(hr)) {
+ continue;
+ }
+ fullID.Append(' ');
+ fullID.Append(faceName);
+
+ // Ignore italic style's "Meiryo" because "Meiryo (Bold) Italic" has
+ // non-italic style glyphs as Japanese characters. However, using it
+ // causes serious problem if web pages wants some elements to be
+ // different style from others only with font-style. For example,
+ // <em> and <i> should be rendered as italic in the default style.
+ if (fullID.EqualsLiteral("Meiryo Italic") ||
+ fullID.EqualsLiteral("Meiryo Bold Italic")) {
+ continue;
+ }
+
+ gfxDWriteFontEntry* fe =
+ new gfxDWriteFontEntry(fullID, font, mIsSystemFontFamily);
+ fe->SetForceGDIClassic(mForceGDIClassic);
+
+ fe->SetupVariationRanges();
+
+ AddFontEntryLocked(fe);
+
+ // postscript/fullname if needed
+ nsAutoCString 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()) {
+ nsAutoCString weightString;
+ fe->Weight().ToString(weightString);
+ LOG_FONTLIST(
+ ("(fontlist) added (%s) to family (%s)"
+ " with style: %s weight: %s stretch: %d psname: %s fullname: %s",
+ fe->Name().get(), Name().get(),
+ (fe->IsItalic()) ? "italic"
+ : (fe->IsOblique() ? "oblique" : "normal"),
+ weightString.get(), fe->Stretch().AsScalar(), psname.get(),
+ 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();
+ }
+
+ CheckForSimpleFamily();
+ if (mIsSimpleFamily) {
+ for (auto& f : mAvailableFonts) {
+ if (f) {
+ static_cast<gfxDWriteFontEntry*>(f.get())->mMayUseGDIAccess = true;
+ }
+ }
+ }
+}
+
+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(nsACString& aLocalizedName) {
+ aLocalizedName = Name(); // just return canonical name in case of failure
+
+ if (!mDWFamily) {
+ return;
+ }
+
+ HRESULT hr;
+ nsAutoCString locale;
+ // We use system locale here because it's what user expects to see.
+ // See bug 1349454 for details.
+ RefPtr<OSPreferences> osprefs = OSPreferences::GetInstanceAddRefed();
+ if (!osprefs) {
+ return;
+ }
+ osprefs->GetSystemLocale(locale);
+
+ RefPtr<IDWriteLocalizedStrings> names;
+
+ hr = mDWFamily->GetFamilyNames(getter_AddRefs(names));
+ if (FAILED(hr)) {
+ return;
+ }
+ UINT32 idx = 0;
+ BOOL exists;
+ hr =
+ names->FindLocaleName(NS_ConvertUTF8toUTF16(locale).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 = NS_ConvertUTF16toUTF8((const char16_t*)famName.Elements(),
+ famName.Length() - 1);
+}
+
+bool gfxDWriteFontFamily::IsSymbolFontFamily() const {
+ // Just check the first font in the family
+ if (mDWFamily->GetFontCount() > 0) {
+ RefPtr<IDWriteFont> font;
+ if (SUCCEEDED(mDWFamily->GetFont(0, getter_AddRefs(font)))) {
+ return font->IsSymbolFont();
+ }
+ }
+ return false;
+}
+
+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
+
+gfxFontEntry* gfxDWriteFontEntry::Clone() const {
+ MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
+ gfxDWriteFontEntry* fe = new gfxDWriteFontEntry(Name(), mFont);
+ fe->mWeightRange = mWeightRange;
+ fe->mStretchRange = mStretchRange;
+ fe->mStyleRange = mStyleRange;
+ return fe;
+}
+
+gfxDWriteFontEntry::~gfxDWriteFontEntry() {}
+
+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 && mMayUseGDIAccess && pFontList->UseGDIFontTableAccess() &&
+ !(!IsUpright() && 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) {
+ AUTO_PROFILER_LABEL("gfxDWriteFontEntry::ReadCMAP", GRAPHICS);
+
+ // attempt this once, if errors occur leave a blank cmap
+ if (mCharacterMap || mShmemCharacterMap) {
+ return NS_OK;
+ }
+
+ RefPtr<gfxCharacterMap> charmap;
+ nsresult rv;
+
+ uint32_t uvsOffset = 0;
+ if (aFontInfoData &&
+ (charmap = GetCMAPFromFontInfo(aFontInfoData, uvsOffset))) {
+ rv = NS_OK;
+ } else {
+ uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
+ charmap = new gfxCharacterMap();
+ AutoTable cmapTable(this, kCMAP);
+
+ if (cmapTable) {
+ uint32_t cmapLen;
+ const uint8_t* cmapData = reinterpret_cast<const uint8_t*>(
+ hb_blob_get_data(cmapTable, &cmapLen));
+ rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, uvsOffset);
+ } else {
+ rv = NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+ mUVSOffset.exchange(uvsOffset);
+
+ bool setCharMap = true;
+ if (NS_SUCCEEDED(rv)) {
+ // 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();
+ fontlist::FontList* sharedFontList = pfl->SharedFontList();
+ if (!IsUserFont() && mShmemFace) {
+ mShmemFace->SetCharacterMap(sharedFontList, charmap); // async
+ if (TrySetShmemCharacterMap()) {
+ setCharMap = false;
+ }
+ } else {
+ charmap = pfl->FindCharMap(charmap);
+ }
+ mHasCmapTable = true;
+ } else {
+ // if error occurred, initialize to null cmap
+ charmap = new gfxCharacterMap();
+ mHasCmapTable = false;
+ }
+ if (setCharMap) {
+ // Temporarily retain charmap, until the shared version is
+ // ready for use.
+ if (mCharacterMap.compareExchange(nullptr, charmap.get())) {
+ charmap.get()->AddRef();
+ }
+ }
+
+ LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
+ 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", mName.get());
+ charmap->Dump(prefix, eGfxLog_cmapdata);
+ }
+
+ return rv;
+}
+
+bool gfxDWriteFontEntry::HasVariations() {
+ if (mHasVariationsInitialized) {
+ return mHasVariations;
+ }
+ mHasVariationsInitialized = true;
+ mHasVariations = false;
+
+ if (!gfxPlatform::HasVariationFontSupport()) {
+ return mHasVariations;
+ }
+
+ if (!mFontFace) {
+ // CreateFontFace will initialize the mFontFace field, and also
+ // mFontFace5 if available on the current DWrite version.
+ RefPtr<IDWriteFontFace> fontFace;
+ if (NS_FAILED(CreateFontFace(getter_AddRefs(fontFace)))) {
+ return mHasVariations;
+ }
+ }
+ if (mFontFace5) {
+ mHasVariations = mFontFace5->HasVariations();
+ }
+ return mHasVariations;
+}
+
+void gfxDWriteFontEntry::GetVariationAxes(
+ nsTArray<gfxFontVariationAxis>& aAxes) {
+ if (!HasVariations()) {
+ return;
+ }
+ // HasVariations() will have ensured the mFontFace5 interface is available;
+ // so we can get an IDWriteFontResource and ask it for the axis info.
+ RefPtr<IDWriteFontResource> resource;
+ HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
+ if (FAILED(hr) || !resource) {
+ return;
+ }
+
+ uint32_t count = resource->GetFontAxisCount();
+ AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> defaultValues;
+ AutoTArray<DWRITE_FONT_AXIS_RANGE, 4> ranges;
+ defaultValues.SetLength(count);
+ ranges.SetLength(count);
+ resource->GetDefaultFontAxisValues(defaultValues.Elements(), count);
+ resource->GetFontAxisRanges(ranges.Elements(), count);
+ for (uint32_t i = 0; i < count; ++i) {
+ gfxFontVariationAxis axis;
+ MOZ_ASSERT(ranges[i].axisTag == defaultValues[i].axisTag);
+ DWRITE_FONT_AXIS_ATTRIBUTES attrs = resource->GetFontAxisAttributes(i);
+ if (attrs & DWRITE_FONT_AXIS_ATTRIBUTES_HIDDEN) {
+ continue;
+ }
+ if (!(attrs & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE)) {
+ continue;
+ }
+ // Extract the 4 chars of the tag from DWrite's packed version,
+ // and reassemble them in the order we use for TRUETYPE_TAG.
+ uint32_t t = defaultValues[i].axisTag;
+ axis.mTag = TRUETYPE_TAG(t & 0xff, (t >> 8) & 0xff, (t >> 16) & 0xff,
+ (t >> 24) & 0xff);
+ // Try to get a human-friendly name (may not be present)
+ RefPtr<IDWriteLocalizedStrings> names;
+ resource->GetAxisNames(i, getter_AddRefs(names));
+ if (names) {
+ GetEnglishOrFirstName(axis.mName, names);
+ }
+ axis.mMinValue = ranges[i].minValue;
+ axis.mMaxValue = ranges[i].maxValue;
+ axis.mDefaultValue = defaultValues[i].value;
+ aAxes.AppendElement(axis);
+ }
+}
+
+void gfxDWriteFontEntry::GetVariationInstances(
+ nsTArray<gfxFontVariationInstance>& aInstances) {
+ gfxFontUtils::GetVariationData(this, nullptr, &aInstances);
+}
+
+gfxFont* gfxDWriteFontEntry::CreateFontInstance(
+ const gfxFontStyle* aFontStyle) {
+ // We use the DirectWrite bold simulation for installed fonts, but NOT for
+ // webfonts; those will use multi-strike synthetic bold instead.
+ bool useBoldSim = false;
+ if (aFontStyle->NeedsSyntheticBold(this)) {
+ switch (StaticPrefs::gfx_font_rendering_directwrite_bold_simulation()) {
+ case 0: // never use the DWrite simulation
+ break;
+ case 1: // use DWrite simulation for installed fonts but not webfonts
+ useBoldSim = !mIsDataUserFont;
+ break;
+ default: // always use DWrite bold simulation
+ useBoldSim = true;
+ break;
+ }
+ }
+ DWRITE_FONT_SIMULATIONS sims =
+ useBoldSim ? DWRITE_FONT_SIMULATIONS_BOLD : DWRITE_FONT_SIMULATIONS_NONE;
+ ThreadSafeWeakPtr<UnscaledFontDWrite>& unscaledFontPtr =
+ useBoldSim ? mUnscaledFontBold : mUnscaledFont;
+ RefPtr<UnscaledFontDWrite> unscaledFont(unscaledFontPtr);
+ if (!unscaledFont) {
+ RefPtr<IDWriteFontFace> fontFace;
+ nsresult rv =
+ CreateFontFace(getter_AddRefs(fontFace), nullptr, sims, nullptr);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+ // Only pass in the underlying IDWriteFont if the unscaled font doesn't
+ // reflect a data font. This signals whether or not we can safely query
+ // a descriptor to represent the font for various transport use-cases.
+ unscaledFont =
+ new UnscaledFontDWrite(fontFace, !mIsDataUserFont ? mFont : nullptr);
+ unscaledFontPtr = unscaledFont;
+ }
+ RefPtr<IDWriteFontFace> fontFace;
+ if (HasVariations()) {
+ // Get the variation settings needed to instantiate the fontEntry
+ // for a particular fontStyle.
+ AutoTArray<gfxFontVariation, 4> vars;
+ GetVariationsForStyle(vars, *aFontStyle);
+
+ if (!vars.IsEmpty()) {
+ nsresult rv =
+ CreateFontFace(getter_AddRefs(fontFace), aFontStyle, sims, &vars);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+ }
+ }
+ return new gfxDWriteFont(unscaledFont, this, aFontStyle, fontFace);
+}
+
+nsresult gfxDWriteFontEntry::CreateFontFace(
+ IDWriteFontFace** aFontFace, const gfxFontStyle* aFontStyle,
+ DWRITE_FONT_SIMULATIONS aSimulations,
+ const nsTArray<gfxFontVariation>* aVariations) {
+ // Convert an OpenType font tag from our uint32_t representation
+ // (as constructed by TRUETYPE_TAG(...)) to the order DWrite wants.
+ auto makeDWriteAxisTag = [](uint32_t aTag) {
+ return DWRITE_MAKE_FONT_AXIS_TAG((aTag >> 24) & 0xff, (aTag >> 16) & 0xff,
+ (aTag >> 8) & 0xff, aTag & 0xff);
+ };
+
+ MOZ_SEH_TRY {
+ // 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 = Factory::GetDWriteFactory()->CreateFontFace(
+ mFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE,
+ getter_AddRefs(mFontFace));
+ } else {
+ MOZ_ASSERT_UNREACHABLE("invalid font entry");
+ return NS_ERROR_FAILURE;
+ }
+ if (FAILED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+ // Also get the IDWriteFontFace5 interface if we're running on a
+ // sufficiently new DWrite version where it is available.
+ if (mFontFace) {
+ mFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
+ (void**)getter_AddRefs(mFontFace5));
+ if (!mVariationSettings.IsEmpty()) {
+ // If the font entry has variations specified, mFontFace5 will
+ // be a distinct face that has the variations applied.
+ RefPtr<IDWriteFontResource> resource;
+ HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
+ if (SUCCEEDED(hr) && resource) {
+ AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
+ for (const auto& v : mVariationSettings) {
+ DWRITE_FONT_AXIS_VALUE axisValue = {makeDWriteAxisTag(v.mTag),
+ v.mValue};
+ fontAxisValues.AppendElement(axisValue);
+ }
+ resource->CreateFontFace(
+ mFontFace->GetSimulations(), fontAxisValues.Elements(),
+ fontAxisValues.Length(), getter_AddRefs(mFontFace5));
+ }
+ }
+ }
+ }
+
+ // Do we need to modify DWrite simulations from what mFontFace has?
+ bool needSimulations =
+ (aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
+ !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
+
+ // If the IDWriteFontFace5 interface is available, we can try using
+ // IDWriteFontResource to create a new modified face.
+ if (mFontFace5 && (HasVariations() || needSimulations)) {
+ RefPtr<IDWriteFontResource> resource;
+ HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
+ if (SUCCEEDED(hr) && resource) {
+ AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
+
+ // Copy variation settings to DWrite's type.
+ if (aVariations) {
+ for (const auto& v : *aVariations) {
+ DWRITE_FONT_AXIS_VALUE axisValue = {makeDWriteAxisTag(v.mTag),
+ v.mValue};
+ fontAxisValues.AppendElement(axisValue);
+ }
+ }
+
+ IDWriteFontFace5* ff5;
+ resource->CreateFontFace(aSimulations, fontAxisValues.Elements(),
+ fontAxisValues.Length(), &ff5);
+ if (ff5) {
+ *aFontFace = ff5;
+ return NS_OK;
+ }
+ }
+ }
+
+ // Do we need to add DWrite simulations to the face?
+ if (needSimulations) {
+ // if so, we need to return not mFontFace itself but a version that
+ // has the Bold simulation - unfortunately, old 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 = Factory::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;
+ }
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ gfxCriticalNote << "Exception occurred creating font face for "
+ << mName.get();
+ }
+
+ // no simulation: we can just add a reference to mFontFace5 (if present)
+ // or mFontFace (otherwise) and return that
+ if (mFontFace5) {
+ *aFontFace = mFontFace5;
+ } else {
+ *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');
+ gfxFontUtils::AutoHBBlob blob(GetFontTable(kOS2Tag));
+ if (!blob) {
+ return mIsCJK;
+ }
+
+ uint32_t len;
+ const OS2Table* os2 =
+ reinterpret_cast<const OS2Table*>(hb_blob_get_data(blob, &len));
+ // 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 (len >= offsetof(OS2Table, sxHeight)) {
+ 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) {
+ CheckFamilyList(kBaseFonts);
+ CheckFamilyList(kLangPackFonts);
+}
+
+// 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.
+
+FontFamily gfxDWriteFontList::GetDefaultFontForPlatform(
+ nsPresContext* aPresContext, const gfxFontStyle* aStyle,
+ nsAtom* aLanguage) {
+ // try Arial first
+ FontFamily ff;
+ ff = FindFamily(aPresContext, "Arial"_ns);
+ if (!ff.IsNull()) {
+ 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(aPresContext,
+ NS_ConvertUTF16toUTF8(ncm.lfMessageFont.lfFaceName));
+ }
+
+ return ff;
+}
+
+gfxFontEntry* gfxDWriteFontList::LookupLocalFont(
+ nsPresContext* aPresContext, const nsACString& aFontName,
+ WeightRange aWeightForEntry, StretchRange aStretchForEntry,
+ SlantStyleRange aStyleForEntry) {
+ AutoLock lock(mLock);
+
+ if (SharedFontList()) {
+ return LookupInSharedFaceNameList(aPresContext, aFontName, aWeightForEntry,
+ aStretchForEntry, aStyleForEntry);
+ }
+
+ gfxFontEntry* lookup;
+
+ lookup = LookupInFaceNameLists(aFontName);
+ if (!lookup) {
+ return nullptr;
+ }
+
+ gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup);
+ gfxDWriteFontEntry* fe =
+ new gfxDWriteFontEntry(lookup->Name(), dwriteLookup->mFont,
+ aWeightForEntry, aStretchForEntry, aStyleForEntry);
+ fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
+ return fe;
+}
+
+gfxFontEntry* gfxDWriteFontList::MakePlatformFont(
+ const nsACString& aFontName, WeightRange aWeightForEntry,
+ StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry,
+ const uint8_t* aFontData, uint32_t aLength) {
+ RefPtr<IDWriteFontFileStream> fontFileStream;
+ RefPtr<IDWriteFontFile> fontFile;
+ HRESULT hr = gfxDWriteFontFileLoader::CreateCustomFontFile(
+ aFontData, aLength, getter_AddRefs(fontFile),
+ getter_AddRefs(fontFileStream));
+ free((void*)aFontData);
+ NS_ASSERTION(SUCCEEDED(hr), "Failed to create font file reference");
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ nsAutoString uniqueName;
+ nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to make unique user font name");
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ BOOL isSupported;
+ DWRITE_FONT_FILE_TYPE fileType;
+ UINT32 numFaces;
+
+ auto entry = MakeUnique<gfxDWriteFontEntry>(
+ NS_ConvertUTF16toUTF8(uniqueName), fontFile, fontFileStream,
+ aWeightForEntry, aStretchForEntry, aStyleForEntry);
+
+ hr = fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces);
+ NS_ASSERTION(SUCCEEDED(hr), "IDWriteFontFile::Analyze failed");
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+ NS_ASSERTION(isSupported, "Unsupported font file");
+ if (!isSupported) {
+ return nullptr;
+ }
+ NS_ASSERTION(numFaces == 1, "Font file does not contain exactly 1 face");
+ if (numFaces != 1) {
+ // We don't know how to deal with 0 faces either.
+ return nullptr;
+ }
+
+ return entry.release();
+}
+
+bool gfxDWriteFontList::UseGDIFontTableAccess() const {
+ // Using GDI font table access for DWrite is controlled by a pref, but also we
+ // must be able to make win32k calls.
+ return mGDIFontTableAccess && !IsWin32kLockedDown();
+}
+
+static void GetPostScriptNameFromNameTable(IDWriteFontFace* aFace,
+ nsCString& aName) {
+ const auto kNAME =
+ NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
+ const char* data;
+ UINT32 size;
+ void* context;
+ BOOL exists;
+ if (SUCCEEDED(aFace->TryGetFontTable(kNAME, (const void**)&data, &size,
+ &context, &exists)) &&
+ exists) {
+ if (NS_FAILED(gfxFontUtils::ReadCanonicalName(
+ data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aName))) {
+ aName.Truncate(0);
+ }
+ aFace->ReleaseFontTable(context);
+ }
+}
+
+gfxFontEntry* gfxDWriteFontList::CreateFontEntry(
+ fontlist::Face* aFace, const fontlist::Family* aFamily) {
+ IDWriteFontCollection* collection =
+#ifdef MOZ_BUNDLED_FONTS
+ aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
+#else
+ mSystemFonts;
+#endif
+ RefPtr<IDWriteFontFamily> family;
+ bool foundExpectedFamily = false;
+ const nsCString& familyName =
+ aFamily->DisplayName().AsString(SharedFontList());
+
+ // The DirectWrite calls here might throw exceptions, e.g. in case of disk
+ // errors when trying to read the font file.
+ MOZ_SEH_TRY {
+ if (aFamily->Index() < collection->GetFontFamilyCount()) {
+ HRESULT hr =
+ collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family));
+ // Check that the family name is what we expected; if not, fall back to
+ // search by name. It's sad we have to do this, but it is possible for
+ // Windows to have given different versions of the system font collection
+ // to the parent and child processes.
+ if (SUCCEEDED(hr) && family) {
+ RefPtr<IDWriteLocalizedStrings> names;
+ hr = family->GetFamilyNames(getter_AddRefs(names));
+ if (SUCCEEDED(hr) && names) {
+ nsAutoCString name;
+ if (GetEnglishOrFirstName(name, names)) {
+ foundExpectedFamily = name.Equals(familyName);
+ }
+ }
+ }
+ }
+ if (!foundExpectedFamily) {
+ // Try to get family by name instead of index (to deal with the case of
+ // collection mismatch).
+ UINT32 index;
+ BOOL exists;
+ NS_ConvertUTF8toUTF16 name16(familyName);
+ HRESULT hr = collection->FindFamilyName(
+ reinterpret_cast<const WCHAR*>(name16.BeginReading()), &index,
+ &exists);
+ if (FAILED(hr) || !exists || index == UINT_MAX ||
+ FAILED(collection->GetFontFamily(index, getter_AddRefs(family))) ||
+ !family) {
+ return nullptr;
+ }
+ }
+
+ // Retrieve the required face by index within the family.
+ RefPtr<IDWriteFont> font;
+ if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(font))) || !font) {
+ return nullptr;
+ }
+
+ // Retrieve the psName from the font, so we can check we've found the
+ // expected face.
+ nsAutoCString psName;
+ if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) {
+ RefPtr<IDWriteFontFace> dwFontFace;
+ if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) {
+ GetPostScriptNameFromNameTable(dwFontFace, psName);
+ }
+ }
+
+ // If it doesn't match, DirectWrite must have shuffled the order of faces
+ // returned for the family; search by name as a fallback.
+ nsCString faceName = aFace->mDescriptor.AsString(SharedFontList());
+ if (psName != faceName) {
+ gfxWarning() << "Face name mismatch for index " << aFace->mIndex
+ << " in family " << familyName.get() << ": expected "
+ << faceName.get() << ", found " << psName.get();
+ for (uint32_t i = 0; i < family->GetFontCount(); ++i) {
+ if (i == aFace->mIndex) {
+ continue; // this was the face we already tried
+ }
+ if (FAILED(family->GetFont(i, getter_AddRefs(font))) || !font) {
+ return nullptr; // this font family is broken!
+ }
+ if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) {
+ RefPtr<IDWriteFontFace> dwFontFace;
+ if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) {
+ GetPostScriptNameFromNameTable(dwFontFace, psName);
+ }
+ }
+ if (psName == faceName) {
+ break;
+ }
+ }
+ }
+ if (psName != faceName) {
+ return nullptr;
+ }
+
+ auto fe = new gfxDWriteFontEntry(faceName, font, !aFamily->IsBundled());
+ fe->InitializeFrom(aFace, aFamily);
+ fe->mForceGDIClassic = aFamily->IsForceClassic();
+ fe->mMayUseGDIAccess = aFamily->IsSimple();
+ return fe;
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ gfxCriticalNote << "Exception occurred while creating font entry for "
+ << familyName.get();
+ }
+ return nullptr;
+}
+
+FontVisibility gfxDWriteFontList::GetVisibilityForFamily(
+ const nsACString& aName) const {
+ if (FamilyInList(aName, kBaseFonts)) {
+ return FontVisibility::Base;
+ }
+ if (FamilyInList(aName, kLangPackFonts)) {
+ return FontVisibility::LangPack;
+ }
+ return FontVisibility::User;
+}
+
+void gfxDWriteFontList::AppendFamiliesFromCollection(
+ IDWriteFontCollection* aCollection,
+ nsTArray<fontlist::Family::InitData>& aFamilies,
+ const nsTArray<nsCString>* aForceClassicFams) {
+ auto allFacesUltraBold = [](IDWriteFontFamily* aFamily) -> bool {
+ for (UINT32 i = 0; i < aFamily->GetFontCount(); i++) {
+ RefPtr<IDWriteFont> font;
+ HRESULT hr = aFamily->GetFont(i, getter_AddRefs(font));
+ if (FAILED(hr)) {
+ NS_WARNING("Failed to get existing font from family.");
+ continue;
+ }
+ nsAutoCString faceName;
+ hr = GetDirectWriteFontName(font, faceName);
+ if (FAILED(hr)) {
+ continue;
+ }
+ if (faceName.Find("Ultra Bold"_ns) == kNotFound) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ nsAutoCString locale;
+ OSPreferences::GetInstance()->GetSystemLocale(locale);
+ ToLowerCase(locale);
+ NS_ConvertUTF8toUTF16 loc16(locale);
+
+ for (unsigned i = 0; i < aCollection->GetFontFamilyCount(); ++i) {
+ RefPtr<IDWriteFontFamily> family;
+ aCollection->GetFontFamily(i, getter_AddRefs(family));
+ RefPtr<IDWriteLocalizedStrings> localizedNames;
+ HRESULT hr = family->GetFamilyNames(getter_AddRefs(localizedNames));
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to get names for font-family " << i;
+ continue;
+ }
+
+ auto addFamily = [&](const nsACString& name, bool altLocale = false) {
+ nsAutoCString key;
+ key = name;
+ BuildKeyNameFromFontName(key);
+ bool bad = mBadUnderlineFamilyNames.ContainsSorted(key);
+ bool classic =
+ aForceClassicFams && aForceClassicFams->ContainsSorted(key);
+ FontVisibility visibility;
+ // Special case: hide the "Gill Sans" family that contains only UltraBold
+ // faces, as this leads to breakage on sites with CSS that targeted the
+ // Gill Sans family as found on macOS. (Bug 551313, bug 1632738)
+ // TODO (jfkthame): the ultrabold faces from Gill Sans should be treated
+ // as belonging to the Gill Sans MT family.
+ if (key.EqualsLiteral("gill sans") && allFacesUltraBold(family)) {
+ visibility = FontVisibility::Hidden;
+ } else {
+ visibility = aCollection == mSystemFonts ? GetVisibilityForFamily(name)
+ : FontVisibility::Base;
+ }
+ aFamilies.AppendElement(fontlist::Family::InitData(
+ key, name, i, visibility, aCollection != mSystemFonts, bad, classic,
+ altLocale));
+ };
+
+ unsigned count = localizedNames->GetCount();
+ if (count == 1) {
+ // This is the common case: the great majority of fonts only provide an
+ // en-US family name.
+ nsAutoCString name;
+ if (!GetNameAsUtf8(name, localizedNames, 0)) {
+ gfxWarning() << "GetNameAsUtf8 failed for index 0 in font-family " << i;
+ continue;
+ }
+ addFamily(name);
+ } else {
+ AutoTArray<nsCString, 4> names;
+ int sysLocIndex = -1;
+ for (unsigned index = 0; index < count; ++index) {
+ nsAutoCString name;
+ if (!GetNameAsUtf8(name, localizedNames, index)) {
+ gfxWarning() << "GetNameAsUtf8 failed for index " << index
+ << " in font-family " << i;
+ continue;
+ }
+ if (!names.Contains(name)) {
+ if (sysLocIndex == -1) {
+ WCHAR buf[32];
+ if (SUCCEEDED(localizedNames->GetLocaleName(index, buf, 32))) {
+ if (loc16.Equals(buf)) {
+ sysLocIndex = names.Length();
+ }
+ }
+ }
+ names.AppendElement(name);
+ }
+ }
+ // If we didn't find a name that matched the system locale, use the
+ // first (which is most often en-US).
+ if (sysLocIndex == -1) {
+ sysLocIndex = 0;
+ }
+ // Hack to work around EPSON fonts with bad names (tagged as MacRoman
+ // but actually contain MacJapanese data): if we've chosen the first
+ // name, *and* it is non-ASCII, *and* there is an alternative present,
+ // use the next option instead as being more likely to be valid.
+ if (sysLocIndex == 0 && names.Length() > 1 && !IsAscii(names[0])) {
+ sysLocIndex = 1;
+ }
+ for (unsigned index = 0; index < names.Length(); ++index) {
+ addFamily(names[index], index != static_cast<unsigned>(sysLocIndex));
+ }
+ }
+ }
+}
+
+void gfxDWriteFontList::GetFacesInitDataForFamily(
+ const fontlist::Family* aFamily, nsTArray<fontlist::Face::InitData>& aFaces,
+ bool aLoadCmaps) const {
+ IDWriteFontCollection* collection =
+#ifdef MOZ_BUNDLED_FONTS
+ aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
+#else
+ mSystemFonts;
+#endif
+ if (!collection) {
+ return;
+ }
+ RefPtr<IDWriteFontFamily> family;
+ collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family));
+ for (unsigned i = 0; i < family->GetFontCount(); ++i) {
+ RefPtr<IDWriteFont> dwFont;
+ family->GetFont(i, getter_AddRefs(dwFont));
+ if (!dwFont || dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
+ continue;
+ }
+ DWRITE_FONT_STYLE dwstyle = dwFont->GetStyle();
+ // Ignore italic styles of Meiryo because "Meiryo (Bold) Italic" has
+ // non-italic style glyphs as Japanese characters. However, using it
+ // causes serious problem if web pages wants some elements to be
+ // different style from others only with font-style. For example,
+ // <em> and <i> should be rendered as italic in the default style.
+ if (dwstyle != DWRITE_FONT_STYLE_NORMAL &&
+ aFamily->Key().AsString(SharedFontList()).EqualsLiteral("meiryo")) {
+ continue;
+ }
+ WeightRange weight(FontWeight::FromInt(dwFont->GetWeight()));
+ StretchRange stretch(FontStretchFromDWriteStretch(dwFont->GetStretch()));
+ // Try to read PSName as a unique face identifier; if this fails we'll get
+ // it directly from the 'name' table, and if that also fails we consider
+ // the face unusable.
+ MOZ_SEH_TRY {
+ nsAutoCString name;
+ RefPtr<gfxCharacterMap> charmap;
+ if (FAILED(GetDirectWriteFaceName(dwFont, PSNAME_ID, name)) ||
+ aLoadCmaps) {
+ RefPtr<IDWriteFontFace> dwFontFace;
+ if (SUCCEEDED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
+ if (name.IsEmpty()) {
+ GetPostScriptNameFromNameTable(dwFontFace, name);
+ }
+ const auto kCMAP =
+ NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
+ const char* data;
+ UINT32 size;
+ void* context;
+ BOOL exists;
+ if (aLoadCmaps) {
+ if (SUCCEEDED(dwFontFace->TryGetFontTable(
+ kCMAP, (const void**)&data, &size, &context, &exists)) &&
+ exists) {
+ charmap = new gfxCharacterMap();
+ uint32_t offset;
+ gfxFontUtils::ReadCMAP((const uint8_t*)data, size, *charmap,
+ offset);
+ dwFontFace->ReleaseFontTable(context);
+ }
+ }
+ }
+ }
+ if (name.IsEmpty()) {
+ gfxWarning() << "Failed to get name for face " << i << " in family "
+ << aFamily->Key().AsString(SharedFontList()).get();
+ continue;
+ }
+ SlantStyleRange slant(
+ dwstyle == DWRITE_FONT_STYLE_NORMAL ? FontSlantStyle::NORMAL
+ : dwstyle == DWRITE_FONT_STYLE_ITALIC ? FontSlantStyle::ITALIC
+ : FontSlantStyle::OBLIQUE);
+ aFaces.AppendElement(fontlist::Face::InitData{
+ name, uint16_t(i), false, weight, stretch, slant, charmap});
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
+ // the font resource. We'll just skip the bad face.
+ gfxCriticalNote << "Exception occurred reading faces for "
+ << aFamily->Key().AsString(SharedFontList()).get();
+ }
+ }
+}
+
+bool gfxDWriteFontList::ReadFaceNames(const fontlist::Family* aFamily,
+ const fontlist::Face* aFace,
+ nsCString& aPSName,
+ nsCString& aFullName) {
+ IDWriteFontCollection* collection =
+#ifdef MOZ_BUNDLED_FONTS
+ aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
+#else
+ mSystemFonts;
+#endif
+ RefPtr<IDWriteFontFamily> family;
+ if (FAILED(collection->GetFontFamily(aFamily->Index(),
+ getter_AddRefs(family)))) {
+ MOZ_ASSERT_UNREACHABLE("failed to get font-family");
+ return false;
+ }
+ RefPtr<IDWriteFont> dwFont;
+ if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(dwFont)))) {
+ MOZ_ASSERT_UNREACHABLE("failed to get font from family");
+ return false;
+ }
+ HRESULT ps = GetDirectWriteFaceName(dwFont, PSNAME_ID, aPSName);
+ HRESULT full = GetDirectWriteFaceName(dwFont, FULLNAME_ID, aFullName);
+ if (FAILED(ps) || FAILED(full) || aPSName.IsEmpty() || aFullName.IsEmpty()) {
+ // We'll return true if either name was found, false if both fail.
+ // Note that on older Win7 systems, GetDirectWriteFaceName may "succeed"
+ // but return an empty string, so we have to check for non-empty strings
+ // to be sure we actually got a usable name.
+
+ // Initialize result to true if either name was already found.
+ bool result = (SUCCEEDED(ps) && !aPSName.IsEmpty()) ||
+ (SUCCEEDED(full) && !aFullName.IsEmpty());
+ RefPtr<IDWriteFontFace> dwFontFace;
+ if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
+ NS_WARNING("failed to create font face");
+ return result;
+ }
+ void* context;
+ const char* data;
+ UINT32 size;
+ BOOL exists;
+ if (FAILED(dwFontFace->TryGetFontTable(
+ NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
+ (const void**)&data, &size, &context, &exists)) ||
+ !exists) {
+ NS_WARNING("failed to get name table");
+ return result;
+ }
+ MOZ_SEH_TRY {
+ // Try to read the name table entries, and ensure result is true if either
+ // one succeeds.
+ if (FAILED(ps) || aPSName.IsEmpty()) {
+ if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
+ data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aPSName))) {
+ result = true;
+ } else {
+ NS_WARNING("failed to read psname");
+ }
+ }
+ if (FAILED(full) || aFullName.IsEmpty()) {
+ if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
+ data, size, gfxFontUtils::NAME_ID_FULL, aFullName))) {
+ result = true;
+ } else {
+ NS_WARNING("failed to read fullname");
+ }
+ }
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ gfxCriticalNote << "Exception occurred reading face names for "
+ << aFamily->Key().AsString(SharedFontList()).get();
+ }
+ if (dwFontFace && context) {
+ dwFontFace->ReleaseFontTable(context);
+ }
+ return result;
+ }
+ return true;
+}
+
+void gfxDWriteFontList::ReadFaceNamesForFamily(
+ fontlist::Family* aFamily, bool aNeedFullnamePostscriptNames) {
+ if (!aFamily->IsInitialized()) {
+ if (!InitializeFamily(aFamily)) {
+ return;
+ }
+ }
+ IDWriteFontCollection* collection =
+#ifdef MOZ_BUNDLED_FONTS
+ aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
+#else
+ mSystemFonts;
+#endif
+ RefPtr<IDWriteFontFamily> family;
+ if (FAILED(collection->GetFontFamily(aFamily->Index(),
+ getter_AddRefs(family)))) {
+ return;
+ }
+ fontlist::FontList* list = SharedFontList();
+ const fontlist::Pointer* facePtrs = aFamily->Faces(list);
+ nsAutoCString familyName(aFamily->DisplayName().AsString(list));
+ nsAutoCString key(aFamily->Key().AsString(list));
+
+ MOZ_SEH_TRY {
+ // Read PS-names and fullnames of the faces, and any alternate family names
+ // (either localizations or legacy subfamily names)
+ for (unsigned i = 0; i < aFamily->NumFaces(); ++i) {
+ auto* face = facePtrs[i].ToPtr<fontlist::Face>(list);
+ if (!face) {
+ continue;
+ }
+ RefPtr<IDWriteFont> dwFont;
+ if (FAILED(family->GetFont(face->mIndex, getter_AddRefs(dwFont)))) {
+ continue;
+ }
+ RefPtr<IDWriteFontFace> dwFontFace;
+ if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
+ continue;
+ }
+
+ const char* data;
+ UINT32 size;
+ void* context;
+ BOOL exists;
+ if (FAILED(dwFontFace->TryGetFontTable(
+ NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
+ (const void**)&data, &size, &context, &exists)) ||
+ !exists) {
+ continue;
+ }
+
+ AutoTArray<nsCString, 4> otherFamilyNames;
+ gfxFontUtils::ReadOtherFamilyNamesForFace(familyName, data, size,
+ otherFamilyNames, false);
+ for (const auto& alias : otherFamilyNames) {
+ nsAutoCString key(alias);
+ ToLowerCase(key);
+ auto aliasData = mAliasTable.GetOrInsertNew(key);
+ aliasData->InitFromFamily(aFamily, familyName);
+ aliasData->mFaces.AppendElement(facePtrs[i]);
+ }
+
+ nsAutoCString psname, fullname;
+ // Bug 1854090: don't load PSname if the family name ends with ".tmp",
+ // as some PDF-related software appears to pollute the font collection
+ // with spurious re-encoded versions of standard fonts like Arial, fails
+ // to alter the PSname, and thus can result in garbled rendering because
+ // the wrong resource may be found via src:local(...).
+ if (!StringEndsWith(key, ".tmp"_ns)) {
+ if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
+ data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, psname))) {
+ ToLowerCase(psname);
+ mLocalNameTable.InsertOrUpdate(
+ psname, fontlist::LocalFaceRec::InitData(key, i));
+ }
+ }
+ if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
+ data, size, gfxFontUtils::NAME_ID_FULL, fullname))) {
+ ToLowerCase(fullname);
+ if (fullname != psname) {
+ mLocalNameTable.InsertOrUpdate(
+ fullname, fontlist::LocalFaceRec::InitData(key, i));
+ }
+ }
+
+ dwFontFace->ReleaseFontTable(context);
+ }
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ gfxCriticalNote << "Exception occurred reading names for "
+ << familyName.get();
+ }
+}
+
+enum DWriteInitError {
+ errGDIInterop = 1,
+ errSystemFontCollection = 2,
+ errNoFonts = 3
+};
+
+void gfxDWriteFontList::InitSharedFontListForPlatform() {
+ mGDIFontTableAccess = Preferences::GetBool(
+ "gfx.font_rendering.directwrite.use_gdi_table_loading", false);
+ mForceGDIClassicMaxFontSize = Preferences::GetInt(
+ "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
+ mForceGDIClassicMaxFontSize);
+
+ mSubstitutions.Clear();
+ mNonExistingFonts.Clear();
+
+ RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
+ HRESULT hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop));
+ if (FAILED(hr)) {
+ Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
+ uint32_t(errGDIInterop));
+ mSharedFontList.reset(nullptr);
+ return;
+ }
+
+ mSystemFonts = Factory::GetDWriteSystemFonts(true);
+ NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!");
+ if (!mSystemFonts) {
+ Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
+ uint32_t(errSystemFontCollection));
+ mSharedFontList.reset(nullptr);
+ return;
+ }
+#ifdef MOZ_BUNDLED_FONTS
+ // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
+ // explicit value of 0 (off) will disable them.
+ TimeStamp start1 = TimeStamp::Now();
+ if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
+ mBundledFonts = CreateBundledFontsCollection(factory);
+ }
+ TimeStamp end1 = TimeStamp::Now();
+#endif
+
+ if (XRE_IsParentProcess()) {
+ nsAutoCString classicFamilies;
+ AutoTArray<nsCString, 16> forceClassicFams;
+ nsresult rv = Preferences::GetCString(
+ "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
+ classicFamilies);
+ if (NS_SUCCEEDED(rv)) {
+ for (auto name :
+ nsCCharSeparatedTokenizer(classicFamilies, ',').ToRange()) {
+ BuildKeyNameFromFontName(name);
+ forceClassicFams.AppendElement(name);
+ }
+ forceClassicFams.Sort();
+ }
+ nsTArray<fontlist::Family::InitData> families;
+ AppendFamiliesFromCollection(mSystemFonts, families, &forceClassicFams);
+#ifdef MOZ_BUNDLED_FONTS
+ if (mBundledFonts) {
+ TimeStamp start2 = TimeStamp::Now();
+ AppendFamiliesFromCollection(mBundledFonts, families);
+ TimeStamp end2 = TimeStamp::Now();
+ Telemetry::Accumulate(
+ Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
+ (end1 - start1).ToMilliseconds() + (end2 - start2).ToMilliseconds());
+ }
+#endif
+ SharedFontList()->SetFamilyNames(families);
+ GetPrefsAndStartLoader();
+ }
+
+ if (!SharedFontList()->Initialized()) {
+ return;
+ }
+
+ GetDirectWriteSubstitutes();
+ GetFontSubstitutes();
+}
+
+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();
+
+ RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
+
+ hr = factory->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
+
+ mSystemFonts = Factory::GetDWriteSystemFonts(true);
+ NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!");
+
+ if (!mSystemFonts) {
+ Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
+ uint32_t(errSystemFontCollection));
+ return NS_ERROR_FAILURE;
+ }
+
+#ifdef MOZ_BUNDLED_FONTS
+ // Get bundled fonts before the system collection, so that in the case of
+ // duplicate names, we have recorded the family as bundled (and therefore
+ // available regardless of visibility settings).
+ // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
+ // explicit value of 0 (off) will disable them.
+ if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
+ TimeStamp start = TimeStamp::Now();
+ mBundledFonts = CreateBundledFontsCollection(factory);
+ if (mBundledFonts) {
+ GetFontsFromCollection(mBundledFonts);
+ }
+ TimeStamp end = TimeStamp::Now();
+ Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
+ (end - start).ToMilliseconds());
+ }
+#endif
+ const uint32_t kBundledCount = mFontFamilies.Count();
+
+ 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() > kBundledCount,
+ "no fonts found in the system fontlist -- holy crap batman!");
+ if (mFontFamilies.Count() == kBundledCount) {
+ Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
+ uint32_t(errNoFonts));
+ return NS_ERROR_FAILURE;
+ }
+
+ QueryPerformanceCounter(&t4); // iterate over system fonts
+
+ 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
+
+ nsAutoCString nameGillSans("Gill Sans");
+ nsAutoCString nameGillSansMT("Gill Sans MT");
+ BuildKeyNameFromFontName(nameGillSans);
+ BuildKeyNameFromFontName(nameGillSansMT);
+
+ gfxFontFamily* gillSansFamily = mFontFamilies.GetWeak(nameGillSans);
+ gfxFontFamily* gillSansMTFamily = mFontFamilies.GetWeak(nameGillSansMT);
+
+ if (gillSansFamily && gillSansMTFamily) {
+ gillSansFamily->FindStyleVariations();
+
+ gillSansFamily->ReadLock();
+ const auto& faces = gillSansFamily->GetFontList();
+
+ bool allUltraBold = true;
+ for (const auto& face : faces) {
+ // does the face have 'Ultra Bold' in the name?
+ if (face->Name().Find("Ultra Bold"_ns) == -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 (const auto& face : faces) {
+ // change the entry's family name to match its adoptive family
+ face->mFamilyName = gillSansMTFamily->Name();
+ gillSansMTFamily->AddFontEntry(face);
+
+ if (LOG_FONTLIST_ENABLED()) {
+ nsAutoCString weightString;
+ face->Weight().ToString(weightString);
+ LOG_FONTLIST(
+ ("(fontlist) moved (%s) to family (%s)"
+ " with style: %s weight: %s stretch: %d",
+ face->Name().get(), gillSansMTFamily->Name().get(),
+ (face->IsItalic()) ? "italic"
+ : (face->IsOblique() ? "oblique" : "normal"),
+ weightString.get(), face->Stretch().AsScalar()));
+ }
+ }
+ gillSansFamily->ReadUnlock();
+
+ // remove Gill Sans
+ mFontFamilies.Remove(nameGillSans);
+ } else {
+ gillSansFamily->ReadUnlock();
+ }
+ }
+
+ nsAutoCString classicFamilies;
+ nsresult rv = Preferences::GetCString(
+ "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
+ classicFamilies);
+ if (NS_SUCCEEDED(rv)) {
+ nsCCharSeparatedTokenizer tokenizer(classicFamilies, ',');
+ while (tokenizer.hasMoreTokens()) {
+ nsAutoCString 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;
+ }
+
+ nsAutoCString name;
+ if (!GetEnglishOrFirstName(name, names)) {
+ continue;
+ }
+ nsAutoCString familyName(
+ name); // keep a copy before we lowercase it as a key
+
+ BuildKeyNameFromFontName(name);
+
+ RefPtr<gfxFontFamily> fam;
+
+ if (mFontFamilies.GetWeak(name)) {
+ continue;
+ }
+
+ FontVisibility visibility = aCollection == mSystemFonts
+ ? GetVisibilityForFamily(familyName)
+ : FontVisibility::Base;
+
+ fam = new gfxDWriteFontFamily(familyName, visibility, family,
+ aCollection == mSystemFonts);
+ if (!fam) {
+ continue;
+ }
+
+ if (mBadUnderlineFamilyNames.ContainsSorted(name)) {
+ fam->SetBadUnderlineFamily();
+ }
+ mFontFamilies.InsertOrUpdate(name, RefPtr{fam});
+
+ // now add other family name localizations, if present
+ uint32_t nameCount = names->GetCount();
+ uint32_t nameIndex;
+
+ if (nameCount > 1) {
+ UINT32 englishIdx = 0;
+ BOOL exists;
+ // if this fails/doesn't exist, we'll have used name index 0,
+ // so that's the one we'll want to skip here
+ names->FindLocaleName(L"en-us", &englishIdx, &exists);
+ AutoTArray<nsCString, 4> otherFamilyNames;
+ 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;
+ }
+
+ NS_ConvertUTF16toUTF8 locName(localizedName.Elements());
+
+ if (!familyName.Equals(locName)) {
+ otherFamilyNames.AppendElement(locName);
+ }
+ }
+ if (!otherFamilyNames.IsEmpty()) {
+ AddOtherFamilyNames(fam, otherFamilyNames);
+ }
+ }
+
+ // at this point, all family names have been read in
+ fam->SetOtherFamilyNamesInitialized();
+ }
+}
+
+static void RemoveCharsetFromFontSubstitute(nsACString& aName) {
+ int32_t comma = aName.FindChar(',');
+ 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;
+ }
+
+ NS_ConvertUTF16toUTF8 substituteName((char16_t*)aliasName);
+ NS_ConvertUTF16toUTF8 actualFontName((char16_t*)actualName);
+ RemoveCharsetFromFontSubstitute(substituteName);
+ BuildKeyNameFromFontName(substituteName);
+ RemoveCharsetFromFontSubstitute(actualFontName);
+ BuildKeyNameFromFontName(actualFontName);
+ if (SharedFontList()) {
+ // Skip substitution if the original font is available, unless the option
+ // to apply substitutions unconditionally is enabled.
+ if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) {
+ // Font substitutions are recorded for the canonical family names; we
+ // don't need FindFamily to consider localized aliases when searching.
+ if (SharedFontList()->FindFamily(substituteName,
+ /*aPrimaryNameOnly*/ true)) {
+ continue;
+ }
+ }
+ if (SharedFontList()->FindFamily(actualFontName,
+ /*aPrimaryNameOnly*/ true)) {
+ mSubstitutions.InsertOrUpdate(substituteName,
+ MakeUnique<nsCString>(actualFontName));
+ } else if (mSubstitutions.Get(actualFontName)) {
+ mSubstitutions.InsertOrUpdate(
+ substituteName,
+ MakeUnique<nsCString>(*mSubstitutions.Get(actualFontName)));
+ } else {
+ mNonExistingFonts.AppendElement(substituteName);
+ }
+ } else {
+ gfxFontFamily* ff;
+ if (!actualFontName.IsEmpty() &&
+ (ff = mFontFamilies.GetWeak(actualFontName))) {
+ mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff});
+ } else {
+ mNonExistingFonts.AppendElement(substituteName);
+ }
+ }
+ }
+ return NS_OK;
+}
+
+struct FontSubstitution {
+ const char* aliasName;
+ const char* actualName;
+};
+
+static const FontSubstitution sDirectWriteSubs[] = {
+ {"MS Sans Serif", "Microsoft Sans Serif"},
+ {"MS Serif", "Times New Roman"},
+ {"Courier", "Courier New"},
+ {"Small Fonts", "Arial"},
+ {"Roman", "Times New Roman"},
+ {"Script", "Mistral"}};
+
+void gfxDWriteFontList::GetDirectWriteSubstitutes() {
+ for (uint32_t i = 0; i < ArrayLength(sDirectWriteSubs); ++i) {
+ const FontSubstitution& sub(sDirectWriteSubs[i]);
+ nsAutoCString substituteName(sub.aliasName);
+ BuildKeyNameFromFontName(substituteName);
+ if (SharedFontList()) {
+ // Skip substitution if the original font is available, unless the option
+ // to apply substitutions unconditionally is enabled.
+ if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) {
+ // We don't need FindFamily to consider localized aliases when searching
+ // for the DirectWrite substitutes, we know the canonical names.
+ if (SharedFontList()->FindFamily(substituteName,
+ /*aPrimaryNameOnly*/ true)) {
+ continue;
+ }
+ }
+ nsAutoCString actualFontName(sub.actualName);
+ BuildKeyNameFromFontName(actualFontName);
+ if (SharedFontList()->FindFamily(actualFontName,
+ /*aPrimaryNameOnly*/ true)) {
+ mSubstitutions.InsertOrUpdate(substituteName,
+ MakeUnique<nsCString>(actualFontName));
+ } else {
+ mNonExistingFonts.AppendElement(substituteName);
+ }
+ } else {
+ if (nullptr != mFontFamilies.GetWeak(substituteName)) {
+ // don't do the substitution if user actually has a usable font
+ // with this name installed
+ continue;
+ }
+ nsAutoCString actualFontName(sub.actualName);
+ BuildKeyNameFromFontName(actualFontName);
+ gfxFontFamily* ff;
+ if (nullptr != (ff = mFontFamilies.GetWeak(actualFontName))) {
+ mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff});
+ } else {
+ mNonExistingFonts.AppendElement(substituteName);
+ }
+ }
+ }
+}
+
+bool gfxDWriteFontList::FindAndAddFamiliesLocked(
+ nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
+ const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
+ FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
+ gfxFloat aDevToCssSize) {
+ nsAutoCString keyName(aFamily);
+ BuildKeyNameFromFontName(keyName);
+
+ if (SharedFontList()) {
+ nsACString* subst = mSubstitutions.Get(keyName);
+ if (subst) {
+ keyName = *subst;
+ }
+ } else {
+ gfxFontFamily* ff = mFontSubstitutes.GetWeak(keyName);
+ FontVisibility level =
+ aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
+ if (ff && IsVisibleToCSS(*ff, level)) {
+ aOutput->AppendElement(FamilyAndGeneric(ff, aGeneric));
+ return true;
+ }
+ }
+
+ if (mNonExistingFonts.Contains(keyName)) {
+ return false;
+ }
+
+ return gfxPlatformFontList::FindAndAddFamiliesLocked(
+ aPresContext, aGeneric, keyName, aOutput, aFlags, aStyle, aLanguage,
+ aDevToCssSize);
+}
+
+void gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const {
+ gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+
+ AutoLock lock(mLock);
+
+ // We are a singleton, so include the font loader singleton's memory.
+ MOZ_ASSERT(static_cast<const gfxPlatformFontList*>(this) ==
+ gfxPlatformFontList::PlatformFontList());
+ gfxDWriteFontFileLoader* loader = static_cast<gfxDWriteFontFileLoader*>(
+ gfxDWriteFontFileLoader::Instance());
+ aSizes->mLoaderSize += loader->SizeOfIncludingThis(aMallocSizeOf);
+
+ 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, nsCString& 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;
+ }
+
+ if (!GetEnglishOrFirstName(aFamilyName, familyNames)) {
+ return E_FAIL;
+ }
+
+ 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(
+ nsPresContext* aPresContext, const uint32_t aCh, Script aRunScript,
+ const gfxFontStyle* aMatchStyle, FontFamily& aMatchedFamily) {
+ HRESULT hr;
+
+ RefPtr<IDWriteFactory> dwFactory = Factory::GetDWriteFactory();
+ if (!dwFactory) {
+ return nullptr;
+ }
+
+ // initialize fallback renderer
+ if (!mFallbackRenderer) {
+ mFallbackRenderer = new DWriteFontFallbackRenderer(dwFactory);
+ }
+ if (!mFallbackRenderer->IsValid()) {
+ return nullptr;
+ }
+
+ // 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
+ MOZ_SEH_TRY {
+ hr = fallbackLayout->Draw(nullptr, mFallbackRenderer, 50.0f, 50.0f);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ gfxCriticalNote << "Exception occurred during DWrite font fallback";
+ return nullptr;
+ }
+
+ FontFamily family =
+ FindFamily(aPresContext, mFallbackRenderer->FallbackFamilyName());
+ if (!family.IsNull()) {
+ gfxFontEntry* fontEntry = nullptr;
+ if (family.mShared) {
+ auto face =
+ family.mShared->FindFaceForStyle(SharedFontList(), *aMatchStyle);
+ if (face) {
+ fontEntry = GetOrCreateFontEntry(face, family.mShared);
+ }
+ } else {
+ fontEntry = family.mUnshared->FindFontForStyle(*aMatchStyle);
+ }
+ 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() = default;
+
+ // loads font data for all members of a given family
+ virtual void LoadFontFamilyData(const nsACString& aFamilyName);
+
+ private:
+ RefPtr<IDWriteFontCollection> mSystemFonts;
+#ifdef MOZ_BUNDLED_FONTS
+ RefPtr<IDWriteFontCollection> mBundledFonts;
+#endif
+};
+
+void DirectWriteFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
+ // lookup the family
+ NS_ConvertUTF8toUTF16 famName(aFamilyName);
+
+ HRESULT hr;
+ BOOL exists = false;
+
+ uint32_t index;
+ RefPtr<IDWriteFontFamily> family;
+ hr = mSystemFonts->FindFamilyName((const wchar_t*)famName.get(), &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((const wchar_t*)famName.get(), &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
+ nsCString fullID(aFamilyName);
+ nsAutoCString 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) {
+ MOZ_SEH_TRY {
+ gfxFontUtils::ReadCanonicalName(nameData, nameSize,
+ gfxFontUtils::NAME_ID_FULL,
+ fontData.mFullName);
+ gfxFontUtils::ReadCanonicalName(nameData, nameSize,
+ gfxFontUtils::NAME_ID_POSTSCRIPT,
+ fontData.mPostscriptName);
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ gfxCriticalNote << "Exception occurred reading names for "
+ << PromiseFlatCString(aFamilyName).get();
+ }
+ 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) && exists) {
+ bool cmapLoaded = false;
+ RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+ uint32_t offset;
+ MOZ_SEH_TRY {
+ if (cmapData && cmapSize > 0 &&
+ NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap,
+ offset))) {
+ fontData.mCharacterMap = charmap;
+ fontData.mUVSOffset = offset;
+ cmapLoaded = true;
+ mLoadStats.cmaps++;
+ }
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ gfxCriticalNote << "Exception occurred reading cmaps for "
+ << PromiseFlatCString(aFamilyName).get();
+ }
+ dwFontFace->ReleaseFontTable(ctx);
+ haveData = haveData || cmapLoaded;
+ }
+ }
+
+ // if have data, load
+ if (haveData) {
+ mFontFaceData.InsertOrUpdate(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();
+}
+
+gfxFontFamily* gfxDWriteFontList::CreateFontFamily(
+ const nsACString& aName, FontVisibility aVisibility) const {
+ return new gfxDWriteFontFamily(aName, aVisibility, nullptr);
+}
+
+#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() = default;
+
+ RefPtr<IDWriteFactory> mFactory;
+
+ nsCOMPtr<nsIFile> mFontDir;
+ nsCOMPtr<nsIDirectoryEnumerator> 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() = default;
+};
+
+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(u"fonts"_ns))) {
+ 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