diff options
Diffstat (limited to 'gfx/thebes/gfxGDIFontList.cpp')
-rw-r--r-- | gfx/thebes/gfxGDIFontList.cpp | 1195 |
1 files changed, 1195 insertions, 0 deletions
diff --git a/gfx/thebes/gfxGDIFontList.cpp b/gfx/thebes/gfxGDIFontList.cpp new file mode 100644 index 000000000..d80c49356 --- /dev/null +++ b/gfx/thebes/gfxGDIFontList.cpp @@ -0,0 +1,1195 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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/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 "nsISimpleEnumerator.h" +#include "nsIWindowsRegKey.h" +#include "gfxFontConstants.h" +#include "GeckoProfiler.h" + +#include "mozilla/MemoryReporting.h" +#include "mozilla/Telemetry.h" +#include "mozilla/WindowsVersion.h" + +#include <usp10.h> + +using namespace mozilla; + +#define ROUND(x) floor((x) + 0.5) + + +#ifndef CLEARTYPE_QUALITY +#define CLEARTYPE_QUALITY 5 +#endif + +#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: + 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 nsAString& aFaceName, + gfxWindowsFontType aFontType, + uint8_t aStyle, uint16_t aWeight, + int16_t aStretch, + gfxUserFontData *aUserFontData, + bool aFamilyHasItalicFace) + : gfxFontEntry(aFaceName), + mWindowsFamily(0), mWindowsPitch(0), + mFontType(aFontType), + mForceGDI(false), + mFamilyHasItalicFace(aFamilyHasItalicFace), + mCharset(), mUnicodeRanges() +{ + mUserFontData.reset(aUserFontData); + mStyle = aStyle; + mWeight = aWeight; + mStretch = aStretch; + if (IsType1()) + mForceGDI = true; + mIsDataUserFont = aUserFontData != nullptr; + + InitLogFont(aFaceName, aFontType); +} + +nsresult +GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::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) + { + mCharacterMap = new gfxCharacterMap(); + mCharacterMap->mBuildOnTheFly = true; + return NS_ERROR_FAILURE; + } + + RefPtr<gfxCharacterMap> charmap; + nsresult rv; + bool unicodeFont = false, symbolFont = false; + + if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, + mUVSOffset, + symbolFont))) { + mSymbolFont = symbolFont; + 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, mUVSOffset, + unicodeFont, symbolFont); + } + mSymbolFont = symbolFont; + } + + mHasCmapTable = NS_SUCCEEDED(rv); + if (mHasCmapTable) { + gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); + mCharacterMap = pfl->FindCharMap(charmap); + } else { + // if error occurred, initialize to null cmap + mCharacterMap = 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 + mCharacterMap->mBuildOnTheFly = true; + } + + LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n", + NS_ConvertUTF16toUTF8(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", + NS_ConvertUTF16toUTF8(mName).get()); + charmap->Dump(prefix, eGfxLog_cmapdata); + } + + return rv; +} + +bool +GDIFontEntry::IsSymbolFont() +{ + // initialize cmap first + HasCmapTable(); + return mSymbolFont; +} + +gfxFont * +GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, bool aNeedsBold) +{ + bool isXP = !IsVistaOrLater(); + + bool useClearType = isXP && !aFontStyle->systemFont && + (gfxWindowsPlatform::GetPlatform()->UseClearTypeAlways() || + (mIsDataUserFont && + gfxWindowsPlatform::GetPlatform()->UseClearTypeForDownloadableFonts())); + + return new gfxGDIFont(this, aFontStyle, aNeedsBold, + (useClearType ? gfxFont::kAntialiasSubpixel + : gfxFont::kAntialiasDefault)); +} + +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; +} + +void +GDIFontEntry::FillLogFont(LOGFONTW *aLogFont, + uint16_t aWeight, gfxFloat aSize, + bool aUseCleartype) +{ + 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) { + 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; + } + + aLogFont->lfQuality = (aUseCleartype ? CLEARTYPE_QUALITY : DEFAULT_QUALITY); +} + +#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 (mCharacterMap->mBuildOnTheFly) { + if (aCh > 0xFFFF) + return false; + + // previous code was using the group style + gfxFontStyle fakeStyle; + if (!IsUpright()) { + fakeStyle.style = NS_FONT_STYLE_ITALIC; + } + fakeStyle.weight = mWeight * 100; + + RefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, false); + 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) { + mCharacterMap->set(aCh); + return true; + } + } else { + // font had a cmap so simply check that + return mCharacterMap->test(aCh); + } + + return false; +} + +void +GDIFontEntry::InitLogFont(const nsAString& 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 = mWeight; + + int len = std::min<int>(aName.Length(), LF_FACESIZE - 1); + memcpy(&mLogFont.lfFaceName, aName.BeginReading(), len * sizeof(char16_t)); + mLogFont.lfFaceName[len] = '\0'; +} + +GDIFontEntry* +GDIFontEntry::CreateFontEntry(const nsAString& aName, + gfxWindowsFontType aFontType, + uint8_t aStyle, + uint16_t aWeight, int16_t aStretch, + gfxUserFontData* aUserFontData, + bool aFamilyHasItalicFace) +{ + // jtdfix - need to set charset, unicode ranges, pitch/family + + GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aStyle, + aWeight, aStretch, aUserFontData, + aFamilyHasItalicFace); + + return fe; +} + +void +GDIFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const +{ + aSizes->mFontListSize += aMallocSizeOf(this); + AddSizeOfExcludingThis(aMallocSizeOf, aSizes); +} + +/*************************************************************** + * + * GDIFontFamily + * + */ + +int CALLBACK +GDIFontFamily::FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe, + const NEWTEXTMETRICEXW *nmetrics, + DWORD fontType, LPARAM data) +{ + const NEWTEXTMETRICW& metrics = nmetrics->ntmTm; + LOGFONTW logFont = lpelfe->elfLogFont; + GDIFontFamily *ff = reinterpret_cast<GDIFontFamily*>(data); + + // 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->mWeight == logFont.lfWeight && + fe->IsItalic() == (logFont.lfItalic == 0xFF)) { + // update the charset bit here since this could be different + fe->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. + uint8_t italicStyle = (logFont.lfItalic == 0xFF ? + NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL); + fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName), + feType, italicStyle, + (uint16_t) (logFont.lfWeight), 0, + nullptr, false); + if (!fe) + return 1; + + ff->AddFontEntry(fe); + + // mark the charset bit + fe->mCharset.set(metrics.tmCharSet); + + fe->mWindowsFamily = logFont.lfPitchAndFamily & 0xF0; + fe->mWindowsPitch = logFont.lfPitchAndFamily & 0x0F; + + if (nmetrics->ntmFontSig.fsUsb[0] != 0x00000000 && + nmetrics->ntmFontSig.fsUsb[1] != 0x00000000 && + nmetrics->ntmFontSig.fsUsb[2] != 0x00000000 && + nmetrics->ntmFontSig.fsUsb[3] != 0x00000000) { + + // set the unicode ranges + uint32_t x = 0; + for (uint32_t i = 0; i < 4; ++i) { + DWORD range = nmetrics->ntmFontSig.fsUsb[i]; + for (uint32_t k = 0; k < 32; ++k) { + fe->mUnicodeRanges.set(x++, (range & (1 << k)) != 0); + } + } + } + + if (LOG_FONTLIST_ENABLED()) { + LOG_FONTLIST(("(fontlist) added (%s) to family (%s)" + " with style: %s weight: %d stretch: %d", + NS_ConvertUTF16toUTF8(fe->Name()).get(), + NS_ConvertUTF16toUTF8(ff->Name()).get(), + (logFont.lfItalic == 0xff) ? "italic" : "normal", + logFont.lfWeight, fe->Stretch())); + } + return 1; +} + +void +GDIFontFamily::FindStyleVariations(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; + uint32_t l = std::min<uint32_t>(mName.Length(), LF_FACESIZE - 1); + memcpy(logFont.lfFaceName, mName.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\"", + NS_ConvertUTF16toUTF8(mName).get())); + } + + ReleaseDC(nullptr, hdc); + + if (mIsBadUnderlineFamily) { + SetBadUnderlineFonts(); + } + + // check for existence of italic face(s); if present, set the + // FamilyHasItalic flag on all faces so that we'll know *not* + // to use GDI's fake-italic effect with them + size_t count = mAvailableFonts.Length(); + for (size_t i = 0; i < count; ++i) { + if (mAvailableFonts[i]->IsItalic()) { + for (uint32_t j = 0; j < count; ++j) { + static_cast<GDIFontEntry*>(mAvailableFonts[j].get())-> + mFamilyHasItalicFace = true; + } + break; + } + } +} + +/*************************************************************** + * + * gfxGDIFontList + * + */ + +gfxGDIFontList::gfxGDIFontList() + : mFontSubstitutes(32) +{ +#ifdef MOZ_BUNDLED_FONTS + ActivateBundledFonts(); +#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; + if (!actualFontName.IsEmpty() && + (ff = mFontFamilies.GetWeak(actualFontName))) { + mFontSubstitutes.Put(substituteName, ff); + } else { + mNonExistingFonts.AppendElement(substituteName); + } + } + + // "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); + if (!mFontSubstitutes.GetWeak(substituteName)) { + gfxFontFamily *ff; + nsAutoString actualFontName; + actualFontName.AssignLiteral("Courier New"); + BuildKeyNameFromFontName(actualFontName); + ff = mFontFamilies.GetWeak(actualFontName); + if (ff) { + mFontSubstitutes.Put(substituteName, 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; + int result = 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 LOGFONTW& lf = lpelfe->elfLogFont; + + if (lf.lfFaceName[0] == '@') { + return 1; + } + + nsAutoString name(lf.lfFaceName); + BuildKeyNameFromFontName(name); + + gfxGDIFontList *fontList = PlatformFontList(); + + if (!fontList->mFontFamilies.GetWeak(name)) { + nsDependentString faceName(lf.lfFaceName); + RefPtr<gfxFontFamily> family = new GDIFontFamily(faceName); + fontList->mFontFamilies.Put(name, 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.Contains(name)) + family->SetBadUnderlineFamily(); + } + + return 1; +} + +gfxFontEntry* +gfxGDIFontList::LookupLocalFont(const nsAString& aFontName, + uint16_t aWeight, + int16_t aStretch, + uint8_t aStyle) +{ + gfxFontEntry *lookup; + + 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->mStyle, lookup->mWeight, aStretch, nullptr, + static_cast<GDIFontEntry*>(lookup)->mFamilyHasItalicFace); + + if (!fe) + return nullptr; + + fe->mIsLocalUserFont = true; + + // make the new font entry match the userfont entry style characteristics + fe->mWeight = (aWeight == 0 ? 400 : aWeight); + fe->mStyle = aStyle; + + 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 nsAString& aFontName, + uint16_t aWeight, + int16_t aStretch, + uint8_t aStyle, + 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 { + 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); + uint16_t w = (aWeight == 0 ? 400 : aWeight); + + GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(uniqueName, + gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, + aStyle, w, aStretch, winUserFontData, false); + + if (!fe) + return fe; + + fe->mIsDataUserFont = true; + + // Uniscribe doesn't place CFF fonts loaded privately + // via AddFontMemResourceEx on XP/Vista + if (isCFF && !IsWin7OrLater()) { + fe->mForceGDI = true; + } + + return fe; +} + +bool +gfxGDIFontList::FindAndAddFamilies(const nsAString& aFamily, + nsTArray<gfxFontFamily*>* aOutput, + gfxFontStyle* aStyle, + gfxFloat aDevToCssSize) +{ + nsAutoString keyName(aFamily); + BuildKeyNameFromFontName(keyName); + + gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName); + if (ff) { + aOutput->AppendElement(ff); + return true; + } + + if (mNonExistingFonts.Contains(keyName)) { + return false; + } + + return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle, + aDevToCssSize); +} + +gfxFontFamily* +gfxGDIFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle) +{ + gfxFontFamily *ff = nullptr; + + // 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(nsDependentString(ncm.lfMessageFont.lfFaceName)); + if (ff) { + 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(nsDependentString(logFont.lfFaceName)); + } + + return ff; +} + +void +gfxGDIFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, + FontListSizes* aSizes) const +{ + gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); + 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() {} + + 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 nsAString& 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 nsAString& aFamilyName, + GDIFontInfo& aFontInfo) + : mFamilyName(aFamilyName), mFontInfo(aFontInfo) + {} + + nsString mFamilyName; + nsTArray<nsString> mOtherFamilyNames; + GDIFontInfo& mFontInfo; + nsString 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; + nsDependentString 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) { + gfxFontFamily::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); + bool cmapLoaded = false; + bool unicodeFont = false, symbolFont = false; + RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(); + uint32_t offset; + + if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(), + cmapSize, *charmap, + offset, unicodeFont, + symbolFont))) { + fontData.mCharacterMap = charmap; + fontData.mUVSOffset = offset; + fontData.mSymbolFont = symbolFont; + cmapLoaded = true; + famData->mFontInfo.mLoadStats.cmaps++; + } + } + } + + if (cmapLoaded || nameDataLoaded) { + famData->mFontInfo.mFontFaceData.Put(fontName, fontData); + } + + return famData->mFontInfo.mCanceled ? 0 : 1; +} + +void +GDIFontInfo::LoadFontFamilyData(const nsAString& aFamilyName) +{ + // iterate over the family + LOGFONTW logFont; + memset(&logFont, 0, sizeof(LOGFONTW)); + logFont.lfCharSet = DEFAULT_CHARSET; + logFont.lfPitchAndFamily = 0; + uint32_t l = std::min<uint32_t>(aFamilyName.Length(), LF_FACESIZE - 1); + memcpy(logFont.lfFaceName, aFamilyName.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.Put(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(); +} + +#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(NS_LITERAL_STRING("fonts")))) { + return; + } + bool isDir; + if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) { + return; + } + + nsCOMPtr<nsISimpleEnumerator> e; + rv = localDir->GetDirectoryEntries(getter_AddRefs(e)); + if (NS_FAILED(rv)) { + return; + } + + bool hasMore; + while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr<nsISupports> entry; + if (NS_FAILED(e->GetNext(getter_AddRefs(entry)))) { + break; + } + nsCOMPtr<nsIFile> file = do_QueryInterface(entry); + if (!file) { + continue; + } + nsAutoString path; + if (NS_FAILED(file->GetPath(path))) { + continue; + } + AddFontResourceExW(path.get(), FR_PRIVATE, nullptr); + } +} + +#endif |