Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/DebugOnly.h"
#include <algorithm>
#include "mozilla/Logging.h"
#include "mozilla/TextUtils.h"
#include "mozilla/Sprintf.h"
#include "gfxGDIFontList.h"
#include "gfxWindowsPlatform.h"
#include "gfxUserFontSet.h"
#include "gfxFontUtils.h"
#include "gfxGDIFont.h"
#include "nsServiceManagerUtils.h"
#include "nsTArray.h"
#include "nsUnicharUtils.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "gfxFontConstants.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/Telemetry.h"
#include <usp10.h>
using namespace mozilla;
using namespace mozilla::gfx;
#define ROUND(x) floor((x) + 0.5)
#define LOG_FONTLIST(args) \
MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
#define LOG_FONTLIST_ENABLED() \
MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
#define LOG_CMAPDATA_ENABLED() \
MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
static __inline void BuildKeyNameFromFontName(nsAString& aName) {
if (aName.Length() >= LF_FACESIZE) aName.Truncate(LF_FACESIZE - 1);
ToLowerCase(aName);
}
// Implementation of gfxPlatformFontList for Win32 GDI,
// using GDI font enumeration APIs to get the list of fonts
class WinUserFontData : public gfxUserFontData {
public:
explicit WinUserFontData(HANDLE aFontRef) : mFontRef(aFontRef) {}
virtual ~WinUserFontData() {
DebugOnly<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 nsACString& aFaceName,
gfxWindowsFontType aFontType, SlantStyleRange aStyle,
WeightRange aWeight, StretchRange aStretch,
gfxUserFontData* aUserFontData)
: gfxFontEntry(aFaceName),
mFontType(aFontType),
mForceGDI(false),
mUnicodeRanges() {
mUserFontData.reset(aUserFontData);
mStyleRange = aStyle;
mWeightRange = aWeight;
mStretchRange = aStretch;
if (IsType1()) mForceGDI = true;
mIsDataUserFont = aUserFontData != nullptr;
InitLogFont(aFaceName, aFontType);
}
gfxFontEntry* GDIFontEntry::Clone() const {
MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
return new GDIFontEntry(Name(), mFontType, SlantStyle(), Weight(), Stretch(),
nullptr);
}
nsresult GDIFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
AUTO_PROFILER_LABEL("GDIFontEntry::ReadCMAP", OTHER);
// attempt this once, if errors occur leave a blank cmap
if (mCharacterMap) {
return NS_OK;
}
// skip non-SFNT fonts completely
if (mFontType != GFX_FONT_TYPE_PS_OPENTYPE &&
mFontType != GFX_FONT_TYPE_TT_OPENTYPE &&
mFontType != GFX_FONT_TYPE_TRUETYPE) {
mCharacterMap = new gfxCharacterMap();
mCharacterMap->mBuildOnTheFly = true;
return NS_ERROR_FAILURE;
}
RefPtr<gfxCharacterMap> charmap;
nsresult rv;
if (aFontInfoData &&
(charmap = GetCMAPFromFontInfo(aFontInfoData, mUVSOffset))) {
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);
}
}
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",
mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
charmap->mHash, mCharacterMap == charmap ? " new" : ""));
if (LOG_CMAPDATA_ENABLED()) {
char prefix[256];
SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
charmap->Dump(prefix, eGfxLog_cmapdata);
}
return rv;
}
gfxFont* GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle) {
return new gfxGDIFont(this, aFontStyle);
}
nsresult GDIFontEntry::CopyFontTable(uint32_t aTableTag,
nsTArray<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;
}
already_AddRefed<UnscaledFontGDI> GDIFontEntry::LookupUnscaledFont(
HFONT aFont) {
RefPtr<UnscaledFontGDI> unscaledFont(mUnscaledFont);
if (!unscaledFont) {
LOGFONT lf;
GetObject(aFont, sizeof(LOGFONT), &lf);
unscaledFont = new UnscaledFontGDI(lf);
mUnscaledFont = unscaledFont;
}
return unscaledFont.forget();
}
void GDIFontEntry::FillLogFont(LOGFONTW* aLogFont, LONG aWeight,
gfxFloat aSize) {
memcpy(aLogFont, &mLogFont, sizeof(LOGFONTW));
aLogFont->lfHeight = (LONG)-ROUND(aSize);
if (aLogFont->lfHeight == 0) {
aLogFont->lfHeight = -1;
}
// If a non-zero weight is passed in, use this to override the original
// weight in the entry's logfont. This is used to control synthetic bolding
// for installed families with no bold face, and for downloaded fonts
// (but NOT for local user fonts, because it could cause a different,
// glyph-incompatible face to be used)
if (aWeight != 0) {
aLogFont->lfWeight = aWeight;
}
// for non-local() user fonts, we never want to apply italics here;
// if the face is described as italic, we should use it as-is,
// and if it's not, but then the element is styled italic, we'll use
// a cairo transform to create fake italic (oblique)
if (mIsDataUserFont) {
aLogFont->lfItalic = 0;
}
}
#define MISSING_GLYPH \
0x1F // glyph index returned for missing characters
// on WinXP with .fon fonts, but not Type1 (.pfb)
bool GDIFontEntry::TestCharacterMap(uint32_t aCh) {
if (!mCharacterMap) {
ReadCMAP();
NS_ASSERTION(mCharacterMap, "failed to initialize a character map");
}
if (mCharacterMap->mBuildOnTheFly) {
if (aCh > 0xFFFF) return false;
// previous code was using the group style
gfxFontStyle fakeStyle;
if (!IsUpright()) {
fakeStyle.style = FontSlantStyle::Italic();
}
fakeStyle.weight = Weight().Min();
RefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, nullptr);
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 nsACString& aName,
gfxWindowsFontType aFontType) {
#define CLIP_TURNOFF_FONTASSOCIATION 0x40
mLogFont.lfHeight = -1;
// Fill in logFont structure
mLogFont.lfWidth = 0;
mLogFont.lfEscapement = 0;
mLogFont.lfOrientation = 0;
mLogFont.lfUnderline = FALSE;
mLogFont.lfStrikeOut = FALSE;
mLogFont.lfCharSet = DEFAULT_CHARSET;
mLogFont.lfOutPrecision = FontTypeToOutPrecision(aFontType);
mLogFont.lfClipPrecision = CLIP_TURNOFF_FONTASSOCIATION;
mLogFont.lfQuality = DEFAULT_QUALITY;
mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
// always force lfItalic if we want it. Font selection code will
// do its best to give us an italic font entry, but if no face exists
// it may give us a regular one based on weight. Windows should
// do fake italic for us in that case.
mLogFont.lfItalic = !IsUpright();
mLogFont.lfWeight = Weight().Min().ToIntRounded();
NS_ConvertUTF8toUTF16 name(aName);
int len = std::min<int>(name.Length(), LF_FACESIZE - 1);
memcpy(&mLogFont.lfFaceName, name.BeginReading(), len * sizeof(char16_t));
mLogFont.lfFaceName[len] = '\0';
}
GDIFontEntry* GDIFontEntry::CreateFontEntry(const nsACString& aName,
gfxWindowsFontType aFontType,
SlantStyleRange aStyle,
WeightRange aWeight,
StretchRange aStretch,
gfxUserFontData* aUserFontData) {
// jtdfix - need to set charset, unicode ranges, pitch/family
GDIFontEntry* fe = new GDIFontEntry(aName, aFontType, aStyle, aWeight,
aStretch, aUserFontData);
return fe;
}
void GDIFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const {
aSizes->mFontListSize += aMallocSizeOf(this);
AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
}
/***************************************************************
*
* GDIFontFamily
*
*/
static bool ShouldIgnoreItalicStyle(const nsACString& aName) {
// Ignore italic style's "Meiryo" because "Meiryo (Bold) Italic" has
// non-italic style glyphs as Japanese characters. However, using it
// causes serious problem if web pages wants some elements to be
// different style from others only with font-style. For example,
// <em> and <i> should be rendered as italic in the default style.
return aName.EqualsLiteral("Meiryo") ||
aName.EqualsLiteral(
"\xe3\x83\xa1\xe3\x82\xa4\xe3\x83\xaa\xe3\x82\xaa");
}
int CALLBACK GDIFontFamily::FamilyAddStylesProc(
const ENUMLOGFONTEXW* lpelfe, const NEWTEXTMETRICEXW* nmetrics,
DWORD fontType, LPARAM data) {
const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
LOGFONTW logFont = lpelfe->elfLogFont;
GDIFontFamily* ff = reinterpret_cast<GDIFontFamily*>(data);
if (logFont.lfItalic && ShouldIgnoreItalicStyle(ff->mName)) {
return 1;
}
// Some fonts claim to support things > 900, but we don't so clamp the sizes
logFont.lfWeight = clamped(logFont.lfWeight, LONG(100), LONG(900));
gfxWindowsFontType feType =
GDIFontEntry::DetermineFontType(metrics, fontType);
GDIFontEntry* fe = nullptr;
for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
fe = static_cast<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->Weight().Min() == FontWeight(int32_t(logFont.lfWeight)) &&
fe->IsItalic() == (logFont.lfItalic == 0xFF)) {
// update the charset bit here since this could be different
// XXX Can we still do this now that we store mCharset
// on the font family rather than the font entry?
ff->mCharset.set(metrics.tmCharSet);
return 1;
}
}
// We can't set the hasItalicFace flag correctly here,
// because we might not have seen the family's italic face(s) yet.
// So we'll set that flag for all members after loading all the faces.
auto italicStyle = (logFont.lfItalic == 0xFF ? FontSlantStyle::Italic()
: FontSlantStyle::Normal());
fe = GDIFontEntry::CreateFontEntry(
NS_ConvertUTF16toUTF8(lpelfe->elfFullName), feType,
SlantStyleRange(italicStyle),
WeightRange(FontWeight(int32_t(logFont.lfWeight))),
StretchRange(FontStretch::Normal()), nullptr);
if (!fe) return 1;
ff->AddFontEntry(fe);
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: normal",
fe->Name().get(), ff->Name().get(),
(logFont.lfItalic == 0xff) ? "italic" : "normal", logFont.lfWeight));
}
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;
NS_ConvertUTF8toUTF16 name(mName);
uint32_t l = std::min<uint32_t>(name.Length(), LF_FACESIZE - 1);
memcpy(logFont.lfFaceName, name.get(), l * sizeof(char16_t));
EnumFontFamiliesExW(hdc, &logFont,
(FONTENUMPROCW)GDIFontFamily::FamilyAddStylesProc,
(LPARAM)this, 0);
if (LOG_FONTLIST_ENABLED() && mAvailableFonts.Length() == 0) {
LOG_FONTLIST(
("(fontlist) no styles available in family \"%s\"", mName.get()));
}
ReleaseDC(nullptr, hdc);
if (mIsBadUnderlineFamily) {
SetBadUnderlineFonts();
}
CheckForSimpleFamily();
}
/***************************************************************
*
* gfxGDIFontList
*
*/
gfxGDIFontList::gfxGDIFontList() : mFontSubstitutes(32) {
#ifdef MOZ_BUNDLED_FONTS
if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
TimeStamp start = TimeStamp::Now();
ActivateBundledFonts();
TimeStamp end = TimeStamp::Now();
Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
(end - start).ToMilliseconds());
}
#endif
}
static void RemoveCharsetFromFontSubstitute(nsAString& aName) {
int32_t comma = aName.FindChar(char16_t(','));
if (comma >= 0) aName.Truncate(comma);
}
#define MAX_VALUE_NAME 512
#define MAX_VALUE_DATA 512
nsresult gfxGDIFontList::GetFontSubstitutes() {
HKEY hKey;
DWORD i, rv, lenAlias, lenActual, valueType;
WCHAR aliasName[MAX_VALUE_NAME];
WCHAR actualName[MAX_VALUE_DATA];
if (RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
0, KEY_READ, &hKey) != ERROR_SUCCESS) {
return NS_ERROR_FAILURE;
}
for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
aliasName[0] = 0;
lenAlias = ArrayLength(aliasName);
actualName[0] = 0;
lenActual = sizeof(actualName);
rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
(LPBYTE)actualName, &lenActual);
if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
continue;
}
if (aliasName[0] == WCHAR('@')) {
continue;
}
nsAutoString substituteName((char16_t*)aliasName);
nsAutoString actualFontName((char16_t*)actualName);
RemoveCharsetFromFontSubstitute(substituteName);
BuildKeyNameFromFontName(substituteName);
RemoveCharsetFromFontSubstitute(actualFontName);
BuildKeyNameFromFontName(actualFontName);
gfxFontFamily* ff;
NS_ConvertUTF16toUTF8 substitute(substituteName);
NS_ConvertUTF16toUTF8 actual(actualFontName);
if (!actual.IsEmpty() && (ff = mFontFamilies.GetWeak(actual))) {
mFontSubstitutes.InsertOrUpdate(substitute, RefPtr{ff});
} else {
mNonExistingFonts.AppendElement(substitute);
}
}
// "Courier" on a default Windows install is an ugly bitmap font.
// If there is no substitution for Courier in the registry
// substitute "Courier" with "Courier New".
nsAutoString substituteName;
substituteName.AssignLiteral("Courier");
BuildKeyNameFromFontName(substituteName);
NS_ConvertUTF16toUTF8 substitute(substituteName);
if (!mFontSubstitutes.GetWeak(substitute)) {
gfxFontFamily* ff;
nsAutoString actualFontName;
actualFontName.AssignLiteral("Courier New");
BuildKeyNameFromFontName(actualFontName);
NS_ConvertUTF16toUTF8 actual(actualFontName);
ff = mFontFamilies.GetWeak(actual);
if (ff) {
mFontSubstitutes.InsertOrUpdate(substitute, RefPtr{ff});
}
}
return NS_OK;
}
nsresult gfxGDIFontList::InitFontListForPlatform() {
Telemetry::AutoTimer<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;
(void)EnumFontFamiliesExW(hdc.GetDC(), &logfont,
(FONTENUMPROCW)&EnumFontFamExProc, 0, 0);
GetFontSubstitutes();
GetPrefsAndStartLoader();
return NS_OK;
}
int CALLBACK gfxGDIFontList::EnumFontFamExProc(ENUMLOGFONTEXW* lpelfe,
NEWTEXTMETRICEXW* lpntme,
DWORD fontType, LPARAM lParam) {
const NEWTEXTMETRICW& metrics = lpntme->ntmTm;
const LOGFONTW& lf = lpelfe->elfLogFont;
if (lf.lfFaceName[0] == '@') {
return 1;
}
nsAutoString name(lf.lfFaceName);
BuildKeyNameFromFontName(name);
NS_ConvertUTF16toUTF8 key(name);
gfxGDIFontList* fontList = PlatformFontList();
if (!fontList->mFontFamilies.Contains(key)) {
NS_ConvertUTF16toUTF8 faceName(lf.lfFaceName);
FontVisibility visibility = FontVisibility::Unknown; // TODO
RefPtr<GDIFontFamily> family = new GDIFontFamily(faceName, visibility);
fontList->mFontFamilies.InsertOrUpdate(key, RefPtr{family});
// if locale is such that CJK font names are the default coming from
// GDI, then if a family name is non-ASCII immediately read in other
// family names. This assures that MS Gothic, MS Mincho are all found
// before lookups begin.
if (!IsAscii(faceName)) {
family->ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList());
}
if (fontList->mBadUnderlineFamilyNames.ContainsSorted(key)) {
family->SetBadUnderlineFamily();
}
family->mWindowsFamily = lf.lfPitchAndFamily & 0xF0;
family->mWindowsPitch = lf.lfPitchAndFamily & 0x0F;
// mark the charset bit
family->mCharset.set(metrics.tmCharSet);
}
return 1;
}
gfxFontEntry* gfxGDIFontList::LookupLocalFont(const nsACString& aFontName,
WeightRange aWeightForEntry,
StretchRange aStretchForEntry,
SlantStyleRange aStyleForEntry) {
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->SlantStyle(), lookup->Weight(), aStretchForEntry, nullptr);
if (!fe) return nullptr;
fe->mIsLocalUserFont = true;
// make the new font entry match the userfont entry style characteristics
fe->mWeightRange = aWeightForEntry;
fe->mStyleRange = aStyleForEntry;
fe->mStretchRange = aStretchForEntry;
return fe;
}
// If aFontData contains only a MS/Symbol cmap subtable, not MS/Unicode,
// we modify the subtable header to mark it as Unicode instead, because
// otherwise GDI will refuse to load the font.
// NOTE that this function does not bounds-check every access to the font data.
// This is OK because we only use it on data that has already been validated
// by OTS, and therefore we will not hit out-of-bounds accesses here.
static bool FixupSymbolEncodedFont(uint8_t* aFontData, uint32_t aLength) {
struct CmapHeader {
AutoSwap_PRUint16 version;
AutoSwap_PRUint16 numTables;
};
struct CmapEncodingRecord {
AutoSwap_PRUint16 platformID;
AutoSwap_PRUint16 encodingID;
AutoSwap_PRUint32 offset;
};
const uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
const TableDirEntry* dir = gfxFontUtils::FindTableDirEntry(aFontData, kCMAP);
if (dir && uint32_t(dir->length) >= sizeof(CmapHeader)) {
CmapHeader* cmap =
reinterpret_cast<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 nsACString& aFontName,
WeightRange aWeightForEntry,
StretchRange aStretchForEntry,
SlantStyleRange aStyleForEntry,
const uint8_t* aFontData,
uint32_t aLength) {
// MakePlatformFont is responsible for deleting the font data with free
// so we set up a stack object to ensure it is freed even if we take an
// early exit
struct FontDataDeleter {
explicit FontDataDeleter(const uint8_t* aFontData) : mFontData(aFontData) {}
~FontDataDeleter() { free((void*)mFontData); }
const uint8_t* mFontData;
};
FontDataDeleter autoDelete(aFontData);
bool isCFF = gfxFontUtils::IsCffFont(aFontData);
nsresult rv;
HANDLE fontRef = nullptr;
nsAutoString uniqueName;
rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
if (NS_FAILED(rv)) return nullptr;
FallibleTArray<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");
// "A font that is added by AddFontMemResourceEx is always private
// to the process that made the call and is not enumerable."
fontRef =
AddFontMemResourceEx(fontData, fontLength, 0 /* reserved */, &numFonts);
if (!fontRef) {
if (FixupSymbolEncodedFont(fontData, fontLength)) {
fontRef = AddFontMemResourceEx(fontData, fontLength, 0, &numFonts);
}
}
if (!fontRef) {
return nullptr;
}
// only load fonts with a single face contained in the data
// AddFontMemResourceEx generates an additional face name for
// vertical text if the font supports vertical writing but since
// the font is referenced via the name this can be ignored
if (fontRef && numFonts > 2) {
RemoveFontMemResourceEx(fontRef);
return nullptr;
}
// make a new font entry using the unique name
WinUserFontData* winUserFontData = new WinUserFontData(fontRef);
GDIFontEntry* fe = GDIFontEntry::CreateFontEntry(
NS_ConvertUTF16toUTF8(uniqueName),
gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE
: GFX_FONT_TYPE_TRUETYPE) /*type*/,
aStyleForEntry, aWeightForEntry, aStretchForEntry, winUserFontData);
if (fe) {
fe->mIsDataUserFont = true;
}
return fe;
}
bool gfxGDIFontList::FindAndAddFamilies(StyleGenericFontFamily aGeneric,
const nsACString& aFamily,
nsTArray<FamilyAndGeneric>* aOutput,
FindFamiliesFlags aFlags,
gfxFontStyle* aStyle, nsAtom* aLanguage,
gfxFloat aDevToCssSize) {
NS_ConvertUTF8toUTF16 key16(aFamily);
BuildKeyNameFromFontName(key16);
NS_ConvertUTF16toUTF8 keyName(key16);
gfxFontFamily* ff = mFontSubstitutes.GetWeak(keyName);
if (ff) {
aOutput->AppendElement(FamilyAndGeneric(ff, aGeneric));
return true;
}
if (mNonExistingFonts.Contains(keyName)) {
return false;
}
return gfxPlatformFontList::FindAndAddFamilies(
aGeneric, aFamily, aOutput, aFlags, aStyle, aLanguage, aDevToCssSize);
}
FontFamily gfxGDIFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle,
nsAtom* aLanguage) {
FontFamily ff;
// this really shouldn't fail to find a font....
NONCLIENTMETRICSW ncm;
ncm.cbSize = sizeof(ncm);
BOOL status =
::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
if (status) {
ff = FindFamily(NS_ConvertUTF16toUTF8(ncm.lfMessageFont.lfFaceName));
if (!ff.IsNull()) {
return ff;
}
}
// ...but just in case, try another (long-deprecated) approach as well
HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
LOGFONTW logFont;
if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) {
ff = FindFamily(NS_ConvertUTF16toUTF8(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() = default;
virtual void Load() {
mHdc = GetDC(nullptr);
SetGraphicsMode(mHdc, GM_ADVANCED);
FontInfoData::Load();
ReleaseDC(nullptr, mHdc);
}
// loads font data for all members of a given family
virtual void LoadFontFamilyData(const nsACString& aFamilyName);
// callback for GDI EnumFontFamiliesExW call
static int CALLBACK EnumerateFontsForFamily(const ENUMLOGFONTEXW* lpelfe,
const NEWTEXTMETRICEXW* nmetrics,
DWORD fontType, LPARAM data);
HDC mHdc;
};
struct EnumerateFontsForFamilyData {
EnumerateFontsForFamilyData(const nsACString& aFamilyName,
GDIFontInfo& aFontInfo)
: mFamilyName(aFamilyName), mFontInfo(aFontInfo) {}
nsCString mFamilyName;
nsTArray<nsCString> mOtherFamilyNames;
GDIFontInfo& mFontInfo;
nsCString 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;
NS_ConvertUTF16toUTF8 fontName(lpelfe->elfFullName);
// callback called for each style-charset so return if style already seen
if (fontName.Equals(famData->mPreviousFontName)) {
return 1;
}
famData->mPreviousFontName = fontName;
famData->mFontInfo.mLoadStats.fonts++;
// read name table info
bool nameDataLoaded = false;
if (famData->mFontInfo.mLoadFaceNames || famData->mFontInfo.mLoadOtherNames) {
uint32_t kNAME =
NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
uint32_t nameSize;
AutoTArray<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) {
gfxFontUtils::ReadOtherFamilyNamesForFace(
famData->mFamilyName, (const char*)(nameData.Elements()), nameSize,
famData->mOtherFamilyNames, false);
}
}
}
// read cmap
bool cmapLoaded = false;
gfxWindowsFontType feType =
GDIFontEntry::DetermineFontType(metrics, fontType);
if (famData->mFontInfo.mLoadCmaps && (feType == GFX_FONT_TYPE_PS_OPENTYPE ||
feType == GFX_FONT_TYPE_TT_OPENTYPE ||
feType == GFX_FONT_TYPE_TRUETYPE)) {
uint32_t kCMAP =
NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
uint32_t cmapSize;
AutoTArray<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;
RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
uint32_t offset;
if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(), cmapSize,
*charmap, offset))) {
fontData.mCharacterMap = charmap;
fontData.mUVSOffset = offset;
cmapLoaded = true;
famData->mFontInfo.mLoadStats.cmaps++;
}
}
}
if (cmapLoaded || nameDataLoaded) {
famData->mFontInfo.mFontFaceData.InsertOrUpdate(fontName, fontData);
}
return famData->mFontInfo.mCanceled ? 0 : 1;
}
void GDIFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
// iterate over the family
LOGFONTW logFont;
memset(&logFont, 0, sizeof(LOGFONTW));
logFont.lfCharSet = DEFAULT_CHARSET;
logFont.lfPitchAndFamily = 0;
NS_ConvertUTF8toUTF16 name(aFamilyName);
uint32_t l = std::min<uint32_t>(name.Length(), LF_FACESIZE - 1);
memcpy(logFont.lfFaceName, name.BeginReading(), l * sizeof(char16_t));
EnumerateFontsForFamilyData data(aFamilyName, *this);
EnumFontFamiliesExW(mHdc, &logFont,
(FONTENUMPROCW)GDIFontInfo::EnumerateFontsForFamily,
(LPARAM)(&data), 0);
// if found other names, insert them
if (data.mOtherFamilyNames.Length() != 0) {
mOtherFamilyNames.InsertOrUpdate(aFamilyName, data.mOtherFamilyNames);
mLoadStats.othernames += data.mOtherFamilyNames.Length();
}
}
already_AddRefed<FontInfoData> gfxGDIFontList::CreateFontInfoData() {
bool loadCmaps = !UsesSystemFallback() ||
gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
RefPtr<GDIFontInfo> fi =
new GDIFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
return fi.forget();
}
gfxFontFamily* gfxGDIFontList::CreateFontFamily(
const nsACString& aName, FontVisibility aVisibility) const {
return new GDIFontFamily(aName, aVisibility);
}
#ifdef MOZ_BUNDLED_FONTS
void gfxGDIFontList::ActivateBundledFonts() {
nsCOMPtr<nsIFile> localDir;
nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
if (NS_FAILED(rv)) {
return;
}
if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
return;
}
bool isDir;
if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
return;
}
nsCOMPtr<nsIDirectoryEnumerator> e;
rv = localDir->GetDirectoryEntries(getter_AddRefs(e));
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIFile> file;
while (NS_SUCCEEDED(e->GetNextFile(getter_AddRefs(file))) && file) {
nsAutoString path;
if (NS_FAILED(file->GetPath(path))) {
continue;
}
AddFontResourceExW(path.get(), FR_PRIVATE, nullptr);
}
}
#endif