tor-browser

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

nsFont.cpp (10823B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsFont.h"
      8 #include "gfxFont.h"          // for gfxFontStyle
      9 #include "gfxFontFeatures.h"  // for gfxFontFeature, etc
     10 #include "gfxFontUtils.h"     // for TRUETYPE_TAG
     11 #include "mozilla/ServoStyleConstsInlines.h"
     12 #include "nsCRT.h"    // for nsCRT
     13 #include "nsDebug.h"  // for NS_ASSERTION
     14 #include "nsISupports.h"
     15 #include "nsUnicharUtils.h"
     16 #include "nscore.h"  // for char16_t
     17 #include "mozilla/gfx/2D.h"
     18 
     19 using namespace mozilla;
     20 
     21 nsFont::nsFont(const StyleFontFamily& aFamily, mozilla::Length aSize)
     22    : family(aFamily), size(aSize) {}
     23 
     24 nsFont::nsFont(StyleGenericFontFamily aGenericType, mozilla::Length aSize)
     25    : family(*Servo_FontFamily_Generic(aGenericType)), size(aSize) {}
     26 
     27 nsFont::nsFont(const nsFont& aOther) = default;
     28 
     29 nsFont::~nsFont() = default;
     30 
     31 nsFont& nsFont::operator=(const nsFont&) = default;
     32 
     33 bool nsFont::Equals(const nsFont& aOther) const {
     34  return CalcDifference(aOther) == MaxDifference::eNone;
     35 }
     36 
     37 nsFont::MaxDifference nsFont::CalcDifference(const nsFont& aOther) const {
     38  if ((style != aOther.style) || (weight != aOther.weight) ||
     39      (stretch != aOther.stretch) || (size != aOther.size) ||
     40      (sizeAdjust != aOther.sizeAdjust) || (family != aOther.family) ||
     41      (kerning != aOther.kerning) || (opticalSizing != aOther.opticalSizing) ||
     42      (synthesisWeight != aOther.synthesisWeight) ||
     43      (synthesisStyle != aOther.synthesisStyle) ||
     44      (synthesisSmallCaps != aOther.synthesisSmallCaps) ||
     45      (synthesisPosition != aOther.synthesisPosition) ||
     46      (fontFeatureSettings != aOther.fontFeatureSettings) ||
     47      (fontVariationSettings != aOther.fontVariationSettings) ||
     48      (languageOverride != aOther.languageOverride) ||
     49      (variantAlternates != aOther.variantAlternates) ||
     50      (variantCaps != aOther.variantCaps) ||
     51      (variantEastAsian != aOther.variantEastAsian) ||
     52      (variantLigatures != aOther.variantLigatures) ||
     53      (variantNumeric != aOther.variantNumeric) ||
     54      (variantPosition != aOther.variantPosition) ||
     55      (variantWidth != aOther.variantWidth) ||
     56      (variantEmoji != aOther.variantEmoji)) {
     57    return MaxDifference::eLayoutAffecting;
     58  }
     59 
     60  if (smoothing != aOther.smoothing) {
     61    return MaxDifference::eVisual;
     62  }
     63 
     64  return MaxDifference::eNone;
     65 }
     66 
     67 // mapping from bitflag to font feature tag/value pair
     68 //
     69 // these need to be kept in sync with the constants listed
     70 // in gfxFontConstants.h (e.g. StyleFontVariantEastAsian::JIS78)
     71 
     72 // StyleFontVariantEastAsian::xxx values
     73 const gfxFontFeature eastAsianDefaults[] = {
     74    {TRUETYPE_TAG('j', 'p', '7', '8'), 1},
     75    {TRUETYPE_TAG('j', 'p', '8', '3'), 1},
     76    {TRUETYPE_TAG('j', 'p', '9', '0'), 1},
     77    {TRUETYPE_TAG('j', 'p', '0', '4'), 1},
     78    {TRUETYPE_TAG('s', 'm', 'p', 'l'), 1},
     79    {TRUETYPE_TAG('t', 'r', 'a', 'd'), 1},
     80    {TRUETYPE_TAG('f', 'w', 'i', 'd'), 1},
     81    {TRUETYPE_TAG('p', 'w', 'i', 'd'), 1},
     82    {TRUETYPE_TAG('r', 'u', 'b', 'y'), 1}};
     83 
     84 static_assert(std::size(eastAsianDefaults) == StyleFontVariantEastAsian::COUNT,
     85              "eastAsianDefaults[] should be correct");
     86 
     87 // StyleFontVariantLigatures::xxx values
     88 const gfxFontFeature ligDefaults[] = {
     89    {TRUETYPE_TAG('l', 'i', 'g', 'a'), 0},  // none value means all off
     90    {TRUETYPE_TAG('l', 'i', 'g', 'a'), 1},
     91    {TRUETYPE_TAG('l', 'i', 'g', 'a'), 0},
     92    {TRUETYPE_TAG('d', 'l', 'i', 'g'), 1},
     93    {TRUETYPE_TAG('d', 'l', 'i', 'g'), 0},
     94    {TRUETYPE_TAG('h', 'l', 'i', 'g'), 1},
     95    {TRUETYPE_TAG('h', 'l', 'i', 'g'), 0},
     96    {TRUETYPE_TAG('c', 'a', 'l', 't'), 1},
     97    {TRUETYPE_TAG('c', 'a', 'l', 't'), 0}};
     98 
     99 static_assert(std::size(ligDefaults) == StyleFontVariantLigatures::COUNT,
    100              "ligDefaults[] should be correct");
    101 
    102 // StyleFontVariantNumeric::xxx values
    103 const gfxFontFeature numericDefaults[] = {
    104    {TRUETYPE_TAG('l', 'n', 'u', 'm'), 1},
    105    {TRUETYPE_TAG('o', 'n', 'u', 'm'), 1},
    106    {TRUETYPE_TAG('p', 'n', 'u', 'm'), 1},
    107    {TRUETYPE_TAG('t', 'n', 'u', 'm'), 1},
    108    {TRUETYPE_TAG('f', 'r', 'a', 'c'), 1},
    109    {TRUETYPE_TAG('a', 'f', 'r', 'c'), 1},
    110    {TRUETYPE_TAG('z', 'e', 'r', 'o'), 1},
    111    {TRUETYPE_TAG('o', 'r', 'd', 'n'), 1}};
    112 
    113 static_assert(std::size(numericDefaults) == StyleFontVariantNumeric::COUNT,
    114              "numericDefaults[] should be correct");
    115 
    116 template <typename T>
    117 static void AddFontFeaturesBitmask(T aValue, T aMin, T aMax,
    118                                   Span<const gfxFontFeature> aFeatureDefaults,
    119                                   nsTArray<gfxFontFeature>& aFeaturesOut)
    120 
    121 {
    122  for (uint32_t i = 0, m = aMin._0; m <= aMax._0; i++, m <<= 1) {
    123    if (m & aValue._0) {
    124      const gfxFontFeature& feature = aFeatureDefaults[i];
    125      aFeaturesOut.AppendElement(feature);
    126    }
    127  }
    128 }
    129 
    130 static uint32_t FontFeatureTagForVariantWidth(uint32_t aVariantWidth) {
    131  switch (aVariantWidth) {
    132    case NS_FONT_VARIANT_WIDTH_FULL:
    133      return TRUETYPE_TAG('f', 'w', 'i', 'd');
    134    case NS_FONT_VARIANT_WIDTH_HALF:
    135      return TRUETYPE_TAG('h', 'w', 'i', 'd');
    136    case NS_FONT_VARIANT_WIDTH_THIRD:
    137      return TRUETYPE_TAG('t', 'w', 'i', 'd');
    138    case NS_FONT_VARIANT_WIDTH_QUARTER:
    139      return TRUETYPE_TAG('q', 'w', 'i', 'd');
    140    default:
    141      return 0;
    142  }
    143 }
    144 
    145 void nsFont::AddFontFeaturesToStyle(gfxFontStyle* aStyle,
    146                                    bool aVertical) const {
    147  // add in font-variant features
    148  gfxFontFeature setting;
    149 
    150  // -- kerning
    151  setting.mTag = aVertical ? TRUETYPE_TAG('v', 'k', 'r', 'n')
    152                           : TRUETYPE_TAG('k', 'e', 'r', 'n');
    153  switch (kerning) {
    154    case NS_FONT_KERNING_NONE:
    155      setting.mValue = 0;
    156      aStyle->featureSettings.AppendElement(setting);
    157      break;
    158    case NS_FONT_KERNING_NORMAL:
    159      setting.mValue = 1;
    160      aStyle->featureSettings.AppendElement(setting);
    161      break;
    162    default:
    163      // auto case implies use user agent default
    164      break;
    165  }
    166 
    167  // -- alternates
    168  //
    169  // NOTE(emilio): We handle historical-forms here because it doesn't depend on
    170  // other values set by @font-face and thus may be less expensive to do here
    171  // than after font-matching.
    172  for (auto& alternate : variantAlternates.AsSpan()) {
    173    if (alternate.IsHistoricalForms()) {
    174      setting.mValue = 1;
    175      setting.mTag = TRUETYPE_TAG('h', 'i', 's', 't');
    176      aStyle->featureSettings.AppendElement(setting);
    177      break;
    178    }
    179  }
    180 
    181  // -- copy font-specific alternate info into style
    182  //    (this will be resolved after font-matching occurs)
    183  aStyle->variantAlternates = variantAlternates;
    184 
    185  // -- caps
    186  aStyle->variantCaps = variantCaps;
    187 
    188  // -- east-asian
    189  if (variantEastAsian) {
    190    AddFontFeaturesBitmask(variantEastAsian, StyleFontVariantEastAsian::JIS78,
    191                           StyleFontVariantEastAsian::RUBY, eastAsianDefaults,
    192                           aStyle->featureSettings);
    193  }
    194 
    195  // -- ligatures
    196  if (variantLigatures) {
    197    AddFontFeaturesBitmask(variantLigatures, StyleFontVariantLigatures::NONE,
    198                           StyleFontVariantLigatures::NO_CONTEXTUAL,
    199                           ligDefaults, aStyle->featureSettings);
    200 
    201    if (variantLigatures & StyleFontVariantLigatures::COMMON_LIGATURES) {
    202      // liga already enabled, need to enable clig also
    203      setting.mTag = TRUETYPE_TAG('c', 'l', 'i', 'g');
    204      setting.mValue = 1;
    205      aStyle->featureSettings.AppendElement(setting);
    206    } else if (variantLigatures &
    207               StyleFontVariantLigatures::NO_COMMON_LIGATURES) {
    208      // liga already disabled, need to disable clig also
    209      setting.mTag = TRUETYPE_TAG('c', 'l', 'i', 'g');
    210      setting.mValue = 0;
    211      aStyle->featureSettings.AppendElement(setting);
    212    } else if (variantLigatures & StyleFontVariantLigatures::NONE) {
    213      // liga already disabled, need to disable dlig, hlig, calt, clig
    214      setting.mValue = 0;
    215      setting.mTag = TRUETYPE_TAG('d', 'l', 'i', 'g');
    216      aStyle->featureSettings.AppendElement(setting);
    217      setting.mTag = TRUETYPE_TAG('h', 'l', 'i', 'g');
    218      aStyle->featureSettings.AppendElement(setting);
    219      setting.mTag = TRUETYPE_TAG('c', 'a', 'l', 't');
    220      aStyle->featureSettings.AppendElement(setting);
    221      setting.mTag = TRUETYPE_TAG('c', 'l', 'i', 'g');
    222      aStyle->featureSettings.AppendElement(setting);
    223    }
    224  }
    225 
    226  // -- numeric
    227  if (variantNumeric) {
    228    AddFontFeaturesBitmask(variantNumeric, StyleFontVariantNumeric::LINING_NUMS,
    229                           StyleFontVariantNumeric::ORDINAL, numericDefaults,
    230                           aStyle->featureSettings);
    231  }
    232 
    233  // -- position
    234  aStyle->variantSubSuper = variantPosition;
    235 
    236  // -- width
    237  setting.mTag = FontFeatureTagForVariantWidth(variantWidth);
    238  if (setting.mTag) {
    239    setting.mValue = 1;
    240    aStyle->featureSettings.AppendElement(setting);
    241  }
    242 
    243  // indicate common-path case when neither variantCaps or variantSubSuper are
    244  // set
    245  aStyle->noFallbackVariantFeatures =
    246      (aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL) &&
    247      (variantPosition == NS_FONT_VARIANT_POSITION_NORMAL);
    248 
    249  // If the feature list is not empty, we insert a "fake" feature with tag=0
    250  // as delimiter between the above "high-level" features from font-variant-*
    251  // etc and those coming from the low-level font-feature-settings property.
    252  // This will allow us to distinguish high- and low-level settings when it
    253  // comes to potentially disabling ligatures because of letter-spacing.
    254  if (!aStyle->featureSettings.IsEmpty() || !fontFeatureSettings.IsEmpty()) {
    255    aStyle->featureSettings.AppendElement(gfxFontFeature{0, 0});
    256  }
    257 
    258  // add in features from font-feature-settings
    259  aStyle->featureSettings.AppendElements(fontFeatureSettings);
    260 
    261  // enable grayscale antialiasing for text
    262  if (smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
    263    aStyle->useGrayscaleAntialiasing = true;
    264  }
    265 }
    266 
    267 void nsFont::AddFontVariationsToStyle(gfxFontStyle* aStyle) const {
    268  // If auto optical sizing is enabled, and if there's no 'opsz' axis in
    269  // fontVariationSettings, then set the automatic value on the style.
    270  class VariationTagComparator {
    271   public:
    272    bool Equals(const gfxFontVariation& aVariation, uint32_t aTag) const {
    273      return aVariation.mTag == aTag;
    274    }
    275  };
    276  const uint32_t kTagOpsz = TRUETYPE_TAG('o', 'p', 's', 'z');
    277  if (opticalSizing == NS_FONT_OPTICAL_SIZING_AUTO &&
    278      !fontVariationSettings.Contains(kTagOpsz, VariationTagComparator())) {
    279    aStyle->autoOpticalSize = size.ToCSSPixels();
    280  }
    281 
    282  // Add in arbitrary values from font-variation-settings
    283  aStyle->variationSettings.AppendElements(fontVariationSettings);
    284 }