1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "gfxGDIFont.h"
7
8 #include "mozilla/MemoryReporting.h"
9 #include "mozilla/Sprintf.h"
10 #include "mozilla/WindowsVersion.h"
11
12 #include <algorithm>
13 #include "gfxWindowsPlatform.h"
14 #include "gfxContext.h"
15 #include "mozilla/Preferences.h"
16 #include "nsUnicodeProperties.h"
17 #include "gfxFontConstants.h"
18 #include "gfxTextRun.h"
19
20 #include "cairo-win32.h"
21
22 #define ROUND(x) floor((x) + 0.5)
23
24 using namespace mozilla;
25 using namespace mozilla::gfx;
26 using namespace mozilla::unicode;
27
gfxGDIFont(GDIFontEntry * aFontEntry,const gfxFontStyle * aFontStyle,AntialiasOption anAAOption)28 gfxGDIFont::gfxGDIFont(GDIFontEntry* aFontEntry, const gfxFontStyle* aFontStyle,
29 AntialiasOption anAAOption)
30 : gfxFont(nullptr, aFontEntry, aFontStyle, anAAOption),
31 mFont(nullptr),
32 mMetrics(nullptr),
33 mIsBitmap(false),
34 mScriptCache(nullptr) {
35 mNeedsSyntheticBold = aFontStyle->NeedsSyntheticBold(aFontEntry);
36
37 Initialize();
38
39 if (mFont) {
40 mUnscaledFont = aFontEntry->LookupUnscaledFont(mFont);
41 }
42 }
43
~gfxGDIFont()44 gfxGDIFont::~gfxGDIFont() {
45 if (mFont) {
46 ::DeleteObject(mFont);
47 }
48 if (mScriptCache) {
49 ScriptFreeCache(&mScriptCache);
50 }
51 delete mMetrics;
52 }
53
CopyWithAntialiasOption(AntialiasOption anAAOption)54 UniquePtr<gfxFont> gfxGDIFont::CopyWithAntialiasOption(
55 AntialiasOption anAAOption) {
56 auto entry = static_cast<GDIFontEntry*>(mFontEntry.get());
57 return MakeUnique<gfxGDIFont>(entry, &mStyle, anAAOption);
58 }
59
ShapeText(DrawTarget * aDrawTarget,const char16_t * aText,uint32_t aOffset,uint32_t aLength,Script aScript,nsAtom * aLanguage,bool aVertical,RoundingFlags aRounding,gfxShapedText * aShapedText)60 bool gfxGDIFont::ShapeText(DrawTarget* aDrawTarget, const char16_t* aText,
61 uint32_t aOffset, uint32_t aLength, Script aScript,
62 nsAtom* aLanguage, bool aVertical,
63 RoundingFlags aRounding,
64 gfxShapedText* aShapedText) {
65 if (!mIsValid) {
66 NS_WARNING("invalid font! expect incorrect text rendering");
67 return false;
68 }
69
70 return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
71 aLanguage, aVertical, aRounding, aShapedText);
72 }
73
GetHorizontalMetrics()74 const gfxFont::Metrics& gfxGDIFont::GetHorizontalMetrics() { return *mMetrics; }
75
GetScaledFont(const TextRunDrawParams & aRunParams)76 already_AddRefed<ScaledFont> gfxGDIFont::GetScaledFont(
77 const TextRunDrawParams& aRunParams) {
78 if (!mAzureScaledFont) {
79 LOGFONT lf;
80 GetObject(GetHFONT(), sizeof(LOGFONT), &lf);
81
82 mAzureScaledFont = Factory::CreateScaledFontForGDIFont(
83 &lf, GetUnscaledFont(), GetAdjustedSize());
84 InitializeScaledFont();
85 }
86
87 RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
88 return scaledFont.forget();
89 }
90
Measure(const gfxTextRun * aTextRun,uint32_t aStart,uint32_t aEnd,BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,Spacing * aSpacing,gfx::ShapedTextFlags aOrientation)91 gfxFont::RunMetrics gfxGDIFont::Measure(const gfxTextRun* aTextRun,
92 uint32_t aStart, uint32_t aEnd,
93 BoundingBoxType aBoundingBoxType,
94 DrawTarget* aRefDrawTarget,
95 Spacing* aSpacing,
96 gfx::ShapedTextFlags aOrientation) {
97 gfxFont::RunMetrics metrics =
98 gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType, aRefDrawTarget,
99 aSpacing, aOrientation);
100
101 // if aBoundingBoxType is LOOSE_INK_EXTENTS
102 // and the underlying cairo font may be antialiased,
103 // we can't trust Windows to have considered all the pixels
104 // so we need to add "padding" to the bounds.
105 // (see bugs 475968, 439831, compare also bug 445087)
106 if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
107 mAntialiasOption != kAntialiasNone && metrics.mBoundingBox.Width() > 0) {
108 metrics.mBoundingBox.MoveByX(-aTextRun->GetAppUnitsPerDevUnit());
109 metrics.mBoundingBox.SetWidth(metrics.mBoundingBox.Width() +
110 aTextRun->GetAppUnitsPerDevUnit() * 3);
111 }
112
113 return metrics;
114 }
115
Initialize()116 void gfxGDIFont::Initialize() {
117 NS_ASSERTION(!mMetrics, "re-creating metrics? this will leak");
118
119 LOGFONTW logFont;
120
121 if (mAdjustedSize == 0.0) {
122 mAdjustedSize = GetAdjustedSize();
123 if (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) !=
124 FontSizeAdjust::Tag::None) {
125 if (mStyle.sizeAdjust > 0.0 && mAdjustedSize > 0.0) {
126 // to implement font-size-adjust, we first create the "unadjusted" font
127 FillLogFont(logFont, mAdjustedSize);
128 mFont = ::CreateFontIndirectW(&logFont);
129
130 // initialize its metrics so we can calculate size adjustment
131 Initialize();
132
133 // Unless the font was so small that GDI metrics rounded to zero,
134 // calculate the properly adjusted size, and then proceed
135 // to recreate mFont and recalculate metrics
136 if (mMetrics->emHeight > 0.0) {
137 gfxFloat aspect;
138 switch (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis)) {
139 default:
140 MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
141 aspect = 0.0;
142 break;
143 case FontSizeAdjust::Tag::ExHeight:
144 aspect = mMetrics->xHeight / mMetrics->emHeight;
145 break;
146 case FontSizeAdjust::Tag::CapHeight:
147 aspect = mMetrics->capHeight / mMetrics->emHeight;
148 break;
149 case FontSizeAdjust::Tag::ChWidth: {
150 gfxFloat advance = GetCharAdvance('0');
151 aspect = advance > 0.0 ? advance / mMetrics->emHeight : 0.5;
152 break;
153 }
154 case FontSizeAdjust::Tag::IcWidth:
155 case FontSizeAdjust::Tag::IcHeight: {
156 bool vertical = FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) ==
157 FontSizeAdjust::Tag::IcHeight;
158 gfxFloat advance = GetCharAdvance(kWaterIdeograph, vertical);
159 aspect = advance > 0.0 ? advance / mMetrics->emHeight : 1.0;
160 break;
161 }
162 }
163 if (aspect > 0.0) {
164 // If we created a shaper above (to measure glyphs), discard it so
165 // we get a new one for the adjusted scaling.
166 mHarfBuzzShaper = nullptr;
167 mAdjustedSize = mStyle.GetAdjustedSize(aspect);
168 }
169 }
170
171 // delete the temporary font and metrics
172 ::DeleteObject(mFont);
173 mFont = nullptr;
174 delete mMetrics;
175 mMetrics = nullptr;
176 } else {
177 mAdjustedSize = 0.0;
178 }
179 }
180 }
181
182 // (bug 724231) for local user fonts, we don't use GDI's synthetic bold,
183 // as it could lead to a different, incompatible face being used
184 // but instead do our own multi-striking
185 if (mNeedsSyntheticBold && GetFontEntry()->IsLocalUserFont()) {
186 mApplySyntheticBold = true;
187 }
188
189 // this may end up being zero
190 mAdjustedSize = ROUND(mAdjustedSize);
191 FillLogFont(logFont, mAdjustedSize);
192 mFont = ::CreateFontIndirectW(&logFont);
193
194 mMetrics = new gfxFont::Metrics;
195 ::memset(mMetrics, 0, sizeof(*mMetrics));
196
197 if (!mFont) {
198 NS_WARNING("Failed creating GDI font");
199 mIsValid = false;
200 return;
201 }
202
203 AutoDC dc;
204 SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
205 AutoSelectFont selectFont(dc.GetDC(), mFont);
206
207 // Get font metrics if size > 0
208 if (mAdjustedSize > 0.0) {
209 OUTLINETEXTMETRIC oMetrics;
210 TEXTMETRIC& metrics = oMetrics.otmTextMetrics;
211
212 if (0 < GetOutlineTextMetrics(dc.GetDC(), sizeof(oMetrics), &oMetrics)) {
213 mMetrics->strikeoutSize = (double)oMetrics.otmsStrikeoutSize;
214 mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition;
215 mMetrics->underlineSize = (double)oMetrics.otmsUnderscoreSize;
216 mMetrics->underlineOffset = (double)oMetrics.otmsUnderscorePosition;
217
218 const MAT2 kIdentityMatrix = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
219 GLYPHMETRICS gm;
220 DWORD len = GetGlyphOutlineW(dc.GetDC(), char16_t('x'), GGO_METRICS, &gm,
221 0, nullptr, &kIdentityMatrix);
222 if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
223 // 56% of ascent, best guess for true type
224 mMetrics->xHeight =
225 ROUND((double)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
226 } else {
227 mMetrics->xHeight = gm.gmptGlyphOrigin.y;
228 }
229 len = GetGlyphOutlineW(dc.GetDC(), char16_t('H'), GGO_METRICS, &gm, 0,
230 nullptr, &kIdentityMatrix);
231 if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
232 mMetrics->capHeight = metrics.tmAscent - metrics.tmInternalLeading;
233 } else {
234 mMetrics->capHeight = gm.gmptGlyphOrigin.y;
235 }
236 mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
237 gfxFloat typEmHeight =
238 (double)oMetrics.otmAscent - (double)oMetrics.otmDescent;
239 mMetrics->emAscent =
240 ROUND(mMetrics->emHeight * (double)oMetrics.otmAscent / typEmHeight);
241 mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
242 if (oMetrics.otmEMSquare > 0) {
243 mFUnitsConvFactor = float(mAdjustedSize / oMetrics.otmEMSquare);
244 }
245 } else {
246 // Make a best-effort guess at extended metrics
247 // this is based on general typographic guidelines
248
249 // GetTextMetrics can fail if the font file has been removed
250 // or corrupted recently.
251 BOOL result = GetTextMetrics(dc.GetDC(), &metrics);
252 if (!result) {
253 NS_WARNING("Missing or corrupt font data, fasten your seatbelt");
254 mIsValid = false;
255 memset(mMetrics, 0, sizeof(*mMetrics));
256 return;
257 }
258
259 mMetrics->xHeight =
260 ROUND((float)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
261 mMetrics->strikeoutSize = 1;
262 mMetrics->strikeoutOffset =
263 ROUND(mMetrics->xHeight * 0.5f); // 50% of xHeight
264 mMetrics->underlineSize = 1;
265 mMetrics->underlineOffset =
266 -ROUND((float)metrics.tmDescent * 0.30f); // 30% of descent
267 mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
268 mMetrics->emAscent = metrics.tmAscent - metrics.tmInternalLeading;
269 mMetrics->emDescent = metrics.tmDescent;
270 mMetrics->capHeight = mMetrics->emAscent;
271 }
272
273 mMetrics->internalLeading = metrics.tmInternalLeading;
274 mMetrics->externalLeading = metrics.tmExternalLeading;
275 mMetrics->maxHeight = metrics.tmHeight;
276 mMetrics->maxAscent = metrics.tmAscent;
277 mMetrics->maxDescent = metrics.tmDescent;
278 mMetrics->maxAdvance = metrics.tmMaxCharWidth;
279 mMetrics->aveCharWidth = std::max<gfxFloat>(1, metrics.tmAveCharWidth);
280 // The font is monospace when TMPF_FIXED_PITCH is *not* set!
281 // See http://msdn2.microsoft.com/en-us/library/ms534202(VS.85).aspx
282 if (!(metrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
283 mMetrics->maxAdvance = mMetrics->aveCharWidth;
284 }
285
286 mIsBitmap = !(metrics.tmPitchAndFamily & TMPF_VECTOR);
287
288 // For fonts with USE_TYPO_METRICS set in the fsSelection field,
289 // let the OS/2 sTypo* metrics override the previous values.
290 // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
291 // Using the equivalent values from oMetrics provides inconsistent
292 // results with CFF fonts, so we instead rely on OS2Table.
293 gfxFontEntry::AutoTable os2Table(mFontEntry,
294 TRUETYPE_TAG('O', 'S', '/', '2'));
295 if (os2Table) {
296 uint32_t len;
297 const OS2Table* os2 =
298 reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
299 if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
300 const uint16_t kUseTypoMetricsMask = 1 << 7;
301 if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask)) {
302 double ascent = int16_t(os2->sTypoAscender);
303 double descent = int16_t(os2->sTypoDescender);
304 double lineGap = int16_t(os2->sTypoLineGap);
305 mMetrics->maxAscent = ROUND(ascent * mFUnitsConvFactor);
306 mMetrics->maxDescent = -ROUND(descent * mFUnitsConvFactor);
307 mMetrics->maxHeight = mMetrics->maxAscent + mMetrics->maxDescent;
308 mMetrics->internalLeading = mMetrics->maxHeight - mMetrics->emHeight;
309 gfxFloat lineHeight =
310 ROUND((ascent - descent + lineGap) * mFUnitsConvFactor);
311 lineHeight = std::max(lineHeight, mMetrics->maxHeight);
312 mMetrics->externalLeading = lineHeight - mMetrics->maxHeight;
313 }
314 }
315 // although sxHeight and sCapHeight are signed fields, we consider
316 // negative values to be erroneous and just ignore them
317 if (uint16_t(os2->version) >= 2) {
318 // version 2 and later includes the x-height and cap-height fields
319 if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
320 int16_t(os2->sxHeight) > 0) {
321 mMetrics->xHeight = ROUND(int16_t(os2->sxHeight) * mFUnitsConvFactor);
322 }
323 if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
324 int16_t(os2->sCapHeight) > 0) {
325 mMetrics->capHeight =
326 ROUND(int16_t(os2->sCapHeight) * mFUnitsConvFactor);
327 }
328 }
329 }
330
331 WORD glyph;
332 SIZE size;
333 DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
334 GGI_MARK_NONEXISTING_GLYPHS);
335 if (ret != GDI_ERROR && glyph != 0xFFFF) {
336 mSpaceGlyph = glyph;
337 // Cache the width of a single space.
338 GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size);
339 mMetrics->spaceWidth = ROUND(size.cx);
340 } else {
341 mMetrics->spaceWidth = mMetrics->aveCharWidth;
342 }
343
344 // Cache the width of digit zero, if available.
345 ret = GetGlyphIndicesW(dc.GetDC(), L"0", 1, &glyph,
346 GGI_MARK_NONEXISTING_GLYPHS);
347 if (ret != GDI_ERROR && glyph != 0xFFFF) {
348 GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size);
349 mMetrics->zeroWidth = ROUND(size.cx);
350 } else {
351 mMetrics->zeroWidth = -1.0; // indicates not found
352 }
353
354 wchar_t ch = kWaterIdeograph;
355 ret = GetGlyphIndicesW(dc.GetDC(), &ch, 1, &glyph,
356 GGI_MARK_NONEXISTING_GLYPHS);
357 if (ret != GDI_ERROR && glyph != 0xFFFF) {
358 GetTextExtentPoint32W(dc.GetDC(), &ch, 1, &size);
359 mMetrics->ideographicWidth = ROUND(size.cx);
360 } else {
361 mMetrics->ideographicWidth = -1.0;
362 }
363
364 SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
365 } else {
366 mFUnitsConvFactor = 0.0; // zero-sized font: all values scale to zero
367 }
368
369 if (ApplySyntheticBold()) {
370 auto delta = GetSyntheticBoldOffset();
371 mMetrics->spaceWidth += delta;
372 mMetrics->aveCharWidth += delta;
373 mMetrics->maxAdvance += delta;
374 if (mMetrics->zeroWidth > 0) {
375 mMetrics->zeroWidth += delta;
376 }
377 if (mMetrics->ideographicWidth > 0) {
378 mMetrics->ideographicWidth += delta;
379 }
380 }
381
382 #if 0
383 printf("Font: %p (%s) size: %f adjusted size: %f valid: %s\n", this,
384 GetName().get(), mStyle.size, mAdjustedSize, (mIsValid ? "yes" : "no"));
385 printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
386 printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
387 printf(" internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
388 printf(" spaceWidth: %f aveCharWidth: %f\n", mMetrics->spaceWidth, mMetrics->aveCharWidth);
389 printf(" xHeight: %f capHeight: %f\n", mMetrics->xHeight, mMetrics->capHeight);
390 printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
391 mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize);
392 #endif
393 }
394
FillLogFont(LOGFONTW & aLogFont,gfxFloat aSize)395 void gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize) {
396 GDIFontEntry* fe = static_cast<GDIFontEntry*>(GetFontEntry());
397
398 // Figure out the lfWeight value to use for GDI font selection,
399 // or zero to use the entry's current LOGFONT value.
400 LONG weight;
401 if (fe->IsUserFont()) {
402 if (fe->IsLocalUserFont()) {
403 // for local user fonts, don't change the original weight
404 // in the entry's logfont, because that could alter the
405 // choice of actual face used (bug 724231)
406 weight = 0;
407 } else {
408 // avoid GDI synthetic bold which occurs when weight
409 // specified is >= font data weight + 200
410 weight = mNeedsSyntheticBold ? 700 : 200;
411 }
412 } else {
413 // GDI doesn't support variation fonts, so for system fonts we know
414 // that the entry has only a single weight, not a range.
415 MOZ_ASSERT(fe->Weight().IsSingle());
416 weight = mNeedsSyntheticBold ? 700 : fe->Weight().Min().ToIntRounded();
417 }
418
419 fe->FillLogFont(&aLogFont, weight, aSize);
420 }
421
GetGlyph(uint32_t aUnicode,uint32_t aVarSelector)422 uint32_t gfxGDIFont::GetGlyph(uint32_t aUnicode, uint32_t aVarSelector) {
423 // Callback used only for fonts that lack a 'cmap' table.
424
425 // We don't support variation selector sequences or non-BMP characters
426 // in the legacy bitmap, vector or postscript fonts that might use
427 // this code path.
428 if (aUnicode > 0xffff || aVarSelector) {
429 return 0;
430 }
431
432 if (!mGlyphIDs) {
433 mGlyphIDs = MakeUnique<nsTHashMap<nsUint32HashKey, uint32_t>>(64);
434 }
435
436 uint32_t gid;
437 if (mGlyphIDs->Get(aUnicode, &gid)) {
438 return gid;
439 }
440
441 wchar_t ch = aUnicode;
442 WORD glyph;
443 DWORD ret = ScriptGetCMap(nullptr, &mScriptCache, &ch, 1, 0, &glyph);
444 if (ret != S_OK) {
445 AutoDC dc;
446 AutoSelectFont fs(dc.GetDC(), GetHFONT());
447 if (ret == E_PENDING) {
448 // Try ScriptGetCMap again now that we've set up the font.
449 ret = ScriptGetCMap(dc.GetDC(), &mScriptCache, &ch, 1, 0, &glyph);
450 }
451 if (ret != S_OK) {
452 // If ScriptGetCMap still failed, fall back to GetGlyphIndicesW
453 // (see bug 1105807).
454 ret = GetGlyphIndicesW(dc.GetDC(), &ch, 1, &glyph,
455 GGI_MARK_NONEXISTING_GLYPHS);
456 if (ret == GDI_ERROR || glyph == 0xFFFF) {
457 glyph = 0;
458 }
459 }
460 }
461
462 mGlyphIDs->InsertOrUpdate(aUnicode, glyph);
463 return glyph;
464 }
465
GetGlyphWidth(uint16_t aGID)466 int32_t gfxGDIFont::GetGlyphWidth(uint16_t aGID) {
467 if (!mGlyphWidths) {
468 mGlyphWidths = MakeUnique<nsTHashMap<nsUint32HashKey, int32_t>>(128);
469 }
470
471 return mGlyphWidths->WithEntryHandle(aGID, [&](auto&& entry) {
472 if (!entry) {
473 DCForMetrics dc;
474 AutoSelectFont fs(dc, GetHFONT());
475
476 int devWidth;
477 if (!GetCharWidthI(dc, aGID, 1, nullptr, &devWidth)) {
478 return -1;
479 }
480 // clamp value to range [0..0x7fff], and convert to 16.16 fixed-point
481 devWidth = std::min(std::max(0, devWidth), 0x7fff);
482 entry.Insert(devWidth << 16);
483 }
484 return *entry;
485 });
486 }
487
GetGlyphBounds(uint16_t aGID,gfxRect * aBounds,bool aTight)488 bool gfxGDIFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) {
489 DCForMetrics dc;
490 AutoSelectFont fs(dc, GetHFONT());
491
492 if (mIsBitmap) {
493 int devWidth;
494 if (!GetCharWidthI(dc, aGID, 1, nullptr, &devWidth)) {
495 return false;
496 }
497 devWidth = std::min(std::max(0, devWidth), 0x7fff);
498
499 *aBounds = gfxRect(0, -mMetrics->maxAscent, devWidth,
500 mMetrics->maxAscent + mMetrics->maxDescent);
501 return true;
502 }
503
504 const MAT2 kIdentityMatrix = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
505 GLYPHMETRICS gm;
506 if (GetGlyphOutlineW(dc, aGID, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr,
507 &kIdentityMatrix) == GDI_ERROR) {
508 return false;
509 }
510
511 if (gm.gmBlackBoxX == 1 && gm.gmBlackBoxY == 1 &&
512 !GetGlyphOutlineW(dc, aGID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, nullptr,
513 &kIdentityMatrix)) {
514 // Workaround for GetGlyphOutline returning 1x1 bounding box
515 // for <space> glyph that is in fact empty.
516 gm.gmBlackBoxX = 0;
517 gm.gmBlackBoxY = 0;
518 } else if (gm.gmBlackBoxX > 0 && !aTight) {
519 // The bounding box reported by Windows supposedly contains the glyph's
520 // "black" area; however, antialiasing (especially with ClearType) means
521 // that the actual image that needs to be rendered may "bleed" into the
522 // adjacent pixels, mainly on the right side.
523 gm.gmptGlyphOrigin.x -= 1;
524 gm.gmBlackBoxX += 3;
525 }
526
527 *aBounds = gfxRect(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y,
528 gm.gmBlackBoxX, gm.gmBlackBoxY);
529 return true;
530 }
531
AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const532 void gfxGDIFont::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
533 FontCacheSizes* aSizes) const {
534 gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
535 aSizes->mFontInstances += aMallocSizeOf(mMetrics);
536 if (mGlyphWidths) {
537 aSizes->mFontInstances +=
538 mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
539 }
540 }
541
AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const542 void gfxGDIFont::AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
543 FontCacheSizes* aSizes) const {
544 aSizes->mFontInstances += aMallocSizeOf(this);
545 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
546 }
547