tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxDWriteFonts.cpp (30432B)


      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/. */
      5 
      6 #include "gfxDWriteFonts.h"
      7 
      8 #include <algorithm>
      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"
     18 
     19 #include "harfbuzz/hb.h"
     20 #include "mozilla/FontPropertyTypes.h"
     21 
     22 using namespace mozilla;
     23 using namespace mozilla::gfx;
     24 
     25 // Code to determine whether Windows is set to use ClearType font smoothing;
     26 // based on private functions in cairo-win32-font.c
     27 
     28 #ifndef SPI_GETFONTSMOOTHINGTYPE
     29 #  define SPI_GETFONTSMOOTHINGTYPE 0x200a
     30 #endif
     31 #ifndef FE_FONTSMOOTHINGCLEARTYPE
     32 #  define FE_FONTSMOOTHINGCLEARTYPE 2
     33 #endif
     34 
     35 // Cleartype can be dynamically enabled/disabled, so we have to allow for
     36 // dynamically updating it.
     37 static BYTE GetSystemTextQuality() {
     38  BOOL font_smoothing;
     39  UINT smoothing_type;
     40 
     41  if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
     42    return DEFAULT_QUALITY;
     43  }
     44 
     45  if (font_smoothing) {
     46    if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smoothing_type,
     47                              0)) {
     48      return DEFAULT_QUALITY;
     49    }
     50 
     51    if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) {
     52      return CLEARTYPE_QUALITY;
     53    }
     54 
     55    return ANTIALIASED_QUALITY;
     56  }
     57 
     58  return DEFAULT_QUALITY;
     59 }
     60 
     61 #ifndef SPI_GETFONTSMOOTHINGCONTRAST
     62 #  define SPI_GETFONTSMOOTHINGCONTRAST 0x200c
     63 #endif
     64 
     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() {
     68  UINT value = 0;
     69  if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &value, 0) ||
     70      value < 1000 || value > 2200) {
     71    value = 1400;
     72  }
     73  return value / 1000.0f;
     74 }
     75 
     76 ////////////////////////////////////////////////////////////////////////////////
     77 // gfxDWriteFont
     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;
     98        break;
     99      case 1:  // use DWrite simulation for installed fonts except COLR fonts,
    100               // but not webfonts
    101        mApplySyntheticBold =
    102            aFontEntry->mIsDataUserFont ||
    103            aFontEntry->HasFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'));
    104        break;
    105      default:  // always use DWrite bold simulation, except for COLR fonts
    106        mApplySyntheticBold =
    107            aFontEntry->HasFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'));
    108        break;
    109    }
    110  }
    111  ComputeMetrics(anAAOption);
    112 }
    113 
    114 gfxDWriteFont::~gfxDWriteFont() {
    115  if (auto* scaledFont = mAzureScaledFontGDI.exchange(nullptr)) {
    116    scaledFont->Release();
    117  }
    118 }
    119 
    120 /* static */
    121 bool gfxDWriteFont::InitDWriteSupport() {
    122  if (!Factory::EnsureDWriteFactory()) {
    123    return false;
    124  }
    125 
    126  if (XRE_IsParentProcess()) {
    127    UpdateSystemTextVars();
    128  } else {
    129    // UpdateClearTypeVars doesn't update the vars in non parent processes, but
    130    // it does set sForceGDIClassicEnabled so we still need to call it.
    131    UpdateClearTypeVars();
    132  }
    133  DWriteSettings::Initialize();
    134 
    135  return true;
    136 }
    137 
    138 /* static */
    139 void gfxDWriteFont::UpdateSystemTextVars() {
    140  MOZ_ASSERT(XRE_IsParentProcess());
    141 
    142  if (!gfxVars::IsInitialized()) {
    143    return;
    144  }
    145 
    146  BYTE newQuality = GetSystemTextQuality();
    147  if (gfxVars::SystemTextQuality() != newQuality) {
    148    gfxVars::SetSystemTextQuality(newQuality);
    149  }
    150 
    151  FLOAT newGDIGamma = GetSystemGDIGamma();
    152  if (gfxVars::SystemGDIGamma() != newGDIGamma) {
    153    gfxVars::SetSystemGDIGamma(newGDIGamma);
    154  }
    155 
    156  UpdateClearTypeVars();
    157 }
    158 
    159 void gfxDWriteFont::SystemTextQualityChanged() {
    160  // If ClearType status has changed, update our value,
    161  Factory::SetSystemTextQuality(gfxVars::SystemTextQuality());
    162  // flush cached stuff that depended on the old setting, and force
    163  // reflow everywhere to ensure we are using correct glyph metrics.
    164  gfxPlatform::FlushFontAndWordCaches();
    165  gfxPlatform::ForceGlobalReflow(gfxPlatform::GlobalReflowFlags::None);
    166 }
    167 
    168 mozilla::Atomic<bool> gfxDWriteFont::sForceGDIClassicEnabled{true};
    169 
    170 /* static */
    171 void gfxDWriteFont::UpdateClearTypeVars() {
    172  // We don't force GDI classic if the cleartype rendering mode pref is set to
    173  // something valid.
    174  int32_t renderingModePref =
    175      Preferences::GetInt(GFX_CLEARTYPE_PARAMS_MODE, -1);
    176  if (renderingModePref < 0 || renderingModePref > 5) {
    177    renderingModePref = -1;
    178  }
    179  sForceGDIClassicEnabled = (renderingModePref == -1);
    180 
    181  if (!XRE_IsParentProcess()) {
    182    return;
    183  }
    184 
    185  if (!Factory::GetDWriteFactory()) {
    186    return;
    187  }
    188 
    189  // First set sensible hard coded defaults.
    190  float clearTypeLevel = 1.0f;
    191  float enhancedContrast = 1.0f;
    192  float gamma = 2.2f;
    193  int pixelGeometry = DWRITE_PIXEL_GEOMETRY_RGB;
    194  int renderingMode = DWRITE_RENDERING_MODE_DEFAULT;
    195 
    196  // Override these from DWrite function if available.
    197  RefPtr<IDWriteRenderingParams> defaultRenderingParams;
    198  HRESULT hr = Factory::GetDWriteFactory()->CreateRenderingParams(
    199      getter_AddRefs(defaultRenderingParams));
    200  if (SUCCEEDED(hr) && defaultRenderingParams) {
    201    clearTypeLevel = defaultRenderingParams->GetClearTypeLevel();
    202 
    203    // For enhanced contrast, we only use the default if the user has set it
    204    // in the registry (by using the ClearType Tuner).
    205    // XXXbobowen it seems slightly odd that we do this and only for enhanced
    206    // contrast, but this reproduces previous functionality from
    207    // gfxWindowsPlatform::SetupClearTypeParams.
    208    HKEY hKey;
    209    LONG res = RegOpenKeyExW(DISPLAY1_REGISTRY_KEY, 0, KEY_READ, &hKey);
    210    if (res == ERROR_SUCCESS) {
    211      res = RegQueryValueExW(hKey, ENHANCED_CONTRAST_VALUE_NAME, nullptr,
    212                             nullptr, nullptr, nullptr);
    213      if (res == ERROR_SUCCESS) {
    214        enhancedContrast = defaultRenderingParams->GetEnhancedContrast();
    215      }
    216      RegCloseKey(hKey);
    217    }
    218 
    219    gamma = defaultRenderingParams->GetGamma();
    220    pixelGeometry = defaultRenderingParams->GetPixelGeometry();
    221    renderingMode = defaultRenderingParams->GetRenderingMode();
    222  } else {
    223    gfxWarning() << "Failed to create default rendering params";
    224  }
    225 
    226  // Finally override from prefs if valid values are set. If ClearType is
    227  // turned off we just use the default params, this reproduces the previous
    228  // functionality that was spread across gfxDWriteFont::GetScaledFont and
    229  // gfxWindowsPlatform::SetupClearTypeParams, but it seems odd because the
    230  // default params will still be the ClearType ones although we won't use the
    231  // anti-alias for ClearType because of GetSystemDefaultAAMode.
    232  if (gfxVars::SystemTextQuality() == CLEARTYPE_QUALITY) {
    233    int32_t prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_LEVEL, -1);
    234    if (prefInt >= 0 && prefInt <= 100) {
    235      clearTypeLevel = float(prefInt / 100.0);
    236    }
    237 
    238    prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_CONTRAST, -1);
    239    if (prefInt >= 0 && prefInt <= 1000) {
    240      enhancedContrast = float(prefInt / 100.0);
    241    }
    242 
    243    prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_GAMMA, -1);
    244    if (prefInt >= 1000 && prefInt <= 2200) {
    245      gamma = float(prefInt / 1000.0);
    246    }
    247 
    248    prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_STRUCTURE, -1);
    249    if (prefInt >= 0 && prefInt <= 2) {
    250      pixelGeometry = prefInt;
    251    }
    252 
    253    // renderingModePref is retrieved and validated above.
    254    if (renderingModePref != -1) {
    255      renderingMode = renderingModePref;
    256    }
    257  }
    258 
    259  if (gfxVars::SystemTextClearTypeLevel() != clearTypeLevel) {
    260    gfxVars::SetSystemTextClearTypeLevel(clearTypeLevel);
    261  }
    262 
    263  if (gfxVars::SystemTextEnhancedContrast() != enhancedContrast) {
    264    gfxVars::SetSystemTextEnhancedContrast(enhancedContrast);
    265  }
    266 
    267  if (gfxVars::SystemTextGamma() != gamma) {
    268    gfxVars::SetSystemTextGamma(gamma);
    269  }
    270 
    271  if (gfxVars::SystemTextPixelGeometry() != pixelGeometry) {
    272    gfxVars::SetSystemTextPixelGeometry(pixelGeometry);
    273  }
    274 
    275  if (gfxVars::SystemTextRenderingMode() != renderingMode) {
    276    gfxVars::SetSystemTextRenderingMode(renderingMode);
    277  }
    278 
    279 #if 0
    280  // Set cairo dwrite params in the parent process where it might still be
    281  // needed for printing. We use the validated pref int directly for rendering
    282  // mode, because a negative (i.e. not set) rendering mode is also used for
    283  // deciding on forcing GDI in cairo.
    284  cairo_dwrite_set_cleartype_params(gamma, enhancedContrast, clearTypeLevel,
    285                                    pixelGeometry, renderingModePref);
    286 #endif
    287 }
    288 
    289 gfxFont* gfxDWriteFont::CopyWithAntialiasOption(
    290    AntialiasOption anAAOption) const {
    291  auto entry = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
    292  RefPtr<UnscaledFontDWrite> unscaledFont =
    293      static_cast<UnscaledFontDWrite*>(mUnscaledFont.get());
    294  return new gfxDWriteFont(unscaledFont, entry, &mStyle, mFontFace, anAAOption);
    295 }
    296 
    297 bool gfxDWriteFont::GetFakeMetricsForArialBlack(
    298    DWRITE_FONT_METRICS* aFontMetrics) {
    299  gfxFontStyle style(mStyle);
    300  style.weight = FontWeight::FromInt(700);
    301 
    302  gfxFontEntry* fe = gfxPlatformFontList::PlatformFontList()->FindFontForFamily(
    303      nullptr, "Arial"_ns, &style);
    304  if (!fe || fe == mFontEntry) {
    305    return false;
    306  }
    307 
    308  RefPtr<gfxFont> font = fe->FindOrMakeFont(&style);
    309  gfxDWriteFont* dwFont = static_cast<gfxDWriteFont*>(font.get());
    310  dwFont->mFontFace->GetMetrics(aFontMetrics);
    311 
    312  return true;
    313 }
    314 
    315 void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
    316  ::memset(&mMetrics, 0, sizeof(mMetrics));
    317 
    318  DWRITE_FONT_METRICS fontMetrics;
    319  if (!(mFontEntry->Weight().Min() == FontWeight::FromInt(900) &&
    320        mFontEntry->Weight().Max() == FontWeight::FromInt(900) &&
    321        !mFontEntry->IsUserFont() &&
    322        mFontEntry->Name().EqualsLiteral("Arial Black") &&
    323        GetFakeMetricsForArialBlack(&fontMetrics))) {
    324    mFontFace->GetMetrics(&fontMetrics);
    325  }
    326 
    327  if (GetAdjustedSize() > 0.0 && mStyle.sizeAdjust >= 0.0 &&
    328      FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) !=
    329          FontSizeAdjust::Tag::None) {
    330    // For accurate measurement during the font-size-adjust computations;
    331    // these may be reset later according to the adjusted size.
    332    mUseSubpixelPositions = true;
    333    mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
    334    gfxFloat aspect;
    335    switch (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis)) {
    336      default:
    337        MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
    338        aspect = 0.0;
    339        break;
    340      case FontSizeAdjust::Tag::ExHeight:
    341        aspect = (gfxFloat)fontMetrics.xHeight / fontMetrics.designUnitsPerEm;
    342        break;
    343      case FontSizeAdjust::Tag::CapHeight:
    344        aspect = (gfxFloat)fontMetrics.capHeight / fontMetrics.designUnitsPerEm;
    345        break;
    346      case FontSizeAdjust::Tag::ChWidth: {
    347        gfxFloat advance = GetCharAdvance('0');
    348        aspect = advance > 0.0 ? advance / mAdjustedSize : 0.5;
    349        break;
    350      }
    351      case FontSizeAdjust::Tag::IcWidth:
    352      case FontSizeAdjust::Tag::IcHeight: {
    353        bool vertical = FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) ==
    354                        FontSizeAdjust::Tag::IcHeight;
    355        gfxFloat advance = GetCharAdvance(kWaterIdeograph, vertical);
    356        aspect = advance > 0.0 ? advance / mAdjustedSize : 1.0;
    357        break;
    358      }
    359    }
    360    if (aspect > 0.0) {
    361      // If we created a shaper above (to measure glyphs), discard it so we
    362      // get a new one for the adjusted scaling.
    363      delete mHarfBuzzShaper.exchange(nullptr);
    364      mAdjustedSize = mStyle.GetAdjustedSize(aspect);
    365    }
    366  }
    367 
    368  // Update now that we've adjusted the size if necessary.
    369  mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
    370 
    371  // Note that GetMeasuringMode depends on mAdjustedSize
    372  if ((anAAOption == gfxFont::kAntialiasDefault && UsingClearType() &&
    373       GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL) ||
    374      anAAOption == gfxFont::kAntialiasSubpixel) {
    375    mUseSubpixelPositions = true;
    376    // note that this may be reset to FALSE if we determine that a bitmap
    377    // strike is going to be used
    378  } else {
    379    mUseSubpixelPositions = false;
    380  }
    381 
    382  gfxDWriteFontEntry* fe = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
    383  if (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize))) {
    384    mAdjustedSize = NS_lround(mAdjustedSize);
    385    mUseSubpixelPositions = false;
    386    // if we have bitmaps, we need to tell Cairo NOT to use subpixel AA,
    387    // to avoid the manual-subpixel codepath in cairo-d2d-surface.cpp
    388    // which fails to render bitmap glyphs (see bug 626299).
    389    // This option will be passed to the cairo_dwrite_scaled_font_t
    390    // after creation.
    391    mAllowManualShowGlyphs = false;
    392  }
    393 
    394  mMetrics.xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
    395  mMetrics.capHeight = fontMetrics.capHeight * mFUnitsConvFactor;
    396 
    397  mMetrics.maxAscent = round(fontMetrics.ascent * mFUnitsConvFactor);
    398  mMetrics.maxDescent = round(fontMetrics.descent * mFUnitsConvFactor);
    399  mMetrics.maxHeight = mMetrics.maxAscent + mMetrics.maxDescent;
    400 
    401  mMetrics.emHeight = mAdjustedSize;
    402  mMetrics.emAscent =
    403      mMetrics.emHeight * mMetrics.maxAscent / mMetrics.maxHeight;
    404  mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent;
    405 
    406  mMetrics.maxAdvance = mAdjustedSize;
    407 
    408  // try to get the true maxAdvance value from 'hhea'
    409  gfxFontEntry::AutoTable hheaTable(GetFontEntry(),
    410                                    TRUETYPE_TAG('h', 'h', 'e', 'a'));
    411  if (hheaTable) {
    412    uint32_t len;
    413    const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
    414        hb_blob_get_data(hheaTable, &len));
    415    if (len >= sizeof(MetricsHeader)) {
    416      mMetrics.maxAdvance = uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
    417    }
    418  }
    419 
    420  mMetrics.internalLeading =
    421      std::max(mMetrics.maxHeight - mMetrics.emHeight, 0.0);
    422  mMetrics.externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
    423 
    424  UINT32 ucs = L' ';
    425  UINT16 glyph;
    426  if (SUCCEEDED(mFontFace->GetGlyphIndices(&ucs, 1, &glyph)) && glyph != 0) {
    427    mSpaceGlyph = glyph;
    428    mMetrics.spaceWidth = MeasureGlyphWidth(glyph);
    429  } else {
    430    mMetrics.spaceWidth = 0;
    431  }
    432 
    433  // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
    434  // if the table is not available or if using hinted/pixel-snapped widths
    435  if (mUseSubpixelPositions) {
    436    mMetrics.aveCharWidth = 0;
    437    gfxFontEntry::AutoTable os2Table(GetFontEntry(),
    438                                     TRUETYPE_TAG('O', 'S', '/', '2'));
    439    if (os2Table) {
    440      uint32_t len;
    441      const OS2Table* os2 =
    442          reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
    443      if (len >= 4) {
    444        // Not checking against sizeof(mozilla::OS2Table) here because older
    445        // versions of the table have different sizes; we only need the first
    446        // two 16-bit fields here.
    447        mMetrics.aveCharWidth = int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor;
    448      }
    449    }
    450  }
    451 
    452  if (mMetrics.aveCharWidth < 1) {
    453    mMetrics.aveCharWidth = GetCharAdvance('x');
    454    if (mMetrics.aveCharWidth < 1) {
    455      // Let's just assume the X is square.
    456      mMetrics.aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
    457    }
    458  }
    459 
    460  mMetrics.zeroWidth = GetCharAdvance('0');
    461 
    462  mMetrics.ideographicWidth = GetCharAdvance(kWaterIdeograph);
    463 
    464  mMetrics.underlineOffset = fontMetrics.underlinePosition * mFUnitsConvFactor;
    465  mMetrics.underlineSize = fontMetrics.underlineThickness * mFUnitsConvFactor;
    466  mMetrics.strikeoutOffset =
    467      fontMetrics.strikethroughPosition * mFUnitsConvFactor;
    468  mMetrics.strikeoutSize =
    469      fontMetrics.strikethroughThickness * mFUnitsConvFactor;
    470 
    471  SanitizeMetrics(&mMetrics, GetFontEntry()->mIsBadUnderlineFont);
    472 
    473  if (ApplySyntheticBold()) {
    474    auto delta = GetSyntheticBoldOffset();
    475    mMetrics.spaceWidth += delta;
    476    mMetrics.aveCharWidth += delta;
    477    mMetrics.maxAdvance += delta;
    478    if (mMetrics.zeroWidth > 0) {
    479      mMetrics.zeroWidth += delta;
    480    }
    481    if (mMetrics.ideographicWidth > 0) {
    482      mMetrics.ideographicWidth += delta;
    483    }
    484  }
    485 
    486 #if 0
    487    printf("Font: %p (%s) size: %f\n", this,
    488           NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
    489    printf("    emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
    490    printf("    maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
    491    printf("    internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
    492    printf("    spaceWidth: %f aveCharWidth: %f zeroWidth: %f\n",
    493           mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.zeroWidth);
    494    printf("    xHeight: %f capHeight: %f\n", mMetrics.xHeight, mMetrics.capHeight);
    495    printf("    uOff: %f uSize: %f stOff: %f stSize: %f\n",
    496           mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
    497 #endif
    498 }
    499 
    500 using namespace mozilla;  // for AutoSwap_* types
    501 
    502 struct EBLCHeader {
    503  AutoSwap_PRUint32 version;
    504  AutoSwap_PRUint32 numSizes;
    505 };
    506 
    507 struct SbitLineMetrics {
    508  int8_t ascender;
    509  int8_t descender;
    510  uint8_t widthMax;
    511  int8_t caretSlopeNumerator;
    512  int8_t caretSlopeDenominator;
    513  int8_t caretOffset;
    514  int8_t minOriginSB;
    515  int8_t minAdvanceSB;
    516  int8_t maxBeforeBL;
    517  int8_t minAfterBL;
    518  int8_t pad1;
    519  int8_t pad2;
    520 };
    521 
    522 struct BitmapSizeTable {
    523  AutoSwap_PRUint32 indexSubTableArrayOffset;
    524  AutoSwap_PRUint32 indexTablesSize;
    525  AutoSwap_PRUint32 numberOfIndexSubTables;
    526  AutoSwap_PRUint32 colorRef;
    527  SbitLineMetrics hori;
    528  SbitLineMetrics vert;
    529  AutoSwap_PRUint16 startGlyphIndex;
    530  AutoSwap_PRUint16 endGlyphIndex;
    531  uint8_t ppemX;
    532  uint8_t ppemY;
    533  uint8_t bitDepth;
    534  uint8_t flags;
    535 };
    536 
    537 typedef EBLCHeader EBSCHeader;
    538 
    539 struct BitmapScaleTable {
    540  SbitLineMetrics hori;
    541  SbitLineMetrics vert;
    542  uint8_t ppemX;
    543  uint8_t ppemY;
    544  uint8_t substitutePpemX;
    545  uint8_t substitutePpemY;
    546 };
    547 
    548 bool gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize) {
    549  uint8_t* tableData;
    550  uint32_t len;
    551  void* tableContext;
    552  BOOL exists;
    553  HRESULT hr = mFontFace->TryGetFontTable(
    554      DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'), (const void**)&tableData,
    555      &len, &tableContext, &exists);
    556  if (FAILED(hr)) {
    557    return false;
    558  }
    559 
    560  bool hasStrike = false;
    561  // not really a loop, but this lets us use 'break' to skip out of the block
    562  // as soon as we know the answer, and skips it altogether if the table is
    563  // not present
    564  while (exists) {
    565    if (len < sizeof(EBLCHeader)) {
    566      break;
    567    }
    568    const EBLCHeader* hdr = reinterpret_cast<const EBLCHeader*>(tableData);
    569    if (hdr->version != 0x00020000) {
    570      break;
    571    }
    572    uint32_t numSizes = hdr->numSizes;
    573    if (numSizes > 0xffff) {  // sanity-check, prevent overflow below
    574      break;
    575    }
    576    if (len < sizeof(EBLCHeader) + numSizes * sizeof(BitmapSizeTable)) {
    577      break;
    578    }
    579    const BitmapSizeTable* sizeTable =
    580        reinterpret_cast<const BitmapSizeTable*>(hdr + 1);
    581    for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
    582      if (sizeTable->ppemX == aSize && sizeTable->ppemY == aSize) {
    583        // we ignore a strike that contains fewer than 4 glyphs,
    584        // as that probably indicates a font such as Courier New
    585        // that provides bitmaps ONLY for the "shading" characters
    586        // U+2591..2593
    587        hasStrike = (uint16_t(sizeTable->endGlyphIndex) >=
    588                     uint16_t(sizeTable->startGlyphIndex) + 3);
    589        break;
    590      }
    591    }
    592    // if we reach here, we didn't find a strike; unconditionally break
    593    // out of the while-loop block
    594    break;
    595  }
    596  mFontFace->ReleaseFontTable(tableContext);
    597 
    598  if (hasStrike) {
    599    return true;
    600  }
    601 
    602  // if we didn't find a real strike, check if the font calls for scaling
    603  // another bitmap to this size
    604  hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'),
    605                                  (const void**)&tableData, &len, &tableContext,
    606                                  &exists);
    607  if (FAILED(hr)) {
    608    return false;
    609  }
    610 
    611  while (exists) {
    612    if (len < sizeof(EBSCHeader)) {
    613      break;
    614    }
    615    const EBSCHeader* hdr = reinterpret_cast<const EBSCHeader*>(tableData);
    616    if (hdr->version != 0x00020000) {
    617      break;
    618    }
    619    uint32_t numSizes = hdr->numSizes;
    620    if (numSizes > 0xffff) {
    621      break;
    622    }
    623    if (len < sizeof(EBSCHeader) + numSizes * sizeof(BitmapScaleTable)) {
    624      break;
    625    }
    626    const BitmapScaleTable* scaleTable =
    627        reinterpret_cast<const BitmapScaleTable*>(hdr + 1);
    628    for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
    629      if (scaleTable->ppemX == aSize && scaleTable->ppemY == aSize) {
    630        hasStrike = true;
    631        break;
    632      }
    633    }
    634    break;
    635  }
    636  mFontFace->ReleaseFontTable(tableContext);
    637 
    638  return hasStrike;
    639 }
    640 
    641 bool gfxDWriteFont::IsValid() const { return mFontFace != nullptr; }
    642 
    643 IDWriteFontFace* gfxDWriteFont::GetFontFace() { return mFontFace.get(); }
    644 
    645 gfxFont::RunMetrics gfxDWriteFont::Measure(const gfxTextRun* aTextRun,
    646                                           uint32_t aStart, uint32_t aEnd,
    647                                           BoundingBoxType aBoundingBoxType,
    648                                           DrawTarget* aRefDrawTarget,
    649                                           Spacing* aSpacing,
    650                                           gfx::ShapedTextFlags aOrientation) {
    651  gfxFont::RunMetrics metrics =
    652      gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType, aRefDrawTarget,
    653                       aSpacing, aOrientation);
    654 
    655  // if aBoundingBoxType is LOOSE_INK_EXTENTS
    656  // and the underlying cairo font may be antialiased,
    657  // we can't trust Windows to have considered all the pixels
    658  // so we need to add "padding" to the bounds.
    659  // (see bugs 475968, 439831, compare also bug 445087)
    660  if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
    661      mAntialiasOption != kAntialiasNone &&
    662      GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC &&
    663      metrics.mBoundingBox.Width() > 0) {
    664    metrics.mBoundingBox.MoveByX(-aTextRun->GetAppUnitsPerDevUnit());
    665    metrics.mBoundingBox.SetWidth(metrics.mBoundingBox.Width() +
    666                                  aTextRun->GetAppUnitsPerDevUnit() * 3);
    667  }
    668 
    669  return metrics;
    670 }
    671 
    672 bool gfxDWriteFont::ProvidesGlyphWidths() const {
    673  return !mUseSubpixelPositions ||
    674         (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD) ||
    675         ((gfxDWriteFontEntry*)(GetFontEntry()))->HasVariations();
    676 }
    677 
    678 int32_t gfxDWriteFont::GetGlyphWidth(uint16_t aGID) {
    679  if (!mGlyphWidths) {
    680    mGlyphWidths = MakeUnique<nsTHashMap<nsUint32HashKey, int32_t>>(128);
    681  }
    682 
    683  return mGlyphWidths->LookupOrInsertWith(
    684      aGID, [&] { return NS_lround(MeasureGlyphWidth(aGID) * 65536.0); });
    685 }
    686 
    687 bool gfxDWriteFont::GetForceGDIClassic() const {
    688  return sForceGDIClassicEnabled && mStyle.allowForceGDIClassic &&
    689         static_cast<gfxDWriteFontEntry*>(mFontEntry.get())
    690             ->GetForceGDIClassic() &&
    691         GetAdjustedSize() <= gfxDWriteFontList::PlatformFontList()
    692                                  ->GetForceGDIClassicMaxFontSize() &&
    693         GetAdjustedSize() >= 6.0;
    694 }
    695 
    696 DWRITE_MEASURING_MODE
    697 gfxDWriteFont::GetMeasuringMode() const {
    698  return DWriteSettings::Get(GetForceGDIClassic()).MeasuringMode();
    699 }
    700 
    701 gfxFloat gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph) {
    702  MOZ_SEH_TRY {
    703    HRESULT hr;
    704    if (mFontFace1) {
    705      int32_t advance;
    706      if (mUseSubpixelPositions) {
    707        hr = mFontFace1->GetDesignGlyphAdvances(1, &aGlyph, &advance, FALSE);
    708        if (SUCCEEDED(hr)) {
    709          return advance * mFUnitsConvFactor;
    710        }
    711      } else {
    712        hr = mFontFace1->GetGdiCompatibleGlyphAdvances(
    713            FLOAT(mAdjustedSize), 1.0f, nullptr,
    714            GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL, FALSE, 1,
    715            &aGlyph, &advance);
    716        if (SUCCEEDED(hr)) {
    717          return NS_lround(advance * mFUnitsConvFactor);
    718        }
    719      }
    720    } else {
    721      DWRITE_GLYPH_METRICS metrics;
    722      if (mUseSubpixelPositions) {
    723        hr = mFontFace->GetDesignGlyphMetrics(&aGlyph, 1, &metrics, FALSE);
    724        if (SUCCEEDED(hr)) {
    725          return metrics.advanceWidth * mFUnitsConvFactor;
    726        }
    727      } else {
    728        hr = mFontFace->GetGdiCompatibleGlyphMetrics(
    729            FLOAT(mAdjustedSize), 1.0f, nullptr,
    730            GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL, &aGlyph, 1,
    731            &metrics, FALSE);
    732        if (SUCCEEDED(hr)) {
    733          return NS_lround(metrics.advanceWidth * mFUnitsConvFactor);
    734        }
    735      }
    736    }
    737  }
    738  MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
    739    // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
    740    // the font resource; possibly a failing drive or similar hardware issue.
    741    // Mark the font as invalid, and wipe the fontEntry's charmap so that font
    742    // selection will skip it; we'll use a fallback font instead.
    743    mIsValid = false;
    744    GetFontEntry()->mCharacterMap = new gfxCharacterMap(0);
    745    GetFontEntry()->mShmemCharacterMap = nullptr;
    746    gfxCriticalError() << "Exception occurred measuring glyph width for "
    747                       << GetFontEntry()->Name().get();
    748  }
    749  return 0.0;
    750 }
    751 
    752 bool gfxDWriteFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
    753                                   bool aTight) {
    754  MOZ_SEH_TRY {
    755    DWRITE_GLYPH_METRICS m;
    756    HRESULT hr = mFontFace->GetDesignGlyphMetrics(&aGID, 1, &m, FALSE);
    757    if (FAILED(hr)) {
    758      return false;
    759    }
    760    gfxRect bounds(m.leftSideBearing, m.topSideBearing - m.verticalOriginY,
    761                   m.advanceWidth - m.leftSideBearing - m.rightSideBearing,
    762                   m.advanceHeight - m.topSideBearing - m.bottomSideBearing);
    763    bounds.Scale(mFUnitsConvFactor);
    764    // GetDesignGlyphMetrics returns 'ideal' glyph metrics, we need to pad to
    765    // account for antialiasing.
    766    if (!aTight && !aBounds->IsEmpty()) {
    767      bounds.Inflate(1.0, 0.0);
    768    }
    769    *aBounds = bounds;
    770    return true;
    771  }
    772  MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
    773    // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
    774    // the font resource; possibly a failing drive or similar hardware issue.
    775    // Mark the font as invalid, and wipe the fontEntry's charmap so that font
    776    // selection will skip it; we'll use a fallback font instead.
    777    mIsValid = false;
    778    GetFontEntry()->mCharacterMap = new gfxCharacterMap(0);
    779    GetFontEntry()->mShmemCharacterMap = nullptr;
    780    gfxCriticalError() << "Exception occurred measuring glyph bounds for "
    781                       << GetFontEntry()->Name().get();
    782  }
    783  return false;
    784 }
    785 
    786 void gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
    787                                           FontCacheSizes* aSizes) const {
    788  gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    789  if (mGlyphWidths) {
    790    aSizes->mFontInstances +=
    791        mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
    792  }
    793 }
    794 
    795 void gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
    796                                           FontCacheSizes* aSizes) const {
    797  aSizes->mFontInstances += aMallocSizeOf(this);
    798  AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    799 }
    800 
    801 already_AddRefed<ScaledFont> gfxDWriteFont::GetScaledFont(
    802    const TextRunDrawParams& aRunParams) {
    803  bool useClearType = UsingClearType();
    804  if (mAzureScaledFontUsedClearType != useClearType) {
    805    if (auto* oldScaledFont = mAzureScaledFont.exchange(nullptr)) {
    806      oldScaledFont->Release();
    807    }
    808    if (auto* oldScaledFont = mAzureScaledFontGDI.exchange(nullptr)) {
    809      oldScaledFont->Release();
    810    }
    811  }
    812  bool forceGDI = aRunParams.allowGDI && GetForceGDIClassic();
    813  ScaledFont* scaledFont = forceGDI ? mAzureScaledFontGDI : mAzureScaledFont;
    814  if (scaledFont) {
    815    return do_AddRef(scaledFont);
    816  }
    817 
    818  gfxDWriteFontEntry* fe = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
    819  bool useEmbeddedBitmap =
    820      (gfxVars::SystemTextRenderingMode() == DWRITE_RENDERING_MODE_DEFAULT ||
    821       forceGDI) &&
    822      fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize));
    823 
    824  const gfxFontStyle* fontStyle = GetStyle();
    825  RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForDWriteFont(
    826      mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(),
    827      useEmbeddedBitmap, ApplySyntheticBold(), forceGDI);
    828  if (!newScaledFont) {
    829    return nullptr;
    830  }
    831  InitializeScaledFont(newScaledFont);
    832 
    833  if (forceGDI) {
    834    if (mAzureScaledFontGDI.compareExchange(nullptr, newScaledFont.get())) {
    835      newScaledFont.forget().leak();
    836      mAzureScaledFontUsedClearType = useClearType;
    837    }
    838    scaledFont = mAzureScaledFontGDI;
    839  } else {
    840    if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
    841      newScaledFont.forget().leak();
    842      mAzureScaledFontUsedClearType = useClearType;
    843    }
    844    scaledFont = mAzureScaledFont;
    845  }
    846  return do_AddRef(scaledFont);
    847 }
    848 
    849 bool gfxDWriteFont::ShouldRoundXOffset(cairo_t* aCairo) const {
    850  // show_glyphs is implemented on the font and so is used for all Cairo
    851  // surface types; however, it may pixel-snap depending on the dwrite
    852  // rendering mode
    853  return GetMeasuringMode() != DWRITE_MEASURING_MODE_NATURAL;
    854 }