/* -*- 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 #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 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 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 cmap = new gfxCharacterMap(); cmap->mBuildOnTheFly = true; if (mCharacterMap.compareExchange(nullptr, cmap.get())) { Unused << cmap.forget(); } return NS_ERROR_FAILURE; } RefPtr 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 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& 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 GDIFontEntry::LookupUnscaledFont( HFONT aFont) { RefPtr 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 tempFont = FindOrMakeFont(&fakeStyle, nullptr); if (!tempFont || !tempFont->Valid()) return false; gfxGDIFont* font = static_cast(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(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, // and 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(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(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(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(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 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 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(aFontData + uint32_t(dir->offset)); CmapEncodingRecord* encRec = reinterpret_cast(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 newFontData; rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData); if (NS_FAILED(rv)) return nullptr; DWORD numFonts = 0; uint8_t* fontData = reinterpret_cast(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* 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 mOtherFamilyNames; GDIFontInfo& mFontInfo; nsCString mPreviousFontName; }; int CALLBACK GDIFontInfo::EnumerateFontsForFamily( const ENUMLOGFONTEXW* lpelfe, const NEWTEXTMETRICEXW* nmetrics, DWORD fontType, LPARAM data) { EnumerateFontsForFamilyData* famData = reinterpret_cast(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 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 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 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(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 gfxGDIFontList::CreateFontInfoData() { bool loadCmaps = !UsesSystemFallback() || gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); RefPtr 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 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 e; rv = localDir->GetDirectoryEntries(getter_AddRefs(e)); if (NS_FAILED(rv)) { return; } nsCOMPtr 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