tor-browser

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

nsFontMetrics.cpp (15290B)


      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 "nsFontMetrics.h"
      7 #include <math.h>                // for floor, ceil
      8 #include <algorithm>             // for max
      9 #include "gfxContext.h"          // for gfxContext
     10 #include "gfxFontConstants.h"    // for NS_FONT_{SUB,SUPER}SCRIPT_OFFSET_RATIO
     11 #include "gfxPlatform.h"         // for gfxPlatform
     12 #include "gfxPoint.h"            // for gfxPoint
     13 #include "gfxRect.h"             // for gfxRect
     14 #include "gfxTextRun.h"          // for gfxFontGroup
     15 #include "gfxTypes.h"            // for gfxFloat
     16 #include "nsAtom.h"              // for nsAtom
     17 #include "nsBoundingMetrics.h"   // for nsBoundingMetrics
     18 #include "nsDebug.h"             // for NS_ERROR
     19 #include "nsDeviceContext.h"     // for nsDeviceContext
     20 #include "nsMathUtils.h"         // for NS_round
     21 #include "nsPresContext.h"       // for nsPresContext
     22 #include "nsString.h"            // for nsString
     23 #include "nsStyleConsts.h"       // for StyleHyphens::None
     24 #include "mozilla/Assertions.h"  // for MOZ_ASSERT
     25 #include "mozilla/UniquePtr.h"   // for UniquePtr
     26 
     27 class gfxUserFontSet;
     28 using namespace mozilla;
     29 
     30 namespace {
     31 
     32 class AutoTextRun {
     33 public:
     34  typedef mozilla::gfx::DrawTarget DrawTarget;
     35 
     36  AutoTextRun(const nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
     37              const char* aString, uint32_t aLength) {
     38    mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
     39        reinterpret_cast<const uint8_t*>(aString), aLength, aDrawTarget,
     40        aMetrics->AppUnitsPerDevPixel(), ComputeFlags(aMetrics),
     41        nsTextFrameUtils::Flags(), nullptr);
     42  }
     43 
     44  AutoTextRun(const nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
     45              const char16_t* aString, uint32_t aLength) {
     46    mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
     47        aString, aLength, aDrawTarget, aMetrics->AppUnitsPerDevPixel(),
     48        ComputeFlags(aMetrics), nsTextFrameUtils::Flags(), nullptr);
     49  }
     50 
     51  gfxTextRun* get() const { return mTextRun.get(); }
     52  gfxTextRun* operator->() const { return mTextRun.get(); }
     53 
     54 private:
     55  static gfx::ShapedTextFlags ComputeFlags(const nsFontMetrics* aMetrics) {
     56    gfx::ShapedTextFlags flags = gfx::ShapedTextFlags();
     57    if (aMetrics->GetTextRunRTL()) {
     58      flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
     59    }
     60    if (aMetrics->GetVertical()) {
     61      switch (aMetrics->GetTextOrientation()) {
     62        case StyleTextOrientation::Mixed:
     63          flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED;
     64          break;
     65        case StyleTextOrientation::Upright:
     66          flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
     67          break;
     68        case StyleTextOrientation::Sideways:
     69          flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
     70          break;
     71      }
     72    }
     73    return flags;
     74  }
     75 
     76  RefPtr<gfxTextRun> mTextRun;
     77 };
     78 
     79 class StubPropertyProvider final : public gfxTextRun::PropertyProvider {
     80 public:
     81  void GetHyphenationBreaks(
     82      gfxTextRun::Range aRange,
     83      gfxTextRun::HyphenType* aBreakBefore) const override {
     84    NS_ERROR(
     85        "This shouldn't be called because we never call BreakAndMeasureText");
     86  }
     87  mozilla::StyleHyphens GetHyphensOption() const override {
     88    NS_ERROR(
     89        "This shouldn't be called because we never call BreakAndMeasureText");
     90    return mozilla::StyleHyphens::None;
     91  }
     92  gfxFloat GetHyphenWidth() const override {
     93    NS_ERROR("This shouldn't be called because we never enable hyphens");
     94    return 0;
     95  }
     96  already_AddRefed<mozilla::gfx::DrawTarget> GetDrawTarget() const override {
     97    NS_ERROR("This shouldn't be called because we never enable hyphens");
     98    return nullptr;
     99  }
    100  uint32_t GetAppUnitsPerDevUnit() const override {
    101    NS_ERROR("This shouldn't be called because we never enable hyphens");
    102    return 60;
    103  }
    104  bool GetSpacing(gfxTextRun::Range aRange, Spacing* aSpacing) const override {
    105    NS_ERROR("This shouldn't be called because we never enable spacing");
    106    return false;
    107  }
    108  gfx::ShapedTextFlags GetShapedTextFlags() const override {
    109    NS_ERROR("This shouldn't be called because we never enable hyphens");
    110    return gfx::ShapedTextFlags();
    111  }
    112 };
    113 
    114 }  // namespace
    115 
    116 nsFontMetrics::nsFontMetrics(const nsFont& aFont, const Params& aParams,
    117                             nsPresContext* aContext)
    118    : mFont(aFont),
    119      mLanguage(aParams.language),
    120      mPresContext(aContext),
    121      mP2A(aContext->DeviceContext()->AppUnitsPerDevPixel()),
    122      mOrientation(aParams.orientation),
    123      mExplicitLanguage(aParams.explicitLanguage),
    124 #ifdef XP_WIN
    125      mAllowForceGDIClassic(aParams.allowForceGDIClassic),
    126 #endif
    127      mTextRunRTL(false),
    128      mVertical(false),
    129      mTextOrientation(mozilla::StyleTextOrientation::Mixed) {
    130  gfxFontStyle style(aFont.style, aFont.weight, aFont.stretch,
    131                     gfxFloat(aFont.size.ToAppUnits()) / mP2A, aFont.sizeAdjust,
    132                     aFont.family.is_system_font,
    133                     aContext->DeviceContext()->IsPrinterContext(),
    134 #ifdef XP_WIN
    135                     mAllowForceGDIClassic,
    136 #endif
    137                     aFont.synthesisWeight == StyleFontSynthesis::Auto,
    138                     aFont.synthesisStyle,
    139                     aFont.synthesisSmallCaps == StyleFontSynthesis::Auto,
    140                     aFont.synthesisPosition == StyleFontSynthesis::Auto,
    141                     aFont.languageOverride);
    142 
    143  aFont.AddFontFeaturesToStyle(&style, mOrientation == eVertical);
    144  style.featureValueLookup = aParams.featureValueLookup;
    145 
    146  aFont.AddFontVariationsToStyle(&style);
    147 
    148  gfxFloat devToCssSize = gfxFloat(mP2A) / gfxFloat(AppUnitsPerCSSPixel());
    149  mFontGroup = new gfxFontGroup(
    150      mPresContext, aFont.family.families, &style, mLanguage, mExplicitLanguage,
    151      aParams.textPerf, aParams.userFontSet, devToCssSize, aFont.variantEmoji);
    152 }
    153 
    154 nsFontMetrics::~nsFontMetrics() {
    155  // Should not be dropped by stylo
    156  MOZ_ASSERT(NS_IsMainThread());
    157  if (mPresContext) {
    158    mPresContext->FontMetricsDeleted(this);
    159  }
    160 }
    161 
    162 void nsFontMetrics::Destroy() { mPresContext = nullptr; }
    163 
    164 // XXXTODO get rid of this macro
    165 #define ROUND_TO_TWIPS(x) (nscoord) floor(((x) * mP2A) + 0.5)
    166 #define CEIL_TO_TWIPS(x) (nscoord) ceil((x) * mP2A)
    167 
    168 static const gfxFont::Metrics& GetMetrics(
    169    const nsFontMetrics* aFontMetrics,
    170    nsFontMetrics::FontOrientation aOrientation) {
    171  RefPtr<gfxFont> font =
    172      aFontMetrics->GetThebesFontGroup()->GetFirstValidFont();
    173  return font->GetMetrics(aOrientation);
    174 }
    175 
    176 static const gfxFont::Metrics& GetMetrics(const nsFontMetrics* aFontMetrics) {
    177  return GetMetrics(aFontMetrics, aFontMetrics->Orientation());
    178 }
    179 
    180 nscoord nsFontMetrics::XHeight() const {
    181  return ROUND_TO_TWIPS(GetMetrics(this).xHeight);
    182 }
    183 
    184 nscoord nsFontMetrics::CapHeight() const {
    185  return ROUND_TO_TWIPS(GetMetrics(this).capHeight);
    186 }
    187 
    188 nscoord nsFontMetrics::SuperscriptOffset() const {
    189  return ROUND_TO_TWIPS(GetMetrics(this).emHeight *
    190                        NS_FONT_SUPERSCRIPT_OFFSET_RATIO);
    191 }
    192 
    193 nscoord nsFontMetrics::SubscriptOffset() const {
    194  return ROUND_TO_TWIPS(GetMetrics(this).emHeight *
    195                        NS_FONT_SUBSCRIPT_OFFSET_RATIO);
    196 }
    197 
    198 void nsFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize) const {
    199  aOffset = ROUND_TO_TWIPS(GetMetrics(this).strikeoutOffset);
    200  aSize = ROUND_TO_TWIPS(GetMetrics(this).strikeoutSize);
    201 }
    202 
    203 void nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize) const {
    204  aOffset = ROUND_TO_TWIPS(mFontGroup->GetUnderlineOffset());
    205  aSize = ROUND_TO_TWIPS(GetMetrics(this).underlineSize);
    206 }
    207 
    208 // GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
    209 // text-decoration lines drawable area. See bug 421353.
    210 // BE CAREFUL for rounding each values. The logic MUST be same as
    211 // nsCSSRendering::GetTextDecorationRectInternal's.
    212 
    213 static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics,
    214                                  gfxFontGroup* aFontGroup) {
    215  gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5);
    216  gfxFloat size = NS_round(aMetrics.underlineSize);
    217  gfxFloat minDescent = offset + size;
    218  return floor(std::max(minDescent, aMetrics.maxDescent) + 0.5);
    219 }
    220 
    221 static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics) {
    222  return floor(aMetrics.maxAscent + 0.5);
    223 }
    224 
    225 nscoord nsFontMetrics::InternalLeading() const {
    226  return ROUND_TO_TWIPS(GetMetrics(this).internalLeading);
    227 }
    228 
    229 nscoord nsFontMetrics::ExternalLeading() const {
    230  return ROUND_TO_TWIPS(GetMetrics(this).externalLeading);
    231 }
    232 
    233 nscoord nsFontMetrics::EmHeight() const {
    234  return ROUND_TO_TWIPS(GetMetrics(this).emHeight);
    235 }
    236 
    237 nscoord nsFontMetrics::TrimmedAscent() const {
    238  const auto& m = GetMetrics(this);
    239  return ROUND_TO_TWIPS(std::max(0.0, m.maxAscent - m.internalLeading / 2));
    240 }
    241 
    242 nscoord nsFontMetrics::TrimmedDescent() const {
    243  const auto& m = GetMetrics(this);
    244  return ROUND_TO_TWIPS(std::max(0.0, m.maxDescent - m.internalLeading / 2));
    245 }
    246 
    247 nscoord nsFontMetrics::MaxHeight() const {
    248  return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics(this))) +
    249         CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(this), mFontGroup));
    250 }
    251 
    252 nscoord nsFontMetrics::MaxAscent() const {
    253  return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics(this)));
    254 }
    255 
    256 nscoord nsFontMetrics::MaxDescent() const {
    257  return CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(this), mFontGroup));
    258 }
    259 
    260 nscoord nsFontMetrics::MaxAdvance() const {
    261  return CEIL_TO_TWIPS(GetMetrics(this).maxAdvance);
    262 }
    263 
    264 nscoord nsFontMetrics::AveCharWidth() const {
    265  // Use CEIL instead of ROUND for consistency with GetMaxAdvance
    266  return CEIL_TO_TWIPS(GetMetrics(this).aveCharWidth);
    267 }
    268 
    269 nscoord nsFontMetrics::ZeroOrAveCharWidth() const {
    270  return CEIL_TO_TWIPS(GetMetrics(this).ZeroOrAveCharWidth());
    271 }
    272 
    273 nscoord nsFontMetrics::SpaceWidth() const {
    274  // For vertical text with mixed or sideways orientation, we want the
    275  // width of a horizontal space (even if we're using vertical line-spacing
    276  // metrics, as with "writing-mode:vertical-*;text-orientation:mixed").
    277  return CEIL_TO_TWIPS(
    278      GetMetrics(this,
    279                 mVertical && mTextOrientation == StyleTextOrientation::Upright
    280                     ? eVertical
    281                     : eHorizontal)
    282          .spaceWidth);
    283 }
    284 
    285 nscoord nsFontMetrics::InterScriptSpacingWidth() const {
    286  const auto& m = GetMetrics(this);
    287  // If there is no advance measure of the CJK water ideograph, use 1em instead.
    288  // https://drafts.csswg.org/css-values-4/#ic
    289  LayoutDeviceDoubleCoord ic =
    290      m.ideographicWidth >= 0.0 ? m.ideographicWidth : m.emHeight;
    291 
    292  // The inter-script spacing is defined as 1/8 of the CJK advance measure, i.e.
    293  // 0.125ic: https://drafts.csswg.org/css-text-4/#inter-script-spacing
    294  constexpr double kFraction = 0.125;
    295  return LayoutDevicePixel::ToAppUnits(ic * kFraction, AppUnitsPerDevPixel());
    296 }
    297 
    298 int32_t nsFontMetrics::GetMaxStringLength() const {
    299  const double x = 32767.0 / std::max(1.0, GetMetrics(this).maxAdvance);
    300  int32_t len = (int32_t)floor(x);
    301  return std::max(1, len);
    302 }
    303 
    304 nscoord nsFontMetrics::GetWidth(const char* aString, uint32_t aLength,
    305                                DrawTarget* aDrawTarget) const {
    306  if (aLength == 0) {
    307    return 0;
    308  }
    309  if (aLength == 1 && aString[0] == ' ') {
    310    return SpaceWidth();
    311  }
    312  StubPropertyProvider provider;
    313  AutoTextRun textRun(this, aDrawTarget, aString, aLength);
    314  if (textRun.get()) {
    315    return NSToCoordRound(
    316        textRun->GetAdvanceWidth(gfxTextRun::Range(0, aLength), &provider));
    317  }
    318  return 0;
    319 }
    320 
    321 nscoord nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength,
    322                                DrawTarget* aDrawTarget) const {
    323  if (aLength == 0) {
    324    return 0;
    325  }
    326  if (aLength == 1 && aString[0] == ' ') {
    327    return SpaceWidth();
    328  }
    329  StubPropertyProvider provider;
    330  AutoTextRun textRun(this, aDrawTarget, aString, aLength);
    331  if (textRun.get()) {
    332    return NSToCoordRound(
    333        textRun->GetAdvanceWidth(gfxTextRun::Range(0, aLength), &provider));
    334  }
    335  return 0;
    336 }
    337 
    338 // Draw a string using this font handle on the surface passed in.
    339 void nsFontMetrics::DrawString(const char* aString, uint32_t aLength,
    340                               nscoord aX, nscoord aY,
    341                               gfxContext* aContext) const {
    342  if (aLength == 0) {
    343    return;
    344  }
    345  StubPropertyProvider provider;
    346  AutoTextRun textRun(this, aContext->GetDrawTarget(), aString, aLength);
    347  if (!textRun.get()) {
    348    return;
    349  }
    350  gfx::Point pt(aX, aY);
    351  gfxTextRun::Range range(0, aLength);
    352  if (mTextRunRTL) {
    353    if (mVertical) {
    354      pt.y += textRun->GetAdvanceWidth(range, &provider);
    355    } else {
    356      pt.x += textRun->GetAdvanceWidth(range, &provider);
    357    }
    358  }
    359  mozilla::gfx::PaletteCache paletteCache;
    360  gfxTextRun::DrawParams params(aContext, paletteCache);
    361  params.provider = &provider;
    362  textRun->Draw(range, pt, params);
    363 }
    364 
    365 void nsFontMetrics::DrawString(
    366    const char16_t* aString, uint32_t aLength, nscoord aX, nscoord aY,
    367    gfxContext* aContext, DrawTarget* aTextRunConstructionDrawTarget) const {
    368  if (aLength == 0) {
    369    return;
    370  }
    371  StubPropertyProvider provider;
    372  AutoTextRun textRun(this, aTextRunConstructionDrawTarget, aString, aLength);
    373  if (!textRun.get()) {
    374    return;
    375  }
    376  gfx::Point pt(aX, aY);
    377  gfxTextRun::Range range(0, aLength);
    378  if (mTextRunRTL) {
    379    if (mVertical) {
    380      pt.y += textRun->GetAdvanceWidth(range, &provider);
    381    } else {
    382      pt.x += textRun->GetAdvanceWidth(range, &provider);
    383    }
    384  }
    385  mozilla::gfx::PaletteCache paletteCache;
    386  gfxTextRun::DrawParams params(aContext, paletteCache);
    387  params.provider = &provider;
    388  textRun->Draw(range, pt, params);
    389 }
    390 
    391 static nsBoundingMetrics GetTextBoundingMetrics(
    392    const nsFontMetrics* aMetrics, const char16_t* aString, uint32_t aLength,
    393    mozilla::gfx::DrawTarget* aDrawTarget, gfxFont::BoundingBoxType aType) {
    394  if (aLength == 0) {
    395    return nsBoundingMetrics();
    396  }
    397  StubPropertyProvider provider;
    398  AutoTextRun textRun(aMetrics, aDrawTarget, aString, aLength);
    399  nsBoundingMetrics m;
    400  if (textRun.get()) {
    401    gfxTextRun::Metrics theMetrics = textRun->MeasureText(
    402        gfxTextRun::Range(0, aLength), aType, aDrawTarget, &provider);
    403 
    404    m.leftBearing = NSToCoordFloor(theMetrics.mBoundingBox.X());
    405    m.rightBearing = NSToCoordCeil(theMetrics.mBoundingBox.XMost());
    406    m.ascent = NSToCoordCeil(-theMetrics.mBoundingBox.Y());
    407    m.descent = NSToCoordCeil(theMetrics.mBoundingBox.YMost());
    408    m.width = NSToCoordRound(theMetrics.mAdvanceWidth);
    409  }
    410  return m;
    411 }
    412 
    413 nsBoundingMetrics nsFontMetrics::GetBoundingMetrics(
    414    const char16_t* aString, uint32_t aLength, DrawTarget* aDrawTarget) const {
    415  return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
    416                                gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS);
    417 }
    418 
    419 nsBoundingMetrics nsFontMetrics::GetInkBoundsForInkOverflow(
    420    const char16_t* aString, uint32_t aLength, DrawTarget* aDrawTarget) const {
    421  return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
    422                                gfxFont::LOOSE_INK_EXTENTS);
    423 }
    424 
    425 gfxUserFontSet* nsFontMetrics::GetUserFontSet() const {
    426  return mFontGroup->GetUserFontSet();
    427 }