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/. */
6 #include "gfxDWriteFonts.h"
9 #include "gfxDWriteFontList.h"
10 #include "gfxContext.h"
11 #include "gfxHarfBuzzShaper.h"
12 #include "gfxTextRun.h"
13 #include "mozilla/gfx/2D.h"
14 #include "mozilla/gfx/DWriteSettings.h"
15 #include "mozilla/gfx/Logging.h"
16 #include "mozilla/gfx/gfxVars.h"
17 #include "mozilla/Preferences.h"
19 #include "harfbuzz/hb.h"
20 #include "mozilla/FontPropertyTypes.h"
22 using namespace mozilla
;
23 using namespace mozilla::gfx
;
25 // Code to determine whether Windows is set to use ClearType font smoothing;
26 // based on private functions in cairo-win32-font.c
28 #ifndef SPI_GETFONTSMOOTHINGTYPE
29 # define SPI_GETFONTSMOOTHINGTYPE 0x200a
31 #ifndef FE_FONTSMOOTHINGCLEARTYPE
32 # define FE_FONTSMOOTHINGCLEARTYPE 2
35 // Cleartype can be dynamically enabled/disabled, so we have to allow for
36 // dynamically updating it.
37 static BYTE
GetSystemTextQuality() {
41 if (!SystemParametersInfo(SPI_GETFONTSMOOTHING
, 0, &font_smoothing
, 0)) {
42 return DEFAULT_QUALITY
;
46 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE
, 0, &smoothing_type
,
48 return DEFAULT_QUALITY
;
51 if (smoothing_type
== FE_FONTSMOOTHINGCLEARTYPE
) {
52 return CLEARTYPE_QUALITY
;
55 return ANTIALIASED_QUALITY
;
58 return DEFAULT_QUALITY
;
61 #ifndef SPI_GETFONTSMOOTHINGCONTRAST
62 # define SPI_GETFONTSMOOTHINGCONTRAST 0x200c
65 // "Retrieves a contrast value that is used in ClearType smoothing. Valid
66 // contrast values are from 1000 to 2200. The default value is 1400."
67 static FLOAT
GetSystemGDIGamma() {
69 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST
, 0, &value
, 0) ||
70 value
< 1000 || value
> 2200) {
73 return value
/ 1000.0f
;
76 ////////////////////////////////////////////////////////////////////////////////
78 gfxDWriteFont::gfxDWriteFont(const RefPtr
<UnscaledFontDWrite
>& aUnscaledFont
,
79 gfxFontEntry
* aFontEntry
,
80 const gfxFontStyle
* aFontStyle
,
81 RefPtr
<IDWriteFontFace
> aFontFace
,
82 AntialiasOption anAAOption
)
83 : gfxFont(aUnscaledFont
, aFontEntry
, aFontStyle
, anAAOption
),
84 mFontFace(aFontFace
? aFontFace
: aUnscaledFont
->GetFontFace()),
85 mUseSubpixelPositions(false),
86 mAllowManualShowGlyphs(true),
87 mAzureScaledFontUsedClearType(false) {
88 // If the IDWriteFontFace1 interface is available, we can use that for
89 // faster glyph width retrieval.
90 mFontFace
->QueryInterface(__uuidof(IDWriteFontFace1
),
91 (void**)getter_AddRefs(mFontFace1
));
92 // If a fake-bold effect is needed, determine whether we're using DWrite's
93 // "simulation" or applying our multi-strike "synthetic bold".
94 if (aFontStyle
->NeedsSyntheticBold(aFontEntry
)) {
95 switch (StaticPrefs::gfx_font_rendering_directwrite_bold_simulation()) {
96 case 0: // never use the DWrite simulation
97 mApplySyntheticBold
= true;
99 case 1: // use DWrite simulation for installed fonts but not webfonts
100 mApplySyntheticBold
= aFontEntry
->mIsDataUserFont
;
102 default: // always use DWrite bold simulation
103 // the flag is initialized to false in gfxFont
107 ComputeMetrics(anAAOption
);
110 gfxDWriteFont::~gfxDWriteFont() {
111 if (auto* scaledFont
= mAzureScaledFontGDI
.exchange(nullptr)) {
112 scaledFont
->Release();
117 bool gfxDWriteFont::InitDWriteSupport() {
118 if (!Factory::EnsureDWriteFactory()) {
122 if (XRE_IsParentProcess()) {
123 UpdateSystemTextVars();
125 // UpdateClearTypeVars doesn't update the vars in non parent processes, but
126 // it does set sForceGDIClassicEnabled so we still need to call it.
127 UpdateClearTypeVars();
129 DWriteSettings::Initialize();
135 void gfxDWriteFont::UpdateSystemTextVars() {
136 MOZ_ASSERT(XRE_IsParentProcess());
138 BYTE newQuality
= GetSystemTextQuality();
139 if (gfxVars::SystemTextQuality() != newQuality
) {
140 gfxVars::SetSystemTextQuality(newQuality
);
143 FLOAT newGDIGamma
= GetSystemGDIGamma();
144 if (gfxVars::SystemGDIGamma() != newGDIGamma
) {
145 gfxVars::SetSystemGDIGamma(newGDIGamma
);
148 UpdateClearTypeVars();
151 void gfxDWriteFont::SystemTextQualityChanged() {
152 // If ClearType status has changed, update our value,
153 Factory::SetSystemTextQuality(gfxVars::SystemTextQuality());
154 // flush cached stuff that depended on the old setting, and force
155 // reflow everywhere to ensure we are using correct glyph metrics.
156 gfxPlatform::FlushFontAndWordCaches();
157 gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::No
);
160 mozilla::Atomic
<bool> gfxDWriteFont::sForceGDIClassicEnabled
{true};
163 void gfxDWriteFont::UpdateClearTypeVars() {
164 // We don't force GDI classic if the cleartype rendering mode pref is set to
166 int32_t renderingModePref
=
167 Preferences::GetInt(GFX_CLEARTYPE_PARAMS_MODE
, -1);
168 if (renderingModePref
< 0 || renderingModePref
> 5) {
169 renderingModePref
= -1;
171 sForceGDIClassicEnabled
= (renderingModePref
== -1);
173 if (!XRE_IsParentProcess()) {
177 if (!Factory::GetDWriteFactory()) {
181 // First set sensible hard coded defaults.
182 float clearTypeLevel
= 1.0f
;
183 float enhancedContrast
= 1.0f
;
185 int pixelGeometry
= DWRITE_PIXEL_GEOMETRY_RGB
;
186 int renderingMode
= DWRITE_RENDERING_MODE_DEFAULT
;
188 // Override these from DWrite function if available.
189 RefPtr
<IDWriteRenderingParams
> defaultRenderingParams
;
190 HRESULT hr
= Factory::GetDWriteFactory()->CreateRenderingParams(
191 getter_AddRefs(defaultRenderingParams
));
192 if (SUCCEEDED(hr
) && defaultRenderingParams
) {
193 clearTypeLevel
= defaultRenderingParams
->GetClearTypeLevel();
195 // For enhanced contrast, we only use the default if the user has set it
196 // in the registry (by using the ClearType Tuner).
197 // XXXbobowen it seems slightly odd that we do this and only for enhanced
198 // contrast, but this reproduces previous functionality from
199 // gfxWindowsPlatform::SetupClearTypeParams.
201 LONG res
= RegOpenKeyExW(DISPLAY1_REGISTRY_KEY
, 0, KEY_READ
, &hKey
);
202 if (res
== ERROR_SUCCESS
) {
203 res
= RegQueryValueExW(hKey
, ENHANCED_CONTRAST_VALUE_NAME
, nullptr,
204 nullptr, nullptr, nullptr);
205 if (res
== ERROR_SUCCESS
) {
206 enhancedContrast
= defaultRenderingParams
->GetEnhancedContrast();
211 gamma
= defaultRenderingParams
->GetGamma();
212 pixelGeometry
= defaultRenderingParams
->GetPixelGeometry();
213 renderingMode
= defaultRenderingParams
->GetRenderingMode();
215 gfxWarning() << "Failed to create default rendering params";
218 // Finally override from prefs if valid values are set. If ClearType is
219 // turned off we just use the default params, this reproduces the previous
220 // functionality that was spread across gfxDWriteFont::GetScaledFont and
221 // gfxWindowsPlatform::SetupClearTypeParams, but it seems odd because the
222 // default params will still be the ClearType ones although we won't use the
223 // anti-alias for ClearType because of GetSystemDefaultAAMode.
224 if (gfxVars::SystemTextQuality() == CLEARTYPE_QUALITY
) {
225 int32_t prefInt
= Preferences::GetInt(GFX_CLEARTYPE_PARAMS_LEVEL
, -1);
226 if (prefInt
>= 0 && prefInt
<= 100) {
227 clearTypeLevel
= float(prefInt
/ 100.0);
230 prefInt
= Preferences::GetInt(GFX_CLEARTYPE_PARAMS_CONTRAST
, -1);
231 if (prefInt
>= 0 && prefInt
<= 1000) {
232 enhancedContrast
= float(prefInt
/ 100.0);
235 prefInt
= Preferences::GetInt(GFX_CLEARTYPE_PARAMS_GAMMA
, -1);
236 if (prefInt
>= 1000 && prefInt
<= 2200) {
237 gamma
= float(prefInt
/ 1000.0);
240 prefInt
= Preferences::GetInt(GFX_CLEARTYPE_PARAMS_STRUCTURE
, -1);
241 if (prefInt
>= 0 && prefInt
<= 2) {
242 pixelGeometry
= prefInt
;
245 // renderingModePref is retrieved and validated above.
246 if (renderingModePref
!= -1) {
247 renderingMode
= renderingModePref
;
251 if (gfxVars::SystemTextClearTypeLevel() != clearTypeLevel
) {
252 gfxVars::SetSystemTextClearTypeLevel(clearTypeLevel
);
255 if (gfxVars::SystemTextEnhancedContrast() != enhancedContrast
) {
256 gfxVars::SetSystemTextEnhancedContrast(enhancedContrast
);
259 if (gfxVars::SystemTextGamma() != gamma
) {
260 gfxVars::SetSystemTextGamma(gamma
);
263 if (gfxVars::SystemTextPixelGeometry() != pixelGeometry
) {
264 gfxVars::SetSystemTextPixelGeometry(pixelGeometry
);
267 if (gfxVars::SystemTextRenderingMode() != renderingMode
) {
268 gfxVars::SetSystemTextRenderingMode(renderingMode
);
271 // Set cairo dwrite params in the parent process where it might still be
272 // needed for printing. We use the validated pref int directly for rendering
273 // mode, because a negative (i.e. not set) rendering mode is also used for
274 // deciding on forcing GDI in cairo.
275 cairo_dwrite_set_cleartype_params(gamma
, enhancedContrast
, clearTypeLevel
,
276 pixelGeometry
, renderingModePref
);
279 gfxFont
* gfxDWriteFont::CopyWithAntialiasOption(
280 AntialiasOption anAAOption
) const {
281 auto entry
= static_cast<gfxDWriteFontEntry
*>(mFontEntry
.get());
282 RefPtr
<UnscaledFontDWrite
> unscaledFont
=
283 static_cast<UnscaledFontDWrite
*>(mUnscaledFont
.get());
284 return new gfxDWriteFont(unscaledFont
, entry
, &mStyle
, mFontFace
, anAAOption
);
287 bool gfxDWriteFont::GetFakeMetricsForArialBlack(
288 DWRITE_FONT_METRICS
* aFontMetrics
) {
289 gfxFontStyle
style(mStyle
);
290 style
.weight
= FontWeight::FromInt(700);
292 gfxFontEntry
* fe
= gfxPlatformFontList::PlatformFontList()->FindFontForFamily(
293 nullptr, "Arial"_ns
, &style
);
294 if (!fe
|| fe
== mFontEntry
) {
298 RefPtr
<gfxFont
> font
= fe
->FindOrMakeFont(&style
);
299 gfxDWriteFont
* dwFont
= static_cast<gfxDWriteFont
*>(font
.get());
300 dwFont
->mFontFace
->GetMetrics(aFontMetrics
);
305 void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption
) {
306 ::memset(&mMetrics
, 0, sizeof(mMetrics
));
308 DWRITE_FONT_METRICS fontMetrics
;
309 if (!(mFontEntry
->Weight().Min() == FontWeight::FromInt(900) &&
310 mFontEntry
->Weight().Max() == FontWeight::FromInt(900) &&
311 !mFontEntry
->IsUserFont() &&
312 mFontEntry
->Name().EqualsLiteral("Arial Black") &&
313 GetFakeMetricsForArialBlack(&fontMetrics
))) {
314 mFontFace
->GetMetrics(&fontMetrics
);
317 if (GetAdjustedSize() > 0.0 && mStyle
.sizeAdjust
>= 0.0 &&
318 FontSizeAdjust::Tag(mStyle
.sizeAdjustBasis
) !=
319 FontSizeAdjust::Tag::None
) {
320 // For accurate measurement during the font-size-adjust computations;
321 // these may be reset later according to the adjusted size.
322 mUseSubpixelPositions
= true;
323 mFUnitsConvFactor
= float(mAdjustedSize
/ fontMetrics
.designUnitsPerEm
);
325 switch (FontSizeAdjust::Tag(mStyle
.sizeAdjustBasis
)) {
327 MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
330 case FontSizeAdjust::Tag::ExHeight
:
331 aspect
= (gfxFloat
)fontMetrics
.xHeight
/ fontMetrics
.designUnitsPerEm
;
333 case FontSizeAdjust::Tag::CapHeight
:
334 aspect
= (gfxFloat
)fontMetrics
.capHeight
/ fontMetrics
.designUnitsPerEm
;
336 case FontSizeAdjust::Tag::ChWidth
: {
337 gfxFloat advance
= GetCharAdvance('0');
338 aspect
= advance
> 0.0 ? advance
/ mAdjustedSize
: 0.5;
341 case FontSizeAdjust::Tag::IcWidth
:
342 case FontSizeAdjust::Tag::IcHeight
: {
343 bool vertical
= FontSizeAdjust::Tag(mStyle
.sizeAdjustBasis
) ==
344 FontSizeAdjust::Tag::IcHeight
;
345 gfxFloat advance
= GetCharAdvance(kWaterIdeograph
, vertical
);
346 aspect
= advance
> 0.0 ? advance
/ mAdjustedSize
: 1.0;
351 // If we created a shaper above (to measure glyphs), discard it so we
352 // get a new one for the adjusted scaling.
353 delete mHarfBuzzShaper
.exchange(nullptr);
354 mAdjustedSize
= mStyle
.GetAdjustedSize(aspect
);
358 // Update now that we've adjusted the size if necessary.
359 mFUnitsConvFactor
= float(mAdjustedSize
/ fontMetrics
.designUnitsPerEm
);
361 // Note that GetMeasuringMode depends on mAdjustedSize
362 if ((anAAOption
== gfxFont::kAntialiasDefault
&& UsingClearType() &&
363 GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL
) ||
364 anAAOption
== gfxFont::kAntialiasSubpixel
) {
365 mUseSubpixelPositions
= true;
366 // note that this may be reset to FALSE if we determine that a bitmap
367 // strike is going to be used
369 mUseSubpixelPositions
= false;
372 gfxDWriteFontEntry
* fe
= static_cast<gfxDWriteFontEntry
*>(mFontEntry
.get());
373 if (fe
->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize
))) {
374 mAdjustedSize
= NS_lround(mAdjustedSize
);
375 mUseSubpixelPositions
= false;
376 // if we have bitmaps, we need to tell Cairo NOT to use subpixel AA,
377 // to avoid the manual-subpixel codepath in cairo-d2d-surface.cpp
378 // which fails to render bitmap glyphs (see bug 626299).
379 // This option will be passed to the cairo_dwrite_scaled_font_t
381 mAllowManualShowGlyphs
= false;
384 mMetrics
.xHeight
= fontMetrics
.xHeight
* mFUnitsConvFactor
;
385 mMetrics
.capHeight
= fontMetrics
.capHeight
* mFUnitsConvFactor
;
387 mMetrics
.maxAscent
= round(fontMetrics
.ascent
* mFUnitsConvFactor
);
388 mMetrics
.maxDescent
= round(fontMetrics
.descent
* mFUnitsConvFactor
);
389 mMetrics
.maxHeight
= mMetrics
.maxAscent
+ mMetrics
.maxDescent
;
391 mMetrics
.emHeight
= mAdjustedSize
;
393 mMetrics
.emHeight
* mMetrics
.maxAscent
/ mMetrics
.maxHeight
;
394 mMetrics
.emDescent
= mMetrics
.emHeight
- mMetrics
.emAscent
;
396 mMetrics
.maxAdvance
= mAdjustedSize
;
398 // try to get the true maxAdvance value from 'hhea'
399 gfxFontEntry::AutoTable
hheaTable(GetFontEntry(),
400 TRUETYPE_TAG('h', 'h', 'e', 'a'));
403 const MetricsHeader
* hhea
= reinterpret_cast<const MetricsHeader
*>(
404 hb_blob_get_data(hheaTable
, &len
));
405 if (len
>= sizeof(MetricsHeader
)) {
406 mMetrics
.maxAdvance
= uint16_t(hhea
->advanceWidthMax
) * mFUnitsConvFactor
;
410 mMetrics
.internalLeading
=
411 std::max(mMetrics
.maxHeight
- mMetrics
.emHeight
, 0.0);
412 mMetrics
.externalLeading
= ceil(fontMetrics
.lineGap
* mFUnitsConvFactor
);
416 if (SUCCEEDED(mFontFace
->GetGlyphIndices(&ucs
, 1, &glyph
)) && glyph
!= 0) {
418 mMetrics
.spaceWidth
= MeasureGlyphWidth(glyph
);
420 mMetrics
.spaceWidth
= 0;
423 // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
424 // if the table is not available or if using hinted/pixel-snapped widths
425 if (mUseSubpixelPositions
) {
426 mMetrics
.aveCharWidth
= 0;
427 gfxFontEntry::AutoTable
os2Table(GetFontEntry(),
428 TRUETYPE_TAG('O', 'S', '/', '2'));
431 const OS2Table
* os2
=
432 reinterpret_cast<const OS2Table
*>(hb_blob_get_data(os2Table
, &len
));
434 // Not checking against sizeof(mozilla::OS2Table) here because older
435 // versions of the table have different sizes; we only need the first
436 // two 16-bit fields here.
437 mMetrics
.aveCharWidth
= int16_t(os2
->xAvgCharWidth
) * mFUnitsConvFactor
;
442 if (mMetrics
.aveCharWidth
< 1) {
443 mMetrics
.aveCharWidth
= GetCharAdvance('x');
444 if (mMetrics
.aveCharWidth
< 1) {
445 // Let's just assume the X is square.
446 mMetrics
.aveCharWidth
= fontMetrics
.xHeight
* mFUnitsConvFactor
;
450 mMetrics
.zeroWidth
= GetCharAdvance('0');
452 mMetrics
.ideographicWidth
= GetCharAdvance(kWaterIdeograph
);
454 mMetrics
.underlineOffset
= fontMetrics
.underlinePosition
* mFUnitsConvFactor
;
455 mMetrics
.underlineSize
= fontMetrics
.underlineThickness
* mFUnitsConvFactor
;
456 mMetrics
.strikeoutOffset
=
457 fontMetrics
.strikethroughPosition
* mFUnitsConvFactor
;
458 mMetrics
.strikeoutSize
=
459 fontMetrics
.strikethroughThickness
* mFUnitsConvFactor
;
461 SanitizeMetrics(&mMetrics
, GetFontEntry()->mIsBadUnderlineFont
);
463 if (ApplySyntheticBold()) {
464 auto delta
= GetSyntheticBoldOffset();
465 mMetrics
.spaceWidth
+= delta
;
466 mMetrics
.aveCharWidth
+= delta
;
467 mMetrics
.maxAdvance
+= delta
;
468 if (mMetrics
.zeroWidth
> 0) {
469 mMetrics
.zeroWidth
+= delta
;
471 if (mMetrics
.ideographicWidth
> 0) {
472 mMetrics
.ideographicWidth
+= delta
;
477 printf("Font: %p (%s) size: %f\n", this,
478 NS_ConvertUTF16toUTF8(GetName()).get(), mStyle
.size
);
479 printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics
.emHeight
, mMetrics
.emAscent
, mMetrics
.emDescent
);
480 printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics
.maxAscent
, mMetrics
.maxDescent
, mMetrics
.maxAdvance
);
481 printf(" internalLeading: %f externalLeading: %f\n", mMetrics
.internalLeading
, mMetrics
.externalLeading
);
482 printf(" spaceWidth: %f aveCharWidth: %f zeroWidth: %f\n",
483 mMetrics
.spaceWidth
, mMetrics
.aveCharWidth
, mMetrics
.zeroWidth
);
484 printf(" xHeight: %f capHeight: %f\n", mMetrics
.xHeight
, mMetrics
.capHeight
);
485 printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
486 mMetrics
.underlineOffset
, mMetrics
.underlineSize
, mMetrics
.strikeoutOffset
, mMetrics
.strikeoutSize
);
490 using namespace mozilla
; // for AutoSwap_* types
493 AutoSwap_PRUint32 version
;
494 AutoSwap_PRUint32 numSizes
;
497 struct SbitLineMetrics
{
501 int8_t caretSlopeNumerator
;
502 int8_t caretSlopeDenominator
;
512 struct BitmapSizeTable
{
513 AutoSwap_PRUint32 indexSubTableArrayOffset
;
514 AutoSwap_PRUint32 indexTablesSize
;
515 AutoSwap_PRUint32 numberOfIndexSubTables
;
516 AutoSwap_PRUint32 colorRef
;
517 SbitLineMetrics hori
;
518 SbitLineMetrics vert
;
519 AutoSwap_PRUint16 startGlyphIndex
;
520 AutoSwap_PRUint16 endGlyphIndex
;
527 typedef EBLCHeader EBSCHeader
;
529 struct BitmapScaleTable
{
530 SbitLineMetrics hori
;
531 SbitLineMetrics vert
;
534 uint8_t substitutePpemX
;
535 uint8_t substitutePpemY
;
538 bool gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize
) {
543 HRESULT hr
= mFontFace
->TryGetFontTable(
544 DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'), (const void**)&tableData
,
545 &len
, &tableContext
, &exists
);
550 bool hasStrike
= false;
551 // not really a loop, but this lets us use 'break' to skip out of the block
552 // as soon as we know the answer, and skips it altogether if the table is
555 if (len
< sizeof(EBLCHeader
)) {
558 const EBLCHeader
* hdr
= reinterpret_cast<const EBLCHeader
*>(tableData
);
559 if (hdr
->version
!= 0x00020000) {
562 uint32_t numSizes
= hdr
->numSizes
;
563 if (numSizes
> 0xffff) { // sanity-check, prevent overflow below
566 if (len
< sizeof(EBLCHeader
) + numSizes
* sizeof(BitmapSizeTable
)) {
569 const BitmapSizeTable
* sizeTable
=
570 reinterpret_cast<const BitmapSizeTable
*>(hdr
+ 1);
571 for (uint32_t i
= 0; i
< numSizes
; ++i
, ++sizeTable
) {
572 if (sizeTable
->ppemX
== aSize
&& sizeTable
->ppemY
== aSize
) {
573 // we ignore a strike that contains fewer than 4 glyphs,
574 // as that probably indicates a font such as Courier New
575 // that provides bitmaps ONLY for the "shading" characters
577 hasStrike
= (uint16_t(sizeTable
->endGlyphIndex
) >=
578 uint16_t(sizeTable
->startGlyphIndex
) + 3);
582 // if we reach here, we didn't find a strike; unconditionally break
583 // out of the while-loop block
586 mFontFace
->ReleaseFontTable(tableContext
);
592 // if we didn't find a real strike, check if the font calls for scaling
593 // another bitmap to this size
594 hr
= mFontFace
->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'),
595 (const void**)&tableData
, &len
, &tableContext
,
602 if (len
< sizeof(EBSCHeader
)) {
605 const EBSCHeader
* hdr
= reinterpret_cast<const EBSCHeader
*>(tableData
);
606 if (hdr
->version
!= 0x00020000) {
609 uint32_t numSizes
= hdr
->numSizes
;
610 if (numSizes
> 0xffff) {
613 if (len
< sizeof(EBSCHeader
) + numSizes
* sizeof(BitmapScaleTable
)) {
616 const BitmapScaleTable
* scaleTable
=
617 reinterpret_cast<const BitmapScaleTable
*>(hdr
+ 1);
618 for (uint32_t i
= 0; i
< numSizes
; ++i
, ++scaleTable
) {
619 if (scaleTable
->ppemX
== aSize
&& scaleTable
->ppemY
== aSize
) {
626 mFontFace
->ReleaseFontTable(tableContext
);
631 bool gfxDWriteFont::IsValid() const { return mFontFace
!= nullptr; }
633 IDWriteFontFace
* gfxDWriteFont::GetFontFace() { return mFontFace
.get(); }
635 gfxFont::RunMetrics
gfxDWriteFont::Measure(const gfxTextRun
* aTextRun
,
636 uint32_t aStart
, uint32_t aEnd
,
637 BoundingBoxType aBoundingBoxType
,
638 DrawTarget
* aRefDrawTarget
,
640 gfx::ShapedTextFlags aOrientation
) {
641 gfxFont::RunMetrics metrics
=
642 gfxFont::Measure(aTextRun
, aStart
, aEnd
, aBoundingBoxType
, aRefDrawTarget
,
643 aSpacing
, aOrientation
);
645 // if aBoundingBoxType is LOOSE_INK_EXTENTS
646 // and the underlying cairo font may be antialiased,
647 // we can't trust Windows to have considered all the pixels
648 // so we need to add "padding" to the bounds.
649 // (see bugs 475968, 439831, compare also bug 445087)
650 if (aBoundingBoxType
== LOOSE_INK_EXTENTS
&&
651 mAntialiasOption
!= kAntialiasNone
&&
652 GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC
&&
653 metrics
.mBoundingBox
.Width() > 0) {
654 metrics
.mBoundingBox
.MoveByX(-aTextRun
->GetAppUnitsPerDevUnit());
655 metrics
.mBoundingBox
.SetWidth(metrics
.mBoundingBox
.Width() +
656 aTextRun
->GetAppUnitsPerDevUnit() * 3);
662 bool gfxDWriteFont::ProvidesGlyphWidths() const {
663 return !mUseSubpixelPositions
||
664 (mFontFace
->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD
) ||
665 ((gfxDWriteFontEntry
*)(GetFontEntry()))->HasVariations();
668 int32_t gfxDWriteFont::GetGlyphWidth(uint16_t aGID
) {
670 mGlyphWidths
= MakeUnique
<nsTHashMap
<nsUint32HashKey
, int32_t>>(128);
673 return mGlyphWidths
->LookupOrInsertWith(
674 aGID
, [&] { return NS_lround(MeasureGlyphWidth(aGID
) * 65536.0); });
677 bool gfxDWriteFont::GetForceGDIClassic() const {
678 return sForceGDIClassicEnabled
&&
679 static_cast<gfxDWriteFontEntry
*>(mFontEntry
.get())
680 ->GetForceGDIClassic() &&
681 GetAdjustedSize() <= gfxDWriteFontList::PlatformFontList()
682 ->GetForceGDIClassicMaxFontSize();
685 DWRITE_MEASURING_MODE
686 gfxDWriteFont::GetMeasuringMode() const {
687 return DWriteSettings::Get(GetForceGDIClassic()).MeasuringMode();
690 gfxFloat
gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph
) {
695 if (mUseSubpixelPositions
) {
696 hr
= mFontFace1
->GetDesignGlyphAdvances(1, &aGlyph
, &advance
, FALSE
);
698 return advance
* mFUnitsConvFactor
;
701 hr
= mFontFace1
->GetGdiCompatibleGlyphAdvances(
702 FLOAT(mAdjustedSize
), 1.0f
, nullptr,
703 GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL
, FALSE
, 1,
706 return NS_lround(advance
* mFUnitsConvFactor
);
710 DWRITE_GLYPH_METRICS metrics
;
711 if (mUseSubpixelPositions
) {
712 hr
= mFontFace
->GetDesignGlyphMetrics(&aGlyph
, 1, &metrics
, FALSE
);
714 return metrics
.advanceWidth
* mFUnitsConvFactor
;
717 hr
= mFontFace
->GetGdiCompatibleGlyphMetrics(
718 FLOAT(mAdjustedSize
), 1.0f
, nullptr,
719 GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL
, &aGlyph
, 1,
722 return NS_lround(metrics
.advanceWidth
* mFUnitsConvFactor
);
727 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
728 // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
729 // the font resource; possibly a failing drive or similar hardware issue.
730 // Mark the font as invalid, and wipe the fontEntry's charmap so that font
731 // selection will skip it; we'll use a fallback font instead.
733 GetFontEntry()->mCharacterMap
= new gfxCharacterMap();
734 GetFontEntry()->mShmemCharacterMap
= nullptr;
735 gfxCriticalError() << "Exception occurred measuring glyph width for "
736 << GetFontEntry()->Name().get();
741 bool gfxDWriteFont::GetGlyphBounds(uint16_t aGID
, gfxRect
* aBounds
,
744 DWRITE_GLYPH_METRICS m
;
745 HRESULT hr
= mFontFace
->GetDesignGlyphMetrics(&aGID
, 1, &m
, FALSE
);
749 gfxRect
bounds(m
.leftSideBearing
, m
.topSideBearing
- m
.verticalOriginY
,
750 m
.advanceWidth
- m
.leftSideBearing
- m
.rightSideBearing
,
751 m
.advanceHeight
- m
.topSideBearing
- m
.bottomSideBearing
);
752 bounds
.Scale(mFUnitsConvFactor
);
753 // GetDesignGlyphMetrics returns 'ideal' glyph metrics, we need to pad to
754 // account for antialiasing.
755 if (!aTight
&& !aBounds
->IsEmpty()) {
756 bounds
.Inflate(1.0, 0.0);
761 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
762 // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
763 // the font resource; possibly a failing drive or similar hardware issue.
764 // Mark the font as invalid, and wipe the fontEntry's charmap so that font
765 // selection will skip it; we'll use a fallback font instead.
767 GetFontEntry()->mCharacterMap
= new gfxCharacterMap();
768 GetFontEntry()->mShmemCharacterMap
= nullptr;
769 gfxCriticalError() << "Exception occurred measuring glyph bounds for "
770 << GetFontEntry()->Name().get();
775 void gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
776 FontCacheSizes
* aSizes
) const {
777 gfxFont::AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
779 aSizes
->mFontInstances
+=
780 mGlyphWidths
->ShallowSizeOfIncludingThis(aMallocSizeOf
);
784 void gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf
,
785 FontCacheSizes
* aSizes
) const {
786 aSizes
->mFontInstances
+= aMallocSizeOf(this);
787 AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
790 already_AddRefed
<ScaledFont
> gfxDWriteFont::GetScaledFont(
791 const TextRunDrawParams
& aRunParams
) {
792 bool useClearType
= UsingClearType();
793 if (mAzureScaledFontUsedClearType
!= useClearType
) {
794 if (auto* oldScaledFont
= mAzureScaledFont
.exchange(nullptr)) {
795 oldScaledFont
->Release();
797 if (auto* oldScaledFont
= mAzureScaledFontGDI
.exchange(nullptr)) {
798 oldScaledFont
->Release();
801 bool forceGDI
= aRunParams
.allowGDI
&& GetForceGDIClassic();
802 ScaledFont
* scaledFont
= forceGDI
? mAzureScaledFontGDI
: mAzureScaledFont
;
804 return do_AddRef(scaledFont
);
807 gfxDWriteFontEntry
* fe
= static_cast<gfxDWriteFontEntry
*>(mFontEntry
.get());
808 bool useEmbeddedBitmap
=
809 (gfxVars::SystemTextRenderingMode() == DWRITE_RENDERING_MODE_DEFAULT
||
811 fe
->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize
));
813 const gfxFontStyle
* fontStyle
= GetStyle();
814 RefPtr
<ScaledFont
> newScaledFont
= Factory::CreateScaledFontForDWriteFont(
815 mFontFace
, fontStyle
, GetUnscaledFont(), GetAdjustedSize(),
816 useEmbeddedBitmap
, ApplySyntheticBold(), forceGDI
);
817 if (!newScaledFont
) {
820 InitializeScaledFont(newScaledFont
);
823 if (mAzureScaledFontGDI
.compareExchange(nullptr, newScaledFont
.get())) {
824 Unused
<< newScaledFont
.forget();
825 mAzureScaledFontUsedClearType
= useClearType
;
827 scaledFont
= mAzureScaledFontGDI
;
829 if (mAzureScaledFont
.compareExchange(nullptr, newScaledFont
.get())) {
830 Unused
<< newScaledFont
.forget();
831 mAzureScaledFontUsedClearType
= useClearType
;
833 scaledFont
= mAzureScaledFont
;
835 return do_AddRef(scaledFont
);
838 bool gfxDWriteFont::ShouldRoundXOffset(cairo_t
* aCairo
) const {
839 // show_glyphs is implemented on the font and so is used for all Cairo
840 // surface types; however, it may pixel-snap depending on the dwrite
842 return GetMeasuringMode() != DWRITE_MEASURING_MODE_NATURAL
;