diff options
author | Andreas Kling <kling@serenityos.org> | 2020-12-14 15:56:01 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-12-14 20:43:25 +0100 |
commit | 08517daa5ab0093538e913205328786966e440bd (patch) | |
tree | e529b8a405509eb5c3331100a57ed3ac5cf134ea /Libraries/LibWeb/CSS | |
parent | 861d22838d21b56478de616dabb56a5c7bbc8b29 (diff) | |
download | serenity-08517daa5ab0093538e913205328786966e440bd.zip |
LibWeb: Improvements to font lookup
Parse out the font-family, font-size and font-weight values from CSS
and use them to perform a kinda-best-effort lookup against the system
font library.
We also now handle standard font names like "sans-serif", "monospace"
and others.
Diffstat (limited to 'Libraries/LibWeb/CSS')
-rw-r--r-- | Libraries/LibWeb/CSS/Default.css | 7 | ||||
-rw-r--r-- | Libraries/LibWeb/CSS/Parser/CSSParser.cpp | 165 | ||||
-rw-r--r-- | Libraries/LibWeb/CSS/StyleProperties.cpp | 158 | ||||
-rw-r--r-- | Libraries/LibWeb/CSS/StyleValue.h | 14 |
4 files changed, 215 insertions, 129 deletions
diff --git a/Libraries/LibWeb/CSS/Default.css b/Libraries/LibWeb/CSS/Default.css index bb0bdea72f..a63c7873ef 100644 --- a/Libraries/LibWeb/CSS/Default.css +++ b/Libraries/LibWeb/CSS/Default.css @@ -1,5 +1,5 @@ html { - font-family: Katica; + font-family: sans-serif; } head, @@ -18,6 +18,7 @@ body { h1, h2 { font-family: Pebbleton; + font-size: 14px; font-weight: bold; } @@ -29,14 +30,14 @@ h6 { } pre { - font-family: Csilla; + font-family: monospace; margin-bottom: 8px; margin-top: 8px; white-space: pre; } code { - font-family: Csilla; + font-family: monospace; } u, diff --git a/Libraries/LibWeb/CSS/Parser/CSSParser.cpp b/Libraries/LibWeb/CSS/Parser/CSSParser.cpp index 9c297e7f87..b633974ecd 100644 --- a/Libraries/LibWeb/CSS/Parser/CSSParser.cpp +++ b/Libraries/LibWeb/CSS/Parser/CSSParser.cpp @@ -71,118 +71,117 @@ bool ParsingContext::in_quirks_mode() const } -static CSS::ValueID value_id_for_palette_string(const StringView& string) +static Optional<CSS::ValueID> value_id_for_palette_string(const StringView& string) { - if (string == "desktop-background") + if (string.equals_ignoring_case("desktop-background")) return CSS::ValueID::VendorSpecificPaletteDesktopBackground; - else if (string == "active-window-border1") + if (string.equals_ignoring_case("active-window-border1")) return CSS::ValueID::VendorSpecificPaletteActiveWindowBorder1; - else if (string == "active-window-border2") + if (string.equals_ignoring_case("active-window-border2")) return CSS::ValueID::VendorSpecificPaletteActiveWindowBorder2; - else if (string == "active-window-title") + if (string.equals_ignoring_case("active-window-title")) return CSS::ValueID::VendorSpecificPaletteActiveWindowTitle; - else if (string == "inactive-window-border1") + if (string.equals_ignoring_case("inactive-window-border1")) return CSS::ValueID::VendorSpecificPaletteInactiveWindowBorder1; - else if (string == "inactive-window-border2") + if (string.equals_ignoring_case("inactive-window-border2")) return CSS::ValueID::VendorSpecificPaletteInactiveWindowBorder2; - else if (string == "inactive-window-title") + if (string.equals_ignoring_case("inactive-window-title")) return CSS::ValueID::VendorSpecificPaletteInactiveWindowTitle; - else if (string == "moving-window-border1") + if (string.equals_ignoring_case("moving-window-border1")) return CSS::ValueID::VendorSpecificPaletteMovingWindowBorder1; - else if (string == "moving-window-border2") + if (string.equals_ignoring_case("moving-window-border2")) return CSS::ValueID::VendorSpecificPaletteMovingWindowBorder2; - else if (string == "moving-window-title") + if (string.equals_ignoring_case("moving-window-title")) return CSS::ValueID::VendorSpecificPaletteMovingWindowTitle; - else if (string == "highlight-window-border1") + if (string.equals_ignoring_case("highlight-window-border1")) return CSS::ValueID::VendorSpecificPaletteHighlightWindowBorder1; - else if (string == "highlight-window-border2") + if (string.equals_ignoring_case("highlight-window-border2")) return CSS::ValueID::VendorSpecificPaletteHighlightWindowBorder2; - else if (string == "highlight-window-title") + if (string.equals_ignoring_case("highlight-window-title")) return CSS::ValueID::VendorSpecificPaletteHighlightWindowTitle; - else if (string == "menu-stripe") + if (string.equals_ignoring_case("menu-stripe")) return CSS::ValueID::VendorSpecificPaletteMenuStripe; - else if (string == "menu-base") + if (string.equals_ignoring_case("menu-base")) return CSS::ValueID::VendorSpecificPaletteMenuBase; - else if (string == "menu-base-text") + if (string.equals_ignoring_case("menu-base-text")) return CSS::ValueID::VendorSpecificPaletteMenuBaseText; - else if (string == "menu-selection") + if (string.equals_ignoring_case("menu-selection")) return CSS::ValueID::VendorSpecificPaletteMenuSelection; - else if (string == "menu-selection-text") + if (string.equals_ignoring_case("menu-selection-text")) return CSS::ValueID::VendorSpecificPaletteMenuSelectionText; - else if (string == "window") + if (string.equals_ignoring_case("window")) return CSS::ValueID::VendorSpecificPaletteWindow; - else if (string == "window-text") + if (string.equals_ignoring_case("window-text")) return CSS::ValueID::VendorSpecificPaletteWindowText; - else if (string == "button") + if (string.equals_ignoring_case("button")) return CSS::ValueID::VendorSpecificPaletteButton; - else if (string == "button-text") + if (string.equals_ignoring_case("button-text")) return CSS::ValueID::VendorSpecificPaletteButtonText; - else if (string == "base") + if (string.equals_ignoring_case("base")) return CSS::ValueID::VendorSpecificPaletteBase; - else if (string == "base-text") + if (string.equals_ignoring_case("base-text")) return CSS::ValueID::VendorSpecificPaletteBaseText; - else if (string == "threed-highlight") + if (string.equals_ignoring_case("threed-highlight")) return CSS::ValueID::VendorSpecificPaletteThreedHighlight; - else if (string == "threed-shadow1") + if (string.equals_ignoring_case("threed-shadow1")) return CSS::ValueID::VendorSpecificPaletteThreedShadow1; - else if (string == "threed-shadow2") + if (string.equals_ignoring_case("threed-shadow2")) return CSS::ValueID::VendorSpecificPaletteThreedShadow2; - else if (string == "hover-highlight") + if (string.equals_ignoring_case("hover-highlight")) return CSS::ValueID::VendorSpecificPaletteHoverHighlight; - else if (string == "selection") + if (string.equals_ignoring_case("selection")) return CSS::ValueID::VendorSpecificPaletteSelection; - else if (string == "selection-text") + if (string.equals_ignoring_case("selection-text")) return CSS::ValueID::VendorSpecificPaletteSelectionText; - else if (string == "inactive-selection") + if (string.equals_ignoring_case("inactive-selection")) return CSS::ValueID::VendorSpecificPaletteInactiveSelection; - else if (string == "inactive-selection-text") + if (string.equals_ignoring_case("inactive-selection-text")) return CSS::ValueID::VendorSpecificPaletteInactiveSelectionText; - else if (string == "rubber-band-fill") + if (string.equals_ignoring_case("rubber-band-fill")) return CSS::ValueID::VendorSpecificPaletteRubberBandFill; - else if (string == "rubber-band-border") + if (string.equals_ignoring_case("rubber-band-border")) return CSS::ValueID::VendorSpecificPaletteRubberBandBorder; - else if (string == "link") + if (string.equals_ignoring_case("link")) return CSS::ValueID::VendorSpecificPaletteLink; - else if (string == "active-link") + if (string.equals_ignoring_case("active-link")) return CSS::ValueID::VendorSpecificPaletteActiveLink; - else if (string == "visited-link") + if (string.equals_ignoring_case("visited-link")) return CSS::ValueID::VendorSpecificPaletteVisitedLink; - else if (string == "ruler") + if (string.equals_ignoring_case("ruler")) return CSS::ValueID::VendorSpecificPaletteRuler; - else if (string == "ruler-border") + if (string.equals_ignoring_case("ruler-border")) return CSS::ValueID::VendorSpecificPaletteRulerBorder; - else if (string == "ruler-active-text") + if (string.equals_ignoring_case("ruler-active-text")) return CSS::ValueID::VendorSpecificPaletteRulerActiveText; - else if (string == "ruler-inactive-text") + if (string.equals_ignoring_case("ruler-inactive-text")) return CSS::ValueID::VendorSpecificPaletteRulerInactiveText; - else if (string == "text-cursor") + if (string.equals_ignoring_case("text-cursor")) return CSS::ValueID::VendorSpecificPaletteTextCursor; - else if (string == "focus-outline") + if (string.equals_ignoring_case("focus-outline")) return CSS::ValueID::VendorSpecificPaletteFocusOutline; - else if (string == "syntax-comment") + if (string.equals_ignoring_case("syntax-comment")) return CSS::ValueID::VendorSpecificPaletteSyntaxComment; - else if (string == "syntax-number") + if (string.equals_ignoring_case("syntax-number")) return CSS::ValueID::VendorSpecificPaletteSyntaxNumber; - else if (string == "syntax-string") + if (string.equals_ignoring_case("syntax-string")) return CSS::ValueID::VendorSpecificPaletteSyntaxString; - else if (string == "syntax-type") + if (string.equals_ignoring_case("syntax-type")) return CSS::ValueID::VendorSpecificPaletteSyntaxType; - else if (string == "syntax-punctuation") + if (string.equals_ignoring_case("syntax-punctuation")) return CSS::ValueID::VendorSpecificPaletteSyntaxPunctuation; - else if (string == "syntax-operator") + if (string.equals_ignoring_case("syntax-operator")) return CSS::ValueID::VendorSpecificPaletteSyntaxOperator; - else if (string == "syntax-keyword") + if (string.equals_ignoring_case("syntax-keyword")) return CSS::ValueID::VendorSpecificPaletteSyntaxKeyword; - else if (string == "syntax-control-keyword") + if (string.equals_ignoring_case("syntax-control-keyword")) return CSS::ValueID::VendorSpecificPaletteSyntaxControlKeyword; - else if (string == "syntax-identifier") + if (string.equals_ignoring_case("syntax-identifier")) return CSS::ValueID::VendorSpecificPaletteSyntaxIdentifier; - else if (string == "syntax-preprocessor-statement") + if (string.equals_ignoring_case("syntax-preprocessor-statement")) return CSS::ValueID::VendorSpecificPaletteSyntaxPreprocessorStatement; - else if (string == "syntax-preprocessor-value") + if (string.equals_ignoring_case("syntax-preprocessor-value")) return CSS::ValueID::VendorSpecificPaletteSyntaxPreprocessorValue; - else - return CSS::ValueID::Invalid; + return {}; } static Optional<Color> parse_css_color(const CSS::ParsingContext&, const StringView& view) @@ -340,7 +339,46 @@ static CSS::Length parse_length(const CSS::ParsingContext& context, const String static bool takes_integer_value(CSS::PropertyID property_id) { - return property_id == CSS::PropertyID::ZIndex; + return property_id == CSS::PropertyID::ZIndex || property_id == CSS::PropertyID::FontWeight; +} + +static Optional<CSS::ValueID> value_id_from_string(const String& string) +{ + // FIXME: Handle all identifiers + // FIXME: Generate this code + if (string.equals_ignoring_case("bold")) + return CSS::ValueID::Bold; + if (string.equals_ignoring_case("bolder")) + return CSS::ValueID::Bolder; + if (string.equals_ignoring_case("large")) + return CSS::ValueID::Large; + if (string.equals_ignoring_case("larger")) + return CSS::ValueID::Larger; + if (string.equals_ignoring_case("lighter")) + return CSS::ValueID::Lighter; + if (string.equals_ignoring_case("medium")) + return CSS::ValueID::Medium; + if (string.equals_ignoring_case("normal")) + return CSS::ValueID::Normal; + if (string.equals_ignoring_case("small")) + return CSS::ValueID::Small; + if (string.equals_ignoring_case("smaller")) + return CSS::ValueID::Smaller; + if (string.equals_ignoring_case("x-large")) + return CSS::ValueID::XLarge; + if (string.equals_ignoring_case("x-small")) + return CSS::ValueID::XSmall; + if (string.equals_ignoring_case("xx-large")) + return CSS::ValueID::XxLarge; + if (string.equals_ignoring_case("xx-small")) + return CSS::ValueID::XxSmall; + if (string.equals_ignoring_case("xxx-large")) + return CSS::ValueID::XxxLarge; + if (string.equals_ignoring_case("-libweb-link")) + return CSS::ValueID::VendorSpecificLink; + if (string.starts_with("-libweb-palette-", CaseSensitivity::CaseInsensitive)) + return value_id_for_palette_string(string.substring_view(16, string.length() - 16)); + return {}; } RefPtr<CSS::StyleValue> parse_css_value(const CSS::ParsingContext& context, const StringView& string, CSS::PropertyID property_id) @@ -366,17 +404,14 @@ RefPtr<CSS::StyleValue> parse_css_value(const CSS::ParsingContext& context, cons if (string.equals_ignoring_case("auto")) return CSS::LengthStyleValue::create(CSS::Length::make_auto()); + auto value_id = value_id_from_string(string); + if (value_id.has_value()) + return CSS::IdentifierStyleValue::create(value_id.value()); + auto color = parse_css_color(context, string); if (color.has_value()) return CSS::ColorStyleValue::create(color.value()); - if (string == "-libweb-link") - return CSS::IdentifierStyleValue::create(CSS::ValueID::VendorSpecificLink); - else if (string.starts_with("-libweb-palette-")) { - auto value_id = value_id_for_palette_string(string.substring_view(16, string.length() - 16)); - return CSS::IdentifierStyleValue::create(value_id); - } - return CSS::StringStyleValue::create(string); } diff --git a/Libraries/LibWeb/CSS/StyleProperties.cpp b/Libraries/LibWeb/CSS/StyleProperties.cpp index b95167dfa1..a9f1b94149 100644 --- a/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -25,6 +25,7 @@ */ #include <LibCore/DirIterator.h> +#include <LibGfx/FontDatabase.h> #include <LibWeb/CSS/StyleProperties.h> #include <LibWeb/FontCache.h> #include <ctype.h> @@ -104,73 +105,108 @@ Color StyleProperties::color_or_fallback(CSS::PropertyID id, const DOM::Document void StyleProperties::load_font() const { - auto font_family = string_or_fallback(CSS::PropertyID::FontFamily, "Katica"); - auto font_weight = string_or_fallback(CSS::PropertyID::FontWeight, "normal"); - - if (auto cached_font = FontCache::the().get({ font_family, font_weight })) { - m_font = cached_font; - return; + auto family_value = string_or_fallback(CSS::PropertyID::FontFamily, "Katica"); + auto font_size = property(CSS::PropertyID::FontSize).value_or(IdentifierStyleValue::create(CSS::ValueID::Medium)); + auto font_weight = property(CSS::PropertyID::FontWeight).value_or(IdentifierStyleValue::create(CSS::ValueID::Normal)); + + auto family_parts = family_value.split(','); + auto family = family_parts[0]; + + if (family.is_one_of("monospace", "ui-monospace")) + family = "Csilla"; + else if (family.is_one_of("serif", "sans-serif", "cursive", "fantasy", "ui-serif", "ui-sans-serif", "ui-rounded")) + family = "Katica"; + + int weight = 400; + if (font_weight->is_identifier()) { + switch (static_cast<const IdentifierStyleValue&>(*font_weight).id()) { + case CSS::ValueID::Normal: + weight = 400; + break; + case CSS::ValueID::Bold: + weight = 700; + break; + case CSS::ValueID::Lighter: + // FIXME: This should be relative to the parent. + weight = 400; + break; + case CSS::ValueID::Bolder: + // FIXME: This should be relative to the parent. + weight = 700; + break; + default: + break; + } + } else if (font_weight->is_length()) { + // FIXME: This isn't really a length, it's a numeric value.. + int font_weight_integer = font_weight->to_length().raw_value(); + if (font_weight_integer <= 400) + weight = 400; + if (font_weight_integer <= 700) + weight = 700; + weight = 900; } - String weight; - if (font_weight == "lighter") - weight = "Thin"; - else if (font_weight == "normal") - weight = "Regular"; - else if (font_weight == "bold") - weight = "Bold"; - else { - dbg() << "Unknown font-weight: " << font_weight; - weight = ""; + int size = 10; + if (font_size->is_identifier()) { + switch (static_cast<const IdentifierStyleValue&>(*font_size).id()) { + case CSS::ValueID::XxSmall: + case CSS::ValueID::XSmall: + case CSS::ValueID::Small: + case CSS::ValueID::Medium: + // FIXME: Should be based on "user's default font size" + size = 10; + break; + case CSS::ValueID::Large: + case CSS::ValueID::XLarge: + case CSS::ValueID::XxLarge: + case CSS::ValueID::XxxLarge: + // FIXME: Should be based on "user's default font size" + size = 12; + break; + case CSS::ValueID::Smaller: + // FIXME: This should be relative to the parent. + size = 10; + break; + case CSS::ValueID::Larger: + // FIXME: This should be relative to the parent. + size = 12; + break; + + default: + break; + } + } else if (font_size->is_length()) { + // FIXME: This isn't really a length, it's a numeric value.. + int font_size_integer = font_size->to_length().raw_value(); + if (font_size_integer <= 10) + size = 10; + else if (font_size_integer <= 12) + size = 12; + else + size = 14; } - auto look_for_file = [](const StringView& expected_name) -> String { - // TODO: handle font sizes properly? - Core::DirIterator it { "/res/fonts/", Core::DirIterator::Flags::SkipDots }; - while (it.has_next()) { - String name = it.next_path(); - - if (!name.ends_with(".font")) - continue; - if (!name.starts_with(expected_name)) - continue; - - // Check that a numeric size immediately - // follows the font name. This prevents, - // for example, matching KaticaBold when - // the regular Katica is requested. - if (!isdigit(name[expected_name.length()])) - continue; - - return name; - } - return {}; - }; - - // FIXME: Do this properly, with quote handling etc. - for (auto& font_name : font_family.split(',')) { - font_name = font_name.trim_whitespace(); - if (font_name == "monospace") - font_name = "Csilla"; - - auto file_name = look_for_file(String::format("%s%s", font_name.characters(), weight.characters())); - if (file_name.is_null() && weight == "") - file_name = look_for_file(String::format("%sRegular", font_name.characters())); - if (file_name.is_null()) - continue; - - m_font = Gfx::Font::load_from_file(String::format("/res/fonts/%s", file_name.characters())); - FontCache::the().set({ font_name, font_weight }, *m_font); + FontSelector font_selector { family, size, weight }; + + auto found_font = FontCache::the().get(font_selector); + if (found_font) { + m_font = found_font; return; } - if (font_weight == "bold") - m_font = Gfx::Font::default_bold_font(); - else - m_font = Gfx::Font::default_font(); - // FIXME: This is a hack to stop chewing CPU on sites that use a font we don't have and have a lot of text - // or changes text often. Examples are the Serenity 2nd birthday page and the JS specification. - FontCache::the().set({ font_family, font_weight }, *m_font); + Gfx::FontDatabase::the().for_each_font([&](auto& font) { + if (font.family() == family && font.weight() == weight && font.presentation_size() == size) + found_font = font; + }); + + if (!found_font) { + dbgln("Font not found: '{}' {} {}", family, size, weight); + found_font = Gfx::Font::default_font(); + } + + m_font = found_font; + FontCache::the().set(font_selector, *m_font); } float StyleProperties::line_height(const Layout::Node& layout_node) const @@ -218,7 +254,7 @@ bool StyleProperties::operator==(const StyleProperties& other) const auto& other_value = *jt->value; if (my_value.type() != other_value.type()) return false; - if (my_value.to_string() != other_value.to_string()) + if (my_value != other_value) return false; } diff --git a/Libraries/LibWeb/CSS/StyleValue.h b/Libraries/LibWeb/CSS/StyleValue.h index 61b119bad1..79b6b87dbd 100644 --- a/Libraries/LibWeb/CSS/StyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValue.h @@ -103,6 +103,20 @@ enum class ValueID { Right, Justify, VendorSpecificCenter, + Bold, + Bolder, + Large, + Larger, + Lighter, + Medium, + Normal, + Small, + Smaller, + XLarge, + XSmall, + XxLarge, + XxSmall, + XxxLarge, }; enum class Position { |