1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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::unicode;
26 
27 static inline cairo_antialias_t
GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)28 GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)
29 {
30     switch (anAntialiasOption) {
31     default:
32     case gfxFont::kAntialiasDefault:
33         return CAIRO_ANTIALIAS_DEFAULT;
34     case gfxFont::kAntialiasNone:
35         return CAIRO_ANTIALIAS_NONE;
36     case gfxFont::kAntialiasGrayscale:
37         return CAIRO_ANTIALIAS_GRAY;
38     case gfxFont::kAntialiasSubpixel:
39         return CAIRO_ANTIALIAS_SUBPIXEL;
40     }
41 }
42 
gfxGDIFont(GDIFontEntry * aFontEntry,const gfxFontStyle * aFontStyle,bool aNeedsBold,AntialiasOption anAAOption)43 gfxGDIFont::gfxGDIFont(GDIFontEntry *aFontEntry,
44                        const gfxFontStyle *aFontStyle,
45                        bool aNeedsBold,
46                        AntialiasOption anAAOption)
47     : gfxFont(aFontEntry, aFontStyle, anAAOption),
48       mFont(nullptr),
49       mFontFace(nullptr),
50       mMetrics(nullptr),
51       mSpaceGlyph(0),
52       mNeedsBold(aNeedsBold),
53       mScriptCache(nullptr)
54 {
55     Initialize();
56 }
57 
~gfxGDIFont()58 gfxGDIFont::~gfxGDIFont()
59 {
60     if (mScaledFont) {
61         cairo_scaled_font_destroy(mScaledFont);
62     }
63     if (mFontFace) {
64         cairo_font_face_destroy(mFontFace);
65     }
66     if (mFont) {
67         ::DeleteObject(mFont);
68     }
69     if (mScriptCache) {
70         ScriptFreeCache(&mScriptCache);
71     }
72     delete mMetrics;
73 }
74 
75 gfxFont*
CopyWithAntialiasOption(AntialiasOption anAAOption)76 gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
77 {
78     return new gfxGDIFont(static_cast<GDIFontEntry*>(mFontEntry.get()),
79                           &mStyle, mNeedsBold, anAAOption);
80 }
81 
82 bool
ShapeText(DrawTarget * aDrawTarget,const char16_t * aText,uint32_t aOffset,uint32_t aLength,Script aScript,bool aVertical,gfxShapedText * aShapedText)83 gfxGDIFont::ShapeText(DrawTarget     *aDrawTarget,
84                       const char16_t *aText,
85                       uint32_t        aOffset,
86                       uint32_t        aLength,
87                       Script          aScript,
88                       bool            aVertical,
89                       gfxShapedText  *aShapedText)
90 {
91     if (!mIsValid) {
92         NS_WARNING("invalid font! expect incorrect text rendering");
93         return false;
94     }
95 
96     // Ensure the cairo font is set up, so there's no risk it'll fall back to
97     // creating a "toy" font internally (see bug 544617).
98     // We must check that this succeeded, otherwise we risk cairo creating the
99     // wrong kind of font internally as a fallback (bug 744480).
100     if (!SetupCairoFont(aDrawTarget)) {
101         return false;
102     }
103 
104     return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
105                               aVertical, aShapedText);
106 }
107 
108 const gfxFont::Metrics&
GetHorizontalMetrics()109 gfxGDIFont::GetHorizontalMetrics()
110 {
111     return *mMetrics;
112 }
113 
114 uint32_t
GetSpaceGlyph()115 gfxGDIFont::GetSpaceGlyph()
116 {
117     return mSpaceGlyph;
118 }
119 
120 bool
SetupCairoFont(DrawTarget * aDrawTarget)121 gfxGDIFont::SetupCairoFont(DrawTarget* aDrawTarget)
122 {
123     if (!mScaledFont ||
124         cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
125         // Don't cairo_set_scaled_font as that would propagate the error to
126         // the cairo_t, precluding any further drawing.
127         return false;
128     }
129     cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), mScaledFont);
130     return true;
131 }
132 
133 gfxFont::RunMetrics
Measure(const gfxTextRun * aTextRun,uint32_t aStart,uint32_t aEnd,BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,Spacing * aSpacing,uint16_t aOrientation)134 gfxGDIFont::Measure(const gfxTextRun *aTextRun,
135                     uint32_t aStart, uint32_t aEnd,
136                     BoundingBoxType aBoundingBoxType,
137                     DrawTarget *aRefDrawTarget,
138                     Spacing *aSpacing,
139                     uint16_t aOrientation)
140 {
141     gfxFont::RunMetrics metrics =
142         gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
143                          aRefDrawTarget, aSpacing, aOrientation);
144 
145     // if aBoundingBoxType is LOOSE_INK_EXTENTS
146     // and the underlying cairo font may be antialiased,
147     // we can't trust Windows to have considered all the pixels
148     // so we need to add "padding" to the bounds.
149     // (see bugs 475968, 439831, compare also bug 445087)
150     if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
151         mAntialiasOption != kAntialiasNone &&
152         metrics.mBoundingBox.width > 0) {
153         metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
154         metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 3;
155     }
156 
157     return metrics;
158 }
159 
160 void
Initialize()161 gfxGDIFont::Initialize()
162 {
163     NS_ASSERTION(!mMetrics, "re-creating metrics? this will leak");
164 
165     LOGFONTW logFont;
166 
167     // Figure out if we want to do synthetic oblique styling.
168     GDIFontEntry* fe = static_cast<GDIFontEntry*>(GetFontEntry());
169     bool wantFakeItalic = mStyle.style != NS_FONT_STYLE_NORMAL &&
170                           fe->IsUpright() && mStyle.allowSyntheticStyle;
171 
172     // If the font's family has an actual italic face (but font matching
173     // didn't choose it), we have to use a cairo transform instead of asking
174     // GDI to italicize, because that would use a different face and result
175     // in a possible glyph ID mismatch between shaping and rendering.
176     //
177     // We use the mFamilyHasItalicFace flag in the entry in case of user fonts,
178     // where the *CSS* family may not know about italic faces that are present
179     // in the *GDI* family, and which GDI would use if we asked it to perform
180     // the "italicization".
181     bool useCairoFakeItalic = wantFakeItalic && fe->mFamilyHasItalicFace;
182 
183     if (mAdjustedSize == 0.0) {
184         mAdjustedSize = mStyle.size;
185         if (mStyle.sizeAdjust > 0.0 && mAdjustedSize > 0.0) {
186             // to implement font-size-adjust, we first create the "unadjusted" font
187             FillLogFont(logFont, mAdjustedSize,
188                         wantFakeItalic && !useCairoFakeItalic);
189             mFont = ::CreateFontIndirectW(&logFont);
190 
191             // initialize its metrics so we can calculate size adjustment
192             Initialize();
193 
194             // Unless the font was so small that GDI metrics rounded to zero,
195             // calculate the properly adjusted size, and then proceed
196             // to recreate mFont and recalculate metrics
197             if (mMetrics->xHeight > 0.0 && mMetrics->emHeight > 0.0) {
198                 gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight;
199                 mAdjustedSize = mStyle.GetAdjustedSize(aspect);
200             }
201 
202             // delete the temporary font and metrics
203             ::DeleteObject(mFont);
204             mFont = nullptr;
205             delete mMetrics;
206             mMetrics = nullptr;
207         } else if (mStyle.sizeAdjust == 0.0) {
208             mAdjustedSize = 0.0;
209         }
210     }
211 
212     // (bug 724231) for local user fonts, we don't use GDI's synthetic bold,
213     // as it could lead to a different, incompatible face being used
214     // but instead do our own multi-striking
215     if (mNeedsBold && GetFontEntry()->IsLocalUserFont()) {
216         mApplySyntheticBold = true;
217     }
218 
219     // this may end up being zero
220     mAdjustedSize = ROUND(mAdjustedSize);
221     FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic);
222     mFont = ::CreateFontIndirectW(&logFont);
223 
224     mMetrics = new gfxFont::Metrics;
225     ::memset(mMetrics, 0, sizeof(*mMetrics));
226 
227     AutoDC dc;
228     SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
229     AutoSelectFont selectFont(dc.GetDC(), mFont);
230 
231     // Get font metrics if size > 0
232     if (mAdjustedSize > 0.0) {
233 
234         OUTLINETEXTMETRIC oMetrics;
235         TEXTMETRIC& metrics = oMetrics.otmTextMetrics;
236 
237         if (0 < GetOutlineTextMetrics(dc.GetDC(), sizeof(oMetrics), &oMetrics)) {
238             mMetrics->strikeoutSize = (double)oMetrics.otmsStrikeoutSize;
239             mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition;
240             mMetrics->underlineSize = (double)oMetrics.otmsUnderscoreSize;
241             mMetrics->underlineOffset = (double)oMetrics.otmsUnderscorePosition;
242 
243             const MAT2 kIdentityMatrix = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
244             GLYPHMETRICS gm;
245             DWORD len = GetGlyphOutlineW(dc.GetDC(), char16_t('x'), GGO_METRICS, &gm, 0, nullptr, &kIdentityMatrix);
246             if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
247                 // 56% of ascent, best guess for true type
248                 mMetrics->xHeight =
249                     ROUND((double)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
250             } else {
251                 mMetrics->xHeight = gm.gmptGlyphOrigin.y;
252             }
253             len = GetGlyphOutlineW(dc.GetDC(), char16_t('H'), GGO_METRICS, &gm, 0, nullptr, &kIdentityMatrix);
254             if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
255                 mMetrics->capHeight = metrics.tmAscent - metrics.tmInternalLeading;
256             } else {
257                 mMetrics->capHeight = gm.gmptGlyphOrigin.y;
258             }
259             mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
260             gfxFloat typEmHeight = (double)oMetrics.otmAscent - (double)oMetrics.otmDescent;
261             mMetrics->emAscent = ROUND(mMetrics->emHeight * (double)oMetrics.otmAscent / typEmHeight);
262             mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
263             if (oMetrics.otmEMSquare > 0) {
264                 mFUnitsConvFactor = float(mAdjustedSize / oMetrics.otmEMSquare);
265             }
266         } else {
267             // Make a best-effort guess at extended metrics
268             // this is based on general typographic guidelines
269 
270             // GetTextMetrics can fail if the font file has been removed
271             // or corrupted recently.
272             BOOL result = GetTextMetrics(dc.GetDC(), &metrics);
273             if (!result) {
274                 NS_WARNING("Missing or corrupt font data, fasten your seatbelt");
275                 mIsValid = false;
276                 memset(mMetrics, 0, sizeof(*mMetrics));
277                 return;
278             }
279 
280             mMetrics->xHeight =
281                 ROUND((float)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
282             mMetrics->strikeoutSize = 1;
283             mMetrics->strikeoutOffset = ROUND(mMetrics->xHeight * 0.5f); // 50% of xHeight
284             mMetrics->underlineSize = 1;
285             mMetrics->underlineOffset = -ROUND((float)metrics.tmDescent * 0.30f); // 30% of descent
286             mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
287             mMetrics->emAscent = metrics.tmAscent - metrics.tmInternalLeading;
288             mMetrics->emDescent = metrics.tmDescent;
289             mMetrics->capHeight = mMetrics->emAscent;
290         }
291 
292         mMetrics->internalLeading = metrics.tmInternalLeading;
293         mMetrics->externalLeading = metrics.tmExternalLeading;
294         mMetrics->maxHeight = metrics.tmHeight;
295         mMetrics->maxAscent = metrics.tmAscent;
296         mMetrics->maxDescent = metrics.tmDescent;
297         mMetrics->maxAdvance = metrics.tmMaxCharWidth;
298         mMetrics->aveCharWidth = std::max<gfxFloat>(1, metrics.tmAveCharWidth);
299         // The font is monospace when TMPF_FIXED_PITCH is *not* set!
300         // See http://msdn2.microsoft.com/en-us/library/ms534202(VS.85).aspx
301         if (!(metrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
302             mMetrics->maxAdvance = mMetrics->aveCharWidth;
303         }
304 
305         // For fonts with USE_TYPO_METRICS set in the fsSelection field,
306         // let the OS/2 sTypo* metrics override the previous values.
307         // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
308         // Using the equivalent values from oMetrics provides inconsistent
309         // results with CFF fonts, so we instead rely on OS2Table.
310         gfxFontEntry::AutoTable os2Table(mFontEntry,
311                                          TRUETYPE_TAG('O','S','/','2'));
312         if (os2Table) {
313             uint32_t len;
314             const OS2Table *os2 =
315                 reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table,
316                                                                    &len));
317             if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
318                 const uint16_t kUseTypoMetricsMask = 1 << 7;
319                 if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask)) {
320                     double ascent = int16_t(os2->sTypoAscender);
321                     double descent = int16_t(os2->sTypoDescender);
322                     double lineGap = int16_t(os2->sTypoLineGap);
323                     mMetrics->maxAscent = ROUND(ascent * mFUnitsConvFactor);
324                     mMetrics->maxDescent = -ROUND(descent * mFUnitsConvFactor);
325                     mMetrics->maxHeight =
326                         mMetrics->maxAscent + mMetrics->maxDescent;
327                     mMetrics->internalLeading =
328                         mMetrics->maxHeight - mMetrics->emHeight;
329                     gfxFloat lineHeight =
330                         ROUND((ascent - descent + lineGap) * mFUnitsConvFactor);
331                     lineHeight = std::max(lineHeight, mMetrics->maxHeight);
332                     mMetrics->externalLeading =
333                         lineHeight - mMetrics->maxHeight;
334                 }
335             }
336             // although sxHeight and sCapHeight are signed fields, we consider
337             // negative values to be erroneous and just ignore them
338             if (uint16_t(os2->version) >= 2) {
339                 // version 2 and later includes the x-height and cap-height fields
340                 if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
341                     int16_t(os2->sxHeight) > 0) {
342                     mMetrics->xHeight = ROUND(int16_t(os2->sxHeight) * mFUnitsConvFactor);
343                 }
344                 if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
345                     int16_t(os2->sCapHeight) > 0) {
346                     mMetrics->capHeight = ROUND(int16_t(os2->sCapHeight) * mFUnitsConvFactor);
347                 }
348             }
349         }
350 
351         WORD glyph;
352         SIZE size;
353         DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
354                                      GGI_MARK_NONEXISTING_GLYPHS);
355         if (ret != GDI_ERROR && glyph != 0xFFFF) {
356             mSpaceGlyph = glyph;
357             // Cache the width of a single space.
358             GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size);
359             mMetrics->spaceWidth = ROUND(size.cx);
360         } else {
361             mMetrics->spaceWidth = mMetrics->aveCharWidth;
362         }
363 
364         // Cache the width of digit zero, if available.
365         ret = GetGlyphIndicesW(dc.GetDC(), L"0", 1, &glyph,
366                                GGI_MARK_NONEXISTING_GLYPHS);
367         if (ret != GDI_ERROR && glyph != 0xFFFF) {
368             GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size);
369             mMetrics->zeroOrAveCharWidth = ROUND(size.cx);
370         } else {
371             mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
372         }
373 
374         SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
375     } else {
376         mFUnitsConvFactor = 0.0; // zero-sized font: all values scale to zero
377     }
378 
379     if (IsSyntheticBold()) {
380         mMetrics->aveCharWidth += GetSyntheticBoldOffset();
381         mMetrics->maxAdvance += GetSyntheticBoldOffset();
382     }
383 
384     mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&logFont,
385                                                                 mFont);
386 
387     cairo_matrix_t sizeMatrix, ctm;
388     cairo_matrix_init_identity(&ctm);
389     cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
390 
391     if (useCairoFakeItalic) {
392         // Skew the matrix to do fake italic if it wasn't already applied
393         // via the LOGFONT
394         double skewfactor = OBLIQUE_SKEW_FACTOR;
395         cairo_matrix_t style;
396         cairo_matrix_init(&style,
397                           1,                //xx
398                           0,                //yx
399                           -1 * skewfactor,  //xy
400                           1,                //yy
401                           0,                //x0
402                           0);               //y0
403         cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
404     }
405 
406     cairo_font_options_t *fontOptions = cairo_font_options_create();
407     if (mAntialiasOption != kAntialiasDefault) {
408         cairo_font_options_set_antialias(fontOptions,
409             GetCairoAntialiasOption(mAntialiasOption));
410     }
411     mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix,
412                                            &ctm, fontOptions);
413     cairo_font_options_destroy(fontOptions);
414 
415     if (!mScaledFont ||
416         cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
417 #ifdef DEBUG
418         char warnBuf[1024];
419         SprintfLiteral(warnBuf, "Failed to create scaled font: %s status: %d",
420                        NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
421                        mScaledFont ? cairo_scaled_font_status(mScaledFont) : 0);
422         NS_WARNING(warnBuf);
423 #endif
424         mIsValid = false;
425     } else {
426         mIsValid = true;
427     }
428 
429 #if 0
430     printf("Font: %p (%s) size: %f adjusted size: %f valid: %s\n", this,
431            NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size, mAdjustedSize, (mIsValid ? "yes" : "no"));
432     printf("    emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
433     printf("    maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
434     printf("    internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
435     printf("    spaceWidth: %f aveCharWidth: %f\n", mMetrics->spaceWidth, mMetrics->aveCharWidth);
436     printf("    xHeight: %f capHeight: %f\n", mMetrics->xHeight, mMetrics->capHeight);
437     printf("    uOff: %f uSize: %f stOff: %f stSize: %f\n",
438            mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize);
439 #endif
440 }
441 
442 void
FillLogFont(LOGFONTW & aLogFont,gfxFloat aSize,bool aUseGDIFakeItalic)443 gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize,
444                         bool aUseGDIFakeItalic)
445 {
446     GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry());
447 
448     uint16_t weight;
449     if (fe->IsUserFont()) {
450         if (fe->IsLocalUserFont()) {
451             // for local user fonts, don't change the original weight
452             // in the entry's logfont, because that could alter the
453             // choice of actual face used (bug 724231)
454             weight = 0;
455         } else {
456             // avoid GDI synthetic bold which occurs when weight
457             // specified is >= font data weight + 200
458             weight = mNeedsBold ? 700 : 200;
459         }
460     } else {
461         weight = mNeedsBold ? 700 : fe->Weight();
462     }
463 
464     fe->FillLogFont(&aLogFont, weight, aSize,
465                     (mAntialiasOption == kAntialiasSubpixel) ? true : false);
466 
467     // If GDI synthetic italic is wanted, force the lfItalic field to true
468     if (aUseGDIFakeItalic) {
469         aLogFont.lfItalic = 1;
470     }
471 }
472 
473 uint32_t
GetGlyph(uint32_t aUnicode,uint32_t aVarSelector)474 gfxGDIFont::GetGlyph(uint32_t aUnicode, uint32_t aVarSelector)
475 {
476     // Callback used only for fonts that lack a 'cmap' table.
477 
478     // We don't support variation selector sequences or non-BMP characters
479     // in the legacy bitmap, vector or postscript fonts that might use
480     // this code path.
481     if (aUnicode > 0xffff || aVarSelector) {
482         return 0;
483     }
484 
485     if (!mGlyphIDs) {
486         mGlyphIDs = MakeUnique<nsDataHashtable<nsUint32HashKey,uint32_t>>(64);
487     }
488 
489     uint32_t gid;
490     if (mGlyphIDs->Get(aUnicode, &gid)) {
491         return gid;
492     }
493 
494     wchar_t ch = aUnicode;
495     WORD glyph;
496     DWORD ret = ScriptGetCMap(nullptr, &mScriptCache, &ch, 1, 0, &glyph);
497     if (ret != S_OK) {
498         AutoDC dc;
499         AutoSelectFont fs(dc.GetDC(), GetHFONT());
500         if (ret == E_PENDING) {
501             // Try ScriptGetCMap again now that we've set up the font.
502             ret = ScriptGetCMap(dc.GetDC(), &mScriptCache, &ch, 1, 0, &glyph);
503         }
504         if (ret != S_OK) {
505             // If ScriptGetCMap still failed, fall back to GetGlyphIndicesW
506             // (see bug 1105807).
507             ret = GetGlyphIndicesW(dc.GetDC(), &ch, 1, &glyph,
508                                    GGI_MARK_NONEXISTING_GLYPHS);
509             if (ret == GDI_ERROR || glyph == 0xFFFF) {
510                 glyph = 0;
511             }
512         }
513     }
514 
515     mGlyphIDs->Put(aUnicode, glyph);
516     return glyph;
517 }
518 
519 int32_t
GetGlyphWidth(DrawTarget & aDrawTarget,uint16_t aGID)520 gfxGDIFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
521 {
522     if (!mGlyphWidths) {
523         mGlyphWidths = MakeUnique<nsDataHashtable<nsUint32HashKey,int32_t>>(128);
524     }
525 
526     int32_t width;
527     if (mGlyphWidths->Get(aGID, &width)) {
528         return width;
529     }
530 
531     DCFromDrawTarget dc(aDrawTarget);
532     AutoSelectFont fs(dc, GetHFONT());
533 
534     int devWidth;
535     if (GetCharWidthI(dc, aGID, 1, nullptr, &devWidth)) {
536         // clamp value to range [0..0x7fff], and convert to 16.16 fixed-point
537         devWidth = std::min(std::max(0, devWidth), 0x7fff);
538         width = devWidth << 16;
539         mGlyphWidths->Put(aGID, width);
540         return width;
541     }
542 
543     return -1;
544 }
545 
546 void
AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const547 gfxGDIFont::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
548                                    FontCacheSizes* aSizes) const
549 {
550     gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
551     aSizes->mFontInstances += aMallocSizeOf(mMetrics);
552     if (mGlyphWidths) {
553         aSizes->mFontInstances +=
554             mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
555     }
556 }
557 
558 void
AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const559 gfxGDIFont::AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
560                                    FontCacheSizes* aSizes) const
561 {
562     aSizes->mFontInstances += aMallocSizeOf(this);
563     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
564 }
565