michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: #include michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG /* Allow logging in the release build */ michael@0: #endif michael@0: #include "prlog.h" michael@0: michael@0: #include "gfxGDIFontList.h" michael@0: #include "gfxWindowsPlatform.h" michael@0: #include "gfxUserFontSet.h" michael@0: #include "gfxFontUtils.h" michael@0: #include "gfxGDIFont.h" michael@0: michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsTArray.h" michael@0: #include "nsUnicharUtils.h" michael@0: michael@0: #include "nsDirectoryServiceUtils.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: #include "nsIWindowsRegKey.h" michael@0: #include "gfxFontConstants.h" michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/Telemetry.h" michael@0: #include "mozilla/WindowsVersion.h" michael@0: michael@0: #include michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #define ROUND(x) floor((x) + 0.5) michael@0: michael@0: michael@0: #ifndef CLEARTYPE_QUALITY michael@0: #define CLEARTYPE_QUALITY 5 michael@0: #endif michael@0: michael@0: #ifdef PR_LOGGING michael@0: #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ michael@0: PR_LOG_DEBUG, args) michael@0: #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \ michael@0: gfxPlatform::GetLog(eGfxLog_fontlist), \ michael@0: PR_LOG_DEBUG) michael@0: michael@0: #define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \ michael@0: gfxPlatform::GetLog(eGfxLog_cmapdata), \ michael@0: PR_LOG_DEBUG) michael@0: michael@0: #endif // PR_LOGGING michael@0: michael@0: static __inline void michael@0: BuildKeyNameFromFontName(nsAString &aName) michael@0: { michael@0: if (aName.Length() >= LF_FACESIZE) michael@0: aName.Truncate(LF_FACESIZE - 1); michael@0: ToLowerCase(aName); michael@0: } michael@0: michael@0: // Implementation of gfxPlatformFontList for Win32 GDI, michael@0: // using GDI font enumeration APIs to get the list of fonts michael@0: michael@0: class WinUserFontData : public gfxUserFontData { michael@0: public: michael@0: WinUserFontData(HANDLE aFontRef) michael@0: : mFontRef(aFontRef) michael@0: { } michael@0: michael@0: virtual ~WinUserFontData() michael@0: { michael@0: DebugOnly success; michael@0: success = RemoveFontMemResourceEx(mFontRef); michael@0: #if DEBUG michael@0: if (!success) { michael@0: char buf[256]; michael@0: sprintf(buf, "error deleting font handle (%p) - RemoveFontMemResourceEx failed", mFontRef); michael@0: NS_ASSERTION(success, buf); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: HANDLE mFontRef; michael@0: }; michael@0: michael@0: BYTE michael@0: FontTypeToOutPrecision(uint8_t fontType) michael@0: { michael@0: BYTE ret; michael@0: switch (fontType) { michael@0: case GFX_FONT_TYPE_TT_OPENTYPE: michael@0: case GFX_FONT_TYPE_TRUETYPE: michael@0: ret = OUT_TT_ONLY_PRECIS; michael@0: break; michael@0: case GFX_FONT_TYPE_PS_OPENTYPE: michael@0: ret = OUT_PS_ONLY_PRECIS; michael@0: break; michael@0: case GFX_FONT_TYPE_TYPE1: michael@0: ret = OUT_OUTLINE_PRECIS; michael@0: break; michael@0: case GFX_FONT_TYPE_RASTER: michael@0: ret = OUT_RASTER_PRECIS; michael@0: break; michael@0: case GFX_FONT_TYPE_DEVICE: michael@0: ret = OUT_DEVICE_PRECIS; michael@0: break; michael@0: default: michael@0: ret = OUT_DEFAULT_PRECIS; michael@0: } michael@0: return ret; michael@0: } michael@0: michael@0: /*************************************************************** michael@0: * michael@0: * GDIFontEntry michael@0: * michael@0: */ michael@0: michael@0: GDIFontEntry::GDIFontEntry(const nsAString& aFaceName, michael@0: gfxWindowsFontType aFontType, michael@0: bool aItalic, uint16_t aWeight, int16_t aStretch, michael@0: gfxUserFontData *aUserFontData, michael@0: bool aFamilyHasItalicFace) michael@0: : gfxFontEntry(aFaceName), michael@0: mWindowsFamily(0), mWindowsPitch(0), michael@0: mFontType(aFontType), michael@0: mForceGDI(false), michael@0: mFamilyHasItalicFace(aFamilyHasItalicFace), michael@0: mCharset(), mUnicodeRanges() michael@0: { michael@0: mUserFontData = aUserFontData; michael@0: mItalic = aItalic; michael@0: mWeight = aWeight; michael@0: mStretch = aStretch; michael@0: if (IsType1()) michael@0: mForceGDI = true; michael@0: mIsUserFont = aUserFontData != nullptr; michael@0: michael@0: InitLogFont(aFaceName, aFontType); michael@0: } michael@0: michael@0: nsresult michael@0: GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData) michael@0: { michael@0: // attempt this once, if errors occur leave a blank cmap michael@0: if (mCharacterMap) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // skip non-SFNT fonts completely michael@0: if (mFontType != GFX_FONT_TYPE_PS_OPENTYPE && michael@0: mFontType != GFX_FONT_TYPE_TT_OPENTYPE && michael@0: mFontType != GFX_FONT_TYPE_TRUETYPE) michael@0: { michael@0: mCharacterMap = new gfxCharacterMap(); michael@0: mCharacterMap->mBuildOnTheFly = true; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRefPtr charmap; michael@0: nsresult rv; michael@0: bool unicodeFont = false, symbolFont = false; michael@0: michael@0: if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, michael@0: mUVSOffset, michael@0: symbolFont))) { michael@0: mSymbolFont = symbolFont; michael@0: rv = NS_OK; michael@0: } else { michael@0: uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); michael@0: charmap = new gfxCharacterMap(); michael@0: AutoFallibleTArray cmap; michael@0: rv = CopyFontTable(kCMAP, cmap); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(), michael@0: *charmap, mUVSOffset, michael@0: unicodeFont, symbolFont); michael@0: } michael@0: mSymbolFont = symbolFont; michael@0: } michael@0: michael@0: mHasCmapTable = NS_SUCCEEDED(rv); michael@0: if (mHasCmapTable) { michael@0: gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); michael@0: mCharacterMap = pfl->FindCharMap(charmap); michael@0: } else { michael@0: // if error occurred, initialize to null cmap michael@0: mCharacterMap = new gfxCharacterMap(); michael@0: // For fonts where we failed to read the character map, michael@0: // we can take a slow path to look up glyphs character by character michael@0: mCharacterMap->mBuildOnTheFly = true; michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n", michael@0: NS_ConvertUTF16toUTF8(mName).get(), michael@0: charmap->SizeOfIncludingThis(moz_malloc_size_of), michael@0: charmap->mHash, mCharacterMap == charmap ? " new" : "")); michael@0: if (LOG_CMAPDATA_ENABLED()) { michael@0: char prefix[256]; michael@0: sprintf(prefix, "(cmapdata) name: %.220s", michael@0: NS_ConvertUTF16toUTF8(mName).get()); michael@0: charmap->Dump(prefix, eGfxLog_cmapdata); michael@0: } michael@0: #endif michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: bool michael@0: GDIFontEntry::IsSymbolFont() michael@0: { michael@0: // initialize cmap first michael@0: HasCmapTable(); michael@0: return mSymbolFont; michael@0: } michael@0: michael@0: gfxFont * michael@0: GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, bool aNeedsBold) michael@0: { michael@0: bool isXP = !IsVistaOrLater(); michael@0: michael@0: bool useClearType = isXP && !aFontStyle->systemFont && michael@0: (gfxWindowsPlatform::GetPlatform()->UseClearTypeAlways() || michael@0: (mIsUserFont && !mIsLocalUserFont && michael@0: gfxWindowsPlatform::GetPlatform()->UseClearTypeForDownloadableFonts())); michael@0: michael@0: return new gfxGDIFont(this, aFontStyle, aNeedsBold, michael@0: (useClearType ? gfxFont::kAntialiasSubpixel michael@0: : gfxFont::kAntialiasDefault)); michael@0: } michael@0: michael@0: nsresult michael@0: GDIFontEntry::CopyFontTable(uint32_t aTableTag, michael@0: FallibleTArray& aBuffer) michael@0: { michael@0: if (!IsTrueType()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: AutoDC dc; michael@0: AutoSelectFont font(dc.GetDC(), &mLogFont); michael@0: if (font.IsValid()) { michael@0: uint32_t tableSize = michael@0: ::GetFontData(dc.GetDC(), michael@0: NativeEndian::swapToBigEndian(aTableTag), michael@0: 0, nullptr, 0); michael@0: if (tableSize != GDI_ERROR) { michael@0: if (aBuffer.SetLength(tableSize)) { michael@0: ::GetFontData(dc.GetDC(), michael@0: NativeEndian::swapToBigEndian(aTableTag), 0, michael@0: aBuffer.Elements(), tableSize); michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: void michael@0: GDIFontEntry::FillLogFont(LOGFONTW *aLogFont, michael@0: uint16_t aWeight, gfxFloat aSize, michael@0: bool aUseCleartype) michael@0: { michael@0: memcpy(aLogFont, &mLogFont, sizeof(LOGFONTW)); michael@0: michael@0: aLogFont->lfHeight = (LONG)-ROUND(aSize); michael@0: michael@0: if (aLogFont->lfHeight == 0) { michael@0: aLogFont->lfHeight = -1; michael@0: } michael@0: michael@0: // If a non-zero weight is passed in, use this to override the original michael@0: // weight in the entry's logfont. This is used to control synthetic bolding michael@0: // for installed families with no bold face, and for downloaded fonts michael@0: // (but NOT for local user fonts, because it could cause a different, michael@0: // glyph-incompatible face to be used) michael@0: if (aWeight) { michael@0: aLogFont->lfWeight = aWeight; michael@0: } michael@0: michael@0: // for non-local() user fonts, we never want to apply italics here; michael@0: // if the face is described as italic, we should use it as-is, michael@0: // and if it's not, but then the element is styled italic, we'll use michael@0: // a cairo transform to create fake italic (oblique) michael@0: if (IsUserFont() && !IsLocalUserFont()) { michael@0: aLogFont->lfItalic = 0; michael@0: } michael@0: michael@0: aLogFont->lfQuality = (aUseCleartype ? CLEARTYPE_QUALITY : DEFAULT_QUALITY); michael@0: } michael@0: michael@0: #define MISSING_GLYPH 0x1F // glyph index returned for missing characters michael@0: // on WinXP with .fon fonts, but not Type1 (.pfb) michael@0: michael@0: bool michael@0: GDIFontEntry::TestCharacterMap(uint32_t aCh) michael@0: { michael@0: if (!mCharacterMap) { michael@0: ReadCMAP(); michael@0: NS_ASSERTION(mCharacterMap, "failed to initialize a character map"); michael@0: } michael@0: michael@0: if (mCharacterMap->mBuildOnTheFly) { michael@0: if (aCh > 0xFFFF) michael@0: return false; michael@0: michael@0: // previous code was using the group style michael@0: gfxFontStyle fakeStyle; michael@0: if (mItalic) michael@0: fakeStyle.style = NS_FONT_STYLE_ITALIC; michael@0: fakeStyle.weight = mWeight * 100; michael@0: michael@0: nsRefPtr tempFont = FindOrMakeFont(&fakeStyle, false); michael@0: if (!tempFont || !tempFont->Valid()) michael@0: return false; michael@0: gfxGDIFont *font = static_cast(tempFont.get()); michael@0: michael@0: HDC dc = GetDC((HWND)nullptr); michael@0: SetGraphicsMode(dc, GM_ADVANCED); michael@0: HFONT hfont = font->GetHFONT(); michael@0: HFONT oldFont = (HFONT)SelectObject(dc, hfont); michael@0: michael@0: wchar_t str[1] = { aCh }; michael@0: WORD glyph[1]; michael@0: michael@0: bool hasGlyph = false; michael@0: michael@0: // Bug 573038 - in some cases GetGlyphIndicesW returns 0xFFFF for a michael@0: // missing glyph or 0x1F in other cases to indicate the "invalid" michael@0: // glyph. Map both cases to "not found" michael@0: if (IsType1() || mForceGDI) { michael@0: // Type1 fonts and uniscribe APIs don't get along. michael@0: // ScriptGetCMap will return E_HANDLE michael@0: DWORD ret = GetGlyphIndicesW(dc, str, 1, michael@0: glyph, GGI_MARK_NONEXISTING_GLYPHS); michael@0: if (ret != GDI_ERROR michael@0: && glyph[0] != 0xFFFF michael@0: && (IsType1() || glyph[0] != MISSING_GLYPH)) michael@0: { michael@0: hasGlyph = true; michael@0: } michael@0: } else { michael@0: // ScriptGetCMap works better than GetGlyphIndicesW michael@0: // for things like bitmap/vector fonts michael@0: SCRIPT_CACHE sc = nullptr; michael@0: HRESULT rv = ScriptGetCMap(dc, &sc, str, 1, 0, glyph); michael@0: if (rv == S_OK) michael@0: hasGlyph = true; michael@0: } michael@0: michael@0: SelectObject(dc, oldFont); michael@0: ReleaseDC(nullptr, dc); michael@0: michael@0: if (hasGlyph) { michael@0: mCharacterMap->set(aCh); michael@0: return true; michael@0: } michael@0: } else { michael@0: // font had a cmap so simply check that michael@0: return mCharacterMap->test(aCh); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: GDIFontEntry::InitLogFont(const nsAString& aName, michael@0: gfxWindowsFontType aFontType) michael@0: { michael@0: #define CLIP_TURNOFF_FONTASSOCIATION 0x40 michael@0: michael@0: mLogFont.lfHeight = -1; michael@0: michael@0: // Fill in logFont structure michael@0: mLogFont.lfWidth = 0; michael@0: mLogFont.lfEscapement = 0; michael@0: mLogFont.lfOrientation = 0; michael@0: mLogFont.lfUnderline = FALSE; michael@0: mLogFont.lfStrikeOut = FALSE; michael@0: mLogFont.lfCharSet = DEFAULT_CHARSET; michael@0: mLogFont.lfOutPrecision = FontTypeToOutPrecision(aFontType); michael@0: mLogFont.lfClipPrecision = CLIP_TURNOFF_FONTASSOCIATION; michael@0: mLogFont.lfQuality = DEFAULT_QUALITY; michael@0: mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; michael@0: // always force lfItalic if we want it. Font selection code will michael@0: // do its best to give us an italic font entry, but if no face exists michael@0: // it may give us a regular one based on weight. Windows should michael@0: // do fake italic for us in that case. michael@0: mLogFont.lfItalic = mItalic; michael@0: mLogFont.lfWeight = mWeight; michael@0: michael@0: int len = std::min(aName.Length(), LF_FACESIZE - 1); michael@0: memcpy(&mLogFont.lfFaceName, aName.BeginReading(), len * sizeof(char16_t)); michael@0: mLogFont.lfFaceName[len] = '\0'; michael@0: } michael@0: michael@0: GDIFontEntry* michael@0: GDIFontEntry::CreateFontEntry(const nsAString& aName, michael@0: gfxWindowsFontType aFontType, bool aItalic, michael@0: uint16_t aWeight, int16_t aStretch, michael@0: gfxUserFontData* aUserFontData, michael@0: bool aFamilyHasItalicFace) michael@0: { michael@0: // jtdfix - need to set charset, unicode ranges, pitch/family michael@0: michael@0: GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aItalic, michael@0: aWeight, aStretch, aUserFontData, michael@0: aFamilyHasItalicFace); michael@0: michael@0: return fe; michael@0: } michael@0: michael@0: void michael@0: GDIFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: aSizes->mFontListSize += aMallocSizeOf(this); michael@0: AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: } michael@0: michael@0: /*************************************************************** michael@0: * michael@0: * GDIFontFamily michael@0: * michael@0: */ michael@0: michael@0: int CALLBACK michael@0: GDIFontFamily::FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe, michael@0: const NEWTEXTMETRICEXW *nmetrics, michael@0: DWORD fontType, LPARAM data) michael@0: { michael@0: const NEWTEXTMETRICW& metrics = nmetrics->ntmTm; michael@0: LOGFONTW logFont = lpelfe->elfLogFont; michael@0: GDIFontFamily *ff = reinterpret_cast(data); michael@0: michael@0: // Some fonts claim to support things > 900, but we don't so clamp the sizes michael@0: logFont.lfWeight = clamped(logFont.lfWeight, LONG(100), LONG(900)); michael@0: michael@0: gfxWindowsFontType feType = GDIFontEntry::DetermineFontType(metrics, fontType); michael@0: michael@0: GDIFontEntry *fe = nullptr; michael@0: for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) { michael@0: fe = static_cast(ff->mAvailableFonts[i].get()); michael@0: if (feType > fe->mFontType) { michael@0: // if the new type is better than the old one, remove the old entries michael@0: ff->mAvailableFonts.RemoveElementAt(i); michael@0: --i; michael@0: } else if (feType < fe->mFontType) { michael@0: // otherwise if the new type is worse, skip it michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) { michael@0: fe = static_cast(ff->mAvailableFonts[i].get()); michael@0: // check if we already know about this face michael@0: if (fe->mWeight == logFont.lfWeight && michael@0: fe->mItalic == (logFont.lfItalic == 0xFF)) { michael@0: // update the charset bit here since this could be different michael@0: fe->mCharset.set(metrics.tmCharSet); michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: // We can't set the hasItalicFace flag correctly here, michael@0: // because we might not have seen the family's italic face(s) yet. michael@0: // So we'll set that flag for all members after loading all the faces. michael@0: fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName), michael@0: feType, (logFont.lfItalic == 0xFF), michael@0: (uint16_t) (logFont.lfWeight), 0, michael@0: nullptr, false); michael@0: if (!fe) michael@0: return 1; michael@0: michael@0: ff->AddFontEntry(fe); michael@0: michael@0: // mark the charset bit michael@0: fe->mCharset.set(metrics.tmCharSet); michael@0: michael@0: fe->mWindowsFamily = logFont.lfPitchAndFamily & 0xF0; michael@0: fe->mWindowsPitch = logFont.lfPitchAndFamily & 0x0F; michael@0: michael@0: if (nmetrics->ntmFontSig.fsUsb[0] != 0x00000000 && michael@0: nmetrics->ntmFontSig.fsUsb[1] != 0x00000000 && michael@0: nmetrics->ntmFontSig.fsUsb[2] != 0x00000000 && michael@0: nmetrics->ntmFontSig.fsUsb[3] != 0x00000000) { michael@0: michael@0: // set the unicode ranges michael@0: uint32_t x = 0; michael@0: for (uint32_t i = 0; i < 4; ++i) { michael@0: DWORD range = nmetrics->ntmFontSig.fsUsb[i]; michael@0: for (uint32_t k = 0; k < 32; ++k) { michael@0: fe->mUnicodeRanges.set(x++, (range & (1 << k)) != 0); michael@0: } michael@0: } michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (LOG_FONTLIST_ENABLED()) { michael@0: LOG_FONTLIST(("(fontlist) added (%s) to family (%s)" michael@0: " with style: %s weight: %d stretch: %d", michael@0: NS_ConvertUTF16toUTF8(fe->Name()).get(), michael@0: NS_ConvertUTF16toUTF8(ff->Name()).get(), michael@0: (logFont.lfItalic == 0xff) ? "italic" : "normal", michael@0: logFont.lfWeight, fe->Stretch())); michael@0: } michael@0: #endif michael@0: return 1; michael@0: } michael@0: michael@0: void michael@0: GDIFontFamily::FindStyleVariations(FontInfoData *aFontInfoData) michael@0: { michael@0: if (mHasStyles) michael@0: return; michael@0: mHasStyles = true; michael@0: michael@0: HDC hdc = GetDC(nullptr); michael@0: SetGraphicsMode(hdc, GM_ADVANCED); michael@0: michael@0: LOGFONTW logFont; michael@0: memset(&logFont, 0, sizeof(LOGFONTW)); michael@0: logFont.lfCharSet = DEFAULT_CHARSET; michael@0: logFont.lfPitchAndFamily = 0; michael@0: uint32_t l = std::min(mName.Length(), LF_FACESIZE - 1); michael@0: memcpy(logFont.lfFaceName, mName.get(), l * sizeof(char16_t)); michael@0: michael@0: EnumFontFamiliesExW(hdc, &logFont, michael@0: (FONTENUMPROCW)GDIFontFamily::FamilyAddStylesProc, michael@0: (LPARAM)this, 0); michael@0: #ifdef PR_LOGGING michael@0: if (LOG_FONTLIST_ENABLED() && mAvailableFonts.Length() == 0) { michael@0: LOG_FONTLIST(("(fontlist) no styles available in family \"%s\"", michael@0: NS_ConvertUTF16toUTF8(mName).get())); michael@0: } michael@0: #endif michael@0: michael@0: ReleaseDC(nullptr, hdc); michael@0: michael@0: if (mIsBadUnderlineFamily) { michael@0: SetBadUnderlineFonts(); michael@0: } michael@0: michael@0: // check for existence of italic face(s); if present, set the michael@0: // FamilyHasItalic flag on all faces so that we'll know *not* michael@0: // to use GDI's fake-italic effect with them michael@0: size_t count = mAvailableFonts.Length(); michael@0: for (size_t i = 0; i < count; ++i) { michael@0: if (mAvailableFonts[i]->IsItalic()) { michael@0: for (uint32_t j = 0; j < count; ++j) { michael@0: static_cast(mAvailableFonts[j].get())-> michael@0: mFamilyHasItalicFace = true; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /*************************************************************** michael@0: * michael@0: * gfxGDIFontList michael@0: * michael@0: */ michael@0: michael@0: gfxGDIFontList::gfxGDIFontList() michael@0: : mFontSubstitutes(50) michael@0: { michael@0: } michael@0: michael@0: static void michael@0: RemoveCharsetFromFontSubstitute(nsAString &aName) michael@0: { michael@0: int32_t comma = aName.FindChar(char16_t(',')); michael@0: if (comma >= 0) michael@0: aName.Truncate(comma); michael@0: } michael@0: michael@0: #define MAX_VALUE_NAME 512 michael@0: #define MAX_VALUE_DATA 512 michael@0: michael@0: nsresult michael@0: gfxGDIFontList::GetFontSubstitutes() michael@0: { michael@0: HKEY hKey; michael@0: DWORD i, rv, lenAlias, lenActual, valueType; michael@0: WCHAR aliasName[MAX_VALUE_NAME]; michael@0: WCHAR actualName[MAX_VALUE_DATA]; michael@0: michael@0: if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, michael@0: L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", michael@0: 0, KEY_READ, &hKey) != ERROR_SUCCESS) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) { michael@0: aliasName[0] = 0; michael@0: lenAlias = ArrayLength(aliasName); michael@0: actualName[0] = 0; michael@0: lenActual = sizeof(actualName); michael@0: rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType, michael@0: (LPBYTE)actualName, &lenActual); michael@0: michael@0: if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) { michael@0: continue; michael@0: } michael@0: michael@0: if (aliasName[0] == WCHAR('@')) { michael@0: continue; michael@0: } michael@0: michael@0: nsAutoString substituteName((char16_t*) aliasName); michael@0: nsAutoString actualFontName((char16_t*) actualName); michael@0: RemoveCharsetFromFontSubstitute(substituteName); michael@0: BuildKeyNameFromFontName(substituteName); michael@0: RemoveCharsetFromFontSubstitute(actualFontName); michael@0: BuildKeyNameFromFontName(actualFontName); michael@0: gfxFontFamily *ff; michael@0: if (!actualFontName.IsEmpty() && michael@0: (ff = mFontFamilies.GetWeak(actualFontName))) { michael@0: mFontSubstitutes.Put(substituteName, ff); michael@0: } else { michael@0: mNonExistingFonts.AppendElement(substituteName); michael@0: } michael@0: } michael@0: michael@0: // "Courier" on a default Windows install is an ugly bitmap font. michael@0: // If there is no substitution for Courier in the registry michael@0: // substitute "Courier" with "Courier New". michael@0: nsAutoString substituteName; michael@0: substituteName.AssignLiteral("Courier"); michael@0: BuildKeyNameFromFontName(substituteName); michael@0: if (!mFontSubstitutes.GetWeak(substituteName)) { michael@0: gfxFontFamily *ff; michael@0: nsAutoString actualFontName; michael@0: actualFontName.AssignLiteral("Courier New"); michael@0: BuildKeyNameFromFontName(actualFontName); michael@0: ff = mFontFamilies.GetWeak(actualFontName); michael@0: if (ff) { michael@0: mFontSubstitutes.Put(substituteName, ff); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: gfxGDIFontList::InitFontList() michael@0: { michael@0: Telemetry::AutoTimer timer; michael@0: gfxFontCache *fc = gfxFontCache::GetCache(); michael@0: if (fc) michael@0: fc->AgeAllGenerations(); michael@0: michael@0: // reset font lists michael@0: gfxPlatformFontList::InitFontList(); michael@0: michael@0: mFontSubstitutes.Clear(); michael@0: mNonExistingFonts.Clear(); michael@0: michael@0: // iterate over available families michael@0: LOGFONTW logfont; michael@0: memset(&logfont, 0, sizeof(logfont)); michael@0: logfont.lfCharSet = DEFAULT_CHARSET; michael@0: michael@0: AutoDC hdc; michael@0: int result = EnumFontFamiliesExW(hdc.GetDC(), &logfont, michael@0: (FONTENUMPROCW)&EnumFontFamExProc, michael@0: 0, 0); michael@0: michael@0: GetFontSubstitutes(); michael@0: michael@0: GetPrefsAndStartLoader(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: int CALLBACK michael@0: gfxGDIFontList::EnumFontFamExProc(ENUMLOGFONTEXW *lpelfe, michael@0: NEWTEXTMETRICEXW *lpntme, michael@0: DWORD fontType, michael@0: LPARAM lParam) michael@0: { michael@0: const LOGFONTW& lf = lpelfe->elfLogFont; michael@0: michael@0: if (lf.lfFaceName[0] == '@') { michael@0: return 1; michael@0: } michael@0: michael@0: nsAutoString name(lf.lfFaceName); michael@0: BuildKeyNameFromFontName(name); michael@0: michael@0: gfxGDIFontList *fontList = PlatformFontList(); michael@0: michael@0: if (!fontList->mFontFamilies.GetWeak(name)) { michael@0: nsDependentString faceName(lf.lfFaceName); michael@0: nsRefPtr family = new GDIFontFamily(faceName); michael@0: fontList->mFontFamilies.Put(name, family); michael@0: michael@0: // if locale is such that CJK font names are the default coming from michael@0: // GDI, then if a family name is non-ASCII immediately read in other michael@0: // family names. This assures that MS Gothic, MS Mincho are all found michael@0: // before lookups begin. michael@0: if (!IsASCII(faceName)) { michael@0: family->ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList()); michael@0: } michael@0: michael@0: if (fontList->mBadUnderlineFamilyNames.Contains(name)) michael@0: family->SetBadUnderlineFamily(); michael@0: } michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: gfxFontEntry* michael@0: gfxGDIFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, michael@0: const nsAString& aFullname) michael@0: { michael@0: gfxFontEntry *lookup; michael@0: michael@0: lookup = LookupInFaceNameLists(aFullname); michael@0: if (!lookup) { michael@0: return nullptr; michael@0: } michael@0: michael@0: bool isCFF = false; // jtdfix -- need to determine this michael@0: michael@0: // use the face name from the lookup font entry, which will be the localized michael@0: // face name which GDI mapping tables use (e.g. with the system locale set to michael@0: // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name michael@0: // 'Arial Vet' which can be used as a key in GDI font lookups). michael@0: GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(lookup->Name(), michael@0: gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, michael@0: lookup->mItalic ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL, michael@0: lookup->mWeight, aProxyEntry->mStretch, nullptr, michael@0: static_cast(lookup)->mFamilyHasItalicFace); michael@0: michael@0: if (!fe) michael@0: return nullptr; michael@0: michael@0: fe->mIsUserFont = true; michael@0: fe->mIsLocalUserFont = true; michael@0: michael@0: // make the new font entry match the proxy entry style characteristics michael@0: fe->mWeight = (aProxyEntry->mWeight == 0 ? 400 : aProxyEntry->mWeight); michael@0: fe->mItalic = aProxyEntry->mItalic; michael@0: michael@0: return fe; michael@0: } michael@0: michael@0: gfxFontEntry* michael@0: gfxGDIFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, michael@0: const uint8_t *aFontData, michael@0: uint32_t aLength) michael@0: { michael@0: // MakePlatformFont is responsible for deleting the font data with NS_Free michael@0: // so we set up a stack object to ensure it is freed even if we take an michael@0: // early exit michael@0: struct FontDataDeleter { michael@0: FontDataDeleter(const uint8_t *aFontData) michael@0: : mFontData(aFontData) { } michael@0: ~FontDataDeleter() { NS_Free((void*)mFontData); } michael@0: const uint8_t *mFontData; michael@0: }; michael@0: FontDataDeleter autoDelete(aFontData); michael@0: michael@0: bool isCFF = gfxFontUtils::IsCffFont(aFontData); michael@0: michael@0: nsresult rv; michael@0: HANDLE fontRef = nullptr; michael@0: michael@0: nsAutoString uniqueName; michael@0: rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: FallibleTArray newFontData; michael@0: michael@0: rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: DWORD numFonts = 0; michael@0: michael@0: uint8_t *fontData = reinterpret_cast (newFontData.Elements()); michael@0: uint32_t fontLength = newFontData.Length(); michael@0: NS_ASSERTION(fontData, "null font data after renaming"); michael@0: michael@0: // http://msdn.microsoft.com/en-us/library/ms533942(VS.85).aspx michael@0: // "A font that is added by AddFontMemResourceEx is always private michael@0: // to the process that made the call and is not enumerable." michael@0: fontRef = AddFontMemResourceEx(fontData, fontLength, michael@0: 0 /* reserved */, &numFonts); michael@0: if (!fontRef) michael@0: return nullptr; michael@0: michael@0: // only load fonts with a single face contained in the data michael@0: // AddFontMemResourceEx generates an additional face name for michael@0: // vertical text if the font supports vertical writing but since michael@0: // the font is referenced via the name this can be ignored michael@0: if (fontRef && numFonts > 2) { michael@0: RemoveFontMemResourceEx(fontRef); michael@0: return nullptr; michael@0: } michael@0: michael@0: // make a new font entry using the unique name michael@0: WinUserFontData *winUserFontData = new WinUserFontData(fontRef); michael@0: uint16_t w = (aProxyEntry->mWeight == 0 ? 400 : aProxyEntry->mWeight); michael@0: michael@0: GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(uniqueName, michael@0: gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, michael@0: uint32_t(aProxyEntry->mItalic ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL), michael@0: w, aProxyEntry->mStretch, winUserFontData, false); michael@0: michael@0: if (!fe) michael@0: return fe; michael@0: michael@0: fe->mIsUserFont = true; michael@0: michael@0: // Uniscribe doesn't place CFF fonts loaded privately michael@0: // via AddFontMemResourceEx on XP/Vista michael@0: if (isCFF && !IsWin7OrLater()) { michael@0: fe->mForceGDI = true; michael@0: } michael@0: michael@0: return fe; michael@0: } michael@0: michael@0: gfxFontFamily* michael@0: gfxGDIFontList::GetDefaultFont(const gfxFontStyle* aStyle) michael@0: { michael@0: // this really shouldn't fail to find a font.... michael@0: HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); michael@0: LOGFONTW logFont; michael@0: if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) { michael@0: nsAutoString resolvedName; michael@0: if (ResolveFontName(nsDependentString(logFont.lfFaceName), resolvedName)) { michael@0: return FindFamily(resolvedName); michael@0: } michael@0: } michael@0: michael@0: // ...but just in case, try another approach as well michael@0: NONCLIENTMETRICSW ncm; michael@0: ncm.cbSize = sizeof(ncm); michael@0: BOOL status = ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, michael@0: sizeof(ncm), &ncm, 0); michael@0: if (status) { michael@0: nsAutoString resolvedName; michael@0: if (ResolveFontName(nsDependentString(ncm.lfMessageFont.lfFaceName), resolvedName)) { michael@0: return FindFamily(resolvedName); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: michael@0: bool michael@0: gfxGDIFontList::ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName) michael@0: { michael@0: nsAutoString keyName(aFontName); michael@0: BuildKeyNameFromFontName(keyName); michael@0: michael@0: gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName); michael@0: if (ff) { michael@0: aResolvedFontName = ff->Name(); michael@0: return true; michael@0: } michael@0: michael@0: if (mNonExistingFonts.Contains(keyName)) michael@0: return false; michael@0: michael@0: if (gfxPlatformFontList::ResolveFontName(aFontName, aResolvedFontName)) michael@0: return true; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: gfxGDIFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: aSizes->mFontListSize += michael@0: mFontSubstitutes.SizeOfExcludingThis(SizeOfFamilyNameEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: aSizes->mFontListSize += michael@0: mNonExistingFonts.SizeOfExcludingThis(aMallocSizeOf); michael@0: for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) { michael@0: aSizes->mFontListSize += michael@0: mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf); michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxGDIFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontListSizes* aSizes) const michael@0: { michael@0: aSizes->mFontListSize += aMallocSizeOf(this); michael@0: AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: } michael@0: michael@0: // used to load system-wide font info on off-main thread michael@0: class GDIFontInfo : public FontInfoData { michael@0: public: michael@0: GDIFontInfo(bool aLoadOtherNames, michael@0: bool aLoadFaceNames, michael@0: bool aLoadCmaps) : michael@0: FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) michael@0: {} michael@0: michael@0: virtual ~GDIFontInfo() {} michael@0: michael@0: virtual void Load() { michael@0: mHdc = GetDC(nullptr); michael@0: SetGraphicsMode(mHdc, GM_ADVANCED); michael@0: FontInfoData::Load(); michael@0: ReleaseDC(nullptr, mHdc); michael@0: } michael@0: michael@0: // loads font data for all members of a given family michael@0: virtual void LoadFontFamilyData(const nsAString& aFamilyName); michael@0: michael@0: // callback for GDI EnumFontFamiliesExW call michael@0: static int CALLBACK EnumerateFontsForFamily(const ENUMLOGFONTEXW *lpelfe, michael@0: const NEWTEXTMETRICEXW *nmetrics, michael@0: DWORD fontType, LPARAM data); michael@0: michael@0: HDC mHdc; michael@0: }; michael@0: michael@0: struct EnumerateFontsForFamilyData { michael@0: EnumerateFontsForFamilyData(const nsAString& aFamilyName, michael@0: GDIFontInfo& aFontInfo) michael@0: : mFamilyName(aFamilyName), mFontInfo(aFontInfo) michael@0: {} michael@0: michael@0: nsString mFamilyName; michael@0: nsTArray mOtherFamilyNames; michael@0: GDIFontInfo& mFontInfo; michael@0: nsString mPreviousFontName; michael@0: }; michael@0: michael@0: int CALLBACK GDIFontInfo::EnumerateFontsForFamily( michael@0: const ENUMLOGFONTEXW *lpelfe, michael@0: const NEWTEXTMETRICEXW *nmetrics, michael@0: DWORD fontType, LPARAM data) michael@0: { michael@0: EnumerateFontsForFamilyData *famData = michael@0: reinterpret_cast(data); michael@0: HDC hdc = famData->mFontInfo.mHdc; michael@0: LOGFONTW logFont = lpelfe->elfLogFont; michael@0: const NEWTEXTMETRICW& metrics = nmetrics->ntmTm; michael@0: michael@0: AutoSelectFont font(hdc, &logFont); michael@0: if (!font.IsValid()) { michael@0: return 1; michael@0: } michael@0: michael@0: FontFaceData fontData; michael@0: nsDependentString fontName(lpelfe->elfFullName); michael@0: michael@0: // callback called for each style-charset so return if style already seen michael@0: if (fontName.Equals(famData->mPreviousFontName)) { michael@0: return 1; michael@0: } michael@0: famData->mPreviousFontName = fontName; michael@0: famData->mFontInfo.mLoadStats.fonts++; michael@0: michael@0: // read name table info michael@0: bool nameDataLoaded = false; michael@0: if (famData->mFontInfo.mLoadFaceNames || famData->mFontInfo.mLoadOtherNames) { michael@0: uint32_t kNAME = michael@0: NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e')); michael@0: uint32_t nameSize; michael@0: AutoFallibleTArray nameData; michael@0: michael@0: nameSize = ::GetFontData(hdc, kNAME, 0, nullptr, 0); michael@0: if (nameSize != GDI_ERROR && michael@0: nameSize > 0 && michael@0: nameData.SetLength(nameSize)) { michael@0: ::GetFontData(hdc, kNAME, 0, nameData.Elements(), nameSize); michael@0: michael@0: // face names michael@0: if (famData->mFontInfo.mLoadFaceNames) { michael@0: gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize, michael@0: gfxFontUtils::NAME_ID_FULL, michael@0: fontData.mFullName); michael@0: gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize, michael@0: gfxFontUtils::NAME_ID_POSTSCRIPT, michael@0: fontData.mPostscriptName); michael@0: nameDataLoaded = true; michael@0: famData->mFontInfo.mLoadStats.facenames++; michael@0: } michael@0: michael@0: // other family names michael@0: if (famData->mFontInfo.mLoadOtherNames) { michael@0: gfxFontFamily::ReadOtherFamilyNamesForFace(famData->mFamilyName, michael@0: (const char*)(nameData.Elements()), michael@0: nameSize, michael@0: famData->mOtherFamilyNames, michael@0: false); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // read cmap michael@0: bool cmapLoaded = false; michael@0: gfxWindowsFontType feType = michael@0: GDIFontEntry::DetermineFontType(metrics, fontType); michael@0: if (famData->mFontInfo.mLoadCmaps && michael@0: (feType == GFX_FONT_TYPE_PS_OPENTYPE || michael@0: feType == GFX_FONT_TYPE_TT_OPENTYPE || michael@0: feType == GFX_FONT_TYPE_TRUETYPE)) michael@0: { michael@0: uint32_t kCMAP = michael@0: NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p')); michael@0: uint32_t cmapSize; michael@0: AutoFallibleTArray cmapData; michael@0: michael@0: cmapSize = ::GetFontData(hdc, kCMAP, 0, nullptr, 0); michael@0: if (cmapSize != GDI_ERROR && michael@0: cmapSize > 0 && michael@0: cmapData.SetLength(cmapSize)) { michael@0: ::GetFontData(hdc, kCMAP, 0, cmapData.Elements(), cmapSize); michael@0: bool cmapLoaded = false; michael@0: bool unicodeFont = false, symbolFont = false; michael@0: nsRefPtr charmap = new gfxCharacterMap(); michael@0: uint32_t offset; michael@0: michael@0: if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(), michael@0: cmapSize, *charmap, michael@0: offset, unicodeFont, michael@0: symbolFont))) { michael@0: fontData.mCharacterMap = charmap; michael@0: fontData.mUVSOffset = offset; michael@0: fontData.mSymbolFont = symbolFont; michael@0: cmapLoaded = true; michael@0: famData->mFontInfo.mLoadStats.cmaps++; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (cmapLoaded || nameDataLoaded) { michael@0: famData->mFontInfo.mFontFaceData.Put(fontName, fontData); michael@0: } michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: void michael@0: GDIFontInfo::LoadFontFamilyData(const nsAString& aFamilyName) michael@0: { michael@0: // iterate over the family michael@0: LOGFONTW logFont; michael@0: memset(&logFont, 0, sizeof(LOGFONTW)); michael@0: logFont.lfCharSet = DEFAULT_CHARSET; michael@0: logFont.lfPitchAndFamily = 0; michael@0: uint32_t l = std::min(aFamilyName.Length(), LF_FACESIZE - 1); michael@0: memcpy(logFont.lfFaceName, aFamilyName.BeginReading(), l * sizeof(char16_t)); michael@0: michael@0: EnumerateFontsForFamilyData data(aFamilyName, *this); michael@0: michael@0: EnumFontFamiliesExW(mHdc, &logFont, michael@0: (FONTENUMPROCW)GDIFontInfo::EnumerateFontsForFamily, michael@0: (LPARAM)(&data), 0); michael@0: michael@0: // if found other names, insert them michael@0: if (data.mOtherFamilyNames.Length() != 0) { michael@0: mOtherFamilyNames.Put(aFamilyName, data.mOtherFamilyNames); michael@0: mLoadStats.othernames += data.mOtherFamilyNames.Length(); michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: gfxGDIFontList::CreateFontInfoData() michael@0: { michael@0: bool loadCmaps = !UsesSystemFallback() || michael@0: gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); michael@0: michael@0: nsRefPtr fi = michael@0: new GDIFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps); michael@0: michael@0: return fi.forget(); michael@0: }