summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxGDIFontList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/thebes/gfxGDIFontList.cpp')
-rw-r--r--gfx/thebes/gfxGDIFontList.cpp1107
1 files changed, 1107 insertions, 0 deletions
diff --git a/gfx/thebes/gfxGDIFontList.cpp b/gfx/thebes/gfxGDIFontList.cpp
new file mode 100644
index 0000000000..3c4c7bce07
--- /dev/null
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -0,0 +1,1107 @@
+/* -*- 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/DebugOnly.h"
+#include <algorithm>
+
+#include "mozilla/Logging.h"
+#include "mozilla/TextUtils.h"
+#include "mozilla/Sprintf.h"
+
+#include "gfxGDIFontList.h"
+#include "gfxWindowsPlatform.h"
+#include "gfxUserFontSet.h"
+#include "gfxFontUtils.h"
+#include "gfxGDIFont.h"
+
+#include "nsServiceManagerUtils.h"
+#include "nsTArray.h"
+#include "nsUnicharUtils.h"
+
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsPresContext.h"
+#include "gfxFontConstants.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/Telemetry.h"
+
+#include <usp10.h>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+#define ROUND(x) floor((x) + 0.5)
+
+#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_CMAPDATA_ENABLED() \
+ MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
+
+static __inline void BuildKeyNameFromFontName(nsAString& aName) {
+ if (aName.Length() >= LF_FACESIZE) aName.Truncate(LF_FACESIZE - 1);
+ ToLowerCase(aName);
+}
+
+// Implementation of gfxPlatformFontList for Win32 GDI,
+// using GDI font enumeration APIs to get the list of fonts
+
+class WinUserFontData : public gfxUserFontData {
+ public:
+ explicit WinUserFontData(HANDLE aFontRef) : mFontRef(aFontRef) {}
+
+ virtual ~WinUserFontData() {
+ DebugOnly<BOOL> success;
+ success = RemoveFontMemResourceEx(mFontRef);
+#if DEBUG
+ if (!success) {
+ char buf[256];
+ SprintfLiteral(
+ buf,
+ "error deleting font handle (%p) - RemoveFontMemResourceEx failed",
+ mFontRef);
+ NS_ASSERTION(success, buf);
+ }
+#endif
+ }
+
+ HANDLE mFontRef;
+};
+
+BYTE FontTypeToOutPrecision(uint8_t fontType) {
+ BYTE ret;
+ switch (fontType) {
+ case GFX_FONT_TYPE_TT_OPENTYPE:
+ case GFX_FONT_TYPE_TRUETYPE:
+ ret = OUT_TT_ONLY_PRECIS;
+ break;
+ case GFX_FONT_TYPE_PS_OPENTYPE:
+ ret = OUT_PS_ONLY_PRECIS;
+ break;
+ case GFX_FONT_TYPE_TYPE1:
+ ret = OUT_OUTLINE_PRECIS;
+ break;
+ case GFX_FONT_TYPE_RASTER:
+ ret = OUT_RASTER_PRECIS;
+ break;
+ case GFX_FONT_TYPE_DEVICE:
+ ret = OUT_DEVICE_PRECIS;
+ break;
+ default:
+ ret = OUT_DEFAULT_PRECIS;
+ }
+ return ret;
+}
+
+/***************************************************************
+ *
+ * GDIFontEntry
+ *
+ */
+
+GDIFontEntry::GDIFontEntry(const nsACString& aFaceName,
+ gfxWindowsFontType aFontType, SlantStyleRange aStyle,
+ WeightRange aWeight, StretchRange aStretch,
+ gfxUserFontData* aUserFontData)
+ : gfxFontEntry(aFaceName), mFontType(aFontType), mForceGDI(false) {
+ mUserFontData.reset(aUserFontData);
+ mStyleRange = aStyle;
+ mWeightRange = aWeight;
+ mStretchRange = aStretch;
+ if (IsType1()) {
+ mForceGDI = true;
+ }
+ mIsDataUserFont = aUserFontData != nullptr;
+
+ InitLogFont(aFaceName, aFontType);
+}
+
+gfxFontEntry* GDIFontEntry::Clone() const {
+ MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
+ return new GDIFontEntry(Name(), mFontType, SlantStyle(), Weight(), Stretch(),
+ nullptr);
+}
+
+nsresult GDIFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
+ AUTO_PROFILER_LABEL("GDIFontEntry::ReadCMAP", OTHER);
+
+ // attempt this once, if errors occur leave a blank cmap
+ if (mCharacterMap) {
+ return NS_OK;
+ }
+
+ // skip non-SFNT fonts completely
+ if (mFontType != GFX_FONT_TYPE_PS_OPENTYPE &&
+ mFontType != GFX_FONT_TYPE_TT_OPENTYPE &&
+ mFontType != GFX_FONT_TYPE_TRUETYPE) {
+ RefPtr<gfxCharacterMap> cmap = new gfxCharacterMap();
+ cmap->mBuildOnTheFly = true;
+ if (mCharacterMap.compareExchange(nullptr, cmap.get())) {
+ Unused << cmap.forget();
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ 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();
+ AutoTArray<uint8_t, 16384> cmap;
+ rv = CopyFontTable(kCMAP, cmap);
+
+ if (NS_SUCCEEDED(rv)) {
+ rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(), *charmap,
+ uvsOffset);
+ }
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
+ charmap = pfl->FindCharMap(charmap);
+ mHasCmapTable = true;
+ } else {
+ // if error occurred, initialize to null cmap
+ charmap = new gfxCharacterMap();
+ // For fonts where we failed to read the character map,
+ // we can take a slow path to look up glyphs character by character
+ charmap->mBuildOnTheFly = true;
+ }
+ if (mCharacterMap.compareExchange(nullptr, charmap.get())) {
+ charmap.get()->AddRef();
+ }
+
+ LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zd 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;
+}
+
+gfxFont* GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle) {
+ return new gfxGDIFont(this, aFontStyle);
+}
+
+nsresult GDIFontEntry::CopyFontTable(uint32_t aTableTag,
+ nsTArray<uint8_t>& aBuffer) {
+ if (!IsTrueType()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoDC dc;
+ AutoSelectFont font(dc.GetDC(), &mLogFont);
+ if (font.IsValid()) {
+ uint32_t tableSize = ::GetFontData(
+ dc.GetDC(), NativeEndian::swapToBigEndian(aTableTag), 0, nullptr, 0);
+ if (tableSize != GDI_ERROR) {
+ if (aBuffer.SetLength(tableSize, fallible)) {
+ ::GetFontData(dc.GetDC(), NativeEndian::swapToBigEndian(aTableTag), 0,
+ aBuffer.Elements(), tableSize);
+ return NS_OK;
+ }
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+already_AddRefed<UnscaledFontGDI> GDIFontEntry::LookupUnscaledFont(
+ HFONT aFont) {
+ RefPtr<UnscaledFontGDI> unscaledFont(mUnscaledFont);
+ if (!unscaledFont) {
+ LOGFONT lf;
+ GetObject(aFont, sizeof(LOGFONT), &lf);
+ unscaledFont = new UnscaledFontGDI(lf);
+ mUnscaledFont = unscaledFont;
+ }
+
+ return unscaledFont.forget();
+}
+
+void GDIFontEntry::FillLogFont(LOGFONTW* aLogFont, LONG aWeight,
+ gfxFloat aSize) {
+ memcpy(aLogFont, &mLogFont, sizeof(LOGFONTW));
+
+ aLogFont->lfHeight = (LONG)-ROUND(aSize);
+
+ if (aLogFont->lfHeight == 0) {
+ aLogFont->lfHeight = -1;
+ }
+
+ // If a non-zero weight is passed in, use this to override the original
+ // weight in the entry's logfont. This is used to control synthetic bolding
+ // for installed families with no bold face, and for downloaded fonts
+ // (but NOT for local user fonts, because it could cause a different,
+ // glyph-incompatible face to be used)
+ if (aWeight != 0) {
+ aLogFont->lfWeight = aWeight;
+ }
+
+ // for non-local() user fonts, we never want to apply italics here;
+ // if the face is described as italic, we should use it as-is,
+ // and if it's not, but then the element is styled italic, we'll use
+ // a cairo transform to create fake italic (oblique)
+ if (mIsDataUserFont) {
+ aLogFont->lfItalic = 0;
+ }
+}
+
+#define MISSING_GLYPH \
+ 0x1F // glyph index returned for missing characters
+ // on WinXP with .fon fonts, but not Type1 (.pfb)
+
+bool GDIFontEntry::TestCharacterMap(uint32_t aCh) {
+ if (!mCharacterMap) {
+ ReadCMAP();
+ NS_ASSERTION(mCharacterMap, "failed to initialize a character map");
+ }
+
+ if (GetCharacterMap()->mBuildOnTheFly) {
+ if (aCh > 0xFFFF) return false;
+
+ // previous code was using the group style
+ gfxFontStyle fakeStyle;
+ if (!IsUpright()) {
+ fakeStyle.style = FontSlantStyle::ITALIC;
+ }
+ fakeStyle.weight = Weight().Min();
+
+ RefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, nullptr);
+ if (!tempFont || !tempFont->Valid()) return false;
+ gfxGDIFont* font = static_cast<gfxGDIFont*>(tempFont.get());
+
+ HDC dc = GetDC((HWND) nullptr);
+ SetGraphicsMode(dc, GM_ADVANCED);
+ HFONT hfont = font->GetHFONT();
+ HFONT oldFont = (HFONT)SelectObject(dc, hfont);
+
+ wchar_t str[1] = {(wchar_t)aCh};
+ WORD glyph[1];
+
+ bool hasGlyph = false;
+
+ // Bug 573038 - in some cases GetGlyphIndicesW returns 0xFFFF for a
+ // missing glyph or 0x1F in other cases to indicate the "invalid"
+ // glyph. Map both cases to "not found"
+ if (IsType1() || mForceGDI) {
+ // Type1 fonts and uniscribe APIs don't get along.
+ // ScriptGetCMap will return E_HANDLE
+ DWORD ret =
+ GetGlyphIndicesW(dc, str, 1, glyph, GGI_MARK_NONEXISTING_GLYPHS);
+ if (ret != GDI_ERROR && glyph[0] != 0xFFFF &&
+ (IsType1() || glyph[0] != MISSING_GLYPH)) {
+ hasGlyph = true;
+ }
+ } else {
+ // ScriptGetCMap works better than GetGlyphIndicesW
+ // for things like bitmap/vector fonts
+ SCRIPT_CACHE sc = nullptr;
+ HRESULT rv = ScriptGetCMap(dc, &sc, str, 1, 0, glyph);
+ if (rv == S_OK) hasGlyph = true;
+ }
+
+ SelectObject(dc, oldFont);
+ ReleaseDC(nullptr, dc);
+
+ if (hasGlyph) {
+ GetCharacterMap()->set(aCh);
+ return true;
+ }
+ } else {
+ // font had a cmap so simply check that
+ return GetCharacterMap()->test(aCh);
+ }
+
+ return false;
+}
+
+void GDIFontEntry::InitLogFont(const nsACString& aName,
+ gfxWindowsFontType aFontType) {
+#define CLIP_TURNOFF_FONTASSOCIATION 0x40
+
+ mLogFont.lfHeight = -1;
+
+ // Fill in logFont structure
+ mLogFont.lfWidth = 0;
+ mLogFont.lfEscapement = 0;
+ mLogFont.lfOrientation = 0;
+ mLogFont.lfUnderline = FALSE;
+ mLogFont.lfStrikeOut = FALSE;
+ mLogFont.lfCharSet = DEFAULT_CHARSET;
+ mLogFont.lfOutPrecision = FontTypeToOutPrecision(aFontType);
+ mLogFont.lfClipPrecision = CLIP_TURNOFF_FONTASSOCIATION;
+ mLogFont.lfQuality = DEFAULT_QUALITY;
+ mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+ // always force lfItalic if we want it. Font selection code will
+ // do its best to give us an italic font entry, but if no face exists
+ // it may give us a regular one based on weight. Windows should
+ // do fake italic for us in that case.
+ mLogFont.lfItalic = !IsUpright();
+ mLogFont.lfWeight = Weight().Min().ToIntRounded();
+
+ NS_ConvertUTF8toUTF16 name(aName);
+ int len = std::min<int>(name.Length(), LF_FACESIZE - 1);
+ memcpy(&mLogFont.lfFaceName, name.BeginReading(), len * sizeof(char16_t));
+ mLogFont.lfFaceName[len] = '\0';
+}
+
+GDIFontEntry* GDIFontEntry::CreateFontEntry(const nsACString& aName,
+ gfxWindowsFontType aFontType,
+ SlantStyleRange aStyle,
+ WeightRange aWeight,
+ StretchRange aStretch,
+ gfxUserFontData* aUserFontData) {
+ // jtdfix - need to set charset, pitch/family
+
+ return new GDIFontEntry(aName, aFontType, aStyle, aWeight, aStretch,
+ aUserFontData);
+}
+
+void GDIFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const {
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+/***************************************************************
+ *
+ * GDIFontFamily
+ *
+ */
+
+static bool ShouldIgnoreItalicStyle(const nsACString& aName) {
+ // 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.
+ return aName.EqualsLiteral("Meiryo") ||
+ aName.EqualsLiteral(
+ "\xe3\x83\xa1\xe3\x82\xa4\xe3\x83\xaa\xe3\x82\xaa");
+}
+
+int CALLBACK GDIFontFamily::FamilyAddStylesProc(
+ const ENUMLOGFONTEXW* lpelfe, const NEWTEXTMETRICEXW* nmetrics,
+ DWORD fontType, LPARAM data) MOZ_NO_THREAD_SAFETY_ANALYSIS {
+ const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
+ LOGFONTW logFont = lpelfe->elfLogFont;
+ GDIFontFamily* ff = reinterpret_cast<GDIFontFamily*>(data);
+ MOZ_ASSERT(ff->mLock.LockedForWritingByCurrentThread());
+
+ if (logFont.lfItalic && ShouldIgnoreItalicStyle(ff->mName)) {
+ return 1;
+ }
+
+ // Some fonts claim to support things > 900, but we don't so clamp the sizes
+ logFont.lfWeight = clamped(logFont.lfWeight, LONG(100), LONG(900));
+
+ gfxWindowsFontType feType =
+ GDIFontEntry::DetermineFontType(metrics, fontType);
+
+ GDIFontEntry* fe = nullptr;
+ for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
+ fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get());
+ if (feType > fe->mFontType) {
+ // if the new type is better than the old one, remove the old entries
+ ff->mAvailableFonts.RemoveElementAt(i);
+ --i;
+ } else if (feType < fe->mFontType) {
+ // otherwise if the new type is worse, skip it
+ return 1;
+ }
+ }
+
+ for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
+ fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get());
+ // check if we already know about this face
+ if (fe->Weight().Min() == FontWeight::FromInt(int32_t(logFont.lfWeight)) &&
+ fe->IsItalic() == (logFont.lfItalic == 0xFF)) {
+ // update the charset bit here since this could be different
+ // XXX Can we still do this now that we store mCharset
+ // on the font family rather than the font entry?
+ ff->mCharset.set(metrics.tmCharSet);
+ return 1;
+ }
+ }
+
+ // We can't set the hasItalicFace flag correctly here,
+ // because we might not have seen the family's italic face(s) yet.
+ // So we'll set that flag for all members after loading all the faces.
+ auto italicStyle = (logFont.lfItalic == 0xFF ? FontSlantStyle::ITALIC
+ : FontSlantStyle::NORMAL);
+ fe = GDIFontEntry::CreateFontEntry(
+ NS_ConvertUTF16toUTF8(lpelfe->elfFullName), feType,
+ SlantStyleRange(italicStyle),
+ WeightRange(FontWeight::FromInt(int32_t(logFont.lfWeight))),
+ StretchRange(FontStretch::NORMAL), nullptr);
+ if (!fe) {
+ return 1;
+ }
+
+ ff->AddFontEntryLocked(fe);
+
+ if (LOG_FONTLIST_ENABLED()) {
+ LOG_FONTLIST(
+ ("(fontlist) added (%s) to family (%s)"
+ " with style: %s weight: %ld stretch: normal",
+ fe->Name().get(), ff->Name().get(),
+ (logFont.lfItalic == 0xff) ? "italic" : "normal", logFont.lfWeight));
+ }
+ return 1;
+}
+
+void GDIFontFamily::FindStyleVariationsLocked(FontInfoData* aFontInfoData) {
+ if (mHasStyles) {
+ return;
+ }
+ mHasStyles = true;
+
+ HDC hdc = GetDC(nullptr);
+ SetGraphicsMode(hdc, GM_ADVANCED);
+
+ LOGFONTW logFont;
+ memset(&logFont, 0, sizeof(LOGFONTW));
+ logFont.lfCharSet = DEFAULT_CHARSET;
+ logFont.lfPitchAndFamily = 0;
+ NS_ConvertUTF8toUTF16 name(mName);
+ uint32_t l = std::min<uint32_t>(name.Length(), LF_FACESIZE - 1);
+ memcpy(logFont.lfFaceName, name.get(), l * sizeof(char16_t));
+
+ EnumFontFamiliesExW(hdc, &logFont,
+ (FONTENUMPROCW)GDIFontFamily::FamilyAddStylesProc,
+ (LPARAM)this, 0);
+ if (LOG_FONTLIST_ENABLED() && mAvailableFonts.Length() == 0) {
+ LOG_FONTLIST(
+ ("(fontlist) no styles available in family \"%s\"", mName.get()));
+ }
+
+ ReleaseDC(nullptr, hdc);
+
+ if (mIsBadUnderlineFamily) {
+ SetBadUnderlineFonts();
+ }
+
+ CheckForSimpleFamily();
+}
+
+/***************************************************************
+ *
+ * gfxGDIFontList
+ *
+ */
+
+gfxGDIFontList::gfxGDIFontList() : mFontSubstitutes(32) {
+#ifdef MOZ_BUNDLED_FONTS
+ if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
+ TimeStamp start = TimeStamp::Now();
+ ActivateBundledFonts();
+ TimeStamp end = TimeStamp::Now();
+ Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
+ (end - start).ToMilliseconds());
+ }
+#endif
+}
+
+static void RemoveCharsetFromFontSubstitute(nsAString& aName) {
+ int32_t comma = aName.FindChar(char16_t(','));
+ if (comma >= 0) aName.Truncate(comma);
+}
+
+#define MAX_VALUE_NAME 512
+#define MAX_VALUE_DATA 512
+
+nsresult gfxGDIFontList::GetFontSubstitutes() {
+ HKEY hKey;
+ DWORD i, rv, lenAlias, lenActual, valueType;
+ WCHAR aliasName[MAX_VALUE_NAME];
+ WCHAR actualName[MAX_VALUE_DATA];
+
+ if (RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
+ 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
+ aliasName[0] = 0;
+ lenAlias = ArrayLength(aliasName);
+ actualName[0] = 0;
+ lenActual = sizeof(actualName);
+ rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
+ (LPBYTE)actualName, &lenActual);
+
+ if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
+ continue;
+ }
+
+ if (aliasName[0] == WCHAR('@')) {
+ continue;
+ }
+
+ nsAutoString substituteName((char16_t*)aliasName);
+ nsAutoString actualFontName((char16_t*)actualName);
+ RemoveCharsetFromFontSubstitute(substituteName);
+ BuildKeyNameFromFontName(substituteName);
+ RemoveCharsetFromFontSubstitute(actualFontName);
+ BuildKeyNameFromFontName(actualFontName);
+ gfxFontFamily* ff;
+ NS_ConvertUTF16toUTF8 substitute(substituteName);
+ NS_ConvertUTF16toUTF8 actual(actualFontName);
+ if (!actual.IsEmpty() && (ff = mFontFamilies.GetWeak(actual))) {
+ mFontSubstitutes.InsertOrUpdate(substitute, RefPtr{ff});
+ } else {
+ mNonExistingFonts.AppendElement(substitute);
+ }
+ }
+
+ // "Courier" on a default Windows install is an ugly bitmap font.
+ // If there is no substitution for Courier in the registry
+ // substitute "Courier" with "Courier New".
+ nsAutoString substituteName;
+ substituteName.AssignLiteral("Courier");
+ BuildKeyNameFromFontName(substituteName);
+ NS_ConvertUTF16toUTF8 substitute(substituteName);
+ if (!mFontSubstitutes.GetWeak(substitute)) {
+ gfxFontFamily* ff;
+ nsAutoString actualFontName;
+ actualFontName.AssignLiteral("Courier New");
+ BuildKeyNameFromFontName(actualFontName);
+ NS_ConvertUTF16toUTF8 actual(actualFontName);
+ ff = mFontFamilies.GetWeak(actual);
+ if (ff) {
+ mFontSubstitutes.InsertOrUpdate(substitute, RefPtr{ff});
+ }
+ }
+ return NS_OK;
+}
+
+nsresult gfxGDIFontList::InitFontListForPlatform() {
+ Telemetry::AutoTimer<Telemetry::GDI_INITFONTLIST_TOTAL> timer;
+
+ mFontSubstitutes.Clear();
+ mNonExistingFonts.Clear();
+
+ // iterate over available families
+ LOGFONTW logfont;
+ memset(&logfont, 0, sizeof(logfont));
+ logfont.lfCharSet = DEFAULT_CHARSET;
+
+ AutoDC hdc;
+ (void)EnumFontFamiliesExW(hdc.GetDC(), &logfont,
+ (FONTENUMPROCW)&EnumFontFamExProc, 0, 0);
+
+ GetFontSubstitutes();
+
+ GetPrefsAndStartLoader();
+
+ return NS_OK;
+}
+
+int CALLBACK gfxGDIFontList::EnumFontFamExProc(ENUMLOGFONTEXW* lpelfe,
+ NEWTEXTMETRICEXW* lpntme,
+ DWORD fontType, LPARAM lParam) {
+ const NEWTEXTMETRICW& metrics = lpntme->ntmTm;
+ const LOGFONTW& lf = lpelfe->elfLogFont;
+
+ if (lf.lfFaceName[0] == '@') {
+ return 1;
+ }
+
+ nsAutoString name(lf.lfFaceName);
+ BuildKeyNameFromFontName(name);
+
+ NS_ConvertUTF16toUTF8 key(name);
+
+ gfxGDIFontList* fontList = PlatformFontList();
+ fontList->mLock.AssertCurrentThreadIn();
+
+ if (!fontList->mFontFamilies.Contains(key)) {
+ NS_ConvertUTF16toUTF8 faceName(lf.lfFaceName);
+ FontVisibility visibility = FontVisibility::Unknown; // TODO
+ RefPtr<GDIFontFamily> family = new GDIFontFamily(faceName, visibility);
+ fontList->mFontFamilies.InsertOrUpdate(key, RefPtr{family});
+
+ // if locale is such that CJK font names are the default coming from
+ // GDI, then if a family name is non-ASCII immediately read in other
+ // family names. This assures that MS Gothic, MS Mincho are all found
+ // before lookups begin.
+ if (!IsAscii(faceName)) {
+ family->ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList());
+ }
+
+ if (fontList->mBadUnderlineFamilyNames.ContainsSorted(key)) {
+ family->SetBadUnderlineFamily();
+ }
+
+ family->mWindowsFamily = lf.lfPitchAndFamily & 0xF0;
+ family->mWindowsPitch = lf.lfPitchAndFamily & 0x0F;
+
+ // mark the charset bit
+ family->mCharset.set(metrics.tmCharSet);
+ }
+
+ return 1;
+}
+
+gfxFontEntry* gfxGDIFontList::LookupLocalFont(nsPresContext* aPresContext,
+ const nsACString& aFontName,
+ WeightRange aWeightForEntry,
+ StretchRange aStretchForEntry,
+ SlantStyleRange aStyleForEntry) {
+ AutoLock lock(mLock);
+
+ gfxFontEntry* lookup = LookupInFaceNameLists(aFontName);
+ if (!lookup) {
+ return nullptr;
+ }
+
+ bool isCFF = false; // jtdfix -- need to determine this
+
+ // use the face name from the lookup font entry, which will be the localized
+ // face name which GDI mapping tables use (e.g. with the system locale set to
+ // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name
+ // 'Arial Vet' which can be used as a key in GDI font lookups).
+ GDIFontEntry* fe = GDIFontEntry::CreateFontEntry(
+ lookup->Name(),
+ gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE
+ : GFX_FONT_TYPE_TRUETYPE) /*type*/,
+ lookup->SlantStyle(), lookup->Weight(), aStretchForEntry, nullptr);
+
+ if (!fe) return nullptr;
+
+ fe->mIsLocalUserFont = true;
+
+ // make the new font entry match the userfont entry style characteristics
+ fe->mWeightRange = aWeightForEntry;
+ fe->mStyleRange = aStyleForEntry;
+ fe->mStretchRange = aStretchForEntry;
+
+ return fe;
+}
+
+// If aFontData contains only a MS/Symbol cmap subtable, not MS/Unicode,
+// we modify the subtable header to mark it as Unicode instead, because
+// otherwise GDI will refuse to load the font.
+// NOTE that this function does not bounds-check every access to the font data.
+// This is OK because we only use it on data that has already been validated
+// by OTS, and therefore we will not hit out-of-bounds accesses here.
+static bool FixupSymbolEncodedFont(uint8_t* aFontData, uint32_t aLength) {
+ struct CmapHeader {
+ AutoSwap_PRUint16 version;
+ AutoSwap_PRUint16 numTables;
+ };
+ struct CmapEncodingRecord {
+ AutoSwap_PRUint16 platformID;
+ AutoSwap_PRUint16 encodingID;
+ AutoSwap_PRUint32 offset;
+ };
+ const uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
+ const TableDirEntry* dir = gfxFontUtils::FindTableDirEntry(aFontData, kCMAP);
+ if (dir && uint32_t(dir->length) >= sizeof(CmapHeader)) {
+ CmapHeader* cmap =
+ reinterpret_cast<CmapHeader*>(aFontData + uint32_t(dir->offset));
+ CmapEncodingRecord* encRec =
+ reinterpret_cast<CmapEncodingRecord*>(cmap + 1);
+ int32_t symbolSubtable = -1;
+ for (uint32_t i = 0; i < (uint16_t)cmap->numTables; ++i) {
+ if (uint16_t(encRec[i].platformID) !=
+ gfxFontUtils::PLATFORM_ID_MICROSOFT) {
+ continue; // only interested in MS platform
+ }
+ if (uint16_t(encRec[i].encodingID) ==
+ gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP) {
+ // We've got a Microsoft/Unicode table, so don't interfere.
+ symbolSubtable = -1;
+ break;
+ }
+ if (uint16_t(encRec[i].encodingID) ==
+ gfxFontUtils::ENCODING_ID_MICROSOFT_SYMBOL) {
+ // Found a symbol subtable; remember it for possible fixup,
+ // but if we subsequently find a Microsoft/Unicode subtable,
+ // we'll cancel this.
+ symbolSubtable = i;
+ }
+ }
+ if (symbolSubtable != -1) {
+ // We found a windows/symbol cmap table, and no windows/unicode one;
+ // change the encoding ID so that AddFontMemResourceEx will accept it
+ encRec[symbolSubtable].encodingID =
+ gfxFontUtils::ENCODING_ID_MICROSOFT_UNICODEBMP;
+ return true;
+ }
+ }
+ return false;
+}
+
+gfxFontEntry* gfxGDIFontList::MakePlatformFont(const nsACString& aFontName,
+ WeightRange aWeightForEntry,
+ StretchRange aStretchForEntry,
+ SlantStyleRange aStyleForEntry,
+ const uint8_t* aFontData,
+ uint32_t aLength) {
+ // MakePlatformFont is responsible for deleting the font data with free
+ // so we set up a stack object to ensure it is freed even if we take an
+ // early exit
+ struct FontDataDeleter {
+ explicit FontDataDeleter(const uint8_t* aFontData) : mFontData(aFontData) {}
+ ~FontDataDeleter() { free((void*)mFontData); }
+ const uint8_t* mFontData;
+ };
+ FontDataDeleter autoDelete(aFontData);
+
+ bool isCFF = gfxFontUtils::IsCffFont(aFontData);
+
+ nsresult rv;
+ HANDLE fontRef = nullptr;
+
+ nsAutoString uniqueName;
+ rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
+ if (NS_FAILED(rv)) return nullptr;
+
+ FallibleTArray<uint8_t> newFontData;
+
+ rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData);
+
+ if (NS_FAILED(rv)) return nullptr;
+
+ DWORD numFonts = 0;
+
+ uint8_t* fontData = reinterpret_cast<uint8_t*>(newFontData.Elements());
+ uint32_t fontLength = newFontData.Length();
+ NS_ASSERTION(fontData, "null font data after renaming");
+
+ // http://msdn.microsoft.com/en-us/library/ms533942(VS.85).aspx
+ // "A font that is added by AddFontMemResourceEx is always private
+ // to the process that made the call and is not enumerable."
+ fontRef =
+ AddFontMemResourceEx(fontData, fontLength, 0 /* reserved */, &numFonts);
+ if (!fontRef) {
+ if (FixupSymbolEncodedFont(fontData, fontLength)) {
+ fontRef = AddFontMemResourceEx(fontData, fontLength, 0, &numFonts);
+ }
+ }
+ if (!fontRef) {
+ return nullptr;
+ }
+
+ // only load fonts with a single face contained in the data
+ // AddFontMemResourceEx generates an additional face name for
+ // vertical text if the font supports vertical writing but since
+ // the font is referenced via the name this can be ignored
+ if (fontRef && numFonts > 2) {
+ RemoveFontMemResourceEx(fontRef);
+ return nullptr;
+ }
+
+ // make a new font entry using the unique name
+ WinUserFontData* winUserFontData = new WinUserFontData(fontRef);
+ GDIFontEntry* fe = GDIFontEntry::CreateFontEntry(
+ NS_ConvertUTF16toUTF8(uniqueName),
+ gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE
+ : GFX_FONT_TYPE_TRUETYPE) /*type*/,
+ aStyleForEntry, aWeightForEntry, aStretchForEntry, winUserFontData);
+
+ if (fe) {
+ fe->mIsDataUserFont = true;
+ }
+
+ return fe;
+}
+
+bool gfxGDIFontList::FindAndAddFamiliesLocked(
+ nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
+ const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
+ FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
+ gfxFloat aDevToCssSize) {
+ NS_ConvertUTF8toUTF16 key16(aFamily);
+ BuildKeyNameFromFontName(key16);
+ NS_ConvertUTF16toUTF8 keyName(key16);
+
+ 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, aFamily, aOutput, aFlags, aStyle, aLanguage,
+ aDevToCssSize);
+}
+
+FontFamily gfxGDIFontList::GetDefaultFontForPlatform(
+ nsPresContext* aPresContext, const gfxFontStyle* aStyle,
+ nsAtom* aLanguage) {
+ FontFamily ff;
+
+ // this really shouldn't fail to find a font....
+ 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));
+ if (!ff.IsNull()) {
+ return ff;
+ }
+ }
+
+ // ...but just in case, try another (long-deprecated) approach as well
+ HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
+ LOGFONTW logFont;
+ if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) {
+ ff = FindFamily(aPresContext, NS_ConvertUTF16toUTF8(logFont.lfFaceName));
+ }
+
+ return ff;
+}
+
+void gfxGDIFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const {
+ gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+
+ AutoLock lock(mLock);
+
+ 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 gfxGDIFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+ FontListSizes* aSizes) const {
+ aSizes->mFontListSize += aMallocSizeOf(this);
+ AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
+// used to load system-wide font info on off-main thread
+class GDIFontInfo : public FontInfoData {
+ public:
+ GDIFontInfo(bool aLoadOtherNames, bool aLoadFaceNames, bool aLoadCmaps)
+ : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) {}
+
+ virtual ~GDIFontInfo() = default;
+
+ virtual void Load() {
+ mHdc = GetDC(nullptr);
+ SetGraphicsMode(mHdc, GM_ADVANCED);
+ FontInfoData::Load();
+ ReleaseDC(nullptr, mHdc);
+ }
+
+ // loads font data for all members of a given family
+ virtual void LoadFontFamilyData(const nsACString& aFamilyName);
+
+ // callback for GDI EnumFontFamiliesExW call
+ static int CALLBACK EnumerateFontsForFamily(const ENUMLOGFONTEXW* lpelfe,
+ const NEWTEXTMETRICEXW* nmetrics,
+ DWORD fontType, LPARAM data);
+
+ HDC mHdc;
+};
+
+struct EnumerateFontsForFamilyData {
+ EnumerateFontsForFamilyData(const nsACString& aFamilyName,
+ GDIFontInfo& aFontInfo)
+ : mFamilyName(aFamilyName), mFontInfo(aFontInfo) {}
+
+ nsCString mFamilyName;
+ nsTArray<nsCString> mOtherFamilyNames;
+ GDIFontInfo& mFontInfo;
+ nsCString mPreviousFontName;
+};
+
+int CALLBACK GDIFontInfo::EnumerateFontsForFamily(
+ const ENUMLOGFONTEXW* lpelfe, const NEWTEXTMETRICEXW* nmetrics,
+ DWORD fontType, LPARAM data) {
+ EnumerateFontsForFamilyData* famData =
+ reinterpret_cast<EnumerateFontsForFamilyData*>(data);
+ HDC hdc = famData->mFontInfo.mHdc;
+ LOGFONTW logFont = lpelfe->elfLogFont;
+ const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
+
+ AutoSelectFont font(hdc, &logFont);
+ if (!font.IsValid()) {
+ return 1;
+ }
+
+ FontFaceData fontData;
+ NS_ConvertUTF16toUTF8 fontName(lpelfe->elfFullName);
+
+ // callback called for each style-charset so return if style already seen
+ if (fontName.Equals(famData->mPreviousFontName)) {
+ return 1;
+ }
+ famData->mPreviousFontName = fontName;
+ famData->mFontInfo.mLoadStats.fonts++;
+
+ // read name table info
+ bool nameDataLoaded = false;
+ if (famData->mFontInfo.mLoadFaceNames || famData->mFontInfo.mLoadOtherNames) {
+ uint32_t kNAME =
+ NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
+ uint32_t nameSize;
+ AutoTArray<uint8_t, 1024> nameData;
+
+ nameSize = ::GetFontData(hdc, kNAME, 0, nullptr, 0);
+ if (nameSize != GDI_ERROR && nameSize > 0 &&
+ nameData.SetLength(nameSize, fallible)) {
+ ::GetFontData(hdc, kNAME, 0, nameData.Elements(), nameSize);
+
+ // face names
+ if (famData->mFontInfo.mLoadFaceNames) {
+ gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()),
+ nameSize, gfxFontUtils::NAME_ID_FULL,
+ fontData.mFullName);
+ gfxFontUtils::ReadCanonicalName(
+ (const char*)(nameData.Elements()), nameSize,
+ gfxFontUtils::NAME_ID_POSTSCRIPT, fontData.mPostscriptName);
+ nameDataLoaded = true;
+ famData->mFontInfo.mLoadStats.facenames++;
+ }
+
+ // other family names
+ if (famData->mFontInfo.mLoadOtherNames) {
+ gfxFontUtils::ReadOtherFamilyNamesForFace(
+ famData->mFamilyName, (const char*)(nameData.Elements()), nameSize,
+ famData->mOtherFamilyNames, false);
+ }
+ }
+ }
+
+ // read cmap
+ bool cmapLoaded = false;
+ gfxWindowsFontType feType =
+ GDIFontEntry::DetermineFontType(metrics, fontType);
+ if (famData->mFontInfo.mLoadCmaps && (feType == GFX_FONT_TYPE_PS_OPENTYPE ||
+ feType == GFX_FONT_TYPE_TT_OPENTYPE ||
+ feType == GFX_FONT_TYPE_TRUETYPE)) {
+ uint32_t kCMAP =
+ NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
+ uint32_t cmapSize;
+ AutoTArray<uint8_t, 1024> cmapData;
+
+ cmapSize = ::GetFontData(hdc, kCMAP, 0, nullptr, 0);
+ if (cmapSize != GDI_ERROR && cmapSize > 0 &&
+ cmapData.SetLength(cmapSize, fallible)) {
+ ::GetFontData(hdc, kCMAP, 0, cmapData.Elements(), cmapSize);
+ RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+ uint32_t offset;
+
+ if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(), cmapSize,
+ *charmap, offset))) {
+ fontData.mCharacterMap = charmap;
+ fontData.mUVSOffset = offset;
+ cmapLoaded = true;
+ famData->mFontInfo.mLoadStats.cmaps++;
+ }
+ }
+ }
+
+ if (cmapLoaded || nameDataLoaded) {
+ famData->mFontInfo.mFontFaceData.InsertOrUpdate(fontName, fontData);
+ }
+
+ return famData->mFontInfo.mCanceled ? 0 : 1;
+}
+
+void GDIFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
+ // iterate over the family
+ LOGFONTW logFont;
+ memset(&logFont, 0, sizeof(LOGFONTW));
+ logFont.lfCharSet = DEFAULT_CHARSET;
+ logFont.lfPitchAndFamily = 0;
+ NS_ConvertUTF8toUTF16 name(aFamilyName);
+ uint32_t l = std::min<uint32_t>(name.Length(), LF_FACESIZE - 1);
+ memcpy(logFont.lfFaceName, name.BeginReading(), l * sizeof(char16_t));
+
+ EnumerateFontsForFamilyData data(aFamilyName, *this);
+
+ EnumFontFamiliesExW(mHdc, &logFont,
+ (FONTENUMPROCW)GDIFontInfo::EnumerateFontsForFamily,
+ (LPARAM)(&data), 0);
+
+ // if found other names, insert them
+ if (data.mOtherFamilyNames.Length() != 0) {
+ mOtherFamilyNames.InsertOrUpdate(aFamilyName, data.mOtherFamilyNames);
+ mLoadStats.othernames += data.mOtherFamilyNames.Length();
+ }
+}
+
+already_AddRefed<FontInfoData> gfxGDIFontList::CreateFontInfoData() {
+ bool loadCmaps = !UsesSystemFallback() ||
+ gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+
+ RefPtr<GDIFontInfo> fi =
+ new GDIFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
+
+ return fi.forget();
+}
+
+gfxFontFamily* gfxGDIFontList::CreateFontFamily(
+ const nsACString& aName, FontVisibility aVisibility) const {
+ return new GDIFontFamily(aName, aVisibility);
+}
+
+#ifdef MOZ_BUNDLED_FONTS
+
+void gfxGDIFontList::ActivateBundledFonts() {
+ nsCOMPtr<nsIFile> localDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
+ return;
+ }
+ bool isDir;
+ if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
+ return;
+ }
+
+ nsCOMPtr<nsIDirectoryEnumerator> e;
+ rv = localDir->GetDirectoryEntries(getter_AddRefs(e));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIFile> file;
+ while (NS_SUCCEEDED(e->GetNextFile(getter_AddRefs(file))) && file) {
+ nsAutoString path;
+ if (NS_FAILED(file->GetPath(path))) {
+ continue;
+ }
+ AddFontResourceExW(path.get(), FR_PRIVATE, nullptr);
+ }
+}
+
+#endif