mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-08 05:27:14 +09:00
LibWeb/CSS: Parse and use tech() in @font-face { src }
This commit is contained in:
parent
5b42f8d707
commit
d611806f18
Notes:
github-actions[bot]
2025-06-05 11:39:21 +00:00
Author: https://github.com/AtkinsSJ
Commit: d611806f18
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4983
Reviewed-by: https://github.com/shannonbooth
10 changed files with 131 additions and 35 deletions
|
@ -258,6 +258,20 @@
|
|||
"right",
|
||||
"oblique"
|
||||
],
|
||||
"font-tech": [
|
||||
"avar2",
|
||||
"color-cbdt",
|
||||
"color-colrv0",
|
||||
"color-colrv1",
|
||||
"color-sbix",
|
||||
"color-svg",
|
||||
"features-aat",
|
||||
"features-graphite",
|
||||
"features-opentype",
|
||||
"incremental",
|
||||
"palettes",
|
||||
"variations"
|
||||
],
|
||||
"font-variant-alternates": [
|
||||
"normal",
|
||||
"historical-forms"
|
||||
|
|
|
@ -534,35 +534,47 @@ bool font_format_is_supported(FlyString const& name)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool font_tech_is_supported(FlyString const& name)
|
||||
bool font_tech_is_supported(FontTech font_tech)
|
||||
{
|
||||
// https://drafts.csswg.org/css-fonts-4/#font-tech-definitions
|
||||
// FIXME: Determine this automatically somehow?
|
||||
if (name.equals_ignoring_ascii_case("features-opentype"sv))
|
||||
switch (font_tech) {
|
||||
case FontTech::FeaturesOpentype:
|
||||
return true;
|
||||
if (name.equals_ignoring_ascii_case("features-aat"sv))
|
||||
case FontTech::FeaturesAat:
|
||||
return false;
|
||||
if (name.equals_ignoring_ascii_case("features-graphite"sv))
|
||||
case FontTech::FeaturesGraphite:
|
||||
return false;
|
||||
if (name.equals_ignoring_ascii_case("variations"sv))
|
||||
case FontTech::Variations:
|
||||
return true;
|
||||
if (name.equals_ignoring_ascii_case("color-colrv0"sv))
|
||||
case FontTech::ColorColrv0:
|
||||
return true;
|
||||
if (name.equals_ignoring_ascii_case("color-colrv1"sv))
|
||||
case FontTech::ColorColrv1:
|
||||
return true;
|
||||
if (name.equals_ignoring_ascii_case("color-svg"sv))
|
||||
case FontTech::ColorSvg:
|
||||
return false;
|
||||
if (name.equals_ignoring_ascii_case("color-sbix"sv))
|
||||
case FontTech::ColorSbix:
|
||||
return false;
|
||||
if (name.equals_ignoring_ascii_case("color-cbdt"sv))
|
||||
case FontTech::ColorCbdt:
|
||||
return false;
|
||||
if (name.equals_ignoring_ascii_case("palettes"sv))
|
||||
case FontTech::Palettes:
|
||||
return false;
|
||||
if (name.equals_ignoring_ascii_case("incremental"sv))
|
||||
case FontTech::Incremental:
|
||||
return false;
|
||||
// https://drafts.csswg.org/css-fonts-5/#font-tech-definitions
|
||||
if (name.equals_ignoring_ascii_case("avar2"sv))
|
||||
case FontTech::Avar2:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool font_tech_is_supported(FlyString const& name)
|
||||
{
|
||||
if (auto keyword = keyword_from_string(name); keyword.has_value()) {
|
||||
if (auto font_tech = keyword_to_font_tech(*keyword); font_tech.has_value()) {
|
||||
return font_tech_is_supported(*font_tech);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -115,6 +115,8 @@ private:
|
|||
};
|
||||
|
||||
bool font_format_is_supported(FlyString const& name);
|
||||
|
||||
bool font_tech_is_supported(FontTech);
|
||||
bool font_tech_is_supported(FlyString const& name);
|
||||
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
"appworkspace",
|
||||
"auto",
|
||||
"auto-add",
|
||||
"avar2",
|
||||
"b4",
|
||||
"b5",
|
||||
"back",
|
||||
|
@ -128,7 +129,12 @@
|
|||
"collapse",
|
||||
"color",
|
||||
"color-burn",
|
||||
"color-cbdt",
|
||||
"color-colrv0",
|
||||
"color-colrv1",
|
||||
"color-dodge",
|
||||
"color-sbix",
|
||||
"color-svg",
|
||||
"column",
|
||||
"column-reverse",
|
||||
"common-ligatures",
|
||||
|
@ -191,6 +197,9 @@
|
|||
"false",
|
||||
"fantasy",
|
||||
"fast",
|
||||
"features-aat",
|
||||
"features-graphite",
|
||||
"features-opentype",
|
||||
"field",
|
||||
"fieldtext",
|
||||
"fill",
|
||||
|
@ -231,6 +240,7 @@
|
|||
"inactivecaption",
|
||||
"inactivecaptiontext",
|
||||
"increasing",
|
||||
"incremental",
|
||||
"infinite",
|
||||
"infinity",
|
||||
"infobackground",
|
||||
|
@ -363,6 +373,7 @@
|
|||
"padding-box",
|
||||
"paged",
|
||||
"paint",
|
||||
"palettes",
|
||||
"pan-down",
|
||||
"pan-left",
|
||||
"pan-right",
|
||||
|
@ -523,6 +534,7 @@
|
|||
"uppercase",
|
||||
"upright",
|
||||
"use-credentials",
|
||||
"variations",
|
||||
"vertical-lr",
|
||||
"vertical-rl",
|
||||
"vertical-text",
|
||||
|
|
|
@ -37,11 +37,10 @@ Vector<ParsedFontFace::Source> ParsedFontFace::sources_from_style_value(CSSStyle
|
|||
auto add_source = [&sources](FontSourceStyleValue const& font_source) {
|
||||
font_source.source().visit(
|
||||
[&](FontSourceStyleValue::Local const& local) {
|
||||
sources.empend(extract_font_name(local.name), OptionalNone {});
|
||||
sources.empend(extract_font_name(local.name), OptionalNone {}, Vector<FontTech> {});
|
||||
},
|
||||
[&](URL const& url) {
|
||||
// FIXME: tech()
|
||||
sources.empend(url, font_source.format());
|
||||
sources.empend(url, font_source.format(), font_source.tech());
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ class ParsedFontFace {
|
|||
public:
|
||||
struct Source {
|
||||
Variant<FlyString, URL> local_or_url;
|
||||
// FIXME: Do we need to keep this around, or is it only needed to discard unwanted formats during parsing?
|
||||
Optional<FlyString> format;
|
||||
Vector<FontTech> tech;
|
||||
};
|
||||
|
||||
static Vector<Source> sources_from_style_value(CSSStyleValue const&);
|
||||
|
|
|
@ -3888,17 +3888,20 @@ RefPtr<FontSourceStyleValue const> Parser::parse_font_source_value(TokenStream<C
|
|||
TokenStream function_tokens { function.value };
|
||||
if (auto family_name = parse_family_name_value(function_tokens)) {
|
||||
transaction.commit();
|
||||
return FontSourceStyleValue::create(FontSourceStyleValue::Local { family_name.release_nonnull() }, {});
|
||||
return FontSourceStyleValue::create(FontSourceStyleValue::Local { family_name.release_nonnull() }, {}, {});
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// <url> [ format(<font-format>)]? [ tech( <font-tech>#)]?
|
||||
|
||||
// <url>
|
||||
auto url = parse_url_function(tokens);
|
||||
if (!url.has_value())
|
||||
return nullptr;
|
||||
|
||||
Optional<FlyString> format;
|
||||
Vector<FontTech> tech;
|
||||
|
||||
tokens.discard_whitespace();
|
||||
|
||||
|
@ -3920,10 +3923,10 @@ RefPtr<FontSourceStyleValue const> Parser::parse_font_source_value(TokenStream<C
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// FIXME: Some of the formats support an optional "-variations" suffix that's really supposed to map to tech(variations).
|
||||
// Once we support tech(*), we should ensure this propagates correctly.
|
||||
// NOTE: Some of the formats support an optional "-variations" suffix that's really supposed to map to tech(variations).
|
||||
if (format_name.is_one_of("woff2-variations"sv, "woff-variations"sv, "truetype-variations"sv, "opentype-variations"sv)) {
|
||||
format_name = MUST(format_name.to_string().substring_from_byte_offset(0, format_name.bytes().size() - strlen("-variations")));
|
||||
tech.append(FontTech::Variations);
|
||||
}
|
||||
|
||||
if (!font_format_is_supported(format_name)) {
|
||||
|
@ -3942,10 +3945,53 @@ RefPtr<FontSourceStyleValue const> Parser::parse_font_source_value(TokenStream<C
|
|||
|
||||
tokens.discard_whitespace();
|
||||
|
||||
// FIXME: [ tech( <font-tech>#)]?
|
||||
// [ tech( <font-tech>#)]?
|
||||
if (tokens.next_token().is_function("tech"sv)) {
|
||||
auto const& function = tokens.consume_a_token().function();
|
||||
auto context_guard = push_temporary_value_parsing_context(FunctionContext { function.name });
|
||||
|
||||
TokenStream function_tokens { function.value };
|
||||
auto tech_items = parse_a_comma_separated_list_of_component_values(function_tokens);
|
||||
if (tech_items.is_empty()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source invalid (`tech()` has no arguments); discarding.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto const& tech_item : tech_items) {
|
||||
TokenStream tech_tokens { tech_item };
|
||||
tech_tokens.discard_whitespace();
|
||||
auto& ident_token = tech_tokens.consume_a_token();
|
||||
if (!ident_token.is(Token::Type::Ident)) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source invalid (`tech()` parameters must be idents, got: {}); discarding.", ident_token.to_debug_string());
|
||||
return nullptr;
|
||||
}
|
||||
tech_tokens.discard_whitespace();
|
||||
if (tech_tokens.has_next_token()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source invalid (`tech()` has trailing tokens); discarding.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& font_tech_name = ident_token.token().ident();
|
||||
if (auto keyword = keyword_from_string(font_tech_name); keyword.has_value()) {
|
||||
if (auto font_tech = keyword_to_font_tech(*keyword); font_tech.has_value()) {
|
||||
|
||||
if (!font_tech_is_supported(*font_tech)) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source tech({}) not supported; skipping.", font_tech_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
tech.append(font_tech.release_value());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source invalid (`{}` is not a supported value in `tech()`); discarding.", font_tech_name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
return FontSourceStyleValue::create(url.release_value(), move(format));
|
||||
return FontSourceStyleValue::create(url.release_value(), move(format), move(tech));
|
||||
}
|
||||
|
||||
NonnullRefPtr<CSSStyleValue const> Parser::resolve_unresolved_style_value(ParsingParams const& context, DOM::Element& element, Optional<PseudoElement> pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved)
|
||||
|
|
|
@ -9,10 +9,11 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
FontSourceStyleValue::FontSourceStyleValue(Source source, Optional<FlyString> format)
|
||||
FontSourceStyleValue::FontSourceStyleValue(Source source, Optional<FlyString> format, Vector<FontTech> tech)
|
||||
: StyleValueWithDefaultOperators(Type::FontSource)
|
||||
, m_source(move(source))
|
||||
, m_format(move(format))
|
||||
, m_tech(move(tech))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -36,7 +37,6 @@ String FontSourceStyleValue::to_string(SerializationMode) const
|
|||
},
|
||||
[this](URL const& url) {
|
||||
// <url> [ format(<font-format>)]? [ tech( <font-tech>#)]?
|
||||
// FIXME: tech()
|
||||
StringBuilder builder;
|
||||
builder.append(url.to_string());
|
||||
|
||||
|
@ -46,6 +46,14 @@ String FontSourceStyleValue::to_string(SerializationMode) const
|
|||
builder.append(")"sv);
|
||||
}
|
||||
|
||||
if (!m_tech.is_empty()) {
|
||||
builder.append(" tech("sv);
|
||||
serialize_a_comma_separated_list(builder, m_tech, [](auto& builder, FontTech const tech) {
|
||||
return builder.append(CSS::to_string(tech));
|
||||
});
|
||||
builder.append(")"sv);
|
||||
}
|
||||
|
||||
return builder.to_string_without_validation();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <AK/FlyString.h>
|
||||
#include <LibWeb/CSS/CSSStyleValue.h>
|
||||
#include <LibWeb/CSS/Enums.h>
|
||||
#include <LibWeb/CSS/URL.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
@ -19,24 +20,26 @@ public:
|
|||
};
|
||||
using Source = Variant<Local, URL>;
|
||||
|
||||
static ValueComparingNonnullRefPtr<FontSourceStyleValue const> create(Source source, Optional<FlyString> format)
|
||||
static ValueComparingNonnullRefPtr<FontSourceStyleValue const> create(Source source, Optional<FlyString> format, Vector<FontTech> tech)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) FontSourceStyleValue(move(source), move(format)));
|
||||
return adopt_ref(*new (nothrow) FontSourceStyleValue(move(source), move(format), move(tech)));
|
||||
}
|
||||
virtual ~FontSourceStyleValue() override;
|
||||
|
||||
Source const& source() const { return m_source; }
|
||||
Optional<FlyString> const& format() const { return m_format; }
|
||||
Vector<FontTech> const& tech() const { return m_tech; }
|
||||
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
|
||||
bool properties_equal(FontSourceStyleValue const&) const;
|
||||
|
||||
private:
|
||||
FontSourceStyleValue(Source source, Optional<FlyString> format);
|
||||
FontSourceStyleValue(Source source, Optional<FlyString> format, Vector<FontTech> tech);
|
||||
|
||||
Source m_source;
|
||||
Optional<FlyString> m_format;
|
||||
Vector<FontTech> m_tech;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -2,17 +2,17 @@ Harness status: OK
|
|||
|
||||
Found 39 tests
|
||||
|
||||
28 Pass
|
||||
11 Fail
|
||||
34 Pass
|
||||
5 Fail
|
||||
Pass Check that src: url("foo.ttf") is valid
|
||||
Pass Check that src: url("foo.ttf") tech() is invalid
|
||||
Fail Check that src: url("foo.ttf") tech(features-opentype) is valid
|
||||
Pass Check that src: url("foo.ttf") tech(features-opentype) is valid
|
||||
Fail Check that src: url("foo.ttf") tech(features-aat) is valid
|
||||
Fail Check that src: url("foo.ttf") tech(color-COLRv0) is valid
|
||||
Fail Check that src: url("foo.ttf") tech(color-COLRv1) is valid
|
||||
Pass Check that src: url("foo.ttf") tech(color-COLRv0) is valid
|
||||
Pass Check that src: url("foo.ttf") tech(color-COLRv1) is valid
|
||||
Fail Check that src: url("foo.ttf") tech(color-sbix) is valid
|
||||
Fail Check that src: url("foo.ttf") tech(color-CBDT) is valid
|
||||
Fail Check that src: url("foo.ttf") tech(variations) is valid
|
||||
Pass Check that src: url("foo.ttf") tech(variations) is valid
|
||||
Fail Check that src: url("foo.ttf") tech(palettes) is valid
|
||||
Pass Check that src: url("foo.ttf") tech("features-opentype") is invalid
|
||||
Pass Check that src: url("foo.ttf") tech("color-COLRv0") is invalid
|
||||
|
@ -31,7 +31,7 @@ Pass Check that src: url("foo.ttf") tech(normal) is invalid
|
|||
Pass Check that src: url("foo.ttf") tech(xyzzy) is invalid
|
||||
Pass Check that src: url("foo.ttf") tech(xyzzy, features-opentype) is invalid
|
||||
Pass Check that src: url("foo.ttf") tech(features-opentype, xyzzy) is invalid
|
||||
Fail Check that src: url("foo.ttf") format(opentype) tech(features-opentype) is valid
|
||||
Pass Check that src: url("foo.ttf") format(opentype) tech(features-opentype) is valid
|
||||
Pass Check that src: url("foo.ttf") tech(features-opentype) format(opentype) is invalid
|
||||
Pass Check that src: url("foo.ttf") tech(incremental), url("bar.html") is valid
|
||||
Pass Check that src: url("foo.ttf") tech(incremental, color-SVG, features-graphite, features-aat), url("bar.html") is valid
|
||||
|
@ -41,5 +41,5 @@ Pass Check that src: url("foo.ttf") tech(features-graphite), url("bar.html") is
|
|||
Pass Check that src: url("foo.ttf") dummy("opentype") tech(variations) is invalid
|
||||
Pass Check that src: url("foo.ttf") dummy("opentype") dummy(variations) is invalid
|
||||
Pass Check that src: url("foo.ttf") format(opentype) tech(features-opentype) dummy(something) is invalid
|
||||
Fail Check that src: url("foo.ttf") format(dummy), url("foo.ttf") tech(variations) is valid
|
||||
Pass Check that src: url("foo.ttf") format(dummy), url("foo.ttf") tech(variations) is valid
|
||||
Pass Check that src: url("foo.ttf") tech(color), url("bar.html") is valid
|
Loading…
Add table
Add a link
Reference in a new issue