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