Bug 1706080 - [css-fonts] Implement 'font-synthesis: small-caps'. r=jfkthame

Differential Revision: https://phabricator.services.mozilla.com/D114313
This commit is contained in:
Mats Palmgren 2021-08-09 21:41:34 +00:00
Родитель 4498297532
Коммит 14c9063444
18 изменённых файлов: 191 добавлений и 47 удалений

Просмотреть файл

@ -6393,7 +6393,9 @@ exports.CSS_PROPERTIES = {
"values": [ "values": [
"inherit", "inherit",
"initial", "initial",
"none",
"revert", "revert",
"small-caps",
"style", "style",
"unset", "unset",
"weight" "weight"

Просмотреть файл

@ -82,8 +82,9 @@ struct nsFont final {
// that include an 'opsz' axis // that include an 'opsz' axis
uint8_t opticalSizing = NS_FONT_OPTICAL_SIZING_AUTO; uint8_t opticalSizing = NS_FONT_OPTICAL_SIZING_AUTO;
// Synthesis setting, controls use of fake bolding/italics // Synthesis setting, controls use of fake bolding/italics/small-caps
uint8_t synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE; uint8_t synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE |
NS_FONT_SYNTHESIS_SMALL_CAPS;
// initialize the font with a fontlist // initialize the font with a fontlist
nsFont(const mozilla::StyleFontFamily&, mozilla::Length aSize); nsFont(const mozilla::StyleFontFamily&, mozilla::Length aSize);

Просмотреть файл

@ -123,7 +123,8 @@ nsFontMetrics::nsFontMetrics(const nsFont& aFont, const Params& aParams,
gfxFloat(aFont.size.ToAppUnits()) / mP2A, aFont.sizeAdjust, gfxFloat(aFont.size.ToAppUnits()) / mP2A, aFont.sizeAdjust,
aFont.family.is_system_font, mDeviceContext->IsPrinterContext(), aFont.family.is_system_font, mDeviceContext->IsPrinterContext(),
aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT, aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
aFont.synthesis & NS_FONT_SYNTHESIS_STYLE, aFont.languageOverride); aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
aFont.synthesis & NS_FONT_SYNTHESIS_SMALL_CAPS, aFont.languageOverride);
aFont.AddFontFeaturesToStyle(&style, mOrientation == eVertical); aFont.AddFontFeaturesToStyle(&style, mOrientation == eVertical);
style.featureValueLookup = aParams.featureValueLookup; style.featureValueLookup = aParams.featureValueLookup;

Просмотреть файл

@ -4032,6 +4032,7 @@ gfxFontStyle::gfxFontStyle()
useGrayscaleAntialiasing(false), useGrayscaleAntialiasing(false),
allowSyntheticWeight(true), allowSyntheticWeight(true),
allowSyntheticStyle(true), allowSyntheticStyle(true),
allowSyntheticSmallCaps(true),
noFallbackVariantFeatures(true) {} noFallbackVariantFeatures(true) {}
gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight, gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight,
@ -4039,6 +4040,7 @@ gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight,
const FontSizeAdjust& aSizeAdjust, bool aSystemFont, const FontSizeAdjust& aSizeAdjust, bool aSystemFont,
bool aPrinterFont, bool aAllowWeightSynthesis, bool aPrinterFont, bool aAllowWeightSynthesis,
bool aAllowStyleSynthesis, bool aAllowStyleSynthesis,
bool aAllowSmallCapsSynthesis,
uint32_t aLanguageOverride) uint32_t aLanguageOverride)
: size(aSize), : size(aSize),
baselineOffset(0.0f), baselineOffset(0.0f),
@ -4054,6 +4056,7 @@ gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight,
useGrayscaleAntialiasing(false), useGrayscaleAntialiasing(false),
allowSyntheticWeight(aAllowWeightSynthesis), allowSyntheticWeight(aAllowWeightSynthesis),
allowSyntheticStyle(aAllowStyleSynthesis), allowSyntheticStyle(aAllowStyleSynthesis),
allowSyntheticSmallCaps(aAllowSmallCapsSynthesis),
noFallbackVariantFeatures(true) { noFallbackVariantFeatures(true) {
MOZ_ASSERT(!mozilla::IsNaN(size)); MOZ_ASSERT(!mozilla::IsNaN(size));

Просмотреть файл

@ -103,7 +103,8 @@ struct gfxFontStyle {
gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight, FontStretch aStretch, gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight, FontStretch aStretch,
gfxFloat aSize, const FontSizeAdjust& aSizeAdjust, gfxFloat aSize, const FontSizeAdjust& aSizeAdjust,
bool aSystemFont, bool aPrinterFont, bool aWeightSynthesis, bool aSystemFont, bool aPrinterFont, bool aWeightSynthesis,
bool aStyleSynthesis, uint32_t aLanguageOverride); bool aStyleSynthesis, bool aSmallCapsSynthesis,
uint32_t aLanguageOverride);
// Features are composed of (1) features from style rules (2) features // Features are composed of (1) features from style rules (2) features
// from feature settings rules and (3) family-specific features. (1) and // from feature settings rules and (3) family-specific features. (1) and
// (3) are guaranteed to be mutually exclusive // (3) are guaranteed to be mutually exclusive
@ -198,6 +199,7 @@ struct gfxFontStyle {
// Whether synthetic styles are allowed // Whether synthetic styles are allowed
bool allowSyntheticWeight : 1; bool allowSyntheticWeight : 1;
bool allowSyntheticStyle : 1; bool allowSyntheticStyle : 1;
bool allowSyntheticSmallCaps : 1;
// some variant features require fallback which complicates the shaping // some variant features require fallback which complicates the shaping
// code, so set up a bool to indicate when shaping with fallback is needed // code, so set up a bool to indicate when shaping with fallback is needed

Просмотреть файл

@ -40,6 +40,7 @@
#define NS_FONT_SYNTHESIS_WEIGHT 0x1 #define NS_FONT_SYNTHESIS_WEIGHT 0x1
#define NS_FONT_SYNTHESIS_STYLE 0x2 #define NS_FONT_SYNTHESIS_STYLE 0x2
#define NS_FONT_SYNTHESIS_SMALL_CAPS 0x4
#define NS_FONT_OPTICAL_SIZING_AUTO 0 #define NS_FONT_OPTICAL_SIZING_AUTO 0
#define NS_FONT_OPTICAL_SIZING_NONE 1 #define NS_FONT_OPTICAL_SIZING_NONE 1

Просмотреть файл

@ -2758,6 +2758,7 @@ void gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun,
matchedFont = nullptr; matchedFont = nullptr;
} }
} else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL && } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL &&
mStyle.allowSyntheticSmallCaps &&
!matchedFont->SupportsVariantCaps( !matchedFont->SupportsVariantCaps(
aRunScript, mStyle.variantCaps, petiteToSmallCaps, aRunScript, mStyle.variantCaps, petiteToSmallCaps,
syntheticLower, syntheticUpper)) { syntheticLower, syntheticUpper)) {

Просмотреть файл

@ -5761,8 +5761,8 @@ var gCSSProperties = {
applies_to_marker: true, applies_to_marker: true,
applies_to_placeholder: true, applies_to_placeholder: true,
applies_to_cue: true, applies_to_cue: true,
initial_values: ["weight style"], initial_values: ["weight style small-caps"],
other_values: ["none", "weight", "style"], other_values: ["none", "weight", "style", "small-caps"],
invalid_values: [ invalid_values: [
"weight none", "weight none",
"style none", "style none",
@ -5770,6 +5770,8 @@ var gCSSProperties = {
"weight 10px", "weight 10px",
"weight weight", "weight weight",
"style style", "style style",
"small-caps none",
"small-caps small-caps",
], ],
}, },
"font-variant": { "font-variant": {

Просмотреть файл

@ -7618,6 +7618,13 @@
mirror: always mirror: always
rust: true rust: true
# Is 'font-synthesis: small-caps' supported?
- name: layout.css.font-synthesis-small-caps.enabled
type: RelaxedAtomicBool
value: true
mirror: always
rust: true
# Whether frame visibility tracking is enabled globally. # Whether frame visibility tracking is enabled globally.
- name: layout.framevisibility.enabled - name: layout.framevisibility.enabled
type: bool type: bool

Просмотреть файл

@ -1950,23 +1950,24 @@ impl Parse for FontFeatureSettings {
Debug, Debug,
MallocSizeOf, MallocSizeOf,
PartialEq, PartialEq,
SpecifiedValueInfo,
ToComputedValue, ToComputedValue,
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
/// Whether user agents are allowed to synthesize bold or oblique font faces /// Whether user agents are allowed to synthesize bold or oblique font faces
/// when a font family lacks bold or italic faces /// when a font family lacks those faces, or a small-caps variant when this is
/// not supported by the face.
pub struct FontSynthesis { pub struct FontSynthesis {
/// If a `font-weight` is requested that the font family does not contain, /// If a `font-weight` is requested that the font family does not contain,
/// the user agent may synthesize the requested weight from the weights /// the user agent may synthesize the requested weight from the weights
/// that do exist in the font family. /// that do exist in the font family.
#[css(represents_keyword)]
pub weight: bool, pub weight: bool,
/// If a font-style is requested that the font family does not contain, /// If a font-style is requested that the font family does not contain,
/// the user agent may synthesize the requested style from the normal face in the font family. /// the user agent may synthesize the requested style from the normal face in the font family.
#[css(represents_keyword)]
pub style: bool, pub style: bool,
/// This bit controls whether the user agent is allowed to synthesize small caps variant
/// when a font face lacks it.
pub small_caps: bool,
} }
impl FontSynthesis { impl FontSynthesis {
@ -1976,6 +1977,7 @@ impl FontSynthesis {
FontSynthesis { FontSynthesis {
weight: true, weight: true,
style: true, style: true,
small_caps: true,
} }
} }
#[inline] #[inline]
@ -1984,8 +1986,14 @@ impl FontSynthesis {
FontSynthesis { FontSynthesis {
weight: false, weight: false,
style: false, style: false,
small_caps: false,
} }
} }
#[inline]
/// Return true if this is the 'none' value
pub fn is_none(&self) -> bool {
*self == Self::none()
}
} }
impl Parse for FontSynthesis { impl Parse for FontSynthesis {
@ -1993,26 +2001,23 @@ impl Parse for FontSynthesis {
_: &ParserContext, _: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<FontSynthesis, ParseError<'i>> { ) -> Result<FontSynthesis, ParseError<'i>> {
let mut result = FontSynthesis { use crate::values::SelectorParseErrorKind;
weight: false, let mut result = Self::none();
style: false, while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
}; match_ignore_ascii_case! { &ident,
try_match_ident_ignore_ascii_case! { input, "none" if result.is_none() => return Ok(result),
"none" => Ok(result), "weight" if !result.weight => result.weight = true,
"weight" => { "style" if !result.style => result.style = true,
result.weight = true; "small-caps" if !result.small_caps &&
if input.try_parse(|input| input.expect_ident_matching("style")).is_ok() { static_prefs::pref!("layout.css.font-synthesis-small-caps.enabled")
result.style = true; => result.small_caps = true,
} _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident))),
Ok(result) }
}, }
"style" => { if !result.is_none() {
result.style = true; Ok(result)
if input.try_parse(|input| input.expect_ident_matching("weight")).is_ok() { } else {
result.weight = true; Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
Ok(result)
},
} }
} }
} }
@ -2022,14 +2027,41 @@ impl ToCss for FontSynthesis {
where where
W: Write, W: Write,
{ {
if self.weight && self.style { if self.is_none() {
dest.write_str("weight style") return dest.write_str("none");
} else if self.style { }
dest.write_str("style")
} else if self.weight { let mut need_space = false;
dest.write_str("weight") if self.weight {
} else { dest.write_str("weight")?;
dest.write_str("none") need_space = true;
}
if self.style {
if need_space {
dest.write_str(" ")?;
}
dest.write_str("style")?;
need_space = true;
}
if self.small_caps {
if need_space {
dest.write_str(" ")?;
}
dest.write_str("small-caps")?;
}
Ok(())
}
}
impl SpecifiedValueInfo for FontSynthesis {
fn collect_completion_keywords(f: KeywordsCollectFn) {
f(&[
"none",
"weight",
"style",
]);
if static_prefs::pref!("layout.css.font-synthesis-small-caps.enabled") {
f(&["small-caps"]);
} }
} }
} }
@ -2042,6 +2074,7 @@ impl From<u8> for FontSynthesis {
FontSynthesis { FontSynthesis {
weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT as u8 != 0, weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT as u8 != 0,
style: bits & structs::NS_FONT_SYNTHESIS_STYLE as u8 != 0, style: bits & structs::NS_FONT_SYNTHESIS_STYLE as u8 != 0,
small_caps: bits & structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8 != 0,
} }
} }
} }
@ -2058,6 +2091,9 @@ impl From<FontSynthesis> for u8 {
if v.style { if v.style {
bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8; bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8;
} }
if v.small_caps {
bits |= structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8;
}
bits bits
} }
} }

Просмотреть файл

@ -0,0 +1,16 @@
<!doctype html>
<meta charset="utf-8">
<title>Reference: 'font-synthesis:none' with a font that support small-caps</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
<style>
@font-face {
font-family: fwf;
src: url(support/fonts/FontWithFancyFeatures.otf);
}
.test {
font: 2em/1 fwf;
}
</style>
<p>Test passes if there are four check marks (✓), and zero crosses (✗). </p>
<p class="test">CCC C</p>

Просмотреть файл

@ -0,0 +1,21 @@
<!doctype html>
<meta charset=utf-8>
<title>CSS Test: 'font-synthesis:none' with a font that support small-caps</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-synthesis">
<link rel="match" href="font-synthesis-06-ref.html">
<meta name="assert" content="font-synthesis:none should not affect a font that supports small-caps">
<style>
@font-face {
font-family: fwf;
src: url(support/fonts/FontWithFancyFeatures.otf);
}
.test {
font: 2em/1 fwf;
font-variant: small-caps;
font-synthesis: none;
}
</style>
<p>Test passes if there are four check marks (✓), and zero crosses (✗). </p>
<p class="test">JJJ J</p>

Просмотреть файл

@ -0,0 +1,16 @@
<!doctype html>
<meta charset=utf-8>
<title>Reference: 'font-synthesis:none' with a font that doesn't support small-caps</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
<style>
@font-face {
font-family: Lato;
src: url(support/fonts/Lato-Medium.ttf);
}
.test {
font: 2em/1 Lato;
}
</style>
<p>Test passes if "A" is upper-case and "bcd e" is lower-case.</p>
<p class="test">Abcd e</p>

Просмотреть файл

@ -0,0 +1,21 @@
<!doctype html>
<meta charset=utf-8>
<title>CSS Test: 'font-synthesis:none' with a font that doesn't support small-caps</title>
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-synthesis">
<link rel="match" href="font-synthesis-07-ref.html">
<meta name="assert" content="font-synthesis:none inhibits synthesising small-caps">
<style>
@font-face {
font-family: Lato;
src: url(support/fonts/Lato-Medium.ttf);
}
.test {
font: 2em/1 Lato;
font-variant: small-caps;
font-synthesis: none;
}
</style>
<p>Test passes if "A" is upper-case and "bcd e" is lower-case.</p>
<p class="test">Abcd e</p>

Просмотреть файл

@ -35,7 +35,7 @@ assert_inherited('font-size', mediumFontSize /* medium */, '123px');
assert_inherited('font-size-adjust', 'none', '1.5'); assert_inherited('font-size-adjust', 'none', '1.5');
assert_inherited('font-stretch', '100%' /* normal */, '75%'); assert_inherited('font-stretch', '100%' /* normal */, '75%');
assert_inherited('font-style', 'normal', 'italic'); assert_inherited('font-style', 'normal', 'italic');
assert_inherited('font-synthesis', 'weight style', 'none'); assert_inherited('font-synthesis', 'weight style small-caps', 'none');
assert_inherited('font-variant', 'normal', 'none'); assert_inherited('font-variant', 'normal', 'none');
assert_inherited('font-variant-alternates', 'normal', 'historical-forms'); assert_inherited('font-variant-alternates', 'normal', 'historical-forms');
assert_inherited('font-variant-caps', 'normal', 'small-caps'); assert_inherited('font-variant-caps', 'normal', 'small-caps');

Просмотреть файл

@ -2,8 +2,8 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>CSS Fonts Module Level 3: getComputedStyle().fontSynthesis</title> <title>CSS Fonts Module: getComputedStyle().fontSynthesis</title>
<link rel="help" href="https://www.w3.org/TR/css-fonts-3/#font-synthesis-prop"> <link rel="help" href="https://drafts.csswg.org/css-fonts/#font-synthesis">
<meta name="assert" content="font-synthesis computed value is as specified."> <meta name="assert" content="font-synthesis computed value is as specified.">
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
@ -15,7 +15,11 @@
test_computed_value('font-synthesis', 'none'); test_computed_value('font-synthesis', 'none');
test_computed_value('font-synthesis', 'weight'); test_computed_value('font-synthesis', 'weight');
test_computed_value('font-synthesis', 'style'); test_computed_value('font-synthesis', 'style');
test_computed_value('font-synthesis', 'small-caps');
test_computed_value('font-synthesis', 'style small-caps');
test_computed_value('font-synthesis', 'weight small-caps');
test_computed_value('font-synthesis', 'weight style'); test_computed_value('font-synthesis', 'weight style');
test_computed_value('font-synthesis', 'weight style small-caps');
</script> </script>
</body> </body>
</html> </html>

Просмотреть файл

@ -2,9 +2,9 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>CSS Fonts Module Level 3: parsing font-synthesis with invalid values</title> <title>CSS Fonts Module: parsing font-synthesis with invalid values</title>
<link rel="help" href="https://www.w3.org/TR/css-fonts-3/#font-synthesis-prop"> <link rel="help" href="https://drafts.csswg.org/css-fonts/#font-synthesis">
<meta name="assert" content="font-synthesis supports only the grammar 'none | [ weight || style ]'."> <meta name="assert" content="font-synthesis supports only the grammar 'none | [ weight || style || small-caps ]'.">
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script> <script src="/css/support/parsing-testcommon.js"></script>
@ -13,6 +13,10 @@
<script> <script>
test_invalid_value('font-synthesis', 'auto'); test_invalid_value('font-synthesis', 'auto');
test_invalid_value('font-synthesis', 'none weight'); test_invalid_value('font-synthesis', 'none weight');
test_invalid_value('font-synthesis', 'none style');
test_invalid_value('font-synthesis', 'style none');
test_invalid_value('font-synthesis', 'none small-caps');
test_invalid_value('font-synthesis', 'small-caps none');
</script> </script>
</body> </body>
</html> </html>

Просмотреть файл

@ -2,9 +2,9 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>CSS Fonts Module Level 3: parsing font-synthesis with valid values</title> <title>CSS Fonts Module: parsing font-synthesis with valid values</title>
<link rel="help" href="https://www.w3.org/TR/css-fonts-3/#font-synthesis-prop"> <link rel="help" href="https://drafts.csswg.org/css-fonts/#font-synthesis">
<meta name="assert" content="font-synthesis supports the full grammar 'none | [ weight || style ]'."> <meta name="assert" content="font-synthesis supports the full grammar 'none | [ weight || style || small-caps ]'.">
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script> <script src="/css/support/parsing-testcommon.js"></script>
@ -14,7 +14,13 @@
test_valid_value('font-synthesis', 'none'); test_valid_value('font-synthesis', 'none');
test_valid_value('font-synthesis', 'weight'); test_valid_value('font-synthesis', 'weight');
test_valid_value('font-synthesis', 'style'); test_valid_value('font-synthesis', 'style');
test_valid_value('font-synthesis', 'small-caps');
test_valid_value('font-synthesis', 'style weight', 'weight style'); test_valid_value('font-synthesis', 'style weight', 'weight style');
test_valid_value('font-synthesis', 'small-caps weight', 'weight small-caps');
test_valid_value('font-synthesis', 'small-caps style', 'style small-caps');
test_valid_value('font-synthesis', 'style weight small-caps', 'weight style small-caps');
test_valid_value('font-synthesis', 'style small-caps weight ', 'weight style small-caps');
test_valid_value('font-synthesis', 'small-caps style weight ', 'weight style small-caps');
</script> </script>
</body> </body>
</html> </html>