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 FontFamilyList
& aFontlist
, mozilla::Length aSize
)
23 : fontlist(aFontlist
), size(aSize
) {}
25 nsFont::nsFont(StyleGenericFontFamily aGenericType
, mozilla::Length aSize
)
26 : fontlist(aGenericType
), size(aSize
) {}
28 nsFont::nsFont(const nsFont
& aOther
) = default;
30 nsFont::~nsFont() = default;
32 bool nsFont::Equals(const nsFont
& aOther
) const {
33 return CalcDifference(aOther
) == MaxDifference::eNone
;
36 nsFont::MaxDifference
nsFont::CalcDifference(const nsFont
& aOther
) const {
37 if ((style
!= aOther
.style
) || (systemFont
!= aOther
.systemFont
) ||
38 (weight
!= aOther
.weight
) || (stretch
!= aOther
.stretch
) ||
39 (size
!= aOther
.size
) || (sizeAdjust
!= aOther
.sizeAdjust
) ||
40 (fontlist
!= aOther
.fontlist
) || (kerning
!= aOther
.kerning
) ||
41 (opticalSizing
!= aOther
.opticalSizing
) ||
42 (synthesis
!= aOther
.synthesis
) ||
43 (fontFeatureSettings
!= aOther
.fontFeatureSettings
) ||
44 (fontVariationSettings
!= aOther
.fontVariationSettings
) ||
45 (languageOverride
!= aOther
.languageOverride
) ||
46 (variantAlternates
!= aOther
.variantAlternates
) ||
47 (variantCaps
!= aOther
.variantCaps
) ||
48 (variantEastAsian
!= aOther
.variantEastAsian
) ||
49 (variantLigatures
!= aOther
.variantLigatures
) ||
50 (variantNumeric
!= aOther
.variantNumeric
) ||
51 (variantPosition
!= aOther
.variantPosition
) ||
52 (variantWidth
!= aOther
.variantWidth
)) {
53 return MaxDifference::eLayoutAffecting
;
56 if ((smoothing
!= aOther
.smoothing
) ||
57 (fontSmoothingBackgroundColor
!= aOther
.fontSmoothingBackgroundColor
)) {
58 return MaxDifference::eVisual
;
61 return MaxDifference::eNone
;
64 nsFont
& nsFont::operator=(const nsFont
& aOther
) = default;
66 // mapping from bitflag to font feature tag/value pair
68 // these need to be kept in sync with the constants listed
69 // in gfxFontConstants.h (e.g. NS_FONT_VARIANT_EAST_ASIAN_JIS78)
71 // NS_FONT_VARIANT_EAST_ASIAN_xxx values
72 const gfxFontFeature eastAsianDefaults
[] = {
73 {TRUETYPE_TAG('j', 'p', '7', '8'), 1},
74 {TRUETYPE_TAG('j', 'p', '8', '3'), 1},
75 {TRUETYPE_TAG('j', 'p', '9', '0'), 1},
76 {TRUETYPE_TAG('j', 'p', '0', '4'), 1},
77 {TRUETYPE_TAG('s', 'm', 'p', 'l'), 1},
78 {TRUETYPE_TAG('t', 'r', 'a', 'd'), 1},
79 {TRUETYPE_TAG('f', 'w', 'i', 'd'), 1},
80 {TRUETYPE_TAG('p', 'w', 'i', 'd'), 1},
81 {TRUETYPE_TAG('r', 'u', 'b', 'y'), 1}};
83 static_assert(MOZ_ARRAY_LENGTH(eastAsianDefaults
) ==
84 NS_FONT_VARIANT_EAST_ASIAN_COUNT
,
85 "eastAsianDefaults[] should be correct");
87 // NS_FONT_VARIANT_LIGATURES_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}};
99 static_assert(MOZ_ARRAY_LENGTH(ligDefaults
) == NS_FONT_VARIANT_LIGATURES_COUNT
,
100 "ligDefaults[] should be correct");
102 // NS_FONT_VARIANT_NUMERIC_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}};
113 static_assert(MOZ_ARRAY_LENGTH(numericDefaults
) ==
114 NS_FONT_VARIANT_NUMERIC_COUNT
,
115 "numericDefaults[] should be correct");
117 static void AddFontFeaturesBitmask(uint32_t aValue
, uint32_t aMin
,
119 const gfxFontFeature aFeatureDefaults
[],
120 nsTArray
<gfxFontFeature
>& aFeaturesOut
)
125 for (i
= 0, m
= aMin
; m
<= aMax
; i
++, m
<<= 1) {
127 const gfxFontFeature
& feature
= aFeatureDefaults
[i
];
128 aFeaturesOut
.AppendElement(feature
);
133 static uint32_t FontFeatureTagForVariantWidth(uint32_t aVariantWidth
) {
134 switch (aVariantWidth
) {
135 case NS_FONT_VARIANT_WIDTH_FULL
:
136 return TRUETYPE_TAG('f', 'w', 'i', 'd');
137 case NS_FONT_VARIANT_WIDTH_HALF
:
138 return TRUETYPE_TAG('h', 'w', 'i', 'd');
139 case NS_FONT_VARIANT_WIDTH_THIRD
:
140 return TRUETYPE_TAG('t', 'w', 'i', 'd');
141 case NS_FONT_VARIANT_WIDTH_QUARTER
:
142 return TRUETYPE_TAG('q', 'w', 'i', 'd');
148 void nsFont::AddFontFeaturesToStyle(gfxFontStyle
* aStyle
,
149 bool aVertical
) const {
150 // add in font-variant features
151 gfxFontFeature setting
;
154 setting
.mTag
= aVertical
? TRUETYPE_TAG('v', 'k', 'r', 'n')
155 : TRUETYPE_TAG('k', 'e', 'r', 'n');
157 case NS_FONT_KERNING_NONE
:
159 aStyle
->featureSettings
.AppendElement(setting
);
161 case NS_FONT_KERNING_NORMAL
:
163 aStyle
->featureSettings
.AppendElement(setting
);
166 // auto case implies use user agent default
172 // NOTE(emilio): We handle historical-forms here because it doesn't depend on
173 // other values set by @font-face and thus may be less expensive to do here
174 // than after font-matching.
175 for (auto& alternate
: variantAlternates
.AsSpan()) {
176 if (alternate
.IsHistoricalForms()) {
178 setting
.mTag
= TRUETYPE_TAG('h', 'i', 's', 't');
179 aStyle
->featureSettings
.AppendElement(setting
);
184 // -- copy font-specific alternate info into style
185 // (this will be resolved after font-matching occurs)
186 aStyle
->variantAlternates
= variantAlternates
;
189 aStyle
->variantCaps
= variantCaps
;
192 if (variantEastAsian
) {
193 AddFontFeaturesBitmask(variantEastAsian
, NS_FONT_VARIANT_EAST_ASIAN_JIS78
,
194 NS_FONT_VARIANT_EAST_ASIAN_RUBY
, eastAsianDefaults
,
195 aStyle
->featureSettings
);
199 if (variantLigatures
) {
200 AddFontFeaturesBitmask(variantLigatures
, NS_FONT_VARIANT_LIGATURES_NONE
,
201 NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL
, ligDefaults
,
202 aStyle
->featureSettings
);
204 if (variantLigatures
& NS_FONT_VARIANT_LIGATURES_COMMON
) {
205 // liga already enabled, need to enable clig also
206 setting
.mTag
= TRUETYPE_TAG('c', 'l', 'i', 'g');
208 aStyle
->featureSettings
.AppendElement(setting
);
209 } else if (variantLigatures
& NS_FONT_VARIANT_LIGATURES_NO_COMMON
) {
210 // liga already disabled, need to disable clig also
211 setting
.mTag
= TRUETYPE_TAG('c', 'l', 'i', 'g');
213 aStyle
->featureSettings
.AppendElement(setting
);
214 } else if (variantLigatures
& NS_FONT_VARIANT_LIGATURES_NONE
) {
215 // liga already disabled, need to disable dlig, hlig, calt, clig
217 setting
.mTag
= TRUETYPE_TAG('d', 'l', 'i', 'g');
218 aStyle
->featureSettings
.AppendElement(setting
);
219 setting
.mTag
= TRUETYPE_TAG('h', 'l', 'i', 'g');
220 aStyle
->featureSettings
.AppendElement(setting
);
221 setting
.mTag
= TRUETYPE_TAG('c', 'a', 'l', 't');
222 aStyle
->featureSettings
.AppendElement(setting
);
223 setting
.mTag
= TRUETYPE_TAG('c', 'l', 'i', 'g');
224 aStyle
->featureSettings
.AppendElement(setting
);
229 if (variantNumeric
) {
230 AddFontFeaturesBitmask(variantNumeric
, NS_FONT_VARIANT_NUMERIC_LINING
,
231 NS_FONT_VARIANT_NUMERIC_ORDINAL
, numericDefaults
,
232 aStyle
->featureSettings
);
236 aStyle
->variantSubSuper
= variantPosition
;
239 setting
.mTag
= FontFeatureTagForVariantWidth(variantWidth
);
242 aStyle
->featureSettings
.AppendElement(setting
);
245 // indicate common-path case when neither variantCaps or variantSubSuper are
247 aStyle
->noFallbackVariantFeatures
=
248 (aStyle
->variantCaps
== NS_FONT_VARIANT_CAPS_NORMAL
) &&
249 (variantPosition
== NS_FONT_VARIANT_POSITION_NORMAL
);
251 // If the feature list is not empty, we insert a "fake" feature with tag=0
252 // as delimiter between the above "high-level" features from font-variant-*
253 // etc and those coming from the low-level font-feature-settings property.
254 // This will allow us to distinguish high- and low-level settings when it
255 // comes to potentially disabling ligatures because of letter-spacing.
256 if (!aStyle
->featureSettings
.IsEmpty() || !fontFeatureSettings
.IsEmpty()) {
257 aStyle
->featureSettings
.AppendElement(gfxFontFeature
{0, 0});
260 // add in features from font-feature-settings
261 aStyle
->featureSettings
.AppendElements(fontFeatureSettings
);
263 // enable grayscale antialiasing for text
264 if (smoothing
== NS_FONT_SMOOTHING_GRAYSCALE
) {
265 aStyle
->useGrayscaleAntialiasing
= true;
268 aStyle
->fontSmoothingBackgroundColor
= fontSmoothingBackgroundColor
.ToColor();
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
);