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/. */
6 #include "gfxDWriteFonts.h"
8 #include "mozilla/MemoryReporting.h"
11 #include "gfxDWriteFontList.h"
12 #include "gfxContext.h"
15 #include "harfbuzz/hb.h"
17 // Chosen this as to resemble DWrite's own oblique face style.
18 #define OBLIQUE_SKEW_FACTOR 0.3
20 using namespace mozilla
;
21 using namespace mozilla::gfx
;
23 // This is also in gfxGDIFont.cpp. Would be nice to put it somewhere common,
24 // but we can't declare it in the gfxFont.h or gfxFontUtils.h headers
25 // because those are exported, and the cairo headers aren't.
26 static inline cairo_antialias_t
27 GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption
)
29 switch (anAntialiasOption
) {
31 case gfxFont::kAntialiasDefault
:
32 return CAIRO_ANTIALIAS_DEFAULT
;
33 case gfxFont::kAntialiasNone
:
34 return CAIRO_ANTIALIAS_NONE
;
35 case gfxFont::kAntialiasGrayscale
:
36 return CAIRO_ANTIALIAS_GRAY
;
37 case gfxFont::kAntialiasSubpixel
:
38 return CAIRO_ANTIALIAS_SUBPIXEL
;
42 // Code to determine whether Windows is set to use ClearType font smoothing;
43 // based on private functions in cairo-win32-font.c
45 #ifndef SPI_GETFONTSMOOTHINGTYPE
46 #define SPI_GETFONTSMOOTHINGTYPE 0x200a
48 #ifndef FE_FONTSMOOTHINGCLEARTYPE
49 #define FE_FONTSMOOTHINGCLEARTYPE 2
56 if (!SystemParametersInfo(SPI_GETFONTSMOOTHING
, 0, &fontSmoothing
, 0) ||
63 if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE
, 0, &type
, 0) &&
64 type
== FE_FONTSMOOTHINGCLEARTYPE
)
71 ////////////////////////////////////////////////////////////////////////////////
73 gfxDWriteFont::gfxDWriteFont(gfxFontEntry
*aFontEntry
,
74 const gfxFontStyle
*aFontStyle
,
76 AntialiasOption anAAOption
)
77 : gfxFont(aFontEntry
, aFontStyle
, anAAOption
)
78 , mCairoFontFace(nullptr)
80 , mNeedsOblique(false)
81 , mNeedsBold(aNeedsBold
)
82 , mUseSubpixelPositions(false)
83 , mAllowManualShowGlyphs(true)
85 gfxDWriteFontEntry
*fe
=
86 static_cast<gfxDWriteFontEntry
*>(aFontEntry
);
88 DWRITE_FONT_SIMULATIONS sims
= DWRITE_FONT_SIMULATIONS_NONE
;
89 if ((GetStyle()->style
& (NS_FONT_STYLE_ITALIC
| NS_FONT_STYLE_OBLIQUE
)) &&
90 !fe
->IsItalic() && GetStyle()->allowSyntheticStyle
) {
91 // For this we always use the font_matrix for uniformity. Not the
96 sims
|= DWRITE_FONT_SIMULATIONS_BOLD
;
99 rv
= fe
->CreateFontFace(getter_AddRefs(mFontFace
), sims
);
106 ComputeMetrics(anAAOption
);
109 gfxDWriteFont::~gfxDWriteFont()
111 if (mCairoFontFace
) {
112 cairo_font_face_destroy(mCairoFontFace
);
115 cairo_scaled_font_destroy(mScaledFont
);
121 gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption
)
123 return new gfxDWriteFont(static_cast<gfxDWriteFontEntry
*>(mFontEntry
.get()),
124 &mStyle
, mNeedsBold
, anAAOption
);
127 const gfxFont::Metrics
&
128 gfxDWriteFont::GetMetrics()
134 gfxDWriteFont::GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS
*aFontMetrics
)
136 gfxFontStyle
style(mStyle
);
141 gfxPlatformFontList::PlatformFontList()->
142 FindFontForFamily(NS_LITERAL_STRING("Arial"), &style
, needsBold
);
143 if (!fe
|| fe
== mFontEntry
) {
147 nsRefPtr
<gfxFont
> font
= fe
->FindOrMakeFont(&style
, needsBold
);
148 gfxDWriteFont
*dwFont
= static_cast<gfxDWriteFont
*>(font
.get());
149 dwFont
->mFontFace
->GetMetrics(aFontMetrics
);
155 gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption
)
157 DWRITE_FONT_METRICS fontMetrics
;
158 if (!(mFontEntry
->Weight() == 900 &&
159 !mFontEntry
->IsUserFont() &&
160 mFontEntry
->Name().EqualsLiteral("Arial Black") &&
161 GetFakeMetricsForArialBlack(&fontMetrics
)))
163 mFontFace
->GetMetrics(&fontMetrics
);
166 if (mStyle
.sizeAdjust
!= 0.0) {
167 gfxFloat aspect
= (gfxFloat
)fontMetrics
.xHeight
/
168 fontMetrics
.designUnitsPerEm
;
169 mAdjustedSize
= mStyle
.GetAdjustedSize(aspect
);
171 mAdjustedSize
= mStyle
.size
;
174 // Note that GetMeasuringMode depends on mAdjustedSize
175 if ((anAAOption
== gfxFont::kAntialiasDefault
&&
177 GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL
) ||
178 anAAOption
== gfxFont::kAntialiasSubpixel
)
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
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
195 mAllowManualShowGlyphs
= false;
198 mMetrics
= new gfxFont::Metrics
;
199 ::memset(mMetrics
, 0, sizeof(*mMetrics
));
201 mFUnitsConvFactor
= float(mAdjustedSize
/ fontMetrics
.designUnitsPerEm
);
203 mMetrics
->xHeight
= fontMetrics
.xHeight
* mFUnitsConvFactor
;
205 mMetrics
->maxAscent
= ceil(fontMetrics
.ascent
* mFUnitsConvFactor
);
206 mMetrics
->maxDescent
= ceil(fontMetrics
.descent
* mFUnitsConvFactor
);
207 mMetrics
->maxHeight
= mMetrics
->maxAscent
+ mMetrics
->maxDescent
;
209 mMetrics
->emHeight
= mAdjustedSize
;
210 mMetrics
->emAscent
= mMetrics
->emHeight
*
211 mMetrics
->maxAscent
/ mMetrics
->maxHeight
;
212 mMetrics
->emDescent
= mMetrics
->emHeight
- mMetrics
->emAscent
;
214 mMetrics
->maxAdvance
= mAdjustedSize
;
216 // try to get the true maxAdvance value from 'hhea'
217 gfxFontEntry::AutoTable
hheaTable(GetFontEntry(),
218 TRUETYPE_TAG('h','h','e','a'));
221 const HheaTable
* hhea
=
222 reinterpret_cast<const HheaTable
*>(hb_blob_get_data(hheaTable
, &len
));
223 if (len
>= sizeof(HheaTable
)) {
224 mMetrics
->maxAdvance
=
225 uint16_t(hhea
->advanceWidthMax
) * mFUnitsConvFactor
;
229 mMetrics
->internalLeading
= std::max(mMetrics
->maxHeight
- mMetrics
->emHeight
, 0.0);
230 mMetrics
->externalLeading
= ceil(fontMetrics
.lineGap
* mFUnitsConvFactor
);
232 UINT16 glyph
= (uint16_t)GetSpaceGlyph();
233 mMetrics
->spaceWidth
= MeasureGlyphWidth(glyph
);
235 // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
236 // if the table is not available or if using hinted/pixel-snapped widths
237 if (mUseSubpixelPositions
) {
238 mMetrics
->aveCharWidth
= 0;
239 gfxFontEntry::AutoTable
os2Table(GetFontEntry(),
240 TRUETYPE_TAG('O','S','/','2'));
243 const OS2Table
* os2
=
244 reinterpret_cast<const OS2Table
*>(hb_blob_get_data(os2Table
, &len
));
246 // Not checking against sizeof(mozilla::OS2Table) here because older
247 // versions of the table have different sizes; we only need the first
248 // two 16-bit fields here.
249 mMetrics
->aveCharWidth
=
250 int16_t(os2
->xAvgCharWidth
) * mFUnitsConvFactor
;
256 if (mMetrics
->aveCharWidth
< 1) {
258 if (SUCCEEDED(mFontFace
->GetGlyphIndicesA(&ucs
, 1, &glyph
))) {
259 mMetrics
->aveCharWidth
= MeasureGlyphWidth(glyph
);
261 if (mMetrics
->aveCharWidth
< 1) {
262 // Let's just assume the X is square.
263 mMetrics
->aveCharWidth
= fontMetrics
.xHeight
* mFUnitsConvFactor
;
268 if (SUCCEEDED(mFontFace
->GetGlyphIndicesA(&ucs
, 1, &glyph
))) {
269 mMetrics
->zeroOrAveCharWidth
= MeasureGlyphWidth(glyph
);
271 if (mMetrics
->zeroOrAveCharWidth
< 1) {
272 mMetrics
->zeroOrAveCharWidth
= mMetrics
->aveCharWidth
;
275 mMetrics
->underlineOffset
=
276 fontMetrics
.underlinePosition
* mFUnitsConvFactor
;
277 mMetrics
->underlineSize
=
278 fontMetrics
.underlineThickness
* mFUnitsConvFactor
;
279 mMetrics
->strikeoutOffset
=
280 fontMetrics
.strikethroughPosition
* mFUnitsConvFactor
;
281 mMetrics
->strikeoutSize
=
282 fontMetrics
.strikethroughThickness
* mFUnitsConvFactor
;
284 SanitizeMetrics(mMetrics
, GetFontEntry()->mIsBadUnderlineFont
);
287 printf("Font: %p (%s) size: %f\n", this,
288 NS_ConvertUTF16toUTF8(GetName()).get(), mStyle
.size
);
289 printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics
->emHeight
, mMetrics
->emAscent
, mMetrics
->emDescent
);
290 printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics
->maxAscent
, mMetrics
->maxDescent
, mMetrics
->maxAdvance
);
291 printf(" internalLeading: %f externalLeading: %f\n", mMetrics
->internalLeading
, mMetrics
->externalLeading
);
292 printf(" spaceWidth: %f aveCharWidth: %f zeroOrAve: %f xHeight: %f\n",
293 mMetrics
->spaceWidth
, mMetrics
->aveCharWidth
, mMetrics
->zeroOrAveCharWidth
, mMetrics
->xHeight
);
294 printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
295 mMetrics
->underlineOffset
, mMetrics
->underlineSize
, mMetrics
->strikeoutOffset
, mMetrics
->strikeoutSize
);
299 using namespace mozilla
; // for AutoSwap_* types
302 AutoSwap_PRUint32 version
;
303 AutoSwap_PRUint32 numSizes
;
306 struct SbitLineMetrics
{
310 int8_t caretSlopeNumerator
;
311 int8_t caretSlopeDenominator
;
321 struct BitmapSizeTable
{
322 AutoSwap_PRUint32 indexSubTableArrayOffset
;
323 AutoSwap_PRUint32 indexTablesSize
;
324 AutoSwap_PRUint32 numberOfIndexSubTables
;
325 AutoSwap_PRUint32 colorRef
;
326 SbitLineMetrics hori
;
327 SbitLineMetrics vert
;
328 AutoSwap_PRUint16 startGlyphIndex
;
329 AutoSwap_PRUint16 endGlyphIndex
;
336 typedef EBLCHeader EBSCHeader
;
338 struct BitmapScaleTable
{
339 SbitLineMetrics hori
;
340 SbitLineMetrics vert
;
343 uint8_t substitutePpemX
;
344 uint8_t substitutePpemY
;
348 gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize
)
355 mFontFace
->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'),
356 (const void**)&tableData
, &len
,
357 &tableContext
, &exists
);
362 bool hasStrike
= false;
363 // not really a loop, but this lets us use 'break' to skip out of the block
364 // as soon as we know the answer, and skips it altogether if the table is
367 if (len
< sizeof(EBLCHeader
)) {
370 const EBLCHeader
*hdr
= reinterpret_cast<const EBLCHeader
*>(tableData
);
371 if (hdr
->version
!= 0x00020000) {
374 uint32_t numSizes
= hdr
->numSizes
;
375 if (numSizes
> 0xffff) { // sanity-check, prevent overflow below
378 if (len
< sizeof(EBLCHeader
) + numSizes
* sizeof(BitmapSizeTable
)) {
381 const BitmapSizeTable
*sizeTable
=
382 reinterpret_cast<const BitmapSizeTable
*>(hdr
+ 1);
383 for (uint32_t i
= 0; i
< numSizes
; ++i
, ++sizeTable
) {
384 if (sizeTable
->ppemX
== aSize
&& sizeTable
->ppemY
== aSize
) {
385 // we ignore a strike that contains fewer than 4 glyphs,
386 // as that probably indicates a font such as Courier New
387 // that provides bitmaps ONLY for the "shading" characters
389 hasStrike
= (uint16_t(sizeTable
->endGlyphIndex
) >=
390 uint16_t(sizeTable
->startGlyphIndex
) + 3);
394 // if we reach here, we didn't find a strike; unconditionally break
395 // out of the while-loop block
398 mFontFace
->ReleaseFontTable(tableContext
);
404 // if we didn't find a real strike, check if the font calls for scaling
405 // another bitmap to this size
406 hr
= mFontFace
->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'),
407 (const void**)&tableData
, &len
,
408 &tableContext
, &exists
);
414 if (len
< sizeof(EBSCHeader
)) {
417 const EBSCHeader
*hdr
= reinterpret_cast<const EBSCHeader
*>(tableData
);
418 if (hdr
->version
!= 0x00020000) {
421 uint32_t numSizes
= hdr
->numSizes
;
422 if (numSizes
> 0xffff) {
425 if (len
< sizeof(EBSCHeader
) + numSizes
* sizeof(BitmapScaleTable
)) {
428 const BitmapScaleTable
*scaleTable
=
429 reinterpret_cast<const BitmapScaleTable
*>(hdr
+ 1);
430 for (uint32_t i
= 0; i
< numSizes
; ++i
, ++scaleTable
) {
431 if (scaleTable
->ppemX
== aSize
&& scaleTable
->ppemY
== aSize
) {
438 mFontFace
->ReleaseFontTable(tableContext
);
444 gfxDWriteFont::GetSpaceGlyph()
449 hr
= mFontFace
->GetGlyphIndicesA(&ucs
, 1, &glyph
);
457 gfxDWriteFont::SetupCairoFont(gfxContext
*aContext
)
459 cairo_scaled_font_t
*scaledFont
= GetCairoScaledFont();
460 if (cairo_scaled_font_status(scaledFont
) != CAIRO_STATUS_SUCCESS
) {
461 // Don't cairo_set_scaled_font as that would propagate the error to
462 // the cairo_t, precluding any further drawing.
465 cairo_set_scaled_font(aContext
->GetCairo(), scaledFont
);
470 gfxDWriteFont::IsValid()
472 return mFontFace
!= nullptr;
476 gfxDWriteFont::GetFontFace()
478 return mFontFace
.get();
482 gfxDWriteFont::CairoFontFace()
484 if (!mCairoFontFace
) {
485 #ifdef CAIRO_HAS_DWRITE_FONT
487 cairo_dwrite_font_face_create_for_dwrite_fontface(
488 ((gfxDWriteFontEntry
*)mFontEntry
.get())->mFont
, mFontFace
);
491 return mCairoFontFace
;
495 cairo_scaled_font_t
*
496 gfxDWriteFont::GetCairoScaledFont()
499 cairo_matrix_t sizeMatrix
;
500 cairo_matrix_t identityMatrix
;
502 cairo_matrix_init_scale(&sizeMatrix
, mAdjustedSize
, mAdjustedSize
);
503 cairo_matrix_init_identity(&identityMatrix
);
505 cairo_font_options_t
*fontOptions
= cairo_font_options_create();
507 double skewfactor
= OBLIQUE_SKEW_FACTOR
;
509 cairo_matrix_t style
;
510 cairo_matrix_init(&style
,
513 -1 * skewfactor
, //xy
517 cairo_matrix_multiply(&sizeMatrix
, &sizeMatrix
, &style
);
520 if (mAntialiasOption
!= kAntialiasDefault
) {
521 cairo_font_options_set_antialias(fontOptions
,
522 GetCairoAntialiasOption(mAntialiasOption
));
525 mScaledFont
= cairo_scaled_font_create(CairoFontFace(),
529 cairo_font_options_destroy(fontOptions
);
531 cairo_dwrite_scaled_font_allow_manual_show_glyphs(mScaledFont
,
532 mAllowManualShowGlyphs
);
534 gfxDWriteFontEntry
*fe
=
535 static_cast<gfxDWriteFontEntry
*>(mFontEntry
.get());
536 cairo_dwrite_scaled_font_set_force_GDI_classic(mScaledFont
,
537 GetForceGDIClassic());
540 NS_ASSERTION(mAdjustedSize
== 0.0 ||
541 cairo_scaled_font_status(mScaledFont
)
542 == CAIRO_STATUS_SUCCESS
,
543 "Failed to make scaled font");
549 gfxDWriteFont::Measure(gfxTextRun
*aTextRun
,
550 uint32_t aStart
, uint32_t aEnd
,
551 BoundingBoxType aBoundingBoxType
,
552 gfxContext
*aRefContext
,
555 gfxFont::RunMetrics metrics
=
556 gfxFont::Measure(aTextRun
, aStart
, aEnd
,
557 aBoundingBoxType
, aRefContext
, aSpacing
);
559 // if aBoundingBoxType is LOOSE_INK_EXTENTS
560 // and the underlying cairo font may be antialiased,
561 // we can't trust Windows to have considered all the pixels
562 // so we need to add "padding" to the bounds.
563 // (see bugs 475968, 439831, compare also bug 445087)
564 if (aBoundingBoxType
== LOOSE_INK_EXTENTS
&&
565 mAntialiasOption
!= kAntialiasNone
&&
566 GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC
&&
567 metrics
.mBoundingBox
.width
> 0) {
568 metrics
.mBoundingBox
.x
-= aTextRun
->GetAppUnitsPerDevUnit();
569 metrics
.mBoundingBox
.width
+= aTextRun
->GetAppUnitsPerDevUnit() * 3;
576 gfxDWriteFont::ProvidesGlyphWidths() const
578 return !mUseSubpixelPositions
||
579 (mFontFace
->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD
);
583 gfxDWriteFont::GetGlyphWidth(gfxContext
*aCtx
, uint16_t aGID
)
586 mGlyphWidths
= new nsDataHashtable
<nsUint32HashKey
,int32_t>(128);
590 if (mGlyphWidths
->Get(aGID
, &width
)) {
594 width
= NS_lround(MeasureGlyphWidth(aGID
) * 65536.0);
595 mGlyphWidths
->Put(aGID
, width
);
599 TemporaryRef
<GlyphRenderingOptions
>
600 gfxDWriteFont::GetGlyphRenderingOptions()
602 if (UsingClearType()) {
603 return Factory::CreateDWriteGlyphRenderingOptions(
604 gfxWindowsPlatform::GetPlatform()->GetRenderingParams(GetForceGDIClassic() ?
605 gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC
: gfxWindowsPlatform::TEXT_RENDERING_NORMAL
));
607 return Factory::CreateDWriteGlyphRenderingOptions(gfxWindowsPlatform::GetPlatform()->
608 GetRenderingParams(gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE
));
613 gfxDWriteFont::GetForceGDIClassic()
615 return static_cast<gfxDWriteFontEntry
*>(mFontEntry
.get())->GetForceGDIClassic() &&
616 cairo_dwrite_get_cleartype_rendering_mode() < 0 &&
618 gfxDWriteFontList::PlatformFontList()->GetForceGDIClassicMaxFontSize();
621 DWRITE_MEASURING_MODE
622 gfxDWriteFont::GetMeasuringMode()
624 return GetForceGDIClassic()
625 ? DWRITE_MEASURING_MODE_GDI_CLASSIC
626 : gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode();
630 gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph
)
632 DWRITE_GLYPH_METRICS metrics
;
634 if (mUseSubpixelPositions
) {
635 hr
= mFontFace
->GetDesignGlyphMetrics(&aGlyph
, 1, &metrics
, FALSE
);
637 return metrics
.advanceWidth
* mFUnitsConvFactor
;
640 hr
= mFontFace
->GetGdiCompatibleGlyphMetrics(
641 FLOAT(mAdjustedSize
), 1.0f
, nullptr,
642 GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL
,
643 &aGlyph
, 1, &metrics
, FALSE
);
645 return NS_lround(metrics
.advanceWidth
* mFUnitsConvFactor
);
652 gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
653 FontCacheSizes
* aSizes
) const
655 gfxFont::AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
656 aSizes
->mFontInstances
+= aMallocSizeOf(mMetrics
);
658 aSizes
->mFontInstances
+=
659 mGlyphWidths
->SizeOfIncludingThis(nullptr, aMallocSizeOf
);
664 gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf
,
665 FontCacheSizes
* aSizes
) const
667 aSizes
->mFontInstances
+= aMallocSizeOf(this);
668 AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
671 TemporaryRef
<ScaledFont
>
672 gfxDWriteFont::GetScaledFont(mozilla::gfx::DrawTarget
*aTarget
)
674 bool wantCairo
= aTarget
->GetBackendType() == BackendType::CAIRO
;
675 if (mAzureScaledFont
&& mAzureScaledFontIsCairo
== wantCairo
) {
676 return mAzureScaledFont
;
679 NativeFont nativeFont
;
680 nativeFont
.mType
= NativeFontType::DWRITE_FONT_FACE
;
681 nativeFont
.mFont
= GetFontFace();
684 mAzureScaledFont
= Factory::CreateScaledFontWithCairo(nativeFont
,
686 GetCairoScaledFont());
688 mAzureScaledFont
= Factory::CreateScaledFontForNativeFont(nativeFont
,
692 mAzureScaledFontIsCairo
= wantCairo
;
694 return mAzureScaledFont
;