/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Foundation code. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2005 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Stuart Parmenter * Vladimir Vukicevic * Masayuki Nakano * Masatoshi Kimura * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "gfxWindowsPlatform.h" #include "gfxImageSurface.h" #include "gfxWindowsSurface.h" #include "nsUnicharUtils.h" #include "nsIPref.h" #include "nsServiceManagerUtils.h" #include "nsIWindowsRegKey.h" #include "gfxWindowsFonts.h" #include #include "lcms.h" //#define DEBUG_CMAP_SIZE 1 static __inline void BuildKeyNameFromFontName(nsAString &aName) { if (aName.Length() >= LF_FACESIZE) aName.Truncate(LF_FACESIZE - 1); ToLowerCase(aName); } int PR_CALLBACK gfxWindowsPlatform::PrefChangedCallback(const char *aPrefName, void *closure) { // XXX this could be made to only clear out the cache for the prefs that were changed // but it probably isn't that big a deal. gfxWindowsPlatform *plat = static_cast(closure); plat->mPrefFonts.Clear(); return 0; } gfxWindowsPlatform::gfxWindowsPlatform() { mFonts.Init(200); mFontAliases.Init(20); mFontSubstitutes.Init(50); mPrefFonts.Init(10); UpdateFontList(); nsCOMPtr pref = do_GetService(NS_PREF_CONTRACTID); pref->RegisterCallback("font.", PrefChangedCallback, this); pref->RegisterCallback("font.name-list.", PrefChangedCallback, this); pref->RegisterCallback("intl.accept_languages", PrefChangedCallback, this); // don't bother unregistering. We'll get shutdown after the pref service } gfxWindowsPlatform::~gfxWindowsPlatform() { } already_AddRefed gfxWindowsPlatform::CreateOffscreenSurface(const gfxIntSize& size, gfxASurface::gfxImageFormat imageFormat) { gfxASurface *surf = new gfxWindowsSurface(size, imageFormat); NS_IF_ADDREF(surf); return surf; } int CALLBACK gfxWindowsPlatform::FontEnumProc(const ENUMLOGFONTEXW *lpelfe, const NEWTEXTMETRICEXW *nmetrics, DWORD fontType, LPARAM data) { FontTable *ht = reinterpret_cast(data); const NEWTEXTMETRICW& metrics = nmetrics->ntmTm; const LOGFONTW& logFont = lpelfe->elfLogFont; // Ignore vertical fonts if (logFont.lfFaceName[0] == L'@') return 1; nsAutoString name(logFont.lfFaceName); BuildKeyNameFromFontName(name); nsRefPtr ff; if (!ht->Get(name, &ff)) { ff = new FontFamily(nsDependentString(logFont.lfFaceName)); ht->Put(name, ff); } return 1; } // general cmap reading routines moved to gfxFontUtils.cpp struct FontListData { FontListData(const nsACString& aLangGroup, const nsACString& aGenericFamily, nsStringArray& aListOfFonts) : mLangGroup(aLangGroup), mGenericFamily(aGenericFamily), mStringArray(aListOfFonts) {} const nsACString& mLangGroup; const nsACString& mGenericFamily; nsStringArray& mStringArray; }; PLDHashOperator PR_CALLBACK gfxWindowsPlatform::HashEnumFunc(nsStringHashKey::KeyType aKey, nsRefPtr& aFontFamily, void* userArg) { FontListData *data = (FontListData*)userArg; // use the first variation for now. This data should be the same // for all the variations and should probably be moved up to // the Family nsRefPtr aFontEntry = aFontFamily->mVariations[0]; /* skip symbol fonts */ if (aFontEntry->mSymbolFont) return PL_DHASH_NEXT; if (aFontEntry->SupportsLangGroup(data->mLangGroup) && aFontEntry->MatchesGenericFamily(data->mGenericFamily)) data->mStringArray.AppendString(aFontFamily->mName); return PL_DHASH_NEXT; } nsresult gfxWindowsPlatform::GetFontList(const nsACString& aLangGroup, const nsACString& aGenericFamily, nsStringArray& aListOfFonts) { FontListData data(aLangGroup, aGenericFamily, aListOfFonts); mFonts.Enumerate(gfxWindowsPlatform::HashEnumFunc, &data); aListOfFonts.Sort(); aListOfFonts.Compact(); return NS_OK; } static void RemoveCharsetFromFontSubstitute(nsAString &aName) { PRInt32 comma = aName.FindChar(PRUnichar(',')); if (comma >= 0) aName.Truncate(comma); } nsresult gfxWindowsPlatform::UpdateFontList() { gfxFontCache *fc = gfxFontCache::GetCache(); if (fc) fc->AgeAllGenerations(); mFonts.Clear(); mFontAliases.Clear(); mNonExistingFonts.Clear(); mFontSubstitutes.Clear(); mPrefFonts.Clear(); mCodepointsWithNoFonts.reset(); LOGFONTW logFont; logFont.lfCharSet = DEFAULT_CHARSET; logFont.lfFaceName[0] = 0; logFont.lfPitchAndFamily = 0; // Use the screen DC here.. should we use something else for printing? HDC dc = ::GetDC(nsnull); EnumFontFamiliesExW(dc, &logFont, (FONTENUMPROCW)gfxWindowsPlatform::FontEnumProc, (LPARAM)&mFonts, 0); ::ReleaseDC(nsnull, dc); // Create the list of FontSubstitutes nsCOMPtr regKey = do_CreateInstance("@mozilla.org/windows-registry-key;1"); if (!regKey) return NS_ERROR_FAILURE; NS_NAMED_LITERAL_STRING(kFontSubstitutesKey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes"); nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, kFontSubstitutesKey, nsIWindowsRegKey::ACCESS_READ); if (NS_FAILED(rv)) return rv; PRUint32 count; rv = regKey->GetValueCount(&count); if (NS_FAILED(rv) || count == 0) return rv; for (PRUint32 i = 0; i < count; i++) { nsAutoString substituteName; rv = regKey->GetValueName(i, substituteName); if (NS_FAILED(rv) || substituteName.IsEmpty() || substituteName.CharAt(1) == PRUnichar('@')) continue; PRUint32 valueType; rv = regKey->GetValueType(substituteName, &valueType); if (NS_FAILED(rv) || valueType != nsIWindowsRegKey::TYPE_STRING) continue; nsAutoString actualFontName; rv = regKey->ReadStringValue(substituteName, actualFontName); if (NS_FAILED(rv)) continue; RemoveCharsetFromFontSubstitute(substituteName); BuildKeyNameFromFontName(substituteName); RemoveCharsetFromFontSubstitute(actualFontName); BuildKeyNameFromFontName(actualFontName); nsRefPtr ff; if (!actualFontName.IsEmpty() && mFonts.Get(actualFontName, &ff)) mFontSubstitutes.Put(substituteName, ff); else mNonExistingFonts.AppendString(substituteName); } // initialize ranges of characters for which system-wide font search should be skipped mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls InitBadUnderlineList(); return NS_OK; } static PRBool SimpleResolverCallback(const nsAString& aName, void* aClosure) { nsString *result = static_cast(aClosure); result->Assign(aName); return PR_FALSE; } void gfxWindowsPlatform::InitBadUnderlineList() { nsAutoTArray blacklist; gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist); PRUint32 numFonts = blacklist.Length(); for (PRUint32 i = 0; i < numFonts; i++) { PRBool aborted; nsAutoString resolved; ResolveFontName(blacklist[i], SimpleResolverCallback, &resolved, aborted); if (resolved.IsEmpty()) continue; FontFamily *ff = FindFontFamily(resolved); if (!ff) continue; for (PRUint32 j = 0; j < ff->mVariations.Length(); ++j) { nsRefPtr fe = ff->mVariations[j]; fe->mIsBadUnderlineFont = 1; } } } nsresult gfxWindowsPlatform::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) { aFamilyName.Truncate(); PRBool aborted; return ResolveFontName(aFontName, SimpleResolverCallback, &aFamilyName, aborted); } struct ResolveData { ResolveData(gfxPlatform::FontResolverCallback aCallback, gfxWindowsPlatform *aCaller, const nsAString *aFontName, void *aClosure) : mFoundCount(0), mCallback(aCallback), mCaller(aCaller), mFontName(aFontName), mClosure(aClosure) {} PRUint32 mFoundCount; gfxPlatform::FontResolverCallback mCallback; gfxWindowsPlatform *mCaller; const nsAString *mFontName; void *mClosure; }; nsresult gfxWindowsPlatform::ResolveFontName(const nsAString& aFontName, FontResolverCallback aCallback, void *aClosure, PRBool& aAborted) { if (aFontName.IsEmpty()) return NS_ERROR_FAILURE; nsAutoString keyName(aFontName); BuildKeyNameFromFontName(keyName); nsRefPtr ff; if (mFonts.Get(keyName, &ff) || mFontSubstitutes.Get(keyName, &ff) || mFontAliases.Get(keyName, &ff)) { aAborted = !(*aCallback)(ff->mName, aClosure); // XXX If the font has font link, we should add the linked font. return NS_OK; } if (mNonExistingFonts.IndexOf(keyName) >= 0) { aAborted = PR_FALSE; return NS_OK; } LOGFONTW logFont; logFont.lfCharSet = DEFAULT_CHARSET; logFont.lfPitchAndFamily = 0; PRInt32 len = aFontName.Length(); if (len >= LF_FACESIZE) len = LF_FACESIZE - 1; memcpy(logFont.lfFaceName, nsPromiseFlatString(aFontName).get(), len * sizeof(PRUnichar)); logFont.lfFaceName[len] = 0; HDC dc = ::GetDC(nsnull); ResolveData data(aCallback, this, &keyName, aClosure); aAborted = !EnumFontFamiliesExW(dc, &logFont, (FONTENUMPROCW)gfxWindowsPlatform::FontResolveProc, (LPARAM)&data, 0); if (data.mFoundCount == 0) mNonExistingFonts.AppendString(keyName); ::ReleaseDC(nsnull, dc); return NS_OK; } int CALLBACK gfxWindowsPlatform::FontResolveProc(const ENUMLOGFONTEXW *lpelfe, const NEWTEXTMETRICEXW *nmetrics, DWORD fontType, LPARAM data) { const LOGFONTW& logFont = lpelfe->elfLogFont; // Ignore vertical fonts if (logFont.lfFaceName[0] == L'@' || logFont.lfFaceName[0] == 0) return 1; ResolveData *rData = reinterpret_cast(data); nsAutoString name(logFont.lfFaceName); // Save the alias name to cache nsRefPtr ff; nsAutoString keyName(name); BuildKeyNameFromFontName(keyName); if (!rData->mCaller->mFonts.Get(keyName, &ff)) { // This case only occurs on failing to build // the list of font substitue. In this case, the user should // reboot the Windows. Probably, we don't have the good way for // resolving in this time. NS_WARNING("Cannot find actual font"); return 1; } rData->mFoundCount++; rData->mCaller->mFontAliases.Put(*(rData->mFontName), ff); return (rData->mCallback)(name, rData->mClosure); // XXX If the font has font link, we should add the linked font. } struct FontSearch { FontSearch(PRUint32 aCh, gfxWindowsFont *aFont) : ch(aCh), fontToMatch(aFont), matchRank(-1) { } PRUint32 ch; nsRefPtr fontToMatch; PRInt32 matchRank; nsRefPtr bestMatch; }; PLDHashOperator PR_CALLBACK gfxWindowsPlatform::FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr& aFontFamily, void* userArg) { FontSearch *data = (FontSearch*)userArg; const PRUint32 ch = data->ch; nsRefPtr fe = aFontFamily->FindFontEntry(*data->fontToMatch->GetStyle()); // skip over non-unicode and bitmap fonts and fonts that don't have // the code point we're looking for if (fe->IsCrappyFont() || !fe->mCharacterMap.test(ch)) return PL_DHASH_NEXT; PRInt32 rank = 0; // fonts that claim to support the range are more // likely to be "better fonts" than ones that don't... (in theory) if (fe->SupportsRange(gfxFontUtils::CharRangeBit(ch))) rank += 1; if (fe->SupportsLangGroup(data->fontToMatch->GetStyle()->langGroup)) rank += 2; if (fe->mWindowsFamily == data->fontToMatch->GetFontEntry()->mWindowsFamily) rank += 3; if (fe->mWindowsPitch == data->fontToMatch->GetFontEntry()->mWindowsFamily) rank += 3; /* italic */ const PRBool italic = (data->fontToMatch->GetStyle()->style != FONT_STYLE_NORMAL); if (fe->mItalic != italic) rank += 3; /* weight */ PRInt8 baseWeight, weightDistance; data->fontToMatch->GetStyle()->ComputeWeightAndOffset(&baseWeight, &weightDistance); if (fe->mWeight == (baseWeight * 100) + (weightDistance * 100)) rank += 2; else if (fe->mWeight == data->fontToMatch->GetFontEntry()->mWeight) rank += 1; if (rank > data->matchRank || (rank == data->matchRank && Compare(fe->GetName(), data->bestMatch->GetName()) > 0)) { data->bestMatch = fe; data->matchRank = rank; } return PL_DHASH_NEXT; } FontEntry * gfxWindowsPlatform::FindFontForChar(PRUint32 aCh, gfxWindowsFont *aFont) { // is codepoint with no matching font? return null immediately if (mCodepointsWithNoFonts.test(aCh)) { return nsnull; } FontSearch data(aCh, aFont); // find fonts that support the character mFonts.Enumerate(gfxWindowsPlatform::FindFontForCharProc, &data); // no match? add to set of non-matching codepoints if (!data.bestMatch) { mCodepointsWithNoFonts.set(aCh); } return data.bestMatch; } gfxFontGroup * gfxWindowsPlatform::CreateFontGroup(const nsAString &aFamilies, const gfxFontStyle *aStyle) { return new gfxWindowsFontGroup(aFamilies, aStyle); } FontFamily * gfxWindowsPlatform::FindFontFamily(const nsAString& aName) { nsAutoString name(aName); BuildKeyNameFromFontName(name); nsRefPtr ff; if (!mFonts.Get(name, &ff) && !mFontSubstitutes.Get(name, &ff) && !mFontAliases.Get(name, &ff)) { return nsnull; } return ff.get(); } FontEntry * gfxWindowsPlatform::FindFontEntry(const nsAString& aName, const gfxFontStyle& aFontStyle) { nsRefPtr ff = FindFontFamily(aName); if (!ff) return nsnull; return ff->FindFontEntry(aFontStyle); } cmsHPROFILE gfxWindowsPlatform::GetPlatformCMSOutputProfile() { WCHAR str[1024+1]; DWORD size = 1024; HDC dc = GetDC(nsnull); GetICMProfileW(dc, &size, (LPWSTR)&str); ReleaseDC(nsnull, dc); cmsHPROFILE profile = cmsOpenProfileFromFile(NS_ConvertUTF16toUTF8(str).get(), "r"); #ifdef DEBUG_tor if (profile) fprintf(stderr, "ICM profile read from %s successfully\n", NS_ConvertUTF16toUTF8(str).get()); #endif return profile; } PRBool gfxWindowsPlatform::GetPrefFontEntries(const nsCString& aKey, nsTArray > *array) { return mPrefFonts.Get(aKey, array); } void gfxWindowsPlatform::SetPrefFontEntries(const nsCString& aKey, nsTArray >& array) { mPrefFonts.Put(aKey, array); }