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