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": [
"inherit",
"initial",
"none",
"revert",
"small-caps",
"style",
"unset",
"weight"

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

@ -82,8 +82,9 @@ struct nsFont final {
// that include an 'opsz' axis
uint8_t opticalSizing = NS_FONT_OPTICAL_SIZING_AUTO;
// Synthesis setting, controls use of fake bolding/italics
uint8_t synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE;
// Synthesis setting, controls use of fake bolding/italics/small-caps
uint8_t synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE |
NS_FONT_SYNTHESIS_SMALL_CAPS;
// initialize the font with a fontlist
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,
aFont.family.is_system_font, mDeviceContext->IsPrinterContext(),
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);
style.featureValueLookup = aParams.featureValueLookup;

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

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

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

@ -103,7 +103,8 @@ struct gfxFontStyle {
gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight, FontStretch aStretch,
gfxFloat aSize, const FontSizeAdjust& aSizeAdjust,
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
// from feature settings rules and (3) family-specific features. (1) and
// (3) are guaranteed to be mutually exclusive
@ -198,6 +199,7 @@ struct gfxFontStyle {
// Whether synthetic styles are allowed
bool allowSyntheticWeight : 1;
bool allowSyntheticStyle : 1;
bool allowSyntheticSmallCaps : 1;
// some variant features require fallback which complicates the shaping
// 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_STYLE 0x2
#define NS_FONT_SYNTHESIS_SMALL_CAPS 0x4
#define NS_FONT_OPTICAL_SIZING_AUTO 0
#define NS_FONT_OPTICAL_SIZING_NONE 1

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

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

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

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

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

@ -7618,6 +7618,13 @@
mirror: always
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.
- name: layout.framevisibility.enabled
type: bool

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

@ -1950,23 +1950,24 @@ impl Parse for FontFeatureSettings {
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
/// 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 {
/// If a `font-weight` is requested that the font family does not contain,
/// the user agent may synthesize the requested weight from the weights
/// that do exist in the font family.
#[css(represents_keyword)]
pub weight: bool,
/// 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.
#[css(represents_keyword)]
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 {
@ -1976,6 +1977,7 @@ impl FontSynthesis {
FontSynthesis {
weight: true,
style: true,
small_caps: true,
}
}
#[inline]
@ -1984,8 +1986,14 @@ impl FontSynthesis {
FontSynthesis {
weight: 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 {
@ -1993,26 +2001,23 @@ impl Parse for FontSynthesis {
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<FontSynthesis, ParseError<'i>> {
let mut result = FontSynthesis {
weight: false,
style: false,
};
try_match_ident_ignore_ascii_case! { input,
"none" => Ok(result),
"weight" => {
result.weight = true;
if input.try_parse(|input| input.expect_ident_matching("style")).is_ok() {
result.style = true;
}
Ok(result)
},
"style" => {
result.style = true;
if input.try_parse(|input| input.expect_ident_matching("weight")).is_ok() {
result.weight = true;
}
Ok(result)
},
use crate::values::SelectorParseErrorKind;
let mut result = Self::none();
while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
match_ignore_ascii_case! { &ident,
"none" if result.is_none() => return Ok(result),
"weight" if !result.weight => result.weight = true,
"style" if !result.style => result.style = true,
"small-caps" if !result.small_caps &&
static_prefs::pref!("layout.css.font-synthesis-small-caps.enabled")
=> result.small_caps = true,
_ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident))),
}
}
if !result.is_none() {
Ok(result)
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
}
@ -2022,14 +2027,41 @@ impl ToCss for FontSynthesis {
where
W: Write,
{
if self.weight && self.style {
dest.write_str("weight style")
} else if self.style {
dest.write_str("style")
} else if self.weight {
dest.write_str("weight")
} else {
dest.write_str("none")
if self.is_none() {
return dest.write_str("none");
}
let mut need_space = false;
if self.weight {
dest.write_str("weight")?;
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 {
weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT 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 {
bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8;
}
if v.small_caps {
bits |= structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8;
}
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-stretch', '100%' /* normal */, '75%');
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-alternates', 'normal', 'historical-forms');
assert_inherited('font-variant-caps', 'normal', 'small-caps');

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

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

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

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

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

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