1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/src/nsFont.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,474 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsFont.h" 1.10 +#include "gfxFont.h" // for gfxFontStyle 1.11 +#include "gfxFontConstants.h" // for NS_FONT_KERNING_AUTO, etc 1.12 +#include "gfxFontFeatures.h" // for gfxFontFeature, etc 1.13 +#include "gfxFontUtils.h" // for TRUETYPE_TAG 1.14 +#include "nsCRT.h" // for nsCRT 1.15 +#include "nsDebug.h" // for NS_ASSERTION 1.16 +#include "nsISupports.h" 1.17 +#include "nsUnicharUtils.h" 1.18 +#include "nscore.h" // for char16_t 1.19 +#include "mozilla/ArrayUtils.h" 1.20 +#include "mozilla/gfx/2D.h" 1.21 + 1.22 +nsFont::nsFont(const char* aName, uint8_t aStyle, uint8_t aVariant, 1.23 + uint16_t aWeight, int16_t aStretch, uint8_t aDecoration, 1.24 + nscoord aSize) 1.25 +{ 1.26 + NS_ASSERTION(aName && IsASCII(nsDependentCString(aName)), 1.27 + "Must only pass ASCII names here"); 1.28 + name.AssignASCII(aName); 1.29 + style = aStyle; 1.30 + systemFont = false; 1.31 + variant = aVariant; 1.32 + weight = aWeight; 1.33 + stretch = aStretch; 1.34 + decorations = aDecoration; 1.35 + smoothing = NS_FONT_SMOOTHING_AUTO; 1.36 + size = aSize; 1.37 + sizeAdjust = 0.0; 1.38 + kerning = NS_FONT_KERNING_AUTO; 1.39 + synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE; 1.40 + 1.41 + variantAlternates = 0; 1.42 + variantCaps = NS_FONT_VARIANT_CAPS_NORMAL; 1.43 + variantEastAsian = 0; 1.44 + variantLigatures = 0; 1.45 + variantNumeric = 0; 1.46 + variantPosition = NS_FONT_VARIANT_POSITION_NORMAL; 1.47 +} 1.48 + 1.49 +nsFont::nsFont(const nsSubstring& aName, uint8_t aStyle, uint8_t aVariant, 1.50 + uint16_t aWeight, int16_t aStretch, uint8_t aDecoration, 1.51 + nscoord aSize) 1.52 + : name(aName) 1.53 +{ 1.54 + style = aStyle; 1.55 + systemFont = false; 1.56 + variant = aVariant; 1.57 + weight = aWeight; 1.58 + stretch = aStretch; 1.59 + decorations = aDecoration; 1.60 + smoothing = NS_FONT_SMOOTHING_AUTO; 1.61 + size = aSize; 1.62 + sizeAdjust = 0.0; 1.63 + kerning = NS_FONT_KERNING_AUTO; 1.64 + synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE; 1.65 + 1.66 + variantAlternates = 0; 1.67 + variantCaps = NS_FONT_VARIANT_CAPS_NORMAL; 1.68 + variantEastAsian = 0; 1.69 + variantLigatures = 0; 1.70 + variantNumeric = 0; 1.71 + variantPosition = NS_FONT_VARIANT_POSITION_NORMAL; 1.72 +} 1.73 + 1.74 +nsFont::nsFont(const nsFont& aOther) 1.75 + : name(aOther.name) 1.76 +{ 1.77 + style = aOther.style; 1.78 + systemFont = aOther.systemFont; 1.79 + variant = aOther.variant; 1.80 + weight = aOther.weight; 1.81 + stretch = aOther.stretch; 1.82 + decorations = aOther.decorations; 1.83 + smoothing = aOther.smoothing; 1.84 + size = aOther.size; 1.85 + sizeAdjust = aOther.sizeAdjust; 1.86 + kerning = aOther.kerning; 1.87 + synthesis = aOther.synthesis; 1.88 + fontFeatureSettings = aOther.fontFeatureSettings; 1.89 + languageOverride = aOther.languageOverride; 1.90 + variantAlternates = aOther.variantAlternates; 1.91 + variantCaps = aOther.variantCaps; 1.92 + variantEastAsian = aOther.variantEastAsian; 1.93 + variantLigatures = aOther.variantLigatures; 1.94 + variantNumeric = aOther.variantNumeric; 1.95 + variantPosition = aOther.variantPosition; 1.96 + alternateValues = aOther.alternateValues; 1.97 + featureValueLookup = aOther.featureValueLookup; 1.98 +} 1.99 + 1.100 +nsFont::nsFont() 1.101 +{ 1.102 +} 1.103 + 1.104 +nsFont::~nsFont() 1.105 +{ 1.106 +} 1.107 + 1.108 +bool nsFont::BaseEquals(const nsFont& aOther) const 1.109 +{ 1.110 + if ((style == aOther.style) && 1.111 + (systemFont == aOther.systemFont) && 1.112 + (weight == aOther.weight) && 1.113 + (stretch == aOther.stretch) && 1.114 + (size == aOther.size) && 1.115 + (sizeAdjust == aOther.sizeAdjust) && 1.116 + name.Equals(aOther.name, nsCaseInsensitiveStringComparator()) && 1.117 + (kerning == aOther.kerning) && 1.118 + (synthesis == aOther.synthesis) && 1.119 + (fontFeatureSettings == aOther.fontFeatureSettings) && 1.120 + (languageOverride == aOther.languageOverride) && 1.121 + (variantAlternates == aOther.variantAlternates) && 1.122 + (variantCaps == aOther.variantCaps) && 1.123 + (variantEastAsian == aOther.variantEastAsian) && 1.124 + (variantLigatures == aOther.variantLigatures) && 1.125 + (variantNumeric == aOther.variantNumeric) && 1.126 + (variantPosition == aOther.variantPosition) && 1.127 + (alternateValues == aOther.alternateValues) && 1.128 + (featureValueLookup == aOther.featureValueLookup) && 1.129 + (smoothing == aOther.smoothing)) { 1.130 + return true; 1.131 + } 1.132 + return false; 1.133 +} 1.134 + 1.135 +bool nsFont::Equals(const nsFont& aOther) const 1.136 +{ 1.137 + if (BaseEquals(aOther) && 1.138 + (variant == aOther.variant) && 1.139 + (decorations == aOther.decorations)) { 1.140 + return true; 1.141 + } 1.142 + return false; 1.143 +} 1.144 + 1.145 +nsFont& nsFont::operator=(const nsFont& aOther) 1.146 +{ 1.147 + name = aOther.name; 1.148 + style = aOther.style; 1.149 + systemFont = aOther.systemFont; 1.150 + variant = aOther.variant; 1.151 + weight = aOther.weight; 1.152 + stretch = aOther.stretch; 1.153 + decorations = aOther.decorations; 1.154 + smoothing = aOther.smoothing; 1.155 + size = aOther.size; 1.156 + sizeAdjust = aOther.sizeAdjust; 1.157 + kerning = aOther.kerning; 1.158 + synthesis = aOther.synthesis; 1.159 + fontFeatureSettings = aOther.fontFeatureSettings; 1.160 + languageOverride = aOther.languageOverride; 1.161 + variantAlternates = aOther.variantAlternates; 1.162 + variantCaps = aOther.variantCaps; 1.163 + variantEastAsian = aOther.variantEastAsian; 1.164 + variantLigatures = aOther.variantLigatures; 1.165 + variantNumeric = aOther.variantNumeric; 1.166 + variantPosition = aOther.variantPosition; 1.167 + alternateValues = aOther.alternateValues; 1.168 + featureValueLookup = aOther.featureValueLookup; 1.169 + return *this; 1.170 +} 1.171 + 1.172 +void 1.173 +nsFont::CopyAlternates(const nsFont& aOther) 1.174 +{ 1.175 + variantAlternates = aOther.variantAlternates; 1.176 + alternateValues = aOther.alternateValues; 1.177 + featureValueLookup = aOther.featureValueLookup; 1.178 +} 1.179 + 1.180 +static bool IsGenericFontFamily(const nsString& aFamily) 1.181 +{ 1.182 + uint8_t generic; 1.183 + nsFont::GetGenericID(aFamily, &generic); 1.184 + return generic != kGenericFont_NONE; 1.185 +} 1.186 + 1.187 +const char16_t kSingleQuote = char16_t('\''); 1.188 +const char16_t kDoubleQuote = char16_t('\"'); 1.189 +const char16_t kComma = char16_t(','); 1.190 + 1.191 +bool nsFont::EnumerateFamilies(nsFontFamilyEnumFunc aFunc, void* aData) const 1.192 +{ 1.193 + const char16_t *p, *p_end; 1.194 + name.BeginReading(p); 1.195 + name.EndReading(p_end); 1.196 + nsAutoString family; 1.197 + 1.198 + while (p < p_end) { 1.199 + while (nsCRT::IsAsciiSpace(*p)) 1.200 + if (++p == p_end) 1.201 + return true; 1.202 + 1.203 + bool generic; 1.204 + if (*p == kSingleQuote || *p == kDoubleQuote) { 1.205 + // quoted font family 1.206 + char16_t quoteMark = *p; 1.207 + if (++p == p_end) 1.208 + return true; 1.209 + const char16_t *nameStart = p; 1.210 + 1.211 + // XXX What about CSS character escapes? 1.212 + while (*p != quoteMark) 1.213 + if (++p == p_end) 1.214 + return true; 1.215 + 1.216 + family = Substring(nameStart, p); 1.217 + generic = false; 1.218 + 1.219 + while (++p != p_end && *p != kComma) 1.220 + /* nothing */ ; 1.221 + 1.222 + } else { 1.223 + // unquoted font family 1.224 + const char16_t *nameStart = p; 1.225 + while (++p != p_end && *p != kComma) 1.226 + /* nothing */ ; 1.227 + 1.228 + family = Substring(nameStart, p); 1.229 + family.CompressWhitespace(false, true); 1.230 + generic = IsGenericFontFamily(family); 1.231 + } 1.232 + 1.233 + if (!family.IsEmpty() && !(*aFunc)(family, generic, aData)) 1.234 + return false; 1.235 + 1.236 + ++p; // may advance past p_end 1.237 + } 1.238 + 1.239 + return true; 1.240 +} 1.241 + 1.242 +// mapping from bitflag to font feature tag/value pair 1.243 +// 1.244 +// these need to be kept in sync with the constants listed 1.245 +// in gfxFontConstants.h (e.g. NS_FONT_VARIANT_EAST_ASIAN_JIS78) 1.246 + 1.247 +// NS_FONT_VARIANT_EAST_ASIAN_xxx values 1.248 +const gfxFontFeature eastAsianDefaults[] = { 1.249 + { TRUETYPE_TAG('j','p','7','8'), 1 }, 1.250 + { TRUETYPE_TAG('j','p','8','3'), 1 }, 1.251 + { TRUETYPE_TAG('j','p','9','0'), 1 }, 1.252 + { TRUETYPE_TAG('j','p','0','4'), 1 }, 1.253 + { TRUETYPE_TAG('s','m','p','l'), 1 }, 1.254 + { TRUETYPE_TAG('t','r','a','d'), 1 }, 1.255 + { TRUETYPE_TAG('f','w','i','d'), 1 }, 1.256 + { TRUETYPE_TAG('p','w','i','d'), 1 }, 1.257 + { TRUETYPE_TAG('r','u','b','y'), 1 } 1.258 +}; 1.259 + 1.260 +static_assert(MOZ_ARRAY_LENGTH(eastAsianDefaults) == 1.261 + eFeatureEastAsian_numFeatures, 1.262 + "eFeatureEastAsian_numFeatures should be correct"); 1.263 + 1.264 +// NS_FONT_VARIANT_LIGATURES_xxx values 1.265 +const gfxFontFeature ligDefaults[] = { 1.266 + { TRUETYPE_TAG('l','i','g','a'), 0 }, // none value means all off 1.267 + { TRUETYPE_TAG('l','i','g','a'), 1 }, 1.268 + { TRUETYPE_TAG('l','i','g','a'), 0 }, 1.269 + { TRUETYPE_TAG('d','l','i','g'), 1 }, 1.270 + { TRUETYPE_TAG('d','l','i','g'), 0 }, 1.271 + { TRUETYPE_TAG('h','l','i','g'), 1 }, 1.272 + { TRUETYPE_TAG('h','l','i','g'), 0 }, 1.273 + { TRUETYPE_TAG('c','a','l','t'), 1 }, 1.274 + { TRUETYPE_TAG('c','a','l','t'), 0 } 1.275 +}; 1.276 + 1.277 +static_assert(MOZ_ARRAY_LENGTH(ligDefaults) == 1.278 + eFeatureLigatures_numFeatures, 1.279 + "eFeatureLigatures_numFeatures should be correct"); 1.280 + 1.281 +// NS_FONT_VARIANT_NUMERIC_xxx values 1.282 +const gfxFontFeature numericDefaults[] = { 1.283 + { TRUETYPE_TAG('l','n','u','m'), 1 }, 1.284 + { TRUETYPE_TAG('o','n','u','m'), 1 }, 1.285 + { TRUETYPE_TAG('p','n','u','m'), 1 }, 1.286 + { TRUETYPE_TAG('t','n','u','m'), 1 }, 1.287 + { TRUETYPE_TAG('f','r','a','c'), 1 }, 1.288 + { TRUETYPE_TAG('a','f','r','c'), 1 }, 1.289 + { TRUETYPE_TAG('z','e','r','o'), 1 }, 1.290 + { TRUETYPE_TAG('o','r','d','n'), 1 } 1.291 +}; 1.292 + 1.293 +static_assert(MOZ_ARRAY_LENGTH(numericDefaults) == 1.294 + eFeatureNumeric_numFeatures, 1.295 + "eFeatureNumeric_numFeatures should be correct"); 1.296 + 1.297 +static void 1.298 +AddFontFeaturesBitmask(uint32_t aValue, uint32_t aMin, uint32_t aMax, 1.299 + const gfxFontFeature aFeatureDefaults[], 1.300 + nsTArray<gfxFontFeature>& aFeaturesOut) 1.301 + 1.302 +{ 1.303 + uint32_t i, m; 1.304 + 1.305 + for (i = 0, m = aMin; m <= aMax; i++, m <<= 1) { 1.306 + if (m & aValue) { 1.307 + const gfxFontFeature& feature = aFeatureDefaults[i]; 1.308 + aFeaturesOut.AppendElement(feature); 1.309 + } 1.310 + } 1.311 +} 1.312 + 1.313 +void nsFont::AddFontFeaturesToStyle(gfxFontStyle *aStyle) const 1.314 +{ 1.315 + // add in font-variant features 1.316 + gfxFontFeature setting; 1.317 + 1.318 + // -- kerning 1.319 + setting.mTag = TRUETYPE_TAG('k','e','r','n'); 1.320 + switch (kerning) { 1.321 + case NS_FONT_KERNING_NONE: 1.322 + setting.mValue = 0; 1.323 + aStyle->featureSettings.AppendElement(setting); 1.324 + break; 1.325 + case NS_FONT_KERNING_NORMAL: 1.326 + setting.mValue = 1; 1.327 + aStyle->featureSettings.AppendElement(setting); 1.328 + break; 1.329 + default: 1.330 + // auto case implies use user agent default 1.331 + break; 1.332 + } 1.333 + 1.334 + // -- alternates 1.335 + if (variantAlternates & NS_FONT_VARIANT_ALTERNATES_HISTORICAL) { 1.336 + setting.mValue = 1; 1.337 + setting.mTag = TRUETYPE_TAG('h','i','s','t'); 1.338 + aStyle->featureSettings.AppendElement(setting); 1.339 + } 1.340 + 1.341 + 1.342 + // -- copy font-specific alternate info into style 1.343 + // (this will be resolved after font-matching occurs) 1.344 + aStyle->alternateValues.AppendElements(alternateValues); 1.345 + aStyle->featureValueLookup = featureValueLookup; 1.346 + 1.347 + // -- caps 1.348 + setting.mValue = 1; 1.349 + switch (variantCaps) { 1.350 + case NS_FONT_VARIANT_CAPS_ALLSMALL: 1.351 + setting.mTag = TRUETYPE_TAG('c','2','s','c'); 1.352 + aStyle->featureSettings.AppendElement(setting); 1.353 + // fall through to the small-caps case 1.354 + case NS_FONT_VARIANT_CAPS_SMALLCAPS: 1.355 + setting.mTag = TRUETYPE_TAG('s','m','c','p'); 1.356 + aStyle->featureSettings.AppendElement(setting); 1.357 + break; 1.358 + 1.359 + case NS_FONT_VARIANT_CAPS_ALLPETITE: 1.360 + setting.mTag = TRUETYPE_TAG('c','2','p','c'); 1.361 + aStyle->featureSettings.AppendElement(setting); 1.362 + // fall through to the petite-caps case 1.363 + case NS_FONT_VARIANT_CAPS_PETITECAPS: 1.364 + setting.mTag = TRUETYPE_TAG('p','c','a','p'); 1.365 + aStyle->featureSettings.AppendElement(setting); 1.366 + break; 1.367 + 1.368 + case NS_FONT_VARIANT_CAPS_TITLING: 1.369 + setting.mTag = TRUETYPE_TAG('t','i','t','l'); 1.370 + aStyle->featureSettings.AppendElement(setting); 1.371 + break; 1.372 + 1.373 + case NS_FONT_VARIANT_CAPS_UNICASE: 1.374 + setting.mTag = TRUETYPE_TAG('u','n','i','c'); 1.375 + aStyle->featureSettings.AppendElement(setting); 1.376 + break; 1.377 + 1.378 + default: 1.379 + break; 1.380 + } 1.381 + 1.382 + // -- east-asian 1.383 + if (variantEastAsian) { 1.384 + AddFontFeaturesBitmask(variantEastAsian, 1.385 + NS_FONT_VARIANT_EAST_ASIAN_JIS78, 1.386 + NS_FONT_VARIANT_EAST_ASIAN_RUBY, 1.387 + eastAsianDefaults, aStyle->featureSettings); 1.388 + } 1.389 + 1.390 + // -- ligatures 1.391 + if (variantLigatures) { 1.392 + AddFontFeaturesBitmask(variantLigatures, 1.393 + NS_FONT_VARIANT_LIGATURES_NONE, 1.394 + NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL, 1.395 + ligDefaults, aStyle->featureSettings); 1.396 + 1.397 + if (variantLigatures & NS_FONT_VARIANT_LIGATURES_COMMON) { 1.398 + // liga already enabled, need to enable clig also 1.399 + setting.mTag = TRUETYPE_TAG('c','l','i','g'); 1.400 + setting.mValue = 1; 1.401 + aStyle->featureSettings.AppendElement(setting); 1.402 + } else if (variantLigatures & NS_FONT_VARIANT_LIGATURES_NO_COMMON) { 1.403 + // liga already disabled, need to disable clig also 1.404 + setting.mTag = TRUETYPE_TAG('c','l','i','g'); 1.405 + setting.mValue = 0; 1.406 + aStyle->featureSettings.AppendElement(setting); 1.407 + } else if (variantLigatures & NS_FONT_VARIANT_LIGATURES_NONE) { 1.408 + // liga already disabled, need to disable dlig, hlig, calt, clig 1.409 + setting.mValue = 0; 1.410 + setting.mTag = TRUETYPE_TAG('d','l','i','g'); 1.411 + aStyle->featureSettings.AppendElement(setting); 1.412 + setting.mTag = TRUETYPE_TAG('h','l','i','g'); 1.413 + aStyle->featureSettings.AppendElement(setting); 1.414 + setting.mTag = TRUETYPE_TAG('c','a','l','t'); 1.415 + aStyle->featureSettings.AppendElement(setting); 1.416 + setting.mTag = TRUETYPE_TAG('c','l','i','g'); 1.417 + aStyle->featureSettings.AppendElement(setting); 1.418 + } 1.419 + } 1.420 + 1.421 + // -- numeric 1.422 + if (variantNumeric) { 1.423 + AddFontFeaturesBitmask(variantNumeric, 1.424 + NS_FONT_VARIANT_NUMERIC_LINING, 1.425 + NS_FONT_VARIANT_NUMERIC_ORDINAL, 1.426 + numericDefaults, aStyle->featureSettings); 1.427 + } 1.428 + 1.429 + // -- position 1.430 + setting.mTag = 0; 1.431 + setting.mValue = 1; 1.432 + switch (variantPosition) { 1.433 + case NS_FONT_VARIANT_POSITION_SUPER: 1.434 + setting.mTag = TRUETYPE_TAG('s','u','p','s'); 1.435 + aStyle->featureSettings.AppendElement(setting); 1.436 + break; 1.437 + 1.438 + case NS_FONT_VARIANT_POSITION_SUB: 1.439 + setting.mTag = TRUETYPE_TAG('s','u','b','s'); 1.440 + aStyle->featureSettings.AppendElement(setting); 1.441 + break; 1.442 + 1.443 + default: 1.444 + break; 1.445 + } 1.446 + 1.447 + // add in features from font-feature-settings 1.448 + aStyle->featureSettings.AppendElements(fontFeatureSettings); 1.449 + 1.450 + // enable grayscale antialiasing for text 1.451 + if (smoothing == NS_FONT_SMOOTHING_GRAYSCALE) { 1.452 + aStyle->useGrayscaleAntialiasing = true; 1.453 + } 1.454 +} 1.455 + 1.456 +static bool FontEnumCallback(const nsString& aFamily, bool aGeneric, void *aData) 1.457 +{ 1.458 + *((nsString*)aData) = aFamily; 1.459 + return false; 1.460 +} 1.461 + 1.462 +void nsFont::GetFirstFamily(nsString& aFamily) const 1.463 +{ 1.464 + EnumerateFamilies(FontEnumCallback, &aFamily); 1.465 +} 1.466 + 1.467 +/*static*/ 1.468 +void nsFont::GetGenericID(const nsString& aGeneric, uint8_t* aID) 1.469 +{ 1.470 + *aID = kGenericFont_NONE; 1.471 + if (aGeneric.LowerCaseEqualsLiteral("-moz-fixed")) *aID = kGenericFont_moz_fixed; 1.472 + else if (aGeneric.LowerCaseEqualsLiteral("serif")) *aID = kGenericFont_serif; 1.473 + else if (aGeneric.LowerCaseEqualsLiteral("sans-serif")) *aID = kGenericFont_sans_serif; 1.474 + else if (aGeneric.LowerCaseEqualsLiteral("cursive")) *aID = kGenericFont_cursive; 1.475 + else if (aGeneric.LowerCaseEqualsLiteral("fantasy")) *aID = kGenericFont_fantasy; 1.476 + else if (aGeneric.LowerCaseEqualsLiteral("monospace")) *aID = kGenericFont_monospace; 1.477 +}