gecko-dev/gfx/thebes/gfxGDIFontList.cpp
Wes Kocher a5249b6201 Backed out 25 changesets (bug 1156742) for build bustage in SkOSLibrary_win.cpp CLOSED TREE
Backed out changeset 31c0aadae8e7 (bug 1156742)
Backed out changeset f08df57ff700 (bug 1156742)
Backed out changeset 90c026d5dcb1 (bug 1156742)
Backed out changeset 8c6d14d80238 (bug 1156742)
Backed out changeset 72d86b0471c9 (bug 1156742)
Backed out changeset 363829accc09 (bug 1156742)
Backed out changeset 16360fe94d54 (bug 1156742)
Backed out changeset 5355c636a81a (bug 1156742)
Backed out changeset 18844d26b873 (bug 1156742)
Backed out changeset 99df86591613 (bug 1156742)
Backed out changeset 7ac1c7e15a11 (bug 1156742)
Backed out changeset de352000aae1 (bug 1156742)
Backed out changeset 4dd34ea230c6 (bug 1156742)
Backed out changeset 48c6ce65a5c2 (bug 1156742)
Backed out changeset dd52947f73c6 (bug 1156742)
Backed out changeset 5fe429ee880c (bug 1156742)
Backed out changeset dedca8fb19b0 (bug 1156742)
Backed out changeset d748b1354f92 (bug 1156742)
Backed out changeset 7f8e59588518 (bug 1156742)
Backed out changeset b90d302c57f6 (bug 1156742)
Backed out changeset 7a4da453572c (bug 1156742)
Backed out changeset a40eea914519 (bug 1156742)
Backed out changeset 99a8859afcdb (bug 1156742)
Backed out changeset 4934e88b2d7a (bug 1156742)
Backed out changeset 79733166f05e (bug 1156742) for build bustage in SkOSLibrary_win.cpp CLOSED TREE

--HG--
extra : amend_source : beeb6abbcc724b79ac166e828efa78dc4c5301da
2015-12-21 13:47:33 -08:00

1129 lines
36 KiB
C++

/* -*- 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 "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 "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];
sprintf(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 = aUserFontData;
mStyle = aStyle;
mWeight = aWeight;
mStretch = aStretch;
if (IsType1())
mForceGDI = true;
mIsDataUserFont = aUserFontData != nullptr;
InitLogFont(aFaceName, aFontType);
}
nsresult
GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
{
// 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();
AutoFallibleTArray<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];
sprintf(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,
FallibleTArray<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] = { 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::InitFontList()
{
Telemetry::AutoTimer<Telemetry::GDI_INITFONTLIST_TOTAL> timer;
// reset font lists
gfxPlatformFontList::InitFontList();
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;
}
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)
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;
}
gfxFontFamily*
gfxGDIFontList::FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle,
gfxFloat aDevToCssSize)
{
nsAutoString keyName(aFamily);
BuildKeyNameFromFontName(keyName);
gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName);
if (ff) {
return ff;
}
if (mNonExistingFonts.Contains(keyName)) {
return nullptr;
}
return gfxPlatformFontList::FindFamily(aFamily);
}
gfxFontFamily*
gfxGDIFontList::GetDefaultFont(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;
AutoFallibleTArray<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;
AutoFallibleTArray<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 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;
}
nsCString path;
if (NS_FAILED(file->GetNativePath(path))) {
continue;
}
AddFontResourceEx(path.get(), FR_PRIVATE, nullptr);
}
}
#endif