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