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 (synthesis
!= aOther
.synthesis
) ||
44 (fontFeatureSettings
!= aOther
.fontFeatureSettings
) ||
45 (fontVariationSettings
!= aOther
.fontVariationSettings
) ||
46 (languageOverride
!= aOther
.languageOverride
) ||
47 (variantAlternates
!= aOther
.variantAlternates
) ||
48 (variantCaps
!= aOther
.variantCaps
) ||
49 (variantEastAsian
!= aOther
.variantEastAsian
) ||
50 (variantLigatures
!= aOther
.variantLigatures
) ||
51 (variantNumeric
!= aOther
.variantNumeric
) ||
52 (variantPosition
!= aOther
.variantPosition
) ||
53 (variantWidth
!= aOther
.variantWidth
)) {
54 return MaxDifference::eLayoutAffecting
;
57 if ((smoothing
!= aOther
.smoothing
) ||
58 (fontSmoothingBackgroundColor
!= aOther
.fontSmoothingBackgroundColor
)) {
59 return MaxDifference::eVisual
;
62 return MaxDifference::eNone
;
65 // mapping from bitflag to font feature tag/value pair
67 // these need to be kept in sync with the constants listed
68 // in gfxFontConstants.h (e.g. NS_FONT_VARIANT_EAST_ASIAN_JIS78)
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}};
82 static_assert(MOZ_ARRAY_LENGTH(eastAsianDefaults
) ==
83 NS_FONT_VARIANT_EAST_ASIAN_COUNT
,
84 "eastAsianDefaults[] should be correct");
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}};
98 static_assert(MOZ_ARRAY_LENGTH(ligDefaults
) == NS_FONT_VARIANT_LIGATURES_COUNT
,
99 "ligDefaults[] should be correct");
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}};
112 static_assert(MOZ_ARRAY_LENGTH(numericDefaults
) ==
113 NS_FONT_VARIANT_NUMERIC_COUNT
,
114 "numericDefaults[] should be correct");
116 static void AddFontFeaturesBitmask(uint32_t aValue
, uint32_t aMin
,
118 const gfxFontFeature aFeatureDefaults
[],
119 nsTArray
<gfxFontFeature
>& aFeaturesOut
)
124 for (i
= 0, m
= aMin
; m
<= aMax
; i
++, m
<<= 1) {
126 const gfxFontFeature
& feature
= aFeatureDefaults
[i
];
127 aFeaturesOut
.AppendElement(feature
);
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');
147 void nsFont::AddFontFeaturesToStyle(gfxFontStyle
* aStyle
,
148 bool aVertical
) const {
149 // add in font-variant features
150 gfxFontFeature setting
;
153 setting
.mTag
= aVertical
? TRUETYPE_TAG('v', 'k', 'r', 'n')
154 : TRUETYPE_TAG('k', 'e', 'r', 'n');
156 case NS_FONT_KERNING_NONE
:
158 aStyle
->featureSettings
.AppendElement(setting
);
160 case NS_FONT_KERNING_NORMAL
:
162 aStyle
->featureSettings
.AppendElement(setting
);
165 // auto case implies use user agent default
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()) {
177 setting
.mTag
= TRUETYPE_TAG('h', 'i', 's', 't');
178 aStyle
->featureSettings
.AppendElement(setting
);
183 // -- copy font-specific alternate info into style
184 // (this will be resolved after font-matching occurs)
185 aStyle
->variantAlternates
= variantAlternates
;
188 aStyle
->variantCaps
= variantCaps
;
191 if (variantEastAsian
) {
192 AddFontFeaturesBitmask(variantEastAsian
, NS_FONT_VARIANT_EAST_ASIAN_JIS78
,
193 NS_FONT_VARIANT_EAST_ASIAN_RUBY
, eastAsianDefaults
,
194 aStyle
->featureSettings
);
198 if (variantLigatures
) {
199 AddFontFeaturesBitmask(variantLigatures
, NS_FONT_VARIANT_LIGATURES_NONE
,
200 NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL
, ligDefaults
,
201 aStyle
->featureSettings
);
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');
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');
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
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
);
228 if (variantNumeric
) {
229 AddFontFeaturesBitmask(variantNumeric
, NS_FONT_VARIANT_NUMERIC_LINING
,
230 NS_FONT_VARIANT_NUMERIC_ORDINAL
, numericDefaults
,
231 aStyle
->featureSettings
);
235 aStyle
->variantSubSuper
= variantPosition
;
238 setting
.mTag
= FontFeatureTagForVariantWidth(variantWidth
);
241 aStyle
->featureSettings
.AppendElement(setting
);
244 // indicate common-path case when neither variantCaps or variantSubSuper are
246 aStyle
->noFallbackVariantFeatures
=
247 (aStyle
->variantCaps
== NS_FONT_VARIANT_CAPS_NORMAL
) &&
248 (variantPosition
== NS_FONT_VARIANT_POSITION_NORMAL
);
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});
259 // add in features from font-feature-settings
260 aStyle
->featureSettings
.AppendElements(fontFeatureSettings
);
262 // enable grayscale antialiasing for text
263 if (smoothing
== NS_FONT_SMOOTHING_GRAYSCALE
) {
264 aStyle
->useGrayscaleAntialiasing
= true;
267 aStyle
->fontSmoothingBackgroundColor
= fontSmoothingBackgroundColor
.ToColor();
270 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
{
275 bool Equals(const gfxFontVariation
& aVariation
, uint32_t aTag
) const {
276 return aVariation
.mTag
== aTag
;
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 aStyle
->autoOpticalSize
= size
.ToCSSPixels();
285 // Add in arbitrary values from font-variation-settings
286 aStyle
->variationSettings
.AppendElements(fontVariationSettings
);