From 2cb0039a13deba1166d6e0bc9ab47e8fd3ed4e71 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sat, 9 Dec 2023 23:42:02 +0100 Subject: [PATCH] LibGfx+LibWeb: Produce font cascade list in CSS font matching algorithm According to the CSS font matching algorithm specification, it is supposed to be executed for each glyph instead of each text run, as is currently done. This change partially implements this by having the font matching algorithm produce a list of fonts against which each glyph will be tested to find its suitable font. Now, it becomes possible to have per-glyph fallback fonts: if the needed glyph is not present in a font, we can check the subsequent fonts in the list. --- Userland/Libraries/LibGfx/CMakeLists.txt | 1 + Userland/Libraries/LibGfx/FontCascadeList.cpp | 54 ++++++++ Userland/Libraries/LibGfx/FontCascadeList.h | 43 ++++++ Userland/Libraries/LibWeb/CSS/Length.cpp | 8 +- .../Libraries/LibWeb/CSS/StyleComputer.cpp | 126 ++++++++++-------- Userland/Libraries/LibWeb/CSS/StyleComputer.h | 11 +- .../Libraries/LibWeb/CSS/StyleProperties.cpp | 8 +- .../Libraries/LibWeb/CSS/StyleProperties.h | 17 +-- Userland/Libraries/LibWeb/DOM/Element.cpp | 2 +- .../HTML/Canvas/CanvasTextDrawingStyles.h | 3 +- .../LibWeb/Layout/BlockFormattingContext.cpp | 6 +- .../Libraries/LibWeb/Layout/ButtonBox.cpp | 4 +- .../LibWeb/Layout/FormattingContext.cpp | 2 +- .../LibWeb/Layout/InlineLevelIterator.cpp | 4 +- Userland/Libraries/LibWeb/Layout/LineBox.cpp | 3 +- .../LibWeb/Layout/LineBoxFragment.cpp | 2 +- .../Libraries/LibWeb/Layout/LineBuilder.cpp | 6 +- Userland/Libraries/LibWeb/Layout/Node.cpp | 4 +- Userland/Libraries/LibWeb/Layout/Node.h | 35 +++-- .../LibWeb/Layout/SVGFormattingContext.cpp | 2 +- .../Libraries/LibWeb/Layout/TreeBuilder.cpp | 8 +- .../LibWeb/Painting/ButtonPaintable.cpp | 2 +- .../LibWeb/Painting/PaintableBox.cpp | 8 +- 23 files changed, 250 insertions(+), 109 deletions(-) create mode 100644 Userland/Libraries/LibGfx/FontCascadeList.cpp create mode 100644 Userland/Libraries/LibGfx/FontCascadeList.h diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index 643b8b7b66..99f8a06cf0 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -13,6 +13,7 @@ set(SOURCES Filters/FastBoxBlurFilter.cpp Filters/LumaFilter.cpp Filters/StackBlurFilter.cpp + FontCascadeList.cpp Font/BitmapFont.cpp Font/Emoji.cpp Font/Font.cpp diff --git a/Userland/Libraries/LibGfx/FontCascadeList.cpp b/Userland/Libraries/LibGfx/FontCascadeList.cpp new file mode 100644 index 0000000000..1104492e10 --- /dev/null +++ b/Userland/Libraries/LibGfx/FontCascadeList.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Gfx { + +void FontCascadeList::add(NonnullRefPtr font) +{ + m_fonts.append({ move(font), {} }); +} + +void FontCascadeList::add(NonnullRefPtr font, Vector unicode_ranges) +{ + m_fonts.append({ move(font), move(unicode_ranges) }); +} + +void FontCascadeList::extend(FontCascadeList const& other) +{ + for (auto const& font : other.m_fonts) { + m_fonts.append({ font.font->clone(), font.unicode_ranges }); + } +} + +Gfx::Font const& FontCascadeList::font_for_code_point(u32 code_point) const +{ + for (auto const& entry : m_fonts) { + if (!entry.unicode_ranges.has_value()) + return entry.font; + if (!entry.font->contains_glyph(code_point)) + continue; + for (auto const& range : *entry.unicode_ranges) { + if (range.contains(code_point)) + return entry.font; + } + } + VERIFY_NOT_REACHED(); +} + +bool FontCascadeList::equals(FontCascadeList const& other) const +{ + if (m_fonts.size() != other.m_fonts.size()) + return false; + for (size_t i = 0; i < m_fonts.size(); ++i) { + if (m_fonts[i].font != other.m_fonts[i].font) + return false; + } + return true; +} + +} diff --git a/Userland/Libraries/LibGfx/FontCascadeList.h b/Userland/Libraries/LibGfx/FontCascadeList.h new file mode 100644 index 0000000000..e3f9dae62e --- /dev/null +++ b/Userland/Libraries/LibGfx/FontCascadeList.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Gfx { + +class FontCascadeList : public RefCounted { +public: + static NonnullRefPtr create() + { + return adopt_ref(*new FontCascadeList()); + } + + size_t size() const { return m_fonts.size(); } + bool is_empty() const { return m_fonts.is_empty(); } + Font const& first() const { return *m_fonts.first().font; } + + void add(NonnullRefPtr font); + void add(NonnullRefPtr font, Vector unicode_ranges); + + void extend(FontCascadeList const& other); + + Gfx::Font const& font_for_code_point(u32 code_point) const; + + bool equals(FontCascadeList const& other) const; + + struct Entry { + NonnullRefPtr font; + Optional> unicode_ranges; + }; + +private: + Vector m_fonts; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/Length.cpp b/Userland/Libraries/LibWeb/CSS/Length.cpp index fc5f0bf681..ec4d571de0 100644 --- a/Userland/Libraries/LibWeb/CSS/Length.cpp +++ b/Userland/Libraries/LibWeb/CSS/Length.cpp @@ -138,8 +138,8 @@ Length::ResolutionContext Length::ResolutionContext::for_layout_node(Layout::Nod VERIFY(root_element->layout_node()); return Length::ResolutionContext { .viewport_rect = node.navigable()->viewport_rect(), - .font_metrics = { node.computed_values().font_size(), node.font().pixel_metrics(), node.line_height() }, - .root_font_metrics = { root_element->layout_node()->computed_values().font_size(), root_element->layout_node()->font().pixel_metrics(), root_element->layout_node()->line_height() }, + .font_metrics = { node.computed_values().font_size(), node.first_available_font().pixel_metrics(), node.line_height() }, + .root_font_metrics = { root_element->layout_node()->computed_values().font_size(), root_element->layout_node()->first_available_font().pixel_metrics(), root_element->layout_node()->line_height() }, }; } @@ -169,12 +169,12 @@ CSSPixels Length::to_px(Layout::Node const& layout_node) const FontMetrics font_metrics { layout_node.computed_values().font_size(), - layout_node.font().pixel_metrics(), + layout_node.first_available_font().pixel_metrics(), layout_node.line_height() }; FontMetrics root_font_metrics { root_element->layout_node()->computed_values().font_size(), - root_element->layout_node()->font().pixel_metrics(), + root_element->layout_node()->first_available_font().pixel_metrics(), root_element->layout_node()->line_height() }; diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index e050af5197..234a49eae1 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -91,15 +91,18 @@ StyleComputer::~StyleComputer() = default; class StyleComputer::FontLoader : public ResourceClient { public: - explicit FontLoader(StyleComputer& style_computer, FlyString family_name, Vector urls) + explicit FontLoader(StyleComputer& style_computer, FlyString family_name, Vector unicode_ranges, Vector urls) : m_style_computer(style_computer) , m_family_name(move(family_name)) + , m_unicode_ranges(move(unicode_ranges)) , m_urls(move(urls)) { } virtual ~FontLoader() override { } + Vector const& unicode_ranges() const { return m_unicode_ranges; } + virtual void resource_did_load() override { auto result = try_load_font(); @@ -183,6 +186,7 @@ private: StyleComputer& m_style_computer; FlyString m_family_name; + Vector m_unicode_ranges; RefPtr m_vector_font; Vector m_urls; @@ -191,14 +195,21 @@ private: struct StyleComputer::MatchingFontCandidate { FontFaceKey key; - Variant loader_or_typeface; + Variant loader_or_typeface; - [[nodiscard]] RefPtr font_with_point_size(float point_size) const + [[nodiscard]] RefPtr font_with_point_size(float point_size) const { - if (auto* loader = loader_or_typeface.get_pointer(); loader) { - return (*loader)->font_with_point_size(point_size); + RefPtr font_list = Gfx::FontCascadeList::create(); + if (auto* loader_list = loader_or_typeface.get_pointer(); loader_list) { + for (auto const& loader : **loader_list) { + if (auto font = loader->font_with_point_size(point_size); font) + font_list->add(*font, loader->unicode_ranges()); + } + return font_list; } - return loader_or_typeface.get()->get_font(point_size); + + font_list->add(*loader_or_typeface.get()->get_font(point_size)); + return font_list; } }; @@ -1529,7 +1540,7 @@ Length::FontMetrics StyleComputer::calculate_root_element_font_metrics(StyleProp { auto root_value = style.property(CSS::PropertyID::FontSize); - auto font_pixel_metrics = style.computed_font().pixel_metrics(); + auto font_pixel_metrics = style.first_available_computed_font().pixel_metrics(); Length::FontMetrics font_metrics { m_default_font_metrics.font_size, font_pixel_metrics, CSSPixels::nearest_value_for(font_pixel_metrics.line_spacing()) }; font_metrics.font_size = root_value->as_length().length().to_px(viewport_rect(), font_metrics, font_metrics); font_metrics.line_height = style.line_height(viewport_rect(), font_metrics, font_metrics); @@ -1537,7 +1548,7 @@ Length::FontMetrics StyleComputer::calculate_root_element_font_metrics(StyleProp return font_metrics; } -RefPtr StyleComputer::find_matching_font_weight_ascending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive) +RefPtr StyleComputer::find_matching_font_weight_ascending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive) { using Fn = AK::Function; auto pred = inclusive ? Fn([&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight >= target_weight; }) @@ -1550,7 +1561,7 @@ RefPtr StyleComputer::find_matching_font_weight_ascending(Vecto return {}; } -RefPtr StyleComputer::find_matching_font_weight_descending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive) +RefPtr StyleComputer::find_matching_font_weight_descending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive) { using Fn = AK::Function; auto pred = inclusive ? Fn([&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight <= target_weight; }) @@ -1565,14 +1576,14 @@ RefPtr StyleComputer::find_matching_font_weight_descending(Vect // Partial implementation of the font-matching algorithm: https://www.w3.org/TR/css-fonts-4/#font-matching-algorithm // FIXME: This should be replaced by the full CSS font selection algorithm. -RefPtr StyleComputer::font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const +RefPtr StyleComputer::font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const { // If a font family match occurs, the user agent assembles the set of font faces in that family and then // narrows the set to a single face using other font properties in the order given below. Vector matching_family_fonts; for (auto const& font_key_and_loader : m_loaded_fonts) { if (font_key_and_loader.key.family_name.equals_ignoring_ascii_case(key.family_name)) - matching_family_fonts.empend(font_key_and_loader.key, font_key_and_loader.value.ptr()); + matching_family_fonts.empend(font_key_and_loader.key, const_cast(&font_key_and_loader.value)); } Gfx::FontDatabase::the().for_each_typeface_with_family_name(key.family_name.to_string(), [&](Gfx::Typeface const& typeface) { matching_family_fonts.empend( @@ -1634,7 +1645,7 @@ RefPtr StyleComputer::font_matching_algorithm(FontFaceKey const return {}; } -RefPtr StyleComputer::compute_font_for_style_values(DOM::Element const* element, Optional pseudo_element, StyleValue const& font_family, StyleValue const& font_size, StyleValue const& font_style, StyleValue const& font_weight, StyleValue const& font_stretch, int math_depth) const +RefPtr StyleComputer::compute_font_for_style_values(DOM::Element const* element, Optional pseudo_element, StyleValue const& font_family, StyleValue const& font_size, StyleValue const& font_style, StyleValue const& font_weight, StyleValue const& font_stretch, int math_depth) const { auto* parent_element = element_to_inherit_style_from(element, pseudo_element); @@ -1649,7 +1660,7 @@ RefPtr StyleComputer::compute_font_for_style_values(DOM::Elemen auto parent_line_height = parent_or_root_element_line_height(element, pseudo_element); Gfx::FontPixelMetrics font_pixel_metrics; if (parent_element && parent_element->computed_css_values()) - font_pixel_metrics = parent_element->computed_css_values()->computed_font().pixel_metrics(); + font_pixel_metrics = parent_element->computed_css_values()->first_available_computed_font().pixel_metrics(); else font_pixel_metrics = Platform::FontPlugin::the().default_font().pixel_metrics(); auto parent_font_size = [&]() -> CSSPixels { @@ -1746,7 +1757,7 @@ RefPtr StyleComputer::compute_font_for_style_values(DOM::Elemen // and smaller may compute the font size to the previous entry in the table. if (identifier == CSS::ValueID::Smaller || identifier == CSS::ValueID::Larger) { if (parent_element && parent_element->computed_css_values()) { - font_size_in_px = CSSPixels::nearest_value_for(parent_element->computed_css_values()->computed_font().pixel_metrics().size); + font_size_in_px = CSSPixels::nearest_value_for(parent_element->computed_css_values()->first_available_computed_font().pixel_metrics().size); } } font_size_in_px *= get_absolute_size_mapping(identifier); @@ -1787,7 +1798,7 @@ RefPtr StyleComputer::compute_font_for_style_values(DOM::Elemen float const font_size_in_pt = font_size_in_px * 0.75f; - auto find_font = [&](FlyString const& family) -> RefPtr { + auto find_font = [&](FlyString const& family) -> RefPtr { font_selector = { family, font_size_in_pt, weight, width, slope }; FontFaceKey key { @@ -1796,25 +1807,29 @@ RefPtr StyleComputer::compute_font_for_style_values(DOM::Elemen .slope = slope, }; + auto result = Gfx::FontCascadeList::create(); if (auto it = m_loaded_fonts.find(key); it != m_loaded_fonts.end()) { - auto& loader = *it->value; - if (auto found_font = loader.font_with_point_size(font_size_in_pt)) - return found_font; + auto const& loaders = it->value; + for (auto const& loader : loaders) { + if (auto found_font = loader->font_with_point_size(font_size_in_pt)) + result->add(*found_font, loader->unicode_ranges()); + } + return result; } - if (auto found_font = m_font_cache.get(font_selector)) + if (auto found_font = font_matching_algorithm(key, font_size_in_pt); found_font && !found_font->is_empty()) { return found_font; + } - if (auto found_font = font_matching_algorithm(key, font_size_in_pt)) - return found_font; - - if (auto found_font = Gfx::FontDatabase::the().get(family, font_size_in_pt, weight, width, slope, Gfx::Font::AllowInexactSizeMatch::Yes)) - return found_font; + if (auto found_font = Gfx::FontDatabase::the().get(family, font_size_in_pt, weight, width, slope, Gfx::Font::AllowInexactSizeMatch::Yes)) { + result->add(*found_font); + return result; + } return {}; }; - auto find_generic_font = [&](ValueID font_id) -> RefPtr { + auto find_generic_font = [&](ValueID font_id) -> RefPtr { Platform::GenericFont generic_font {}; switch (font_id) { case ValueID::Monospace: @@ -1849,40 +1864,38 @@ RefPtr StyleComputer::compute_font_for_style_values(DOM::Elemen return find_font(Platform::FontPlugin::the().generic_font_name(generic_font)); }; - RefPtr found_font; - + auto font_list = Gfx::FontCascadeList::create(); if (font_family.is_value_list()) { auto const& family_list = static_cast(font_family).values(); for (auto const& family : family_list) { + RefPtr other_font_list; if (family->is_identifier()) { - found_font = find_generic_font(family->to_identifier()); + other_font_list = find_generic_font(family->to_identifier()); } else if (family->is_string()) { - found_font = find_font(family->as_string().string_value()); + other_font_list = find_font(family->as_string().string_value()); } else if (family->is_custom_ident()) { - found_font = find_font(family->as_custom_ident().custom_ident()); + other_font_list = find_font(family->as_custom_ident().custom_ident()); } - if (found_font) - break; + if (other_font_list) + font_list->extend(*other_font_list); } } else if (font_family.is_identifier()) { - found_font = find_generic_font(font_family.to_identifier()); + if (auto other_font_list = find_generic_font(font_family.to_identifier())) + font_list->extend(*other_font_list); } else if (font_family.is_string()) { - found_font = find_font(font_family.as_string().string_value()); + if (auto other_font_list = find_font(font_family.as_string().string_value())) + font_list->extend(*other_font_list); } else if (font_family.is_custom_ident()) { - found_font = find_font(font_family.as_custom_ident().custom_ident()); + if (auto other_font_list = find_font(font_family.as_custom_ident().custom_ident())) + font_list->extend(*other_font_list); } - if (!found_font) { - found_font = StyleProperties::font_fallback(monospace, bold); - if (found_font) { - if (auto scaled_fallback_font = found_font->with_size(font_size_in_pt)) - found_font = scaled_fallback_font; - } + auto found_font = StyleProperties::font_fallback(monospace, bold); + if (auto scaled_fallback_font = found_font->with_size(font_size_in_pt)) { + font_list->add(*scaled_fallback_font); } - m_font_cache.set(font_selector, *found_font); - - return found_font; + return font_list; } void StyleComputer::compute_font(StyleProperties& style, DOM::Element const* element, Optional pseudo_element) const @@ -1902,12 +1915,16 @@ void StyleComputer::compute_font(StyleProperties& style, DOM::Element const* ele auto font_weight = style.property(CSS::PropertyID::FontWeight); auto font_stretch = style.property(CSS::PropertyID::FontStretch); - auto found_font = compute_font_for_style_values(element, pseudo_element, font_family, font_size, font_style, font_weight, font_stretch, style.math_depth()); + auto font_list = compute_font_for_style_values(element, pseudo_element, font_family, font_size, font_style, font_weight, font_stretch, style.math_depth()); + VERIFY(font_list); + VERIFY(!font_list->is_empty()); + + RefPtr const found_font = font_list->first(); style.set_property(CSS::PropertyID::FontSize, LengthStyleValue::create(CSS::Length::make_px(CSSPixels::nearest_value_for(found_font->pixel_size()))), nullptr); style.set_property(CSS::PropertyID::FontWeight, NumberStyleValue::create(font_weight->to_font_weight())); - style.set_computed_font(found_font.release_nonnull()); + style.set_computed_font_list(*font_list); if (element && is(*element)) { const_cast(*this).m_root_element_font_metrics = calculate_root_element_font_metrics(style); @@ -1928,7 +1945,7 @@ CSSPixels StyleComputer::parent_or_root_element_line_height(DOM::Element const* auto const* computed_values = parent_element->computed_css_values(); if (!computed_values) return m_root_element_font_metrics.line_height; - auto parent_font_pixel_metrics = computed_values->computed_font().pixel_metrics(); + auto parent_font_pixel_metrics = computed_values->first_available_computed_font().pixel_metrics(); auto parent_font_size = computed_values->property(CSS::PropertyID::FontSize)->as_length().length(); // FIXME: Can the parent font size be non-absolute here? auto parent_font_size_value = parent_font_size.is_absolute() ? parent_font_size.absolute_length_to_px() : m_root_element_font_metrics.font_size; @@ -1941,7 +1958,7 @@ void StyleComputer::absolutize_values(StyleProperties& style, DOM::Element const { auto parent_or_root_line_height = parent_or_root_element_line_height(element, pseudo_element); - auto font_pixel_metrics = style.computed_font().pixel_metrics(); + auto font_pixel_metrics = style.first_available_computed_font().pixel_metrics(); Length::FontMetrics font_metrics { m_root_element_font_metrics.font_size, font_pixel_metrics, parent_or_root_line_height }; @@ -2367,8 +2384,6 @@ void StyleComputer::load_fonts_from_sheet(CSSStyleSheet const& sheet) .weight = font_face.weight().value_or(0), .slope = font_face.slope().value_or(0), }; - if (m_loaded_fonts.contains(key)) - continue; Vector urls; for (auto& source : font_face.sources()) { @@ -2381,8 +2396,15 @@ void StyleComputer::load_fonts_from_sheet(CSSStyleSheet const& sheet) if (urls.is_empty()) continue; - auto loader = make(const_cast(*this), font_face.font_family(), move(urls)); - const_cast(*this).m_loaded_fonts.set(key, move(loader)); + auto loader = make(const_cast(*this), font_face.font_family(), font_face.unicode_ranges(), move(urls)); + auto maybe_font_loaders_list = const_cast(*this).m_loaded_fonts.get(key); + if (maybe_font_loaders_list.has_value()) { + maybe_font_loaders_list->append(move(loader)); + } else { + FontLoaderList loaders; + loaders.append(move(loader)); + const_cast(*this).m_loaded_fonts.set(key, move(loaders)); + } } } diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.h b/Userland/Libraries/LibWeb/CSS/StyleComputer.h index 2145dc006d..7e658c224c 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.h @@ -74,7 +74,7 @@ public: void load_fonts_from_sheet(CSSStyleSheet const&); - RefPtr compute_font_for_style_values(DOM::Element const* element, Optional pseudo_element, StyleValue const& font_family, StyleValue const& font_size, StyleValue const& font_style, StyleValue const& font_weight, StyleValue const& font_stretch, int math_depth = 0) const; + RefPtr compute_font_for_style_values(DOM::Element const* element, Optional pseudo_element, StyleValue const& font_family, StyleValue const& font_size, StyleValue const& font_style, StyleValue const& font_weight, StyleValue const& font_stretch, int math_depth = 0) const; struct AnimationKey { CSS::CSSStyleDeclaration const* source_declaration; @@ -123,9 +123,9 @@ private: ErrorOr> compute_style_impl(DOM::Element&, Optional, ComputeStyleMode) const; ErrorOr compute_cascaded_values(StyleProperties&, DOM::Element&, Optional, bool& did_match_any_pseudo_element_rules, ComputeStyleMode) const; - static RefPtr find_matching_font_weight_ascending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive); - static RefPtr find_matching_font_weight_descending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive); - RefPtr font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const; + static RefPtr find_matching_font_weight_ascending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive); + static RefPtr find_matching_font_weight_descending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive); + RefPtr font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const; void compute_font(StyleProperties&, DOM::Element const*, Optional) const; void compute_math_depth(StyleProperties&, DOM::Element const*, Optional) const; void compute_defaulted_values(StyleProperties&, DOM::Element const*, Optional) const; @@ -186,7 +186,8 @@ private: mutable FontCache m_font_cache; - HashMap> m_loaded_fonts; + using FontLoaderList = Vector>; + HashMap m_loaded_fonts; Length::FontMetrics m_default_font_metrics; Length::FontMetrics m_root_element_font_metrics; diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index 1ac1151790..ad767d6551 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -191,7 +191,7 @@ CSSPixels StyleProperties::line_height(Layout::Node const& layout_node) const auto line_height = property(CSS::PropertyID::LineHeight); if (line_height->is_identifier() && line_height->to_identifier() == ValueID::Normal) - return CSSPixels::nearest_value_for(layout_node.font().pixel_metrics().line_spacing()); + return CSSPixels::nearest_value_for(layout_node.first_available_font().pixel_metrics().line_spacing()); if (line_height->is_length()) { auto line_height_length = line_height->as_length().length(); @@ -213,7 +213,7 @@ CSSPixels StyleProperties::line_height(Layout::Node const& layout_node) const auto resolved = line_height->as_calculated().resolve_number(); if (!resolved.has_value()) { dbgln("FIXME: Failed to resolve calc() line-height (number): {}", line_height->as_calculated().to_string()); - return CSSPixels::nearest_value_for(layout_node.font().pixel_metrics().line_spacing()); + return CSSPixels::nearest_value_for(layout_node.first_available_font().pixel_metrics().line_spacing()); } return Length(resolved.value(), Length::Type::Em).to_px(layout_node); } @@ -221,12 +221,12 @@ CSSPixels StyleProperties::line_height(Layout::Node const& layout_node) const auto resolved = line_height->as_calculated().resolve_length(layout_node); if (!resolved.has_value()) { dbgln("FIXME: Failed to resolve calc() line-height: {}", line_height->as_calculated().to_string()); - return CSSPixels::nearest_value_for(layout_node.font().pixel_metrics().line_spacing()); + return CSSPixels::nearest_value_for(layout_node.first_available_font().pixel_metrics().line_spacing()); } return resolved->to_px(layout_node); } - return CSSPixels::nearest_value_for(layout_node.font().pixel_metrics().line_spacing()); + return CSSPixels::nearest_value_for(layout_node.first_available_font().pixel_metrics().line_spacing()); } Optional StyleProperties::z_index() const diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index be587b0a82..88cb18d4ac 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -22,8 +23,6 @@ public: static NonnullRefPtr create() { return adopt_ref(*new StyleProperties); } - NonnullRefPtr clone() const; - template inline void for_each_property(Callback callback) const { @@ -128,15 +127,17 @@ public: float stroke_opacity() const; Optional fill_rule() const; - Gfx::Font const& computed_font() const + Gfx::Font const& first_available_computed_font() const { return m_font_list->first(); } + + Gfx::FontCascadeList const& computed_font_list() const { - VERIFY(m_font); - return *m_font; + VERIFY(m_font_list); + return *m_font_list; } - void set_computed_font(NonnullRefPtr font) + void set_computed_font_list(NonnullRefPtr font_list) const { - m_font = move(font); + m_font_list = move(font_list); } CSSPixels line_height(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const; @@ -162,7 +163,7 @@ private: Vector shadow(CSS::PropertyID, Layout::Node const&) const; int m_math_depth { InitialValues::math_depth() }; - mutable RefPtr m_font; + mutable RefPtr m_font_list; }; } diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index 32d62dcc2a..7bb9dc9ae6 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -492,7 +492,7 @@ static Element::RequiredInvalidationAfterStyleChange compute_required_invalidati { Element::RequiredInvalidationAfterStyleChange invalidation; - if (&old_style.computed_font() != &new_style.computed_font()) + if (!old_style.computed_font_list().equals(new_style.computed_font_list())) invalidation.relayout = true; for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) { diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h index 208bd4dedc..56b0100440 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h @@ -58,7 +58,8 @@ public: auto& font_stretch = *font_style_value.longhand(CSS::PropertyID::FontStretch); auto& font_size = *font_style_value.longhand(CSS::PropertyID::FontSize); auto& font_family = *font_style_value.longhand(CSS::PropertyID::FontFamily); - my_drawing_state().current_font = canvas_element.document().style_computer().compute_font_for_style_values(&canvas_element, {}, font_family, font_size, font_style, font_weight, font_stretch); + auto font_list = canvas_element.document().style_computer().compute_font_for_style_values(&canvas_element, {}, font_family, font_size, font_style, font_weight, font_stretch); + my_drawing_state().current_font = font_list->first(); } Bindings::CanvasTextAlign text_align() const { return my_drawing_state().text_align; } diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 27590d4437..c06f4e0422 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -1093,17 +1093,17 @@ void BlockFormattingContext::layout_list_item_marker(ListItemBox const& list_ite image_height = list_style_image->natural_height().value_or(0); } - CSSPixels default_marker_width = max(4, marker.font().pixel_size_rounded_up() - 4); + CSSPixels default_marker_width = max(4, marker.first_available_font().pixel_size_rounded_up() - 4); auto marker_text = marker.text().value_or(""); if (marker_text.is_empty()) { marker_state.set_content_width(image_width + default_marker_width); } else { - auto text_width = marker.font().width(marker_text); + auto text_width = marker.first_available_font().width(marker_text); marker_state.set_content_width(image_width + CSSPixels::nearest_value_for(text_width)); } - marker_state.set_content_height(max(image_height, marker.font().pixel_size_rounded_up() + 1)); + marker_state.set_content_height(max(image_height, marker.first_available_font().pixel_size_rounded_up() + 1)); auto final_marker_width = marker_state.content_width() + default_marker_width; diff --git a/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp b/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp index bd53ad536b..d578ac8f1a 100644 --- a/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp @@ -25,8 +25,8 @@ void ButtonBox::prepare_for_replaced_layout() // value attribute. This is not the case with