Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "mozilla/DebugOnly.h" |
michael@0 | 7 | #include <algorithm> |
michael@0 | 8 | |
michael@0 | 9 | #ifdef MOZ_LOGGING |
michael@0 | 10 | #define FORCE_PR_LOG /* Allow logging in the release build */ |
michael@0 | 11 | #endif |
michael@0 | 12 | #include "prlog.h" |
michael@0 | 13 | |
michael@0 | 14 | #include "gfxGDIFontList.h" |
michael@0 | 15 | #include "gfxWindowsPlatform.h" |
michael@0 | 16 | #include "gfxUserFontSet.h" |
michael@0 | 17 | #include "gfxFontUtils.h" |
michael@0 | 18 | #include "gfxGDIFont.h" |
michael@0 | 19 | |
michael@0 | 20 | #include "nsServiceManagerUtils.h" |
michael@0 | 21 | #include "nsTArray.h" |
michael@0 | 22 | #include "nsUnicharUtils.h" |
michael@0 | 23 | |
michael@0 | 24 | #include "nsDirectoryServiceUtils.h" |
michael@0 | 25 | #include "nsDirectoryServiceDefs.h" |
michael@0 | 26 | #include "nsAppDirectoryServiceDefs.h" |
michael@0 | 27 | #include "nsISimpleEnumerator.h" |
michael@0 | 28 | #include "nsIWindowsRegKey.h" |
michael@0 | 29 | #include "gfxFontConstants.h" |
michael@0 | 30 | |
michael@0 | 31 | #include "mozilla/MemoryReporting.h" |
michael@0 | 32 | #include "mozilla/Telemetry.h" |
michael@0 | 33 | #include "mozilla/WindowsVersion.h" |
michael@0 | 34 | |
michael@0 | 35 | #include <usp10.h> |
michael@0 | 36 | |
michael@0 | 37 | using namespace mozilla; |
michael@0 | 38 | |
michael@0 | 39 | #define ROUND(x) floor((x) + 0.5) |
michael@0 | 40 | |
michael@0 | 41 | |
michael@0 | 42 | #ifndef CLEARTYPE_QUALITY |
michael@0 | 43 | #define CLEARTYPE_QUALITY 5 |
michael@0 | 44 | #endif |
michael@0 | 45 | |
michael@0 | 46 | #ifdef PR_LOGGING |
michael@0 | 47 | #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ |
michael@0 | 48 | PR_LOG_DEBUG, args) |
michael@0 | 49 | #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \ |
michael@0 | 50 | gfxPlatform::GetLog(eGfxLog_fontlist), \ |
michael@0 | 51 | PR_LOG_DEBUG) |
michael@0 | 52 | |
michael@0 | 53 | #define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \ |
michael@0 | 54 | gfxPlatform::GetLog(eGfxLog_cmapdata), \ |
michael@0 | 55 | PR_LOG_DEBUG) |
michael@0 | 56 | |
michael@0 | 57 | #endif // PR_LOGGING |
michael@0 | 58 | |
michael@0 | 59 | static __inline void |
michael@0 | 60 | BuildKeyNameFromFontName(nsAString &aName) |
michael@0 | 61 | { |
michael@0 | 62 | if (aName.Length() >= LF_FACESIZE) |
michael@0 | 63 | aName.Truncate(LF_FACESIZE - 1); |
michael@0 | 64 | ToLowerCase(aName); |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | // Implementation of gfxPlatformFontList for Win32 GDI, |
michael@0 | 68 | // using GDI font enumeration APIs to get the list of fonts |
michael@0 | 69 | |
michael@0 | 70 | class WinUserFontData : public gfxUserFontData { |
michael@0 | 71 | public: |
michael@0 | 72 | WinUserFontData(HANDLE aFontRef) |
michael@0 | 73 | : mFontRef(aFontRef) |
michael@0 | 74 | { } |
michael@0 | 75 | |
michael@0 | 76 | virtual ~WinUserFontData() |
michael@0 | 77 | { |
michael@0 | 78 | DebugOnly<BOOL> success; |
michael@0 | 79 | success = RemoveFontMemResourceEx(mFontRef); |
michael@0 | 80 | #if DEBUG |
michael@0 | 81 | if (!success) { |
michael@0 | 82 | char buf[256]; |
michael@0 | 83 | sprintf(buf, "error deleting font handle (%p) - RemoveFontMemResourceEx failed", mFontRef); |
michael@0 | 84 | NS_ASSERTION(success, buf); |
michael@0 | 85 | } |
michael@0 | 86 | #endif |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | HANDLE mFontRef; |
michael@0 | 90 | }; |
michael@0 | 91 | |
michael@0 | 92 | BYTE |
michael@0 | 93 | FontTypeToOutPrecision(uint8_t fontType) |
michael@0 | 94 | { |
michael@0 | 95 | BYTE ret; |
michael@0 | 96 | switch (fontType) { |
michael@0 | 97 | case GFX_FONT_TYPE_TT_OPENTYPE: |
michael@0 | 98 | case GFX_FONT_TYPE_TRUETYPE: |
michael@0 | 99 | ret = OUT_TT_ONLY_PRECIS; |
michael@0 | 100 | break; |
michael@0 | 101 | case GFX_FONT_TYPE_PS_OPENTYPE: |
michael@0 | 102 | ret = OUT_PS_ONLY_PRECIS; |
michael@0 | 103 | break; |
michael@0 | 104 | case GFX_FONT_TYPE_TYPE1: |
michael@0 | 105 | ret = OUT_OUTLINE_PRECIS; |
michael@0 | 106 | break; |
michael@0 | 107 | case GFX_FONT_TYPE_RASTER: |
michael@0 | 108 | ret = OUT_RASTER_PRECIS; |
michael@0 | 109 | break; |
michael@0 | 110 | case GFX_FONT_TYPE_DEVICE: |
michael@0 | 111 | ret = OUT_DEVICE_PRECIS; |
michael@0 | 112 | break; |
michael@0 | 113 | default: |
michael@0 | 114 | ret = OUT_DEFAULT_PRECIS; |
michael@0 | 115 | } |
michael@0 | 116 | return ret; |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | /*************************************************************** |
michael@0 | 120 | * |
michael@0 | 121 | * GDIFontEntry |
michael@0 | 122 | * |
michael@0 | 123 | */ |
michael@0 | 124 | |
michael@0 | 125 | GDIFontEntry::GDIFontEntry(const nsAString& aFaceName, |
michael@0 | 126 | gfxWindowsFontType aFontType, |
michael@0 | 127 | bool aItalic, uint16_t aWeight, int16_t aStretch, |
michael@0 | 128 | gfxUserFontData *aUserFontData, |
michael@0 | 129 | bool aFamilyHasItalicFace) |
michael@0 | 130 | : gfxFontEntry(aFaceName), |
michael@0 | 131 | mWindowsFamily(0), mWindowsPitch(0), |
michael@0 | 132 | mFontType(aFontType), |
michael@0 | 133 | mForceGDI(false), |
michael@0 | 134 | mFamilyHasItalicFace(aFamilyHasItalicFace), |
michael@0 | 135 | mCharset(), mUnicodeRanges() |
michael@0 | 136 | { |
michael@0 | 137 | mUserFontData = aUserFontData; |
michael@0 | 138 | mItalic = aItalic; |
michael@0 | 139 | mWeight = aWeight; |
michael@0 | 140 | mStretch = aStretch; |
michael@0 | 141 | if (IsType1()) |
michael@0 | 142 | mForceGDI = true; |
michael@0 | 143 | mIsUserFont = aUserFontData != nullptr; |
michael@0 | 144 | |
michael@0 | 145 | InitLogFont(aFaceName, aFontType); |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | nsresult |
michael@0 | 149 | GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData) |
michael@0 | 150 | { |
michael@0 | 151 | // attempt this once, if errors occur leave a blank cmap |
michael@0 | 152 | if (mCharacterMap) { |
michael@0 | 153 | return NS_OK; |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | // skip non-SFNT fonts completely |
michael@0 | 157 | if (mFontType != GFX_FONT_TYPE_PS_OPENTYPE && |
michael@0 | 158 | mFontType != GFX_FONT_TYPE_TT_OPENTYPE && |
michael@0 | 159 | mFontType != GFX_FONT_TYPE_TRUETYPE) |
michael@0 | 160 | { |
michael@0 | 161 | mCharacterMap = new gfxCharacterMap(); |
michael@0 | 162 | mCharacterMap->mBuildOnTheFly = true; |
michael@0 | 163 | return NS_ERROR_FAILURE; |
michael@0 | 164 | } |
michael@0 | 165 | |
michael@0 | 166 | nsRefPtr<gfxCharacterMap> charmap; |
michael@0 | 167 | nsresult rv; |
michael@0 | 168 | bool unicodeFont = false, symbolFont = false; |
michael@0 | 169 | |
michael@0 | 170 | if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, |
michael@0 | 171 | mUVSOffset, |
michael@0 | 172 | symbolFont))) { |
michael@0 | 173 | mSymbolFont = symbolFont; |
michael@0 | 174 | rv = NS_OK; |
michael@0 | 175 | } else { |
michael@0 | 176 | uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p'); |
michael@0 | 177 | charmap = new gfxCharacterMap(); |
michael@0 | 178 | AutoFallibleTArray<uint8_t,16384> cmap; |
michael@0 | 179 | rv = CopyFontTable(kCMAP, cmap); |
michael@0 | 180 | |
michael@0 | 181 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 182 | rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(), |
michael@0 | 183 | *charmap, mUVSOffset, |
michael@0 | 184 | unicodeFont, symbolFont); |
michael@0 | 185 | } |
michael@0 | 186 | mSymbolFont = symbolFont; |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | mHasCmapTable = NS_SUCCEEDED(rv); |
michael@0 | 190 | if (mHasCmapTable) { |
michael@0 | 191 | gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); |
michael@0 | 192 | mCharacterMap = pfl->FindCharMap(charmap); |
michael@0 | 193 | } else { |
michael@0 | 194 | // if error occurred, initialize to null cmap |
michael@0 | 195 | mCharacterMap = new gfxCharacterMap(); |
michael@0 | 196 | // For fonts where we failed to read the character map, |
michael@0 | 197 | // we can take a slow path to look up glyphs character by character |
michael@0 | 198 | mCharacterMap->mBuildOnTheFly = true; |
michael@0 | 199 | } |
michael@0 | 200 | |
michael@0 | 201 | #ifdef PR_LOGGING |
michael@0 | 202 | LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n", |
michael@0 | 203 | NS_ConvertUTF16toUTF8(mName).get(), |
michael@0 | 204 | charmap->SizeOfIncludingThis(moz_malloc_size_of), |
michael@0 | 205 | charmap->mHash, mCharacterMap == charmap ? " new" : "")); |
michael@0 | 206 | if (LOG_CMAPDATA_ENABLED()) { |
michael@0 | 207 | char prefix[256]; |
michael@0 | 208 | sprintf(prefix, "(cmapdata) name: %.220s", |
michael@0 | 209 | NS_ConvertUTF16toUTF8(mName).get()); |
michael@0 | 210 | charmap->Dump(prefix, eGfxLog_cmapdata); |
michael@0 | 211 | } |
michael@0 | 212 | #endif |
michael@0 | 213 | |
michael@0 | 214 | return rv; |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | bool |
michael@0 | 218 | GDIFontEntry::IsSymbolFont() |
michael@0 | 219 | { |
michael@0 | 220 | // initialize cmap first |
michael@0 | 221 | HasCmapTable(); |
michael@0 | 222 | return mSymbolFont; |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | gfxFont * |
michael@0 | 226 | GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, bool aNeedsBold) |
michael@0 | 227 | { |
michael@0 | 228 | bool isXP = !IsVistaOrLater(); |
michael@0 | 229 | |
michael@0 | 230 | bool useClearType = isXP && !aFontStyle->systemFont && |
michael@0 | 231 | (gfxWindowsPlatform::GetPlatform()->UseClearTypeAlways() || |
michael@0 | 232 | (mIsUserFont && !mIsLocalUserFont && |
michael@0 | 233 | gfxWindowsPlatform::GetPlatform()->UseClearTypeForDownloadableFonts())); |
michael@0 | 234 | |
michael@0 | 235 | return new gfxGDIFont(this, aFontStyle, aNeedsBold, |
michael@0 | 236 | (useClearType ? gfxFont::kAntialiasSubpixel |
michael@0 | 237 | : gfxFont::kAntialiasDefault)); |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | nsresult |
michael@0 | 241 | GDIFontEntry::CopyFontTable(uint32_t aTableTag, |
michael@0 | 242 | FallibleTArray<uint8_t>& aBuffer) |
michael@0 | 243 | { |
michael@0 | 244 | if (!IsTrueType()) { |
michael@0 | 245 | return NS_ERROR_FAILURE; |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | AutoDC dc; |
michael@0 | 249 | AutoSelectFont font(dc.GetDC(), &mLogFont); |
michael@0 | 250 | if (font.IsValid()) { |
michael@0 | 251 | uint32_t tableSize = |
michael@0 | 252 | ::GetFontData(dc.GetDC(), |
michael@0 | 253 | NativeEndian::swapToBigEndian(aTableTag), |
michael@0 | 254 | 0, nullptr, 0); |
michael@0 | 255 | if (tableSize != GDI_ERROR) { |
michael@0 | 256 | if (aBuffer.SetLength(tableSize)) { |
michael@0 | 257 | ::GetFontData(dc.GetDC(), |
michael@0 | 258 | NativeEndian::swapToBigEndian(aTableTag), 0, |
michael@0 | 259 | aBuffer.Elements(), tableSize); |
michael@0 | 260 | return NS_OK; |
michael@0 | 261 | } |
michael@0 | 262 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 263 | } |
michael@0 | 264 | } |
michael@0 | 265 | return NS_ERROR_FAILURE; |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | void |
michael@0 | 269 | GDIFontEntry::FillLogFont(LOGFONTW *aLogFont, |
michael@0 | 270 | uint16_t aWeight, gfxFloat aSize, |
michael@0 | 271 | bool aUseCleartype) |
michael@0 | 272 | { |
michael@0 | 273 | memcpy(aLogFont, &mLogFont, sizeof(LOGFONTW)); |
michael@0 | 274 | |
michael@0 | 275 | aLogFont->lfHeight = (LONG)-ROUND(aSize); |
michael@0 | 276 | |
michael@0 | 277 | if (aLogFont->lfHeight == 0) { |
michael@0 | 278 | aLogFont->lfHeight = -1; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | // If a non-zero weight is passed in, use this to override the original |
michael@0 | 282 | // weight in the entry's logfont. This is used to control synthetic bolding |
michael@0 | 283 | // for installed families with no bold face, and for downloaded fonts |
michael@0 | 284 | // (but NOT for local user fonts, because it could cause a different, |
michael@0 | 285 | // glyph-incompatible face to be used) |
michael@0 | 286 | if (aWeight) { |
michael@0 | 287 | aLogFont->lfWeight = aWeight; |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | // for non-local() user fonts, we never want to apply italics here; |
michael@0 | 291 | // if the face is described as italic, we should use it as-is, |
michael@0 | 292 | // and if it's not, but then the element is styled italic, we'll use |
michael@0 | 293 | // a cairo transform to create fake italic (oblique) |
michael@0 | 294 | if (IsUserFont() && !IsLocalUserFont()) { |
michael@0 | 295 | aLogFont->lfItalic = 0; |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | aLogFont->lfQuality = (aUseCleartype ? CLEARTYPE_QUALITY : DEFAULT_QUALITY); |
michael@0 | 299 | } |
michael@0 | 300 | |
michael@0 | 301 | #define MISSING_GLYPH 0x1F // glyph index returned for missing characters |
michael@0 | 302 | // on WinXP with .fon fonts, but not Type1 (.pfb) |
michael@0 | 303 | |
michael@0 | 304 | bool |
michael@0 | 305 | GDIFontEntry::TestCharacterMap(uint32_t aCh) |
michael@0 | 306 | { |
michael@0 | 307 | if (!mCharacterMap) { |
michael@0 | 308 | ReadCMAP(); |
michael@0 | 309 | NS_ASSERTION(mCharacterMap, "failed to initialize a character map"); |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | if (mCharacterMap->mBuildOnTheFly) { |
michael@0 | 313 | if (aCh > 0xFFFF) |
michael@0 | 314 | return false; |
michael@0 | 315 | |
michael@0 | 316 | // previous code was using the group style |
michael@0 | 317 | gfxFontStyle fakeStyle; |
michael@0 | 318 | if (mItalic) |
michael@0 | 319 | fakeStyle.style = NS_FONT_STYLE_ITALIC; |
michael@0 | 320 | fakeStyle.weight = mWeight * 100; |
michael@0 | 321 | |
michael@0 | 322 | nsRefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, false); |
michael@0 | 323 | if (!tempFont || !tempFont->Valid()) |
michael@0 | 324 | return false; |
michael@0 | 325 | gfxGDIFont *font = static_cast<gfxGDIFont*>(tempFont.get()); |
michael@0 | 326 | |
michael@0 | 327 | HDC dc = GetDC((HWND)nullptr); |
michael@0 | 328 | SetGraphicsMode(dc, GM_ADVANCED); |
michael@0 | 329 | HFONT hfont = font->GetHFONT(); |
michael@0 | 330 | HFONT oldFont = (HFONT)SelectObject(dc, hfont); |
michael@0 | 331 | |
michael@0 | 332 | wchar_t str[1] = { aCh }; |
michael@0 | 333 | WORD glyph[1]; |
michael@0 | 334 | |
michael@0 | 335 | bool hasGlyph = false; |
michael@0 | 336 | |
michael@0 | 337 | // Bug 573038 - in some cases GetGlyphIndicesW returns 0xFFFF for a |
michael@0 | 338 | // missing glyph or 0x1F in other cases to indicate the "invalid" |
michael@0 | 339 | // glyph. Map both cases to "not found" |
michael@0 | 340 | if (IsType1() || mForceGDI) { |
michael@0 | 341 | // Type1 fonts and uniscribe APIs don't get along. |
michael@0 | 342 | // ScriptGetCMap will return E_HANDLE |
michael@0 | 343 | DWORD ret = GetGlyphIndicesW(dc, str, 1, |
michael@0 | 344 | glyph, GGI_MARK_NONEXISTING_GLYPHS); |
michael@0 | 345 | if (ret != GDI_ERROR |
michael@0 | 346 | && glyph[0] != 0xFFFF |
michael@0 | 347 | && (IsType1() || glyph[0] != MISSING_GLYPH)) |
michael@0 | 348 | { |
michael@0 | 349 | hasGlyph = true; |
michael@0 | 350 | } |
michael@0 | 351 | } else { |
michael@0 | 352 | // ScriptGetCMap works better than GetGlyphIndicesW |
michael@0 | 353 | // for things like bitmap/vector fonts |
michael@0 | 354 | SCRIPT_CACHE sc = nullptr; |
michael@0 | 355 | HRESULT rv = ScriptGetCMap(dc, &sc, str, 1, 0, glyph); |
michael@0 | 356 | if (rv == S_OK) |
michael@0 | 357 | hasGlyph = true; |
michael@0 | 358 | } |
michael@0 | 359 | |
michael@0 | 360 | SelectObject(dc, oldFont); |
michael@0 | 361 | ReleaseDC(nullptr, dc); |
michael@0 | 362 | |
michael@0 | 363 | if (hasGlyph) { |
michael@0 | 364 | mCharacterMap->set(aCh); |
michael@0 | 365 | return true; |
michael@0 | 366 | } |
michael@0 | 367 | } else { |
michael@0 | 368 | // font had a cmap so simply check that |
michael@0 | 369 | return mCharacterMap->test(aCh); |
michael@0 | 370 | } |
michael@0 | 371 | |
michael@0 | 372 | return false; |
michael@0 | 373 | } |
michael@0 | 374 | |
michael@0 | 375 | void |
michael@0 | 376 | GDIFontEntry::InitLogFont(const nsAString& aName, |
michael@0 | 377 | gfxWindowsFontType aFontType) |
michael@0 | 378 | { |
michael@0 | 379 | #define CLIP_TURNOFF_FONTASSOCIATION 0x40 |
michael@0 | 380 | |
michael@0 | 381 | mLogFont.lfHeight = -1; |
michael@0 | 382 | |
michael@0 | 383 | // Fill in logFont structure |
michael@0 | 384 | mLogFont.lfWidth = 0; |
michael@0 | 385 | mLogFont.lfEscapement = 0; |
michael@0 | 386 | mLogFont.lfOrientation = 0; |
michael@0 | 387 | mLogFont.lfUnderline = FALSE; |
michael@0 | 388 | mLogFont.lfStrikeOut = FALSE; |
michael@0 | 389 | mLogFont.lfCharSet = DEFAULT_CHARSET; |
michael@0 | 390 | mLogFont.lfOutPrecision = FontTypeToOutPrecision(aFontType); |
michael@0 | 391 | mLogFont.lfClipPrecision = CLIP_TURNOFF_FONTASSOCIATION; |
michael@0 | 392 | mLogFont.lfQuality = DEFAULT_QUALITY; |
michael@0 | 393 | mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; |
michael@0 | 394 | // always force lfItalic if we want it. Font selection code will |
michael@0 | 395 | // do its best to give us an italic font entry, but if no face exists |
michael@0 | 396 | // it may give us a regular one based on weight. Windows should |
michael@0 | 397 | // do fake italic for us in that case. |
michael@0 | 398 | mLogFont.lfItalic = mItalic; |
michael@0 | 399 | mLogFont.lfWeight = mWeight; |
michael@0 | 400 | |
michael@0 | 401 | int len = std::min<int>(aName.Length(), LF_FACESIZE - 1); |
michael@0 | 402 | memcpy(&mLogFont.lfFaceName, aName.BeginReading(), len * sizeof(char16_t)); |
michael@0 | 403 | mLogFont.lfFaceName[len] = '\0'; |
michael@0 | 404 | } |
michael@0 | 405 | |
michael@0 | 406 | GDIFontEntry* |
michael@0 | 407 | GDIFontEntry::CreateFontEntry(const nsAString& aName, |
michael@0 | 408 | gfxWindowsFontType aFontType, bool aItalic, |
michael@0 | 409 | uint16_t aWeight, int16_t aStretch, |
michael@0 | 410 | gfxUserFontData* aUserFontData, |
michael@0 | 411 | bool aFamilyHasItalicFace) |
michael@0 | 412 | { |
michael@0 | 413 | // jtdfix - need to set charset, unicode ranges, pitch/family |
michael@0 | 414 | |
michael@0 | 415 | GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aItalic, |
michael@0 | 416 | aWeight, aStretch, aUserFontData, |
michael@0 | 417 | aFamilyHasItalicFace); |
michael@0 | 418 | |
michael@0 | 419 | return fe; |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | void |
michael@0 | 423 | GDIFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, |
michael@0 | 424 | FontListSizes* aSizes) const |
michael@0 | 425 | { |
michael@0 | 426 | aSizes->mFontListSize += aMallocSizeOf(this); |
michael@0 | 427 | AddSizeOfExcludingThis(aMallocSizeOf, aSizes); |
michael@0 | 428 | } |
michael@0 | 429 | |
michael@0 | 430 | /*************************************************************** |
michael@0 | 431 | * |
michael@0 | 432 | * GDIFontFamily |
michael@0 | 433 | * |
michael@0 | 434 | */ |
michael@0 | 435 | |
michael@0 | 436 | int CALLBACK |
michael@0 | 437 | GDIFontFamily::FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe, |
michael@0 | 438 | const NEWTEXTMETRICEXW *nmetrics, |
michael@0 | 439 | DWORD fontType, LPARAM data) |
michael@0 | 440 | { |
michael@0 | 441 | const NEWTEXTMETRICW& metrics = nmetrics->ntmTm; |
michael@0 | 442 | LOGFONTW logFont = lpelfe->elfLogFont; |
michael@0 | 443 | GDIFontFamily *ff = reinterpret_cast<GDIFontFamily*>(data); |
michael@0 | 444 | |
michael@0 | 445 | // Some fonts claim to support things > 900, but we don't so clamp the sizes |
michael@0 | 446 | logFont.lfWeight = clamped(logFont.lfWeight, LONG(100), LONG(900)); |
michael@0 | 447 | |
michael@0 | 448 | gfxWindowsFontType feType = GDIFontEntry::DetermineFontType(metrics, fontType); |
michael@0 | 449 | |
michael@0 | 450 | GDIFontEntry *fe = nullptr; |
michael@0 | 451 | for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) { |
michael@0 | 452 | fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get()); |
michael@0 | 453 | if (feType > fe->mFontType) { |
michael@0 | 454 | // if the new type is better than the old one, remove the old entries |
michael@0 | 455 | ff->mAvailableFonts.RemoveElementAt(i); |
michael@0 | 456 | --i; |
michael@0 | 457 | } else if (feType < fe->mFontType) { |
michael@0 | 458 | // otherwise if the new type is worse, skip it |
michael@0 | 459 | return 1; |
michael@0 | 460 | } |
michael@0 | 461 | } |
michael@0 | 462 | |
michael@0 | 463 | for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) { |
michael@0 | 464 | fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get()); |
michael@0 | 465 | // check if we already know about this face |
michael@0 | 466 | if (fe->mWeight == logFont.lfWeight && |
michael@0 | 467 | fe->mItalic == (logFont.lfItalic == 0xFF)) { |
michael@0 | 468 | // update the charset bit here since this could be different |
michael@0 | 469 | fe->mCharset.set(metrics.tmCharSet); |
michael@0 | 470 | return 1; |
michael@0 | 471 | } |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | // We can't set the hasItalicFace flag correctly here, |
michael@0 | 475 | // because we might not have seen the family's italic face(s) yet. |
michael@0 | 476 | // So we'll set that flag for all members after loading all the faces. |
michael@0 | 477 | fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName), |
michael@0 | 478 | feType, (logFont.lfItalic == 0xFF), |
michael@0 | 479 | (uint16_t) (logFont.lfWeight), 0, |
michael@0 | 480 | nullptr, false); |
michael@0 | 481 | if (!fe) |
michael@0 | 482 | return 1; |
michael@0 | 483 | |
michael@0 | 484 | ff->AddFontEntry(fe); |
michael@0 | 485 | |
michael@0 | 486 | // mark the charset bit |
michael@0 | 487 | fe->mCharset.set(metrics.tmCharSet); |
michael@0 | 488 | |
michael@0 | 489 | fe->mWindowsFamily = logFont.lfPitchAndFamily & 0xF0; |
michael@0 | 490 | fe->mWindowsPitch = logFont.lfPitchAndFamily & 0x0F; |
michael@0 | 491 | |
michael@0 | 492 | if (nmetrics->ntmFontSig.fsUsb[0] != 0x00000000 && |
michael@0 | 493 | nmetrics->ntmFontSig.fsUsb[1] != 0x00000000 && |
michael@0 | 494 | nmetrics->ntmFontSig.fsUsb[2] != 0x00000000 && |
michael@0 | 495 | nmetrics->ntmFontSig.fsUsb[3] != 0x00000000) { |
michael@0 | 496 | |
michael@0 | 497 | // set the unicode ranges |
michael@0 | 498 | uint32_t x = 0; |
michael@0 | 499 | for (uint32_t i = 0; i < 4; ++i) { |
michael@0 | 500 | DWORD range = nmetrics->ntmFontSig.fsUsb[i]; |
michael@0 | 501 | for (uint32_t k = 0; k < 32; ++k) { |
michael@0 | 502 | fe->mUnicodeRanges.set(x++, (range & (1 << k)) != 0); |
michael@0 | 503 | } |
michael@0 | 504 | } |
michael@0 | 505 | } |
michael@0 | 506 | |
michael@0 | 507 | #ifdef PR_LOGGING |
michael@0 | 508 | if (LOG_FONTLIST_ENABLED()) { |
michael@0 | 509 | LOG_FONTLIST(("(fontlist) added (%s) to family (%s)" |
michael@0 | 510 | " with style: %s weight: %d stretch: %d", |
michael@0 | 511 | NS_ConvertUTF16toUTF8(fe->Name()).get(), |
michael@0 | 512 | NS_ConvertUTF16toUTF8(ff->Name()).get(), |
michael@0 | 513 | (logFont.lfItalic == 0xff) ? "italic" : "normal", |
michael@0 | 514 | logFont.lfWeight, fe->Stretch())); |
michael@0 | 515 | } |
michael@0 | 516 | #endif |
michael@0 | 517 | return 1; |
michael@0 | 518 | } |
michael@0 | 519 | |
michael@0 | 520 | void |
michael@0 | 521 | GDIFontFamily::FindStyleVariations(FontInfoData *aFontInfoData) |
michael@0 | 522 | { |
michael@0 | 523 | if (mHasStyles) |
michael@0 | 524 | return; |
michael@0 | 525 | mHasStyles = true; |
michael@0 | 526 | |
michael@0 | 527 | HDC hdc = GetDC(nullptr); |
michael@0 | 528 | SetGraphicsMode(hdc, GM_ADVANCED); |
michael@0 | 529 | |
michael@0 | 530 | LOGFONTW logFont; |
michael@0 | 531 | memset(&logFont, 0, sizeof(LOGFONTW)); |
michael@0 | 532 | logFont.lfCharSet = DEFAULT_CHARSET; |
michael@0 | 533 | logFont.lfPitchAndFamily = 0; |
michael@0 | 534 | uint32_t l = std::min<uint32_t>(mName.Length(), LF_FACESIZE - 1); |
michael@0 | 535 | memcpy(logFont.lfFaceName, mName.get(), l * sizeof(char16_t)); |
michael@0 | 536 | |
michael@0 | 537 | EnumFontFamiliesExW(hdc, &logFont, |
michael@0 | 538 | (FONTENUMPROCW)GDIFontFamily::FamilyAddStylesProc, |
michael@0 | 539 | (LPARAM)this, 0); |
michael@0 | 540 | #ifdef PR_LOGGING |
michael@0 | 541 | if (LOG_FONTLIST_ENABLED() && mAvailableFonts.Length() == 0) { |
michael@0 | 542 | LOG_FONTLIST(("(fontlist) no styles available in family \"%s\"", |
michael@0 | 543 | NS_ConvertUTF16toUTF8(mName).get())); |
michael@0 | 544 | } |
michael@0 | 545 | #endif |
michael@0 | 546 | |
michael@0 | 547 | ReleaseDC(nullptr, hdc); |
michael@0 | 548 | |
michael@0 | 549 | if (mIsBadUnderlineFamily) { |
michael@0 | 550 | SetBadUnderlineFonts(); |
michael@0 | 551 | } |
michael@0 | 552 | |
michael@0 | 553 | // check for existence of italic face(s); if present, set the |
michael@0 | 554 | // FamilyHasItalic flag on all faces so that we'll know *not* |
michael@0 | 555 | // to use GDI's fake-italic effect with them |
michael@0 | 556 | size_t count = mAvailableFonts.Length(); |
michael@0 | 557 | for (size_t i = 0; i < count; ++i) { |
michael@0 | 558 | if (mAvailableFonts[i]->IsItalic()) { |
michael@0 | 559 | for (uint32_t j = 0; j < count; ++j) { |
michael@0 | 560 | static_cast<GDIFontEntry*>(mAvailableFonts[j].get())-> |
michael@0 | 561 | mFamilyHasItalicFace = true; |
michael@0 | 562 | } |
michael@0 | 563 | break; |
michael@0 | 564 | } |
michael@0 | 565 | } |
michael@0 | 566 | } |
michael@0 | 567 | |
michael@0 | 568 | /*************************************************************** |
michael@0 | 569 | * |
michael@0 | 570 | * gfxGDIFontList |
michael@0 | 571 | * |
michael@0 | 572 | */ |
michael@0 | 573 | |
michael@0 | 574 | gfxGDIFontList::gfxGDIFontList() |
michael@0 | 575 | : mFontSubstitutes(50) |
michael@0 | 576 | { |
michael@0 | 577 | } |
michael@0 | 578 | |
michael@0 | 579 | static void |
michael@0 | 580 | RemoveCharsetFromFontSubstitute(nsAString &aName) |
michael@0 | 581 | { |
michael@0 | 582 | int32_t comma = aName.FindChar(char16_t(',')); |
michael@0 | 583 | if (comma >= 0) |
michael@0 | 584 | aName.Truncate(comma); |
michael@0 | 585 | } |
michael@0 | 586 | |
michael@0 | 587 | #define MAX_VALUE_NAME 512 |
michael@0 | 588 | #define MAX_VALUE_DATA 512 |
michael@0 | 589 | |
michael@0 | 590 | nsresult |
michael@0 | 591 | gfxGDIFontList::GetFontSubstitutes() |
michael@0 | 592 | { |
michael@0 | 593 | HKEY hKey; |
michael@0 | 594 | DWORD i, rv, lenAlias, lenActual, valueType; |
michael@0 | 595 | WCHAR aliasName[MAX_VALUE_NAME]; |
michael@0 | 596 | WCHAR actualName[MAX_VALUE_DATA]; |
michael@0 | 597 | |
michael@0 | 598 | if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, |
michael@0 | 599 | L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", |
michael@0 | 600 | 0, KEY_READ, &hKey) != ERROR_SUCCESS) |
michael@0 | 601 | { |
michael@0 | 602 | return NS_ERROR_FAILURE; |
michael@0 | 603 | } |
michael@0 | 604 | |
michael@0 | 605 | for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) { |
michael@0 | 606 | aliasName[0] = 0; |
michael@0 | 607 | lenAlias = ArrayLength(aliasName); |
michael@0 | 608 | actualName[0] = 0; |
michael@0 | 609 | lenActual = sizeof(actualName); |
michael@0 | 610 | rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType, |
michael@0 | 611 | (LPBYTE)actualName, &lenActual); |
michael@0 | 612 | |
michael@0 | 613 | if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) { |
michael@0 | 614 | continue; |
michael@0 | 615 | } |
michael@0 | 616 | |
michael@0 | 617 | if (aliasName[0] == WCHAR('@')) { |
michael@0 | 618 | continue; |
michael@0 | 619 | } |
michael@0 | 620 | |
michael@0 | 621 | nsAutoString substituteName((char16_t*) aliasName); |
michael@0 | 622 | nsAutoString actualFontName((char16_t*) actualName); |
michael@0 | 623 | RemoveCharsetFromFontSubstitute(substituteName); |
michael@0 | 624 | BuildKeyNameFromFontName(substituteName); |
michael@0 | 625 | RemoveCharsetFromFontSubstitute(actualFontName); |
michael@0 | 626 | BuildKeyNameFromFontName(actualFontName); |
michael@0 | 627 | gfxFontFamily *ff; |
michael@0 | 628 | if (!actualFontName.IsEmpty() && |
michael@0 | 629 | (ff = mFontFamilies.GetWeak(actualFontName))) { |
michael@0 | 630 | mFontSubstitutes.Put(substituteName, ff); |
michael@0 | 631 | } else { |
michael@0 | 632 | mNonExistingFonts.AppendElement(substituteName); |
michael@0 | 633 | } |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | // "Courier" on a default Windows install is an ugly bitmap font. |
michael@0 | 637 | // If there is no substitution for Courier in the registry |
michael@0 | 638 | // substitute "Courier" with "Courier New". |
michael@0 | 639 | nsAutoString substituteName; |
michael@0 | 640 | substituteName.AssignLiteral("Courier"); |
michael@0 | 641 | BuildKeyNameFromFontName(substituteName); |
michael@0 | 642 | if (!mFontSubstitutes.GetWeak(substituteName)) { |
michael@0 | 643 | gfxFontFamily *ff; |
michael@0 | 644 | nsAutoString actualFontName; |
michael@0 | 645 | actualFontName.AssignLiteral("Courier New"); |
michael@0 | 646 | BuildKeyNameFromFontName(actualFontName); |
michael@0 | 647 | ff = mFontFamilies.GetWeak(actualFontName); |
michael@0 | 648 | if (ff) { |
michael@0 | 649 | mFontSubstitutes.Put(substituteName, ff); |
michael@0 | 650 | } |
michael@0 | 651 | } |
michael@0 | 652 | return NS_OK; |
michael@0 | 653 | } |
michael@0 | 654 | |
michael@0 | 655 | nsresult |
michael@0 | 656 | gfxGDIFontList::InitFontList() |
michael@0 | 657 | { |
michael@0 | 658 | Telemetry::AutoTimer<Telemetry::GDI_INITFONTLIST_TOTAL> timer; |
michael@0 | 659 | gfxFontCache *fc = gfxFontCache::GetCache(); |
michael@0 | 660 | if (fc) |
michael@0 | 661 | fc->AgeAllGenerations(); |
michael@0 | 662 | |
michael@0 | 663 | // reset font lists |
michael@0 | 664 | gfxPlatformFontList::InitFontList(); |
michael@0 | 665 | |
michael@0 | 666 | mFontSubstitutes.Clear(); |
michael@0 | 667 | mNonExistingFonts.Clear(); |
michael@0 | 668 | |
michael@0 | 669 | // iterate over available families |
michael@0 | 670 | LOGFONTW logfont; |
michael@0 | 671 | memset(&logfont, 0, sizeof(logfont)); |
michael@0 | 672 | logfont.lfCharSet = DEFAULT_CHARSET; |
michael@0 | 673 | |
michael@0 | 674 | AutoDC hdc; |
michael@0 | 675 | int result = EnumFontFamiliesExW(hdc.GetDC(), &logfont, |
michael@0 | 676 | (FONTENUMPROCW)&EnumFontFamExProc, |
michael@0 | 677 | 0, 0); |
michael@0 | 678 | |
michael@0 | 679 | GetFontSubstitutes(); |
michael@0 | 680 | |
michael@0 | 681 | GetPrefsAndStartLoader(); |
michael@0 | 682 | |
michael@0 | 683 | return NS_OK; |
michael@0 | 684 | } |
michael@0 | 685 | |
michael@0 | 686 | int CALLBACK |
michael@0 | 687 | gfxGDIFontList::EnumFontFamExProc(ENUMLOGFONTEXW *lpelfe, |
michael@0 | 688 | NEWTEXTMETRICEXW *lpntme, |
michael@0 | 689 | DWORD fontType, |
michael@0 | 690 | LPARAM lParam) |
michael@0 | 691 | { |
michael@0 | 692 | const LOGFONTW& lf = lpelfe->elfLogFont; |
michael@0 | 693 | |
michael@0 | 694 | if (lf.lfFaceName[0] == '@') { |
michael@0 | 695 | return 1; |
michael@0 | 696 | } |
michael@0 | 697 | |
michael@0 | 698 | nsAutoString name(lf.lfFaceName); |
michael@0 | 699 | BuildKeyNameFromFontName(name); |
michael@0 | 700 | |
michael@0 | 701 | gfxGDIFontList *fontList = PlatformFontList(); |
michael@0 | 702 | |
michael@0 | 703 | if (!fontList->mFontFamilies.GetWeak(name)) { |
michael@0 | 704 | nsDependentString faceName(lf.lfFaceName); |
michael@0 | 705 | nsRefPtr<gfxFontFamily> family = new GDIFontFamily(faceName); |
michael@0 | 706 | fontList->mFontFamilies.Put(name, family); |
michael@0 | 707 | |
michael@0 | 708 | // if locale is such that CJK font names are the default coming from |
michael@0 | 709 | // GDI, then if a family name is non-ASCII immediately read in other |
michael@0 | 710 | // family names. This assures that MS Gothic, MS Mincho are all found |
michael@0 | 711 | // before lookups begin. |
michael@0 | 712 | if (!IsASCII(faceName)) { |
michael@0 | 713 | family->ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList()); |
michael@0 | 714 | } |
michael@0 | 715 | |
michael@0 | 716 | if (fontList->mBadUnderlineFamilyNames.Contains(name)) |
michael@0 | 717 | family->SetBadUnderlineFamily(); |
michael@0 | 718 | } |
michael@0 | 719 | |
michael@0 | 720 | return 1; |
michael@0 | 721 | } |
michael@0 | 722 | |
michael@0 | 723 | gfxFontEntry* |
michael@0 | 724 | gfxGDIFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, |
michael@0 | 725 | const nsAString& aFullname) |
michael@0 | 726 | { |
michael@0 | 727 | gfxFontEntry *lookup; |
michael@0 | 728 | |
michael@0 | 729 | lookup = LookupInFaceNameLists(aFullname); |
michael@0 | 730 | if (!lookup) { |
michael@0 | 731 | return nullptr; |
michael@0 | 732 | } |
michael@0 | 733 | |
michael@0 | 734 | bool isCFF = false; // jtdfix -- need to determine this |
michael@0 | 735 | |
michael@0 | 736 | // use the face name from the lookup font entry, which will be the localized |
michael@0 | 737 | // face name which GDI mapping tables use (e.g. with the system locale set to |
michael@0 | 738 | // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name |
michael@0 | 739 | // 'Arial Vet' which can be used as a key in GDI font lookups). |
michael@0 | 740 | GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(lookup->Name(), |
michael@0 | 741 | gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, |
michael@0 | 742 | lookup->mItalic ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL, |
michael@0 | 743 | lookup->mWeight, aProxyEntry->mStretch, nullptr, |
michael@0 | 744 | static_cast<GDIFontEntry*>(lookup)->mFamilyHasItalicFace); |
michael@0 | 745 | |
michael@0 | 746 | if (!fe) |
michael@0 | 747 | return nullptr; |
michael@0 | 748 | |
michael@0 | 749 | fe->mIsUserFont = true; |
michael@0 | 750 | fe->mIsLocalUserFont = true; |
michael@0 | 751 | |
michael@0 | 752 | // make the new font entry match the proxy entry style characteristics |
michael@0 | 753 | fe->mWeight = (aProxyEntry->mWeight == 0 ? 400 : aProxyEntry->mWeight); |
michael@0 | 754 | fe->mItalic = aProxyEntry->mItalic; |
michael@0 | 755 | |
michael@0 | 756 | return fe; |
michael@0 | 757 | } |
michael@0 | 758 | |
michael@0 | 759 | gfxFontEntry* |
michael@0 | 760 | gfxGDIFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, |
michael@0 | 761 | const uint8_t *aFontData, |
michael@0 | 762 | uint32_t aLength) |
michael@0 | 763 | { |
michael@0 | 764 | // MakePlatformFont is responsible for deleting the font data with NS_Free |
michael@0 | 765 | // so we set up a stack object to ensure it is freed even if we take an |
michael@0 | 766 | // early exit |
michael@0 | 767 | struct FontDataDeleter { |
michael@0 | 768 | FontDataDeleter(const uint8_t *aFontData) |
michael@0 | 769 | : mFontData(aFontData) { } |
michael@0 | 770 | ~FontDataDeleter() { NS_Free((void*)mFontData); } |
michael@0 | 771 | const uint8_t *mFontData; |
michael@0 | 772 | }; |
michael@0 | 773 | FontDataDeleter autoDelete(aFontData); |
michael@0 | 774 | |
michael@0 | 775 | bool isCFF = gfxFontUtils::IsCffFont(aFontData); |
michael@0 | 776 | |
michael@0 | 777 | nsresult rv; |
michael@0 | 778 | HANDLE fontRef = nullptr; |
michael@0 | 779 | |
michael@0 | 780 | nsAutoString uniqueName; |
michael@0 | 781 | rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName); |
michael@0 | 782 | if (NS_FAILED(rv)) |
michael@0 | 783 | return nullptr; |
michael@0 | 784 | |
michael@0 | 785 | FallibleTArray<uint8_t> newFontData; |
michael@0 | 786 | |
michael@0 | 787 | rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData); |
michael@0 | 788 | |
michael@0 | 789 | if (NS_FAILED(rv)) |
michael@0 | 790 | return nullptr; |
michael@0 | 791 | |
michael@0 | 792 | DWORD numFonts = 0; |
michael@0 | 793 | |
michael@0 | 794 | uint8_t *fontData = reinterpret_cast<uint8_t*> (newFontData.Elements()); |
michael@0 | 795 | uint32_t fontLength = newFontData.Length(); |
michael@0 | 796 | NS_ASSERTION(fontData, "null font data after renaming"); |
michael@0 | 797 | |
michael@0 | 798 | // http://msdn.microsoft.com/en-us/library/ms533942(VS.85).aspx |
michael@0 | 799 | // "A font that is added by AddFontMemResourceEx is always private |
michael@0 | 800 | // to the process that made the call and is not enumerable." |
michael@0 | 801 | fontRef = AddFontMemResourceEx(fontData, fontLength, |
michael@0 | 802 | 0 /* reserved */, &numFonts); |
michael@0 | 803 | if (!fontRef) |
michael@0 | 804 | return nullptr; |
michael@0 | 805 | |
michael@0 | 806 | // only load fonts with a single face contained in the data |
michael@0 | 807 | // AddFontMemResourceEx generates an additional face name for |
michael@0 | 808 | // vertical text if the font supports vertical writing but since |
michael@0 | 809 | // the font is referenced via the name this can be ignored |
michael@0 | 810 | if (fontRef && numFonts > 2) { |
michael@0 | 811 | RemoveFontMemResourceEx(fontRef); |
michael@0 | 812 | return nullptr; |
michael@0 | 813 | } |
michael@0 | 814 | |
michael@0 | 815 | // make a new font entry using the unique name |
michael@0 | 816 | WinUserFontData *winUserFontData = new WinUserFontData(fontRef); |
michael@0 | 817 | uint16_t w = (aProxyEntry->mWeight == 0 ? 400 : aProxyEntry->mWeight); |
michael@0 | 818 | |
michael@0 | 819 | GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(uniqueName, |
michael@0 | 820 | gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, |
michael@0 | 821 | uint32_t(aProxyEntry->mItalic ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL), |
michael@0 | 822 | w, aProxyEntry->mStretch, winUserFontData, false); |
michael@0 | 823 | |
michael@0 | 824 | if (!fe) |
michael@0 | 825 | return fe; |
michael@0 | 826 | |
michael@0 | 827 | fe->mIsUserFont = true; |
michael@0 | 828 | |
michael@0 | 829 | // Uniscribe doesn't place CFF fonts loaded privately |
michael@0 | 830 | // via AddFontMemResourceEx on XP/Vista |
michael@0 | 831 | if (isCFF && !IsWin7OrLater()) { |
michael@0 | 832 | fe->mForceGDI = true; |
michael@0 | 833 | } |
michael@0 | 834 | |
michael@0 | 835 | return fe; |
michael@0 | 836 | } |
michael@0 | 837 | |
michael@0 | 838 | gfxFontFamily* |
michael@0 | 839 | gfxGDIFontList::GetDefaultFont(const gfxFontStyle* aStyle) |
michael@0 | 840 | { |
michael@0 | 841 | // this really shouldn't fail to find a font.... |
michael@0 | 842 | HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); |
michael@0 | 843 | LOGFONTW logFont; |
michael@0 | 844 | if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) { |
michael@0 | 845 | nsAutoString resolvedName; |
michael@0 | 846 | if (ResolveFontName(nsDependentString(logFont.lfFaceName), resolvedName)) { |
michael@0 | 847 | return FindFamily(resolvedName); |
michael@0 | 848 | } |
michael@0 | 849 | } |
michael@0 | 850 | |
michael@0 | 851 | // ...but just in case, try another approach as well |
michael@0 | 852 | NONCLIENTMETRICSW ncm; |
michael@0 | 853 | ncm.cbSize = sizeof(ncm); |
michael@0 | 854 | BOOL status = ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, |
michael@0 | 855 | sizeof(ncm), &ncm, 0); |
michael@0 | 856 | if (status) { |
michael@0 | 857 | nsAutoString resolvedName; |
michael@0 | 858 | if (ResolveFontName(nsDependentString(ncm.lfMessageFont.lfFaceName), resolvedName)) { |
michael@0 | 859 | return FindFamily(resolvedName); |
michael@0 | 860 | } |
michael@0 | 861 | } |
michael@0 | 862 | |
michael@0 | 863 | return nullptr; |
michael@0 | 864 | } |
michael@0 | 865 | |
michael@0 | 866 | |
michael@0 | 867 | bool |
michael@0 | 868 | gfxGDIFontList::ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName) |
michael@0 | 869 | { |
michael@0 | 870 | nsAutoString keyName(aFontName); |
michael@0 | 871 | BuildKeyNameFromFontName(keyName); |
michael@0 | 872 | |
michael@0 | 873 | gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName); |
michael@0 | 874 | if (ff) { |
michael@0 | 875 | aResolvedFontName = ff->Name(); |
michael@0 | 876 | return true; |
michael@0 | 877 | } |
michael@0 | 878 | |
michael@0 | 879 | if (mNonExistingFonts.Contains(keyName)) |
michael@0 | 880 | return false; |
michael@0 | 881 | |
michael@0 | 882 | if (gfxPlatformFontList::ResolveFontName(aFontName, aResolvedFontName)) |
michael@0 | 883 | return true; |
michael@0 | 884 | |
michael@0 | 885 | return false; |
michael@0 | 886 | } |
michael@0 | 887 | |
michael@0 | 888 | void |
michael@0 | 889 | gfxGDIFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, |
michael@0 | 890 | FontListSizes* aSizes) const |
michael@0 | 891 | { |
michael@0 | 892 | gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); |
michael@0 | 893 | aSizes->mFontListSize += |
michael@0 | 894 | mFontSubstitutes.SizeOfExcludingThis(SizeOfFamilyNameEntryExcludingThis, |
michael@0 | 895 | aMallocSizeOf); |
michael@0 | 896 | aSizes->mFontListSize += |
michael@0 | 897 | mNonExistingFonts.SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 898 | for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) { |
michael@0 | 899 | aSizes->mFontListSize += |
michael@0 | 900 | mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
michael@0 | 901 | } |
michael@0 | 902 | } |
michael@0 | 903 | |
michael@0 | 904 | void |
michael@0 | 905 | gfxGDIFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, |
michael@0 | 906 | FontListSizes* aSizes) const |
michael@0 | 907 | { |
michael@0 | 908 | aSizes->mFontListSize += aMallocSizeOf(this); |
michael@0 | 909 | AddSizeOfExcludingThis(aMallocSizeOf, aSizes); |
michael@0 | 910 | } |
michael@0 | 911 | |
michael@0 | 912 | // used to load system-wide font info on off-main thread |
michael@0 | 913 | class GDIFontInfo : public FontInfoData { |
michael@0 | 914 | public: |
michael@0 | 915 | GDIFontInfo(bool aLoadOtherNames, |
michael@0 | 916 | bool aLoadFaceNames, |
michael@0 | 917 | bool aLoadCmaps) : |
michael@0 | 918 | FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) |
michael@0 | 919 | {} |
michael@0 | 920 | |
michael@0 | 921 | virtual ~GDIFontInfo() {} |
michael@0 | 922 | |
michael@0 | 923 | virtual void Load() { |
michael@0 | 924 | mHdc = GetDC(nullptr); |
michael@0 | 925 | SetGraphicsMode(mHdc, GM_ADVANCED); |
michael@0 | 926 | FontInfoData::Load(); |
michael@0 | 927 | ReleaseDC(nullptr, mHdc); |
michael@0 | 928 | } |
michael@0 | 929 | |
michael@0 | 930 | // loads font data for all members of a given family |
michael@0 | 931 | virtual void LoadFontFamilyData(const nsAString& aFamilyName); |
michael@0 | 932 | |
michael@0 | 933 | // callback for GDI EnumFontFamiliesExW call |
michael@0 | 934 | static int CALLBACK EnumerateFontsForFamily(const ENUMLOGFONTEXW *lpelfe, |
michael@0 | 935 | const NEWTEXTMETRICEXW *nmetrics, |
michael@0 | 936 | DWORD fontType, LPARAM data); |
michael@0 | 937 | |
michael@0 | 938 | HDC mHdc; |
michael@0 | 939 | }; |
michael@0 | 940 | |
michael@0 | 941 | struct EnumerateFontsForFamilyData { |
michael@0 | 942 | EnumerateFontsForFamilyData(const nsAString& aFamilyName, |
michael@0 | 943 | GDIFontInfo& aFontInfo) |
michael@0 | 944 | : mFamilyName(aFamilyName), mFontInfo(aFontInfo) |
michael@0 | 945 | {} |
michael@0 | 946 | |
michael@0 | 947 | nsString mFamilyName; |
michael@0 | 948 | nsTArray<nsString> mOtherFamilyNames; |
michael@0 | 949 | GDIFontInfo& mFontInfo; |
michael@0 | 950 | nsString mPreviousFontName; |
michael@0 | 951 | }; |
michael@0 | 952 | |
michael@0 | 953 | int CALLBACK GDIFontInfo::EnumerateFontsForFamily( |
michael@0 | 954 | const ENUMLOGFONTEXW *lpelfe, |
michael@0 | 955 | const NEWTEXTMETRICEXW *nmetrics, |
michael@0 | 956 | DWORD fontType, LPARAM data) |
michael@0 | 957 | { |
michael@0 | 958 | EnumerateFontsForFamilyData *famData = |
michael@0 | 959 | reinterpret_cast<EnumerateFontsForFamilyData*>(data); |
michael@0 | 960 | HDC hdc = famData->mFontInfo.mHdc; |
michael@0 | 961 | LOGFONTW logFont = lpelfe->elfLogFont; |
michael@0 | 962 | const NEWTEXTMETRICW& metrics = nmetrics->ntmTm; |
michael@0 | 963 | |
michael@0 | 964 | AutoSelectFont font(hdc, &logFont); |
michael@0 | 965 | if (!font.IsValid()) { |
michael@0 | 966 | return 1; |
michael@0 | 967 | } |
michael@0 | 968 | |
michael@0 | 969 | FontFaceData fontData; |
michael@0 | 970 | nsDependentString fontName(lpelfe->elfFullName); |
michael@0 | 971 | |
michael@0 | 972 | // callback called for each style-charset so return if style already seen |
michael@0 | 973 | if (fontName.Equals(famData->mPreviousFontName)) { |
michael@0 | 974 | return 1; |
michael@0 | 975 | } |
michael@0 | 976 | famData->mPreviousFontName = fontName; |
michael@0 | 977 | famData->mFontInfo.mLoadStats.fonts++; |
michael@0 | 978 | |
michael@0 | 979 | // read name table info |
michael@0 | 980 | bool nameDataLoaded = false; |
michael@0 | 981 | if (famData->mFontInfo.mLoadFaceNames || famData->mFontInfo.mLoadOtherNames) { |
michael@0 | 982 | uint32_t kNAME = |
michael@0 | 983 | NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e')); |
michael@0 | 984 | uint32_t nameSize; |
michael@0 | 985 | AutoFallibleTArray<uint8_t, 1024> nameData; |
michael@0 | 986 | |
michael@0 | 987 | nameSize = ::GetFontData(hdc, kNAME, 0, nullptr, 0); |
michael@0 | 988 | if (nameSize != GDI_ERROR && |
michael@0 | 989 | nameSize > 0 && |
michael@0 | 990 | nameData.SetLength(nameSize)) { |
michael@0 | 991 | ::GetFontData(hdc, kNAME, 0, nameData.Elements(), nameSize); |
michael@0 | 992 | |
michael@0 | 993 | // face names |
michael@0 | 994 | if (famData->mFontInfo.mLoadFaceNames) { |
michael@0 | 995 | gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize, |
michael@0 | 996 | gfxFontUtils::NAME_ID_FULL, |
michael@0 | 997 | fontData.mFullName); |
michael@0 | 998 | gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize, |
michael@0 | 999 | gfxFontUtils::NAME_ID_POSTSCRIPT, |
michael@0 | 1000 | fontData.mPostscriptName); |
michael@0 | 1001 | nameDataLoaded = true; |
michael@0 | 1002 | famData->mFontInfo.mLoadStats.facenames++; |
michael@0 | 1003 | } |
michael@0 | 1004 | |
michael@0 | 1005 | // other family names |
michael@0 | 1006 | if (famData->mFontInfo.mLoadOtherNames) { |
michael@0 | 1007 | gfxFontFamily::ReadOtherFamilyNamesForFace(famData->mFamilyName, |
michael@0 | 1008 | (const char*)(nameData.Elements()), |
michael@0 | 1009 | nameSize, |
michael@0 | 1010 | famData->mOtherFamilyNames, |
michael@0 | 1011 | false); |
michael@0 | 1012 | } |
michael@0 | 1013 | } |
michael@0 | 1014 | } |
michael@0 | 1015 | |
michael@0 | 1016 | // read cmap |
michael@0 | 1017 | bool cmapLoaded = false; |
michael@0 | 1018 | gfxWindowsFontType feType = |
michael@0 | 1019 | GDIFontEntry::DetermineFontType(metrics, fontType); |
michael@0 | 1020 | if (famData->mFontInfo.mLoadCmaps && |
michael@0 | 1021 | (feType == GFX_FONT_TYPE_PS_OPENTYPE || |
michael@0 | 1022 | feType == GFX_FONT_TYPE_TT_OPENTYPE || |
michael@0 | 1023 | feType == GFX_FONT_TYPE_TRUETYPE)) |
michael@0 | 1024 | { |
michael@0 | 1025 | uint32_t kCMAP = |
michael@0 | 1026 | NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p')); |
michael@0 | 1027 | uint32_t cmapSize; |
michael@0 | 1028 | AutoFallibleTArray<uint8_t, 1024> cmapData; |
michael@0 | 1029 | |
michael@0 | 1030 | cmapSize = ::GetFontData(hdc, kCMAP, 0, nullptr, 0); |
michael@0 | 1031 | if (cmapSize != GDI_ERROR && |
michael@0 | 1032 | cmapSize > 0 && |
michael@0 | 1033 | cmapData.SetLength(cmapSize)) { |
michael@0 | 1034 | ::GetFontData(hdc, kCMAP, 0, cmapData.Elements(), cmapSize); |
michael@0 | 1035 | bool cmapLoaded = false; |
michael@0 | 1036 | bool unicodeFont = false, symbolFont = false; |
michael@0 | 1037 | nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(); |
michael@0 | 1038 | uint32_t offset; |
michael@0 | 1039 | |
michael@0 | 1040 | if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(), |
michael@0 | 1041 | cmapSize, *charmap, |
michael@0 | 1042 | offset, unicodeFont, |
michael@0 | 1043 | symbolFont))) { |
michael@0 | 1044 | fontData.mCharacterMap = charmap; |
michael@0 | 1045 | fontData.mUVSOffset = offset; |
michael@0 | 1046 | fontData.mSymbolFont = symbolFont; |
michael@0 | 1047 | cmapLoaded = true; |
michael@0 | 1048 | famData->mFontInfo.mLoadStats.cmaps++; |
michael@0 | 1049 | } |
michael@0 | 1050 | } |
michael@0 | 1051 | } |
michael@0 | 1052 | |
michael@0 | 1053 | if (cmapLoaded || nameDataLoaded) { |
michael@0 | 1054 | famData->mFontInfo.mFontFaceData.Put(fontName, fontData); |
michael@0 | 1055 | } |
michael@0 | 1056 | |
michael@0 | 1057 | return 1; |
michael@0 | 1058 | } |
michael@0 | 1059 | |
michael@0 | 1060 | void |
michael@0 | 1061 | GDIFontInfo::LoadFontFamilyData(const nsAString& aFamilyName) |
michael@0 | 1062 | { |
michael@0 | 1063 | // iterate over the family |
michael@0 | 1064 | LOGFONTW logFont; |
michael@0 | 1065 | memset(&logFont, 0, sizeof(LOGFONTW)); |
michael@0 | 1066 | logFont.lfCharSet = DEFAULT_CHARSET; |
michael@0 | 1067 | logFont.lfPitchAndFamily = 0; |
michael@0 | 1068 | uint32_t l = std::min<uint32_t>(aFamilyName.Length(), LF_FACESIZE - 1); |
michael@0 | 1069 | memcpy(logFont.lfFaceName, aFamilyName.BeginReading(), l * sizeof(char16_t)); |
michael@0 | 1070 | |
michael@0 | 1071 | EnumerateFontsForFamilyData data(aFamilyName, *this); |
michael@0 | 1072 | |
michael@0 | 1073 | EnumFontFamiliesExW(mHdc, &logFont, |
michael@0 | 1074 | (FONTENUMPROCW)GDIFontInfo::EnumerateFontsForFamily, |
michael@0 | 1075 | (LPARAM)(&data), 0); |
michael@0 | 1076 | |
michael@0 | 1077 | // if found other names, insert them |
michael@0 | 1078 | if (data.mOtherFamilyNames.Length() != 0) { |
michael@0 | 1079 | mOtherFamilyNames.Put(aFamilyName, data.mOtherFamilyNames); |
michael@0 | 1080 | mLoadStats.othernames += data.mOtherFamilyNames.Length(); |
michael@0 | 1081 | } |
michael@0 | 1082 | } |
michael@0 | 1083 | |
michael@0 | 1084 | already_AddRefed<FontInfoData> |
michael@0 | 1085 | gfxGDIFontList::CreateFontInfoData() |
michael@0 | 1086 | { |
michael@0 | 1087 | bool loadCmaps = !UsesSystemFallback() || |
michael@0 | 1088 | gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); |
michael@0 | 1089 | |
michael@0 | 1090 | nsRefPtr<GDIFontInfo> fi = |
michael@0 | 1091 | new GDIFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps); |
michael@0 | 1092 | |
michael@0 | 1093 | return fi.forget(); |
michael@0 | 1094 | } |