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 "gfxDWriteFonts.h"
7 
8 #include "mozilla/MemoryReporting.h"
9 
10 #include <algorithm>
11 #include "gfxDWriteFontList.h"
12 #include "gfxContext.h"
13 #include "gfxTextRun.h"
14 #include <dwrite.h>
15 
16 #include "harfbuzz/hb.h"
17 
18 using namespace mozilla;
19 using namespace mozilla::gfx;
20 
21 // This is also in gfxGDIFont.cpp. Would be nice to put it somewhere common,
22 // but we can't declare it in the gfxFont.h or gfxFontUtils.h headers
23 // because those are exported, and the cairo headers aren't.
24 static inline cairo_antialias_t
GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)25 GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)
26 {
27     switch (anAntialiasOption) {
28     default:
29     case gfxFont::kAntialiasDefault:
30         return CAIRO_ANTIALIAS_DEFAULT;
31     case gfxFont::kAntialiasNone:
32         return CAIRO_ANTIALIAS_NONE;
33     case gfxFont::kAntialiasGrayscale:
34         return CAIRO_ANTIALIAS_GRAY;
35     case gfxFont::kAntialiasSubpixel:
36         return CAIRO_ANTIALIAS_SUBPIXEL;
37     }
38 }
39 
40 // Code to determine whether Windows is set to use ClearType font smoothing;
41 // based on private functions in cairo-win32-font.c
42 
43 #ifndef SPI_GETFONTSMOOTHINGTYPE
44 #define SPI_GETFONTSMOOTHINGTYPE 0x200a
45 #endif
46 #ifndef FE_FONTSMOOTHINGCLEARTYPE
47 #define FE_FONTSMOOTHINGCLEARTYPE 2
48 #endif
49 
50 static bool
UsingClearType()51 UsingClearType()
52 {
53     BOOL fontSmoothing;
54     if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothing, 0) ||
55         !fontSmoothing)
56     {
57         return false;
58     }
59 
60     UINT type;
61     if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &type, 0) &&
62         type == FE_FONTSMOOTHINGCLEARTYPE)
63     {
64         return true;
65     }
66     return false;
67 }
68 
69 ////////////////////////////////////////////////////////////////////////////////
70 // gfxDWriteFont
gfxDWriteFont(gfxFontEntry * aFontEntry,const gfxFontStyle * aFontStyle,bool aNeedsBold,AntialiasOption anAAOption)71 gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
72                              const gfxFontStyle *aFontStyle,
73                              bool aNeedsBold,
74                              AntialiasOption anAAOption)
75     : gfxFont(aFontEntry, aFontStyle, anAAOption)
76     , mCairoFontFace(nullptr)
77     , mMetrics(nullptr)
78     , mSpaceGlyph(0)
79     , mNeedsOblique(false)
80     , mNeedsBold(aNeedsBold)
81     , mUseSubpixelPositions(false)
82     , mAllowManualShowGlyphs(true)
83 {
84     gfxDWriteFontEntry *fe =
85 	        static_cast<gfxDWriteFontEntry*>(aFontEntry);
86     nsresult rv;
87     DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
88     if ((GetStyle()->style != NS_FONT_STYLE_NORMAL) &&
89         fe->IsUpright() &&
90         GetStyle()->allowSyntheticStyle) {
91             // For this we always use the font_matrix for uniformity. Not the
92             // DWrite simulation.
93             mNeedsOblique = true;
94     }
95     if (aNeedsBold) {
96         sims |= DWRITE_FONT_SIMULATIONS_BOLD;
97     }
98 
99     rv = fe->CreateFontFace(getter_AddRefs(mFontFace), sims);
100 
101     if (NS_FAILED(rv)) {
102         mIsValid = false;
103         return;
104     }
105 
106     ComputeMetrics(anAAOption);
107 }
108 
~gfxDWriteFont()109 gfxDWriteFont::~gfxDWriteFont()
110 {
111     if (mCairoFontFace) {
112         cairo_font_face_destroy(mCairoFontFace);
113     }
114     if (mScaledFont) {
115         cairo_scaled_font_destroy(mScaledFont);
116     }
117     delete mMetrics;
118 }
119 
120 gfxFont*
CopyWithAntialiasOption(AntialiasOption anAAOption)121 gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
122 {
123     return new gfxDWriteFont(static_cast<gfxDWriteFontEntry*>(mFontEntry.get()),
124                              &mStyle, mNeedsBold, anAAOption);
125 }
126 
127 const gfxFont::Metrics&
GetHorizontalMetrics()128 gfxDWriteFont::GetHorizontalMetrics()
129 {
130     return *mMetrics;
131 }
132 
133 bool
GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS * aFontMetrics)134 gfxDWriteFont::GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics)
135 {
136     gfxFontStyle style(mStyle);
137     style.weight = 700;
138     bool needsBold;
139 
140     gfxFontEntry* fe =
141         gfxPlatformFontList::PlatformFontList()->
142             FindFontForFamily(NS_LITERAL_STRING("Arial"), &style, needsBold);
143     if (!fe || fe == mFontEntry) {
144         return false;
145     }
146 
147     RefPtr<gfxFont> font = fe->FindOrMakeFont(&style, needsBold);
148     gfxDWriteFont *dwFont = static_cast<gfxDWriteFont*>(font.get());
149     dwFont->mFontFace->GetMetrics(aFontMetrics);
150 
151     return true;
152 }
153 
154 void
ComputeMetrics(AntialiasOption anAAOption)155 gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption)
156 {
157     DWRITE_FONT_METRICS fontMetrics;
158     if (!(mFontEntry->Weight() == 900 &&
159           !mFontEntry->IsUserFont() &&
160           mFontEntry->Name().EqualsLiteral("Arial Black") &&
161           GetFakeMetricsForArialBlack(&fontMetrics)))
162     {
163         mFontFace->GetMetrics(&fontMetrics);
164     }
165 
166     if (mStyle.sizeAdjust >= 0.0) {
167         gfxFloat aspect = (gfxFloat)fontMetrics.xHeight /
168                    fontMetrics.designUnitsPerEm;
169         mAdjustedSize = mStyle.GetAdjustedSize(aspect);
170     } else {
171         mAdjustedSize = mStyle.size;
172     }
173 
174     // Note that GetMeasuringMode depends on mAdjustedSize
175     if ((anAAOption == gfxFont::kAntialiasDefault &&
176          UsingClearType() &&
177          GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL) ||
178         anAAOption == gfxFont::kAntialiasSubpixel)
179     {
180         mUseSubpixelPositions = true;
181         // note that this may be reset to FALSE if we determine that a bitmap
182         // strike is going to be used
183     }
184 
185     gfxDWriteFontEntry *fe =
186         static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
187     if (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize))) {
188         mAdjustedSize = NS_lround(mAdjustedSize);
189         mUseSubpixelPositions = false;
190         // if we have bitmaps, we need to tell Cairo NOT to use subpixel AA,
191         // to avoid the manual-subpixel codepath in cairo-d2d-surface.cpp
192         // which fails to render bitmap glyphs (see bug 626299).
193         // This option will be passed to the cairo_dwrite_scaled_font_t
194         // after creation.
195         mAllowManualShowGlyphs = false;
196     }
197 
198     mMetrics = new gfxFont::Metrics;
199     ::memset(mMetrics, 0, sizeof(*mMetrics));
200 
201     mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
202 
203     mMetrics->xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
204     mMetrics->capHeight = fontMetrics.capHeight * mFUnitsConvFactor;
205 
206     mMetrics->maxAscent = ceil(fontMetrics.ascent * mFUnitsConvFactor);
207     mMetrics->maxDescent = ceil(fontMetrics.descent * mFUnitsConvFactor);
208     mMetrics->maxHeight = mMetrics->maxAscent + mMetrics->maxDescent;
209 
210     mMetrics->emHeight = mAdjustedSize;
211     mMetrics->emAscent = mMetrics->emHeight *
212         mMetrics->maxAscent / mMetrics->maxHeight;
213     mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
214 
215     mMetrics->maxAdvance = mAdjustedSize;
216 
217     // try to get the true maxAdvance value from 'hhea'
218     gfxFontEntry::AutoTable hheaTable(GetFontEntry(),
219                                       TRUETYPE_TAG('h','h','e','a'));
220     if (hheaTable) {
221         uint32_t len;
222         const MetricsHeader* hhea =
223             reinterpret_cast<const MetricsHeader*>
224                 (hb_blob_get_data(hheaTable, &len));
225         if (len >= sizeof(MetricsHeader)) {
226             mMetrics->maxAdvance =
227                 uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
228         }
229     }
230 
231     mMetrics->internalLeading = std::max(mMetrics->maxHeight - mMetrics->emHeight, 0.0);
232     mMetrics->externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
233 
234     UINT32 ucs = L' ';
235     UINT16 glyph;
236     HRESULT hr = mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph);
237     if (FAILED(hr)) {
238         mMetrics->spaceWidth = 0;
239     } else {
240         mSpaceGlyph = glyph;
241         mMetrics->spaceWidth = MeasureGlyphWidth(glyph);
242     }
243 
244     // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
245     // if the table is not available or if using hinted/pixel-snapped widths
246     if (mUseSubpixelPositions) {
247         mMetrics->aveCharWidth = 0;
248         gfxFontEntry::AutoTable os2Table(GetFontEntry(),
249                                          TRUETYPE_TAG('O','S','/','2'));
250         if (os2Table) {
251             uint32_t len;
252             const OS2Table* os2 =
253                 reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
254             if (len >= 4) {
255                 // Not checking against sizeof(mozilla::OS2Table) here because older
256                 // versions of the table have different sizes; we only need the first
257                 // two 16-bit fields here.
258                 mMetrics->aveCharWidth =
259                     int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor;
260             }
261         }
262     }
263 
264     if (mMetrics->aveCharWidth < 1) {
265         ucs = L'x';
266         if (SUCCEEDED(mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph))) {
267             mMetrics->aveCharWidth = MeasureGlyphWidth(glyph);
268         }
269         if (mMetrics->aveCharWidth < 1) {
270             // Let's just assume the X is square.
271             mMetrics->aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
272         }
273     }
274 
275     ucs = L'0';
276     if (SUCCEEDED(mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph))) {
277         mMetrics->zeroOrAveCharWidth = MeasureGlyphWidth(glyph);
278     }
279     if (mMetrics->zeroOrAveCharWidth < 1) {
280         mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
281     }
282 
283     mMetrics->underlineOffset =
284         fontMetrics.underlinePosition * mFUnitsConvFactor;
285     mMetrics->underlineSize =
286         fontMetrics.underlineThickness * mFUnitsConvFactor;
287     mMetrics->strikeoutOffset =
288         fontMetrics.strikethroughPosition * mFUnitsConvFactor;
289     mMetrics->strikeoutSize =
290         fontMetrics.strikethroughThickness * mFUnitsConvFactor;
291 
292     SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
293 
294 #if 0
295     printf("Font: %p (%s) size: %f\n", this,
296            NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
297     printf("    emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
298     printf("    maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
299     printf("    internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
300     printf("    spaceWidth: %f aveCharWidth: %f zeroOrAve: %f\n",
301            mMetrics->spaceWidth, mMetrics->aveCharWidth, mMetrics->zeroOrAveCharWidth);
302     printf("    xHeight: %f capHeight: %f\n", mMetrics->xHeight, mMetrics->capHeight);
303     printf("    uOff: %f uSize: %f stOff: %f stSize: %f\n",
304            mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize);
305 #endif
306 }
307 
308 using namespace mozilla; // for AutoSwap_* types
309 
310 struct EBLCHeader {
311     AutoSwap_PRUint32 version;
312     AutoSwap_PRUint32 numSizes;
313 };
314 
315 struct SbitLineMetrics {
316     int8_t  ascender;
317     int8_t  descender;
318     uint8_t widthMax;
319     int8_t  caretSlopeNumerator;
320     int8_t  caretSlopeDenominator;
321     int8_t  caretOffset;
322     int8_t  minOriginSB;
323     int8_t  minAdvanceSB;
324     int8_t  maxBeforeBL;
325     int8_t  minAfterBL;
326     int8_t  pad1;
327     int8_t  pad2;
328 };
329 
330 struct BitmapSizeTable {
331     AutoSwap_PRUint32 indexSubTableArrayOffset;
332     AutoSwap_PRUint32 indexTablesSize;
333     AutoSwap_PRUint32 numberOfIndexSubTables;
334     AutoSwap_PRUint32 colorRef;
335     SbitLineMetrics   hori;
336     SbitLineMetrics   vert;
337     AutoSwap_PRUint16 startGlyphIndex;
338     AutoSwap_PRUint16 endGlyphIndex;
339     uint8_t           ppemX;
340     uint8_t           ppemY;
341     uint8_t           bitDepth;
342     uint8_t           flags;
343 };
344 
345 typedef EBLCHeader EBSCHeader;
346 
347 struct BitmapScaleTable {
348     SbitLineMetrics   hori;
349     SbitLineMetrics   vert;
350     uint8_t           ppemX;
351     uint8_t           ppemY;
352     uint8_t           substitutePpemX;
353     uint8_t           substitutePpemY;
354 };
355 
356 bool
HasBitmapStrikeForSize(uint32_t aSize)357 gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize)
358 {
359     uint8_t *tableData;
360     uint32_t len;
361     void *tableContext;
362     BOOL exists;
363     HRESULT hr =
364         mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'),
365                                    (const void**)&tableData, &len,
366                                    &tableContext, &exists);
367     if (FAILED(hr)) {
368         return false;
369     }
370 
371     bool hasStrike = false;
372     // not really a loop, but this lets us use 'break' to skip out of the block
373     // as soon as we know the answer, and skips it altogether if the table is
374     // not present
375     while (exists) {
376         if (len < sizeof(EBLCHeader)) {
377             break;
378         }
379         const EBLCHeader *hdr = reinterpret_cast<const EBLCHeader*>(tableData);
380         if (hdr->version != 0x00020000) {
381             break;
382         }
383         uint32_t numSizes = hdr->numSizes;
384         if (numSizes > 0xffff) { // sanity-check, prevent overflow below
385             break;
386         }
387         if (len < sizeof(EBLCHeader) + numSizes * sizeof(BitmapSizeTable)) {
388             break;
389         }
390         const BitmapSizeTable *sizeTable =
391             reinterpret_cast<const BitmapSizeTable*>(hdr + 1);
392         for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
393             if (sizeTable->ppemX == aSize && sizeTable->ppemY == aSize) {
394                 // we ignore a strike that contains fewer than 4 glyphs,
395                 // as that probably indicates a font such as Courier New
396                 // that provides bitmaps ONLY for the "shading" characters
397                 // U+2591..2593
398                 hasStrike = (uint16_t(sizeTable->endGlyphIndex) >=
399                              uint16_t(sizeTable->startGlyphIndex) + 3);
400                 break;
401             }
402         }
403         // if we reach here, we didn't find a strike; unconditionally break
404         // out of the while-loop block
405         break;
406     }
407     mFontFace->ReleaseFontTable(tableContext);
408 
409     if (hasStrike) {
410         return true;
411     }
412 
413     // if we didn't find a real strike, check if the font calls for scaling
414     // another bitmap to this size
415     hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'),
416                                     (const void**)&tableData, &len,
417                                     &tableContext, &exists);
418     if (FAILED(hr)) {
419         return false;
420     }
421 
422     while (exists) {
423         if (len < sizeof(EBSCHeader)) {
424             break;
425         }
426         const EBSCHeader *hdr = reinterpret_cast<const EBSCHeader*>(tableData);
427         if (hdr->version != 0x00020000) {
428             break;
429         }
430         uint32_t numSizes = hdr->numSizes;
431         if (numSizes > 0xffff) {
432             break;
433         }
434         if (len < sizeof(EBSCHeader) + numSizes * sizeof(BitmapScaleTable)) {
435             break;
436         }
437         const BitmapScaleTable *scaleTable =
438             reinterpret_cast<const BitmapScaleTable*>(hdr + 1);
439         for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
440             if (scaleTable->ppemX == aSize && scaleTable->ppemY == aSize) {
441                 hasStrike = true;
442                 break;
443             }
444         }
445         break;
446     }
447     mFontFace->ReleaseFontTable(tableContext);
448 
449     return hasStrike;
450 }
451 
452 uint32_t
GetSpaceGlyph()453 gfxDWriteFont::GetSpaceGlyph()
454 {
455     return mSpaceGlyph;
456 }
457 
458 bool
SetupCairoFont(DrawTarget * aDrawTarget)459 gfxDWriteFont::SetupCairoFont(DrawTarget* aDrawTarget)
460 {
461     cairo_scaled_font_t *scaledFont = GetCairoScaledFont();
462     if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
463         // Don't cairo_set_scaled_font as that would propagate the error to
464         // the cairo_t, precluding any further drawing.
465         return false;
466     }
467     cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), scaledFont);
468     return true;
469 }
470 
471 bool
IsValid() const472 gfxDWriteFont::IsValid() const
473 {
474     return mFontFace != nullptr;
475 }
476 
477 IDWriteFontFace*
GetFontFace()478 gfxDWriteFont::GetFontFace()
479 {
480     return  mFontFace.get();
481 }
482 
483 cairo_font_face_t *
CairoFontFace()484 gfxDWriteFont::CairoFontFace()
485 {
486     if (!mCairoFontFace) {
487 #ifdef CAIRO_HAS_DWRITE_FONT
488         mCairoFontFace =
489             cairo_dwrite_font_face_create_for_dwrite_fontface(
490             ((gfxDWriteFontEntry*)mFontEntry.get())->mFont, mFontFace);
491 #endif
492     }
493     return mCairoFontFace;
494 }
495 
496 
497 cairo_scaled_font_t *
GetCairoScaledFont()498 gfxDWriteFont::GetCairoScaledFont()
499 {
500     if (!mScaledFont) {
501         cairo_matrix_t sizeMatrix;
502         cairo_matrix_t identityMatrix;
503 
504         cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
505         cairo_matrix_init_identity(&identityMatrix);
506 
507         cairo_font_options_t *fontOptions = cairo_font_options_create();
508         if (mNeedsOblique) {
509             double skewfactor = OBLIQUE_SKEW_FACTOR;
510 
511             cairo_matrix_t style;
512             cairo_matrix_init(&style,
513                               1,                //xx
514                               0,                //yx
515                               -1 * skewfactor,  //xy
516                               1,                //yy
517                               0,                //x0
518                               0);               //y0
519             cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
520         }
521 
522         if (mAntialiasOption != kAntialiasDefault) {
523             cairo_font_options_set_antialias(fontOptions,
524                 GetCairoAntialiasOption(mAntialiasOption));
525         }
526 
527         mScaledFont = cairo_scaled_font_create(CairoFontFace(),
528                                                     &sizeMatrix,
529                                                     &identityMatrix,
530                                                     fontOptions);
531         cairo_font_options_destroy(fontOptions);
532 
533         cairo_dwrite_scaled_font_allow_manual_show_glyphs(mScaledFont,
534                                                           mAllowManualShowGlyphs);
535 
536         cairo_dwrite_scaled_font_set_force_GDI_classic(mScaledFont,
537                                                        GetForceGDIClassic());
538     }
539 
540     NS_ASSERTION(mAdjustedSize == 0.0 ||
541                  cairo_scaled_font_status(mScaledFont)
542                    == CAIRO_STATUS_SUCCESS,
543                  "Failed to make scaled font");
544 
545     return mScaledFont;
546 }
547 
548 gfxFont::RunMetrics
Measure(const gfxTextRun * aTextRun,uint32_t aStart,uint32_t aEnd,BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,Spacing * aSpacing,uint16_t aOrientation)549 gfxDWriteFont::Measure(const gfxTextRun* aTextRun,
550                        uint32_t aStart, uint32_t aEnd,
551                        BoundingBoxType aBoundingBoxType,
552                        DrawTarget* aRefDrawTarget,
553                        Spacing* aSpacing,
554                        uint16_t aOrientation)
555 {
556     gfxFont::RunMetrics metrics =
557         gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
558                          aRefDrawTarget, aSpacing, aOrientation);
559 
560     // if aBoundingBoxType is LOOSE_INK_EXTENTS
561     // and the underlying cairo font may be antialiased,
562     // we can't trust Windows to have considered all the pixels
563     // so we need to add "padding" to the bounds.
564     // (see bugs 475968, 439831, compare also bug 445087)
565     if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
566         mAntialiasOption != kAntialiasNone &&
567         GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC &&
568         metrics.mBoundingBox.width > 0) {
569         metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
570         metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 3;
571     }
572 
573     return metrics;
574 }
575 
576 bool
ProvidesGlyphWidths() const577 gfxDWriteFont::ProvidesGlyphWidths() const
578 {
579     return !mUseSubpixelPositions ||
580            (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
581 }
582 
583 int32_t
GetGlyphWidth(DrawTarget & aDrawTarget,uint16_t aGID)584 gfxDWriteFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
585 {
586     if (!mGlyphWidths) {
587         mGlyphWidths = MakeUnique<nsDataHashtable<nsUint32HashKey,int32_t>>(128);
588     }
589 
590     int32_t width = -1;
591     if (mGlyphWidths->Get(aGID, &width)) {
592         return width;
593     }
594 
595     width = NS_lround(MeasureGlyphWidth(aGID) * 65536.0);
596     mGlyphWidths->Put(aGID, width);
597     return width;
598 }
599 
600 already_AddRefed<GlyphRenderingOptions>
GetGlyphRenderingOptions(const TextRunDrawParams * aRunParams)601 gfxDWriteFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
602 {
603   if (UsingClearType()) {
604     return Factory::CreateDWriteGlyphRenderingOptions(
605       gfxWindowsPlatform::GetPlatform()->GetRenderingParams(GetForceGDIClassic() ?
606         gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC : gfxWindowsPlatform::TEXT_RENDERING_NORMAL));
607   } else {
608     return Factory::CreateDWriteGlyphRenderingOptions(gfxWindowsPlatform::GetPlatform()->
609       GetRenderingParams(gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE));
610   }
611 }
612 
613 bool
GetForceGDIClassic()614 gfxDWriteFont::GetForceGDIClassic()
615 {
616     return static_cast<gfxDWriteFontEntry*>(mFontEntry.get())->GetForceGDIClassic() &&
617          cairo_dwrite_get_cleartype_rendering_mode() < 0 &&
618          GetAdjustedSize() <=
619             gfxDWriteFontList::PlatformFontList()->GetForceGDIClassicMaxFontSize();
620 }
621 
622 DWRITE_MEASURING_MODE
GetMeasuringMode()623 gfxDWriteFont::GetMeasuringMode()
624 {
625     return GetForceGDIClassic()
626         ? DWRITE_MEASURING_MODE_GDI_CLASSIC
627         : gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode();
628 }
629 
630 gfxFloat
MeasureGlyphWidth(uint16_t aGlyph)631 gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph)
632 {
633     DWRITE_GLYPH_METRICS metrics;
634     HRESULT hr;
635     if (mUseSubpixelPositions) {
636         hr = mFontFace->GetDesignGlyphMetrics(&aGlyph, 1, &metrics, FALSE);
637         if (SUCCEEDED(hr)) {
638             return metrics.advanceWidth * mFUnitsConvFactor;
639         }
640     } else {
641         hr = mFontFace->GetGdiCompatibleGlyphMetrics(
642                   FLOAT(mAdjustedSize), 1.0f, nullptr,
643                   GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL,
644                   &aGlyph, 1, &metrics, FALSE);
645         if (SUCCEEDED(hr)) {
646             return NS_lround(metrics.advanceWidth * mFUnitsConvFactor);
647         }
648     }
649     return 0;
650 }
651 
652 void
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const653 gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
654                                       FontCacheSizes* aSizes) const
655 {
656     gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
657     aSizes->mFontInstances += aMallocSizeOf(mMetrics);
658     if (mGlyphWidths) {
659         aSizes->mFontInstances +=
660             mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
661     }
662 }
663 
664 void
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const665 gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
666                                       FontCacheSizes* aSizes) const
667 {
668     aSizes->mFontInstances += aMallocSizeOf(this);
669     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
670 }
671 
672 already_AddRefed<ScaledFont>
GetScaledFont(mozilla::gfx::DrawTarget * aTarget)673 gfxDWriteFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
674 {
675   bool wantCairo = aTarget->GetBackendType() == BackendType::CAIRO;
676   if (mAzureScaledFont && mAzureScaledFontIsCairo == wantCairo) {
677     RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
678     return scaledFont.forget();
679   }
680 
681   NativeFont nativeFont;
682   nativeFont.mType = NativeFontType::DWRITE_FONT_FACE;
683   nativeFont.mFont = GetFontFace();
684 
685   if (wantCairo) {
686     mAzureScaledFont = Factory::CreateScaledFontWithCairo(nativeFont,
687                                                         GetAdjustedSize(),
688                                                         GetCairoScaledFont());
689   } else if (aTarget->GetBackendType() == BackendType::SKIA) {
690     gfxDWriteFontEntry *fe =
691         static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
692     bool useEmbeddedBitmap = (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize)));
693 
694     const gfxFontStyle* fontStyle = GetStyle();
695     mAzureScaledFont =
696             Factory::CreateScaledFontForDWriteFont(mFontFace, fontStyle,
697                                                    GetAdjustedSize(),
698                                                    useEmbeddedBitmap,
699                                                    GetForceGDIClassic());
700   } else {
701     mAzureScaledFont = Factory::CreateScaledFontForNativeFont(nativeFont,
702                                                             GetAdjustedSize());
703   }
704 
705   mAzureScaledFontIsCairo = wantCairo;
706 
707   RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
708   return scaledFont.forget();
709 }
710