Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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,
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(fontlist::Family* aFamily,
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 = static_cast<fontlist::Face*>(facePtrs[i].ToPtr(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;
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);