summaryrefslogtreecommitdiff
path: root/Libraries/LibWeb/CSS
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-12-14 15:56:01 +0100
committerAndreas Kling <kling@serenityos.org>2020-12-14 20:43:25 +0100
commit08517daa5ab0093538e913205328786966e440bd (patch)
treee529b8a405509eb5c3331100a57ed3ac5cf134ea /Libraries/LibWeb/CSS
parent861d22838d21b56478de616dabb56a5c7bbc8b29 (diff)
downloadserenity-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.css7
-rw-r--r--Libraries/LibWeb/CSS/Parser/CSSParser.cpp165
-rw-r--r--Libraries/LibWeb/CSS/StyleProperties.cpp158
-rw-r--r--Libraries/LibWeb/CSS/StyleValue.h14
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 {