/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "gfxDWriteFonts.h" #include #include "gfxDWriteFontList.h" #include "gfxContext.h" #include "gfxHarfBuzzShaper.h" #include "gfxTextRun.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/DWriteSettings.h" #include "mozilla/gfx/Logging.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/Preferences.h" #include "harfbuzz/hb.h" #include "mozilla/FontPropertyTypes.h" using namespace mozilla; using namespace mozilla::gfx; // Code to determine whether Windows is set to use ClearType font smoothing; // based on private functions in cairo-win32-font.c #ifndef SPI_GETFONTSMOOTHINGTYPE # define SPI_GETFONTSMOOTHINGTYPE 0x200a #endif #ifndef FE_FONTSMOOTHINGCLEARTYPE # define FE_FONTSMOOTHINGCLEARTYPE 2 #endif // Cleartype can be dynamically enabled/disabled, so we have to allow for // dynamically updating it. static BYTE GetSystemTextQuality() { BOOL font_smoothing; UINT smoothing_type; if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { return DEFAULT_QUALITY; } if (font_smoothing) { if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smoothing_type, 0)) { return DEFAULT_QUALITY; } if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) { return CLEARTYPE_QUALITY; } return ANTIALIASED_QUALITY; } return DEFAULT_QUALITY; } #ifndef SPI_GETFONTSMOOTHINGCONTRAST # define SPI_GETFONTSMOOTHINGCONTRAST 0x200c #endif // "Retrieves a contrast value that is used in ClearType smoothing. Valid // contrast values are from 1000 to 2200. The default value is 1400." static FLOAT GetSystemGDIGamma() { UINT value = 0; if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &value, 0) || value < 1000 || value > 2200) { value = 1400; } return value / 1000.0f; } //////////////////////////////////////////////////////////////////////////////// // gfxDWriteFont gfxDWriteFont::gfxDWriteFont(const RefPtr& aUnscaledFont, gfxFontEntry* aFontEntry, const gfxFontStyle* aFontStyle, RefPtr aFontFace, AntialiasOption anAAOption) : gfxFont(aUnscaledFont, aFontEntry, aFontStyle, anAAOption), mFontFace(aFontFace ? aFontFace : aUnscaledFont->GetFontFace()), mUseSubpixelPositions(false), mAllowManualShowGlyphs(true), mAzureScaledFontUsedClearType(false) { // If the IDWriteFontFace1 interface is available, we can use that for // faster glyph width retrieval. mFontFace->QueryInterface(__uuidof(IDWriteFontFace1), (void**)getter_AddRefs(mFontFace1)); // If a fake-bold effect is needed, determine whether we're using DWrite's // "simulation" or applying our multi-strike "synthetic bold". if (aFontStyle->NeedsSyntheticBold(aFontEntry)) { switch (StaticPrefs::gfx_font_rendering_directwrite_bold_simulation()) { case 0: // never use the DWrite simulation mApplySyntheticBold = true; break; case 1: // use DWrite simulation for installed fonts but not webfonts mApplySyntheticBold = aFontEntry->mIsDataUserFont; break; default: // always use DWrite bold simulation // the flag is initialized to false in gfxFont break; } } ComputeMetrics(anAAOption); } gfxDWriteFont::~gfxDWriteFont() { if (auto* scaledFont = mAzureScaledFontGDI.exchange(nullptr)) { scaledFont->Release(); } } /* static */ bool gfxDWriteFont::InitDWriteSupport() { if (!Factory::EnsureDWriteFactory()) { return false; } if (XRE_IsParentProcess()) { UpdateSystemTextVars(); } else { // UpdateClearTypeVars doesn't update the vars in non parent processes, but // it does set sForceGDIClassicEnabled so we still need to call it. UpdateClearTypeVars(); } DWriteSettings::Initialize(); return true; } /* static */ void gfxDWriteFont::UpdateSystemTextVars() { MOZ_ASSERT(XRE_IsParentProcess()); BYTE newQuality = GetSystemTextQuality(); if (gfxVars::SystemTextQuality() != newQuality) { gfxVars::SetSystemTextQuality(newQuality); } FLOAT newGDIGamma = GetSystemGDIGamma(); if (gfxVars::SystemGDIGamma() != newGDIGamma) { gfxVars::SetSystemGDIGamma(newGDIGamma); } UpdateClearTypeVars(); } void gfxDWriteFont::SystemTextQualityChanged() { // If ClearType status has changed, update our value, Factory::SetSystemTextQuality(gfxVars::SystemTextQuality()); // flush cached stuff that depended on the old setting, and force // reflow everywhere to ensure we are using correct glyph metrics. gfxPlatform::FlushFontAndWordCaches(); gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::No); } mozilla::Atomic gfxDWriteFont::sForceGDIClassicEnabled{true}; /* static */ void gfxDWriteFont::UpdateClearTypeVars() { // We don't force GDI classic if the cleartype rendering mode pref is set to // something valid. int32_t renderingModePref = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_MODE, -1); if (renderingModePref < 0 || renderingModePref > 5) { renderingModePref = -1; } sForceGDIClassicEnabled = (renderingModePref == -1); if (!XRE_IsParentProcess()) { return; } if (!Factory::GetDWriteFactory()) { return; } // First set sensible hard coded defaults. float clearTypeLevel = 1.0f; float enhancedContrast = 1.0f; float gamma = 2.2f; int pixelGeometry = DWRITE_PIXEL_GEOMETRY_RGB; int renderingMode = DWRITE_RENDERING_MODE_DEFAULT; // Override these from DWrite function if available. RefPtr defaultRenderingParams; HRESULT hr = Factory::GetDWriteFactory()->CreateRenderingParams( getter_AddRefs(defaultRenderingParams)); if (SUCCEEDED(hr) && defaultRenderingParams) { clearTypeLevel = defaultRenderingParams->GetClearTypeLevel(); // For enhanced contrast, we only use the default if the user has set it // in the registry (by using the ClearType Tuner). // XXXbobowen it seems slightly odd that we do this and only for enhanced // contrast, but this reproduces previous functionality from // gfxWindowsPlatform::SetupClearTypeParams. HKEY hKey; LONG res = RegOpenKeyExW(DISPLAY1_REGISTRY_KEY, 0, KEY_READ, &hKey); if (res == ERROR_SUCCESS) { res = RegQueryValueExW(hKey, ENHANCED_CONTRAST_VALUE_NAME, nullptr, nullptr, nullptr, nullptr); if (res == ERROR_SUCCESS) { enhancedContrast = defaultRenderingParams->GetEnhancedContrast(); } RegCloseKey(hKey); } gamma = defaultRenderingParams->GetGamma(); pixelGeometry = defaultRenderingParams->GetPixelGeometry(); renderingMode = defaultRenderingParams->GetRenderingMode(); } else { gfxWarning() << "Failed to create default rendering params"; } // Finally override from prefs if valid values are set. If ClearType is // turned off we just use the default params, this reproduces the previous // functionality that was spread across gfxDWriteFont::GetScaledFont and // gfxWindowsPlatform::SetupClearTypeParams, but it seems odd because the // default params will still be the ClearType ones although we won't use the // anti-alias for ClearType because of GetSystemDefaultAAMode. if (gfxVars::SystemTextQuality() == CLEARTYPE_QUALITY) { int32_t prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_LEVEL, -1); if (prefInt >= 0 && prefInt <= 100) { clearTypeLevel = float(prefInt / 100.0); } prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_CONTRAST, -1); if (prefInt >= 0 && prefInt <= 1000) { enhancedContrast = float(prefInt / 100.0); } prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_GAMMA, -1); if (prefInt >= 1000 && prefInt <= 2200) { gamma = float(prefInt / 1000.0); } prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_STRUCTURE, -1); if (prefInt >= 0 && prefInt <= 2) { pixelGeometry = prefInt; } // renderingModePref is retrieved and validated above. if (renderingModePref != -1) { renderingMode = renderingModePref; } } if (gfxVars::SystemTextClearTypeLevel() != clearTypeLevel) { gfxVars::SetSystemTextClearTypeLevel(clearTypeLevel); } if (gfxVars::SystemTextEnhancedContrast() != enhancedContrast) { gfxVars::SetSystemTextEnhancedContrast(enhancedContrast); } if (gfxVars::SystemTextGamma() != gamma) { gfxVars::SetSystemTextGamma(gamma); } if (gfxVars::SystemTextPixelGeometry() != pixelGeometry) { gfxVars::SetSystemTextPixelGeometry(pixelGeometry); } if (gfxVars::SystemTextRenderingMode() != renderingMode) { gfxVars::SetSystemTextRenderingMode(renderingMode); } // Set cairo dwrite params in the parent process where it might still be // needed for printing. We use the validated pref int directly for rendering // mode, because a negative (i.e. not set) rendering mode is also used for // deciding on forcing GDI in cairo. cairo_dwrite_set_cleartype_params(gamma, enhancedContrast, clearTypeLevel, pixelGeometry, renderingModePref); } gfxFont* gfxDWriteFont::CopyWithAntialiasOption( AntialiasOption anAAOption) const { auto entry = static_cast(mFontEntry.get()); RefPtr unscaledFont = static_cast(mUnscaledFont.get()); return new gfxDWriteFont(unscaledFont, entry, &mStyle, mFontFace, anAAOption); } bool gfxDWriteFont::GetFakeMetricsForArialBlack( DWRITE_FONT_METRICS* aFontMetrics) { gfxFontStyle style(mStyle); style.weight = FontWeight::FromInt(700); gfxFontEntry* fe = gfxPlatformFontList::PlatformFontList()->FindFontForFamily( nullptr, "Arial"_ns, &style); if (!fe || fe == mFontEntry) { return false; } RefPtr font = fe->FindOrMakeFont(&style); gfxDWriteFont* dwFont = static_cast(font.get()); dwFont->mFontFace->GetMetrics(aFontMetrics); return true; } void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) { ::memset(&mMetrics, 0, sizeof(mMetrics)); DWRITE_FONT_METRICS fontMetrics; if (!(mFontEntry->Weight().Min() == FontWeight::FromInt(900) && mFontEntry->Weight().Max() == FontWeight::FromInt(900) && !mFontEntry->IsUserFont() && mFontEntry->Name().EqualsLiteral("Arial Black") && GetFakeMetricsForArialBlack(&fontMetrics))) { mFontFace->GetMetrics(&fontMetrics); } if (GetAdjustedSize() > 0.0 && mStyle.sizeAdjust >= 0.0 && FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) != FontSizeAdjust::Tag::None) { // For accurate measurement during the font-size-adjust computations; // these may be reset later according to the adjusted size. mUseSubpixelPositions = true; mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm); gfxFloat aspect; switch (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis)) { default: MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?"); aspect = 0.0; break; case FontSizeAdjust::Tag::ExHeight: aspect = (gfxFloat)fontMetrics.xHeight / fontMetrics.designUnitsPerEm; break; case FontSizeAdjust::Tag::CapHeight: aspect = (gfxFloat)fontMetrics.capHeight / fontMetrics.designUnitsPerEm; break; case FontSizeAdjust::Tag::ChWidth: { gfxFloat advance = GetCharAdvance('0'); aspect = advance > 0.0 ? advance / mAdjustedSize : 0.5; break; } case FontSizeAdjust::Tag::IcWidth: case FontSizeAdjust::Tag::IcHeight: { bool vertical = FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) == FontSizeAdjust::Tag::IcHeight; gfxFloat advance = GetCharAdvance(kWaterIdeograph, vertical); aspect = advance > 0.0 ? advance / mAdjustedSize : 1.0; break; } } if (aspect > 0.0) { // If we created a shaper above (to measure glyphs), discard it so we // get a new one for the adjusted scaling. delete mHarfBuzzShaper.exchange(nullptr); mAdjustedSize = mStyle.GetAdjustedSize(aspect); } } // Update now that we've adjusted the size if necessary. mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm); // Note that GetMeasuringMode depends on mAdjustedSize if ((anAAOption == gfxFont::kAntialiasDefault && UsingClearType() && GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL) || anAAOption == gfxFont::kAntialiasSubpixel) { mUseSubpixelPositions = true; // note that this may be reset to FALSE if we determine that a bitmap // strike is going to be used } else { mUseSubpixelPositions = false; } gfxDWriteFontEntry* fe = static_cast(mFontEntry.get()); if (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize))) { mAdjustedSize = NS_lround(mAdjustedSize); mUseSubpixelPositions = false; // if we have bitmaps, we need to tell Cairo NOT to use subpixel AA, // to avoid the manual-subpixel codepath in cairo-d2d-surface.cpp // which fails to render bitmap glyphs (see bug 626299). // This option will be passed to the cairo_dwrite_scaled_font_t // after creation. mAllowManualShowGlyphs = false; } mMetrics.xHeight = fontMetrics.xHeight * mFUnitsConvFactor; mMetrics.capHeight = fontMetrics.capHeight * mFUnitsConvFactor; mMetrics.maxAscent = round(fontMetrics.ascent * mFUnitsConvFactor); mMetrics.maxDescent = round(fontMetrics.descent * mFUnitsConvFactor); mMetrics.maxHeight = mMetrics.maxAscent + mMetrics.maxDescent; mMetrics.emHeight = mAdjustedSize; mMetrics.emAscent = mMetrics.emHeight * mMetrics.maxAscent / mMetrics.maxHeight; mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent; mMetrics.maxAdvance = mAdjustedSize; // try to get the true maxAdvance value from 'hhea' gfxFontEntry::AutoTable hheaTable(GetFontEntry(), TRUETYPE_TAG('h', 'h', 'e', 'a')); if (hheaTable) { uint32_t len; const MetricsHeader* hhea = reinterpret_cast( hb_blob_get_data(hheaTable, &len)); if (len >= sizeof(MetricsHeader)) { mMetrics.maxAdvance = uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor; } } mMetrics.internalLeading = std::max(mMetrics.maxHeight - mMetrics.emHeight, 0.0); mMetrics.externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor); UINT32 ucs = L' '; UINT16 glyph; if (SUCCEEDED(mFontFace->GetGlyphIndices(&ucs, 1, &glyph)) && glyph != 0) { mSpaceGlyph = glyph; mMetrics.spaceWidth = MeasureGlyphWidth(glyph); } else { mMetrics.spaceWidth = 0; } // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x' // if the table is not available or if using hinted/pixel-snapped widths if (mUseSubpixelPositions) { mMetrics.aveCharWidth = 0; gfxFontEntry::AutoTable os2Table(GetFontEntry(), TRUETYPE_TAG('O', 'S', '/', '2')); if (os2Table) { uint32_t len; const OS2Table* os2 = reinterpret_cast(hb_blob_get_data(os2Table, &len)); if (len >= 4) { // Not checking against sizeof(mozilla::OS2Table) here because older // versions of the table have different sizes; we only need the first // two 16-bit fields here. mMetrics.aveCharWidth = int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor; } } } if (mMetrics.aveCharWidth < 1) { mMetrics.aveCharWidth = GetCharAdvance('x'); if (mMetrics.aveCharWidth < 1) { // Let's just assume the X is square. mMetrics.aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor; } } mMetrics.zeroWidth = GetCharAdvance('0'); mMetrics.ideographicWidth = GetCharAdvance(kWaterIdeograph); mMetrics.underlineOffset = fontMetrics.underlinePosition * mFUnitsConvFactor; mMetrics.underlineSize = fontMetrics.underlineThickness * mFUnitsConvFactor; mMetrics.strikeoutOffset = fontMetrics.strikethroughPosition * mFUnitsConvFactor; mMetrics.strikeoutSize = fontMetrics.strikethroughThickness * mFUnitsConvFactor; SanitizeMetrics(&mMetrics, GetFontEntry()->mIsBadUnderlineFont); if (ApplySyntheticBold()) { auto delta = GetSyntheticBoldOffset(); mMetrics.spaceWidth += delta; mMetrics.aveCharWidth += delta; mMetrics.maxAdvance += delta; if (mMetrics.zeroWidth > 0) { mMetrics.zeroWidth += delta; } if (mMetrics.ideographicWidth > 0) { mMetrics.ideographicWidth += delta; } } #if 0 printf("Font: %p (%s) size: %f\n", this, NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size); printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent); printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance); printf(" internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading); printf(" spaceWidth: %f aveCharWidth: %f zeroWidth: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.zeroWidth); printf(" xHeight: %f capHeight: %f\n", mMetrics.xHeight, mMetrics.capHeight); printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize); #endif } using namespace mozilla; // for AutoSwap_* types struct EBLCHeader { AutoSwap_PRUint32 version; AutoSwap_PRUint32 numSizes; }; struct SbitLineMetrics { int8_t ascender; int8_t descender; uint8_t widthMax; int8_t caretSlopeNumerator; int8_t caretSlopeDenominator; int8_t caretOffset; int8_t minOriginSB; int8_t minAdvanceSB; int8_t maxBeforeBL; int8_t minAfterBL; int8_t pad1; int8_t pad2; }; struct BitmapSizeTable { AutoSwap_PRUint32 indexSubTableArrayOffset; AutoSwap_PRUint32 indexTablesSize; AutoSwap_PRUint32 numberOfIndexSubTables; AutoSwap_PRUint32 colorRef; SbitLineMetrics hori; SbitLineMetrics vert; AutoSwap_PRUint16 startGlyphIndex; AutoSwap_PRUint16 endGlyphIndex; uint8_t ppemX; uint8_t ppemY; uint8_t bitDepth; uint8_t flags; }; typedef EBLCHeader EBSCHeader; struct BitmapScaleTable { SbitLineMetrics hori; SbitLineMetrics vert; uint8_t ppemX; uint8_t ppemY; uint8_t substitutePpemX; uint8_t substitutePpemY; }; bool gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize) { uint8_t* tableData; uint32_t len; void* tableContext; BOOL exists; HRESULT hr = mFontFace->TryGetFontTable( DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'), (const void**)&tableData, &len, &tableContext, &exists); if (FAILED(hr)) { return false; } bool hasStrike = false; // not really a loop, but this lets us use 'break' to skip out of the block // as soon as we know the answer, and skips it altogether if the table is // not present while (exists) { if (len < sizeof(EBLCHeader)) { break; } const EBLCHeader* hdr = reinterpret_cast(tableData); if (hdr->version != 0x00020000) { break; } uint32_t numSizes = hdr->numSizes; if (numSizes > 0xffff) { // sanity-check, prevent overflow below break; } if (len < sizeof(EBLCHeader) + numSizes * sizeof(BitmapSizeTable)) { break; } const BitmapSizeTable* sizeTable = reinterpret_cast(hdr + 1); for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) { if (sizeTable->ppemX == aSize && sizeTable->ppemY == aSize) { // we ignore a strike that contains fewer than 4 glyphs, // as that probably indicates a font such as Courier New // that provides bitmaps ONLY for the "shading" characters // U+2591..2593 hasStrike = (uint16_t(sizeTable->endGlyphIndex) >= uint16_t(sizeTable->startGlyphIndex) + 3); break; } } // if we reach here, we didn't find a strike; unconditionally break // out of the while-loop block break; } mFontFace->ReleaseFontTable(tableContext); if (hasStrike) { return true; } // if we didn't find a real strike, check if the font calls for scaling // another bitmap to this size hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'), (const void**)&tableData, &len, &tableContext, &exists); if (FAILED(hr)) { return false; } while (exists) { if (len < sizeof(EBSCHeader)) { break; } const EBSCHeader* hdr = reinterpret_cast(tableData); if (hdr->version != 0x00020000) { break; } uint32_t numSizes = hdr->numSizes; if (numSizes > 0xffff) { break; } if (len < sizeof(EBSCHeader) + numSizes * sizeof(BitmapScaleTable)) { break; } const BitmapScaleTable* scaleTable = reinterpret_cast(hdr + 1); for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) { if (scaleTable->ppemX == aSize && scaleTable->ppemY == aSize) { hasStrike = true; break; } } break; } mFontFace->ReleaseFontTable(tableContext); return hasStrike; } bool gfxDWriteFont::IsValid() const { return mFontFace != nullptr; } IDWriteFontFace* gfxDWriteFont::GetFontFace() { return mFontFace.get(); } gfxFont::RunMetrics gfxDWriteFont::Measure(const gfxTextRun* aTextRun, uint32_t aStart, uint32_t aEnd, BoundingBoxType aBoundingBoxType, DrawTarget* aRefDrawTarget, Spacing* aSpacing, gfx::ShapedTextFlags aOrientation) { gfxFont::RunMetrics metrics = gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType, aRefDrawTarget, aSpacing, aOrientation); // if aBoundingBoxType is LOOSE_INK_EXTENTS // and the underlying cairo font may be antialiased, // we can't trust Windows to have considered all the pixels // so we need to add "padding" to the bounds. // (see bugs 475968, 439831, compare also bug 445087) if (aBoundingBoxType == LOOSE_INK_EXTENTS && mAntialiasOption != kAntialiasNone && GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC && metrics.mBoundingBox.Width() > 0) { metrics.mBoundingBox.MoveByX(-aTextRun->GetAppUnitsPerDevUnit()); metrics.mBoundingBox.SetWidth(metrics.mBoundingBox.Width() + aTextRun->GetAppUnitsPerDevUnit() * 3); } return metrics; } bool gfxDWriteFont::ProvidesGlyphWidths() const { return !mUseSubpixelPositions || (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD) || ((gfxDWriteFontEntry*)(GetFontEntry()))->HasVariations(); } int32_t gfxDWriteFont::GetGlyphWidth(uint16_t aGID) { if (!mGlyphWidths) { mGlyphWidths = MakeUnique>(128); } return mGlyphWidths->LookupOrInsertWith( aGID, [&] { return NS_lround(MeasureGlyphWidth(aGID) * 65536.0); }); } bool gfxDWriteFont::GetForceGDIClassic() const { return sForceGDIClassicEnabled && static_cast(mFontEntry.get()) ->GetForceGDIClassic() && GetAdjustedSize() <= gfxDWriteFontList::PlatformFontList() ->GetForceGDIClassicMaxFontSize(); } DWRITE_MEASURING_MODE gfxDWriteFont::GetMeasuringMode() const { return DWriteSettings::Get(GetForceGDIClassic()).MeasuringMode(); } gfxFloat gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph) { MOZ_SEH_TRY { HRESULT hr; if (mFontFace1) { int32_t advance; if (mUseSubpixelPositions) { hr = mFontFace1->GetDesignGlyphAdvances(1, &aGlyph, &advance, FALSE); if (SUCCEEDED(hr)) { return advance * mFUnitsConvFactor; } } else { hr = mFontFace1->GetGdiCompatibleGlyphAdvances( FLOAT(mAdjustedSize), 1.0f, nullptr, GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL, FALSE, 1, &aGlyph, &advance); if (SUCCEEDED(hr)) { return NS_lround(advance * mFUnitsConvFactor); } } } else { DWRITE_GLYPH_METRICS metrics; if (mUseSubpixelPositions) { hr = mFontFace->GetDesignGlyphMetrics(&aGlyph, 1, &metrics, FALSE); if (SUCCEEDED(hr)) { return metrics.advanceWidth * mFUnitsConvFactor; } } else { hr = mFontFace->GetGdiCompatibleGlyphMetrics( FLOAT(mAdjustedSize), 1.0f, nullptr, GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL, &aGlyph, 1, &metrics, FALSE); if (SUCCEEDED(hr)) { return NS_lround(metrics.advanceWidth * mFUnitsConvFactor); } } } } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use // the font resource; possibly a failing drive or similar hardware issue. // Mark the font as invalid, and wipe the fontEntry's charmap so that font // selection will skip it; we'll use a fallback font instead. mIsValid = false; GetFontEntry()->mCharacterMap = new gfxCharacterMap(); GetFontEntry()->mShmemCharacterMap = nullptr; gfxCriticalError() << "Exception occurred measuring glyph width for " << GetFontEntry()->Name().get(); } return 0.0; } bool gfxDWriteFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) { MOZ_SEH_TRY { DWRITE_GLYPH_METRICS m; HRESULT hr = mFontFace->GetDesignGlyphMetrics(&aGID, 1, &m, FALSE); if (FAILED(hr)) { return false; } gfxRect bounds(m.leftSideBearing, m.topSideBearing - m.verticalOriginY, m.advanceWidth - m.leftSideBearing - m.rightSideBearing, m.advanceHeight - m.topSideBearing - m.bottomSideBearing); bounds.Scale(mFUnitsConvFactor); // GetDesignGlyphMetrics returns 'ideal' glyph metrics, we need to pad to // account for antialiasing. if (!aTight && !aBounds->IsEmpty()) { bounds.Inflate(1.0, 0.0); } *aBounds = bounds; return true; } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use // the font resource; possibly a failing drive or similar hardware issue. // Mark the font as invalid, and wipe the fontEntry's charmap so that font // selection will skip it; we'll use a fallback font instead. mIsValid = false; GetFontEntry()->mCharacterMap = new gfxCharacterMap(); GetFontEntry()->mShmemCharacterMap = nullptr; gfxCriticalError() << "Exception occurred measuring glyph bounds for " << GetFontEntry()->Name().get(); } return false; } void gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, FontCacheSizes* aSizes) const { gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); if (mGlyphWidths) { aSizes->mFontInstances += mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf); } } void gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, FontCacheSizes* aSizes) const { aSizes->mFontInstances += aMallocSizeOf(this); AddSizeOfExcludingThis(aMallocSizeOf, aSizes); } already_AddRefed gfxDWriteFont::GetScaledFont( const TextRunDrawParams& aRunParams) { bool useClearType = UsingClearType(); if (mAzureScaledFontUsedClearType != useClearType) { if (auto* oldScaledFont = mAzureScaledFont.exchange(nullptr)) { oldScaledFont->Release(); } if (auto* oldScaledFont = mAzureScaledFontGDI.exchange(nullptr)) { oldScaledFont->Release(); } } bool forceGDI = aRunParams.allowGDI && GetForceGDIClassic(); ScaledFont* scaledFont = forceGDI ? mAzureScaledFontGDI : mAzureScaledFont; if (scaledFont) { return do_AddRef(scaledFont); } gfxDWriteFontEntry* fe = static_cast(mFontEntry.get()); bool useEmbeddedBitmap = (gfxVars::SystemTextRenderingMode() == DWRITE_RENDERING_MODE_DEFAULT || forceGDI) && fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize)); const gfxFontStyle* fontStyle = GetStyle(); RefPtr newScaledFont = Factory::CreateScaledFontForDWriteFont( mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(), useEmbeddedBitmap, ApplySyntheticBold(), forceGDI); if (!newScaledFont) { return nullptr; } InitializeScaledFont(newScaledFont); if (forceGDI) { if (mAzureScaledFontGDI.compareExchange(nullptr, newScaledFont.get())) { Unused << newScaledFont.forget(); mAzureScaledFontUsedClearType = useClearType; } scaledFont = mAzureScaledFontGDI; } else { if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) { Unused << newScaledFont.forget(); mAzureScaledFontUsedClearType = useClearType; } scaledFont = mAzureScaledFont; } return do_AddRef(scaledFont); } bool gfxDWriteFont::ShouldRoundXOffset(cairo_t* aCairo) const { // show_glyphs is implemented on the font and so is used for all Cairo // surface types; however, it may pixel-snap depending on the dwrite // rendering mode return GetMeasuringMode() != DWRITE_MEASURING_MODE_NATURAL; }