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/. */
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/ArrayUtils.h"
18 #include "mozilla/gfx/2D.h"
20 using namespace mozilla
;
22 nsFont::nsFont(const StyleFontFamily
& aFamily
, mozilla::Length aSize
)
23 : family(aFamily
), size(aSize
) {}
25 nsFont::nsFont(StyleGenericFontFamily aGenericType
, mozilla::Length aSize
)
26 : family(*Servo_FontFamily_Generic(aGenericType
)), size(aSize
) {}
28 nsFont::nsFont(const nsFont
& aOther
) = default;
30 nsFont::~nsFont() = default;
32 nsFont
& nsFont::operator=(const nsFont
&) = default;
34 bool nsFont::Equals(const nsFont
& aOther
) const {
35 return CalcDifference(aOther
) == MaxDifference::eNone
;
38 nsFont::MaxDifference
nsFont::CalcDifference(const nsFont
& aOther
) const {
39 if ((style
!= aOther
.style
) || (weight
!= aOther
.weight
) ||
40 (stretch
!= aOther
.stretch
) || (size
!= aOther
.size
) ||
41 (sizeAdjust
!= aOther
.sizeAdjust
) || (family
!= aOther
.family
) ||
42 (kerning
!= aOther
.kerning
) || (opticalSizing
!= aOther
.opticalSizing
) ||
43 (synthesisWeight
!= aOther
.synthesisWeight
) ||
44 (synthesisStyle
!= aOther
.synthesisStyle
) ||
45 (synthesisSmallCaps
!= aOther
.synthesisSmallCaps
) ||
46 (synthesisPosition
!= aOther
.synthesisPosition
) ||
47 (fontFeatureSettings
!= aOther
.fontFeatureSettings
) ||
48 (fontVariationSettings
!= aOther
.fontVariationSettings
) ||
49 (languageOverride
!= aOther
.languageOverride
) ||
50 (variantAlternates
!= aOther
.variantAlternates
) ||
51 (variantCaps
!= aOther
.variantCaps
) ||
52 (variantEastAsian
!= aOther
.variantEastAsian
) ||
53 (variantLigatures
!= aOther
.variantLigatures
) ||
54 (variantNumeric
!= aOther
.variantNumeric
) ||
55 (variantPosition
!= aOther
.variantPosition
) ||
56 (variantWidth
!= aOther
.variantWidth
) ||
57 (variantEmoji
!= aOther
.variantEmoji
)) {
58 return MaxDifference::eLayoutAffecting
;
61 if (smoothing
!= aOther
.smoothing
) {
62 return MaxDifference::eVisual
;
65 return MaxDifference::eNone
;
68 // mapping from bitflag to font feature tag/value pair
70 // these need to be kept in sync with the constants listed
71 // in gfxFontConstants.h (e.g. NS_FONT_VARIANT_EAST_ASIAN_JIS78)
73 // NS_FONT_VARIANT_EAST_ASIAN_xxx values
74 const gfxFontFeature eastAsianDefaults
[] = {
75 {TRUETYPE_TAG('j', 'p', '7', '8'), 1},
76 {TRUETYPE_TAG('j', 'p', '8', '3'), 1},
77 {TRUETYPE_TAG('j', 'p', '9', '0'), 1},
78 {TRUETYPE_TAG('j', 'p', '0', '4'), 1},
79 {TRUETYPE_TAG('s', 'm', 'p', 'l'), 1},
80 {TRUETYPE_TAG('t', 'r', 'a', 'd'), 1},
81 {TRUETYPE_TAG('f', 'w', 'i', 'd'), 1},
82 {TRUETYPE_TAG('p', 'w', 'i', 'd'), 1},
83 {TRUETYPE_TAG('r', 'u', 'b', 'y'), 1}};
85 static_assert(MOZ_ARRAY_LENGTH(eastAsianDefaults
) ==
86 NS_FONT_VARIANT_EAST_ASIAN_COUNT
,
87 "eastAsianDefaults[] should be correct");
89 // NS_FONT_VARIANT_LIGATURES_xxx values
90 const gfxFontFeature ligDefaults
[] = {
91 {TRUETYPE_TAG('l', 'i', 'g', 'a'), 0}, // none value means all off
92 {TRUETYPE_TAG('l', 'i', 'g', 'a'), 1},
93 {TRUETYPE_TAG('l', 'i', 'g', 'a'), 0},
94 {TRUETYPE_TAG('d', 'l', 'i', 'g'), 1},
95 {TRUETYPE_TAG('d', 'l', 'i', 'g'), 0},
96 {TRUETYPE_TAG('h', 'l', 'i', 'g'), 1},
97 {TRUETYPE_TAG('h', 'l', 'i', 'g'), 0},
98 {TRUETYPE_TAG('c', 'a', 'l', 't'), 1},
99 {TRUETYPE_TAG('c', 'a', 'l', 't'), 0}};
101 static_assert(MOZ_ARRAY_LENGTH(ligDefaults
) == NS_FONT_VARIANT_LIGATURES_COUNT
,
102 "ligDefaults[] should be correct");
104 // NS_FONT_VARIANT_NUMERIC_xxx values
105 const gfxFontFeature numericDefaults
[] = {
106 {TRUETYPE_TAG('l', 'n', 'u', 'm'), 1},
107 {TRUETYPE_TAG('o', 'n', 'u', 'm'), 1},
108 {TRUETYPE_TAG('p', 'n', 'u', 'm'), 1},
109 {TRUETYPE_TAG('t', 'n', 'u', 'm'), 1},
110 {TRUETYPE_TAG('f', 'r', 'a', 'c'), 1},
111 {TRUETYPE_TAG('a', 'f', 'r', 'c'), 1},
112 {TRUETYPE_TAG('z', 'e', 'r', 'o'), 1},
113 {TRUETYPE_TAG('o', 'r', 'd', 'n'), 1}};
115 static_assert(MOZ_ARRAY_LENGTH(numericDefaults
) ==
116 NS_FONT_VARIANT_NUMERIC_COUNT
,
117 "numericDefaults[] should be correct");
119 static void AddFontFeaturesBitmask(uint32_t aValue
, uint32_t aMin
,
121 const gfxFontFeature aFeatureDefaults
[],
122 nsTArray
<gfxFontFeature
>& aFeaturesOut
)
127 for (i
= 0, m
= aMin
; m
<= aMax
; i
++, m
<<= 1) {
129 const gfxFontFeature
& feature
= aFeatureDefaults
[i
];
130 aFeaturesOut
.AppendElement(feature
);
135 static uint32_t FontFeatureTagForVariantWidth(uint32_t aVariantWidth
) {
136 switch (aVariantWidth
) {
137 case NS_FONT_VARIANT_WIDTH_FULL
:
138 return TRUETYPE_TAG('f', 'w', 'i', 'd');
139 case NS_FONT_VARIANT_WIDTH_HALF
:
140 return TRUETYPE_TAG('h', 'w', 'i', 'd');
141 case NS_FONT_VARIANT_WIDTH_THIRD
:
142 return TRUETYPE_TAG('t', 'w', 'i', 'd');
143 case NS_FONT_VARIANT_WIDTH_QUARTER
:
144 return TRUETYPE_TAG('q', 'w', 'i', 'd');
150 void nsFont::AddFontFeaturesToStyle(gfxFontStyle
* aStyle
,
151 bool aVertical
) const {
152 // add in font-variant features
153 gfxFontFeature setting
;
156 setting
.mTag
= aVertical
? TRUETYPE_TAG('v', 'k', 'r', 'n')
157 : TRUETYPE_TAG('k', 'e', 'r', 'n');
159 case NS_FONT_KERNING_NONE
:
161 aStyle
->featureSettings
.AppendElement(setting
);
163 case NS_FONT_KERNING_NORMAL
:
165 aStyle
->featureSettings
.AppendElement(setting
);
168 // auto case implies use user agent default
174 // NOTE(emilio): We handle historical-forms here because it doesn't depend on
175 // other values set by @font-face and thus may be less expensive to do here
176 // than after font-matching.
177 for (auto& alternate
: variantAlternates
.AsSpan()) {
178 if (alternate
.IsHistoricalForms()) {
180 setting
.mTag
= TRUETYPE_TAG('h', 'i', 's', 't');
181 aStyle
->featureSettings
.AppendElement(setting
);
186 // -- copy font-specific alternate info into style
187 // (this will be resolved after font-matching occurs)
188 aStyle
->variantAlternates
= variantAlternates
;
191 aStyle
->variantCaps
= variantCaps
;
194 if (variantEastAsian
) {
195 AddFontFeaturesBitmask(variantEastAsian
, NS_FONT_VARIANT_EAST_ASIAN_JIS78
,
196 NS_FONT_VARIANT_EAST_ASIAN_RUBY
, eastAsianDefaults
,
197 aStyle
->featureSettings
);
201 if (variantLigatures
) {
202 AddFontFeaturesBitmask(variantLigatures
, NS_FONT_VARIANT_LIGATURES_NONE
,
203 NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL
, ligDefaults
,
204 aStyle
->featureSettings
);
206 if (variantLigatures
& NS_FONT_VARIANT_LIGATURES_COMMON
) {
207 // liga already enabled, need to enable clig also
208 setting
.mTag
= TRUETYPE_TAG('c', 'l', 'i', 'g');
210 aStyle
->featureSettings
.AppendElement(setting
);
211 } else if (variantLigatures
& NS_FONT_VARIANT_LIGATURES_NO_COMMON
) {
212 // liga already disabled, need to disable clig also
213 setting
.mTag
= TRUETYPE_TAG('c', 'l', 'i', 'g');
215 aStyle
->featureSettings
.AppendElement(setting
);
216 } else if (variantLigatures
& NS_FONT_VARIANT_LIGATURES_NONE
) {
217 // liga already disabled, need to disable dlig, hlig, calt, clig
219 setting
.mTag
= TRUETYPE_TAG('d', 'l', 'i', 'g');
220 aStyle
->featureSettings
.AppendElement(setting
);
221 setting
.mTag
= TRUETYPE_TAG('h', 'l', 'i', 'g');
222 aStyle
->featureSettings
.AppendElement(setting
);
223 setting
.mTag
= TRUETYPE_TAG('c', 'a', 'l', 't');
224 aStyle
->featureSettings
.AppendElement(setting
);
225 setting
.mTag
= TRUETYPE_TAG('c', 'l', 'i', 'g');
226 aStyle
->featureSettings
.AppendElement(setting
);
231 if (variantNumeric
) {
232 AddFontFeaturesBitmask(variantNumeric
, NS_FONT_VARIANT_NUMERIC_LINING
,
233 NS_FONT_VARIANT_NUMERIC_ORDINAL
, numericDefaults
,
234 aStyle
->featureSettings
);
238 aStyle
->variantSubSuper
= variantPosition
;
241 setting
.mTag
= FontFeatureTagForVariantWidth(variantWidth
);
244 aStyle
->featureSettings
.AppendElement(setting
);
247 // indicate common-path case when neither variantCaps or variantSubSuper are
249 aStyle
->noFallbackVariantFeatures
=
250 (aStyle
->variantCaps
== NS_FONT_VARIANT_CAPS_NORMAL
) &&
251 (variantPosition
== NS_FONT_VARIANT_POSITION_NORMAL
);
253 // If the feature list is not empty, we insert a "fake" feature with tag=0
254 // as delimiter between the above "high-level" features from font-variant-*
255 // etc and those coming from the low-level font-feature-settings property.
256 // This will allow us to distinguish high- and low-level settings when it
257 // comes to potentially disabling ligatures because of letter-spacing.
258 if (!aStyle
->featureSettings
.IsEmpty() || !fontFeatureSettings
.IsEmpty()) {
259 aStyle
->featureSettings
.AppendElement(gfxFontFeature
{0, 0});
262 // add in features from font-feature-settings
263 aStyle
->featureSettings
.AppendElements(fontFeatureSettings
);
265 // enable grayscale antialiasing for text
266 if (smoothing
== NS_FONT_SMOOTHING_GRAYSCALE
) {
267 aStyle
->useGrayscaleAntialiasing
= true;
271 void nsFont::AddFontVariationsToStyle(gfxFontStyle
* aStyle
) const {
272 // If auto optical sizing is enabled, and if there's no 'opsz' axis in
273 // fontVariationSettings, then set the automatic value on the style.
274 class VariationTagComparator
{
276 bool Equals(const gfxFontVariation
& aVariation
, uint32_t aTag
) const {
277 return aVariation
.mTag
== aTag
;
280 const uint32_t kTagOpsz
= TRUETYPE_TAG('o', 'p', 's', 'z');
281 if (opticalSizing
== NS_FONT_OPTICAL_SIZING_AUTO
&&
282 !fontVariationSettings
.Contains(kTagOpsz
, VariationTagComparator())) {
283 aStyle
->autoOpticalSize
= size
.ToCSSPixels();
286 // Add in arbitrary values from font-variation-settings
287 aStyle
->variationSettings
.AppendElements(fontVariationSettings
);