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 }