summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxGDIFontList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/thebes/gfxGDIFontList.cpp')
-rw-r--r--gfx/thebes/gfxGDIFontList.cpp1195
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