Commit e826d831 authored by Dominik Röttsches's avatar Dominik Röttsches Committed by Commit Bot

Allow specifying variable font range descriptors in either direction

Style, stretch and weight descriptor allow specifying ranges for the
variable font that the src: descriptor is referencing. The range
definitions allow both the smaller or larger value to go first and need
to be swapped by the UA.

Keep range value lists in reversed order in the computed style, but swap
them in the FontFace implementation to become meaningful values.This
keeps serialisation intact as specified in the @font - face declaration(
as opposed to reversing the list at the CSS parsing level) and matches
Firefox' behaviour.

Adjust test expectations in at-font-face-descriptors.html for bounds out
of order test cases: expect out of order bounds to be returned in order
as specified. Test case was previously failing in all browsers [1] -
with this change it will pass in Chrome and Firefox. Mark
font-parse-numeric-stretch-style-weight.html test case "500 400 matches
500 400 for weight in @font-face" as passing in Chrome.

[1] https://wpt.fyi/results/css/css-fonts/variations/at-font-face-descriptors.html?label=master&label=experimental&aligned&q=css%2Fcss-fonts%2Fvariations%2F

Fixed: 1063867
Change-Id: I5ae7bc5f3e3a75fa9dce09ca0a66dd083ef29b0b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2116447
Commit-Queue: Dominik Röttsches <drott@chromium.org>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#753192}
parent 09339b96
......@@ -559,8 +559,17 @@ FontSelectionCapabilities FontFace::GetFontSelectionCapabilities() const {
return normal_capabilities;
if (!stretch_from->IsPercentage() || !stretch_to->IsPercentage())
return normal_capabilities;
capabilities.width = {FontSelectionValue(stretch_from->GetFloatValue()),
FontSelectionValue(stretch_to->GetFloatValue())};
// https://drafts.csswg.org/css-fonts/#font-prop-desc
// "User agents must swap the computed value of the startpoint and
// endpoint of the range in order to forbid decreasing ranges."
if (stretch_from->GetFloatValue() < stretch_to->GetFloatValue()) {
capabilities.width = {FontSelectionValue(stretch_from->GetFloatValue()),
FontSelectionValue(stretch_to->GetFloatValue())};
} else {
capabilities.width = {
FontSelectionValue(stretch_to->GetFloatValue()),
FontSelectionValue(stretch_from->GetFloatValue())};
}
} else if (auto* stretch_primitive_value =
DynamicTo<CSSPrimitiveValue>(stretch_.Get())) {
float stretch_value = stretch_primitive_value->GetFloatValue();
......@@ -613,9 +622,18 @@ FontSelectionCapabilities FontFace::GetFontSelectionCapabilities() const {
To<CSSPrimitiveValue>(range_value->GetObliqueValues()->Item(0));
const auto& range_end =
To<CSSPrimitiveValue>(range_value->GetObliqueValues()->Item(1));
capabilities.slope = {
FontSelectionValue(range_start.GetFloatValue()),
FontSelectionValue(range_end.GetFloatValue())};
// https://drafts.csswg.org/css-fonts/#font-prop-desc
// "User agents must swap the computed value of the startpoint and
// endpoint of the range in order to forbid decreasing ranges."
if (range_start.GetFloatValue() < range_end.GetFloatValue()) {
capabilities.slope = {
FontSelectionValue(range_start.GetFloatValue()),
FontSelectionValue(range_end.GetFloatValue())};
} else {
capabilities.slope = {
FontSelectionValue(range_end.GetFloatValue()),
FontSelectionValue(range_start.GetFloatValue())};
}
}
}
}
......@@ -658,8 +676,17 @@ FontSelectionCapabilities FontFace::GetFontSelectionCapabilities() const {
if (!weight_from->IsNumber() || !weight_to->IsNumber() ||
weight_from->GetFloatValue() < 1 || weight_to->GetFloatValue() > 1000)
return normal_capabilities;
capabilities.weight = {FontSelectionValue(weight_from->GetFloatValue()),
FontSelectionValue(weight_to->GetFloatValue())};
// https://drafts.csswg.org/css-fonts/#font-prop-desc
// "User agents must swap the computed value of the startpoint and
// endpoint of the range in order to forbid decreasing ranges."
if (weight_from->GetFloatValue() < weight_to->GetFloatValue()) {
capabilities.weight = {FontSelectionValue(weight_from->GetFloatValue()),
FontSelectionValue(weight_to->GetFloatValue())};
} else {
capabilities.weight = {
FontSelectionValue(weight_to->GetFloatValue()),
FontSelectionValue(weight_from->GetFloatValue())};
}
} else if (auto* weight_primitive_value =
DynamicTo<CSSPrimitiveValue>(weight_.Get())) {
float weight_value = weight_primitive_value->GetFloatValue();
......
......@@ -1413,12 +1413,13 @@ String ConcatenateFamilyName(CSSParserTokenRange& range) {
return builder.ToString();
}
CSSValueList* CombineToRangeListOrNull(const CSSPrimitiveValue* range_start,
const CSSPrimitiveValue* range_end) {
CSSValueList* CombineToRangeList(const CSSPrimitiveValue* range_start,
const CSSPrimitiveValue* range_end) {
DCHECK(range_start);
DCHECK(range_end);
if (range_end->GetFloatValue() < range_start->GetFloatValue())
return nullptr;
// Reversed ranges are valid, let them pass through here and swap them in
// FontFace to keep serialisation of the value as specified.
// https://drafts.csswg.org/css-fonts/#font-prop-desc
CSSValueList* value_list = CSSValueList::CreateSpaceSeparated();
value_list->Append(*range_start);
value_list->Append(*range_end);
......@@ -1462,7 +1463,7 @@ CSSValue* ConsumeFontStyle(CSSParserTokenRange& range,
if (!end_angle || !IsAngleWithinLimits(end_angle))
return nullptr;
CSSValueList* range_list = CombineToRangeListOrNull(start_angle, end_angle);
CSSValueList* range_list = CombineToRangeList(start_angle, end_angle);
if (!range_list)
return nullptr;
return MakeGarbageCollected<cssvalue::CSSFontStyleRangeValue>(
......@@ -1499,7 +1500,7 @@ CSSValue* ConsumeFontStretch(CSSParserTokenRange& range,
if (!end_percent)
return nullptr;
return CombineToRangeListOrNull(start_percent, end_percent);
return CombineToRangeList(start_percent, end_percent);
}
CSSValue* ConsumeFontWeight(CSSParserTokenRange& range,
......@@ -1537,7 +1538,7 @@ CSSValue* ConsumeFontWeight(CSSParserTokenRange& range,
end_weight->GetFloatValue() > 1000)
return nullptr;
return CombineToRangeListOrNull(start_weight, end_weight);
return CombineToRangeList(start_weight, end_weight);
}
CSSValue* ConsumeFontFeatureSettings(CSSParserTokenRange& range,
......
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Fonts Module Level 3: Order of values in @font-face range descriptors</title>
<link rel="author" title="Dominik Röttsches" href="mailto:drott@chromium.org"/>
<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-prop-desc">
<meta name="assert" content="Allow ranges to be specified in reverse order.">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<style>
@font-face {
font-family: reversed-range-test;
font-stretch: 200% 50%;
font-style: oblique 90deg -90deg;
font-weight: 900 100;
src: local(Ahem);
}
</style>
<script>
async_test(function(t) {
document.fonts.load("12px reversed-range-test").then((fonts) => {
t.step( () => {
assert_equals(fonts[0].stretch, "200% 50%", "Stretch value must be returned as specified.");
assert_equals(fonts[0].style, "oblique 90deg -90deg", "Style value must be returned as specified.");
assert_equals(fonts[0].weight, "900 100", "Weight value must be returned as specified.");
});
t.done()
})});
</script>
</body>
</html>
<!DOCTYPE html>
<html class="reftest-wait">
<style>
@font-face {
font-family: variable_axes;
src: url("resources/variabletest_matching.ttf");
}
body {
font-family: variable_axes, sans-serif;
font-size: 80px;
}
</style>
<span>0p;/</span>
<span>8i;,</span>
<script>
document.fonts.ready.then(
() => { document.documentElement.classList.remove("reftest-wait"); });
</script>
</html>
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Fonts Module Level 3: Property descriptor ranges</title>
<link rel="author" title="Dominik Röttsches" href="mailto:drott@chromium.org"/>
<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-prop-desc">
<link rel="match" href="range-descriptor-reversed-ref.html">
<meta name="assert" content="User agents must swap the computed value of the startpoint and endpoint of the range in order to forbid decreasing ranges.">
</head>
<link rel="stylesheet" href="font-matching.css">
<style>
@font-face {
font-family: variable_axes;
src: url("resources/variabletest_matching.ttf");
font-stretch: 200% 50%;
font-style: oblique 90deg -90deg;
font-weight: 900 100;
}
</style>
<link rel="match" href="stretch-distance-over-weight-distance-ref.html">
<span id="stretch_style_weight_1">MNOP</span>
<span id="stretch_style_weight_9">MNOP</span>
<script>
document.fonts.ready.then(
() => { document.documentElement.classList.remove("reftest-wait"); });
</script>
</html>
This is a testharness.js-based test.
Found 84 tests; 66 PASS, 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 84 tests; 69 PASS, 15 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS font-weight(valid): 'normal' keyword: normal
PASS font-weight(valid): 'bold' keyword: bold
FAIL font-weight(invalid): 'lighter' keyword iside @font-face: lighter assert_equals: No properties should be set. expected "" but got "lighter"
......@@ -25,7 +25,7 @@ PASS font-weight(invalid): Upper bound out of range: 100 1001
PASS font-weight(valid): Lower bound calc(): calc(100 + 100) 400
PASS font-weight(valid): Upper bound calc(): 200 calc(200 + 200)
PASS font-weight(valid): Both bounds are calc(): calc(100 + 100) calc(200 + 200)
FAIL font-weight(valid): Bounds out of order are valid: 400 200 assert_not_equals: Valid value should be accepted. got disallowed value ""
PASS font-weight(valid): Bounds out of order are valid: 400 200
PASS font-weight(invalid): Extra content after upper bound: 100 200 300
PASS font-stretch(valid): 'ultra-condensed' keyword: ultra-condensed
PASS font-stretch(valid): 'extra-condensed' keyword: extra-condensed
......@@ -57,7 +57,7 @@ PASS font-stretch(invalid): Lower bound out of range: -100% 100%
FAIL font-stretch(valid): Lower bound calc(): calc(10% + 10%) 30% assert_equals: Unexpected resulting value. expected "20% 30%" but got "calc(20%) 30%"
FAIL font-stretch(valid): Upper bound calc(): 10% calc(10% + 10%) assert_equals: Unexpected resulting value. expected "10% 20%" but got "10% calc(20%)"
FAIL font-stretch(valid): Both bounds are calc(): calc(10% + 10%) calc(20% + 20%) assert_equals: Unexpected resulting value. expected "20% 40%" but got "calc(20%) calc(40%)"
FAIL font-stretch(valid): Bounds out of order: 200% 100% assert_not_equals: Valid value should be accepted. got disallowed value ""
PASS font-stretch(valid): Bounds out of order: 200% 100%
PASS font-stretch(invalid): Extra content after upper bound: 100% 200% 300%
PASS font-style(valid): 'normal' keyword: normal
PASS font-style(valid): 'italic' keyword: italic
......@@ -80,7 +80,7 @@ PASS font-style(invalid): 'oblique' followed by minus and non-number: oblique -a
PASS font-style(valid): Simple range: oblique 10deg 20deg
FAIL font-style(valid): Simple range with equal upper and lower bounds: oblique 10deg 10deg assert_equals: Unexpected resulting value. expected "oblique 10deg" but got "oblique 10deg 10deg"
FAIL font-style(valid): Simple range with default angle for both bounds: oblique 20deg 20deg assert_equals: Unexpected resulting value. expected "oblique" but got "oblique 20deg 20deg"
FAIL font-style(valid): Bounds out of order: oblique 20deg 10deg assert_not_equals: Valid value should be accepted. got disallowed value ""
PASS font-style(valid): Bounds out of order: oblique 20deg 10deg
PASS font-style(invalid): Lower bound out of range: oblique -100deg 20deg
PASS font-style(invalid): Upper bound out of range: oblique 20deg 100deg
PASS font-style(invalid): Extra content after upper bound: oblique 10deg 20deg 30deg
......
......@@ -88,7 +88,7 @@
{ value: "calc(100 + 100) 400", isValid: true, expectedValue: "200 400", description: "Lower bound calc()" },
{ value: "200 calc(200 + 200)", isValid: true, expectedValue: "200 400", description: "Upper bound calc()" },
{ value: "calc(100 + 100) calc(200 + 200)", isValid: true, expectedValue: "200 400", description: "Both bounds are calc()" },
{ value: "400 200", isValid: true, expectedValue: "200 400", description: "Bounds out of order are valid" },
{ value: "400 200", isValid: true, expectedValue: "400 200", description: "Bounds out of order are valid" },
{ value: "100 200 300", isValid: false, description: "Extra content after upper bound" },
]);
......@@ -130,7 +130,7 @@
{ value: "calc(10% + 10%) 30%", isValid: true, expectedValue: "20% 30%", description: "Lower bound calc()" },
{ value: "10% calc(10% + 10%)", isValid: true, expectedValue: "10% 20%", description: "Upper bound calc()" },
{ value: "calc(10% + 10%) calc(20% + 20%)", isValid: true, expectedValue: "20% 40%", description: "Both bounds are calc()" },
{ value: "200% 100%", isValid: true, expectedValue: "100% 200%", description: "Bounds out of order" },
{ value: "200% 100%", isValid: true, expectedValue: "200% 100%", description: "Bounds out of order" },
{ value: "100% 200% 300%", isValid: false, description: "Extra content after upper bound" },
]);
......@@ -161,7 +161,7 @@
{ value: "oblique 10deg 20deg", isValid: true, description: "Simple range" },
{ value: "oblique 10deg 10deg", isValid: true, expectedValue: "oblique 10deg", description: "Simple range with equal upper and lower bounds" },
{ value: "oblique 20deg 20deg", isValid: true, expectedValue: "oblique", description: "Simple range with default angle for both bounds" },
{ value: "oblique 20deg 10deg", isValid: true, expectedValue: "oblique 10deg 20deg", description: "Bounds out of order" },
{ value: "oblique 20deg 10deg", isValid: true, expectedValue: "oblique 20deg 10deg", description: "Bounds out of order" },
{ value: "oblique -100deg 20deg", isValid: false, description: "Lower bound out of range" },
{ value: "oblique 20deg 100deg", isValid: false, description: "Upper bound out of range" },
{ value: "oblique 10deg 20deg 30deg", isValid: false, description: "Extra content after upper bound" },
......
This is a testharness.js-based test.
Found 81 tests; 78 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 81 tests; 79 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS Valid value bold for font property weight used for styling.
PASS Valid value 700 for font property weight used for styling.
PASS Valid value 900 for font property weight used for styling.
......@@ -37,7 +37,7 @@ PASS Valid value normal matches normal for weight in @font-face.
PASS Valid value 100 400 matches 100 400 for weight in @font-face.
PASS Valid value 100 101.5 matches 100 101.5 for weight in @font-face.
PASS Valid value 999.8 999.9 matches 999.8 999.9 for weight in @font-face.
FAIL Valid value 500 400 matches 500 400 for weight in @font-face. Failed to set the 'weight' property on 'FontFace': Failed to set '500 400' as a property value.
PASS Valid value 500 400 matches 500 400 for weight in @font-face.
PASS Valid value 0% matches 0% for stretch in @font-face.
PASS Valid value calc(0% - 10%) matches calc(-10%) for stretch in @font-face.
PASS Valid value 100% matches 100% for stretch in @font-face.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment