1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 18:47:34 +00:00

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.
This commit is contained in:
Aliaksandr Kalenik 2023-12-09 23:42:02 +01:00 committed by Andreas Kling
parent f50bf00814
commit 2cb0039a13
23 changed files with 250 additions and 109 deletions

View file

@ -13,6 +13,7 @@ set(SOURCES
Filters/FastBoxBlurFilter.cpp Filters/FastBoxBlurFilter.cpp
Filters/LumaFilter.cpp Filters/LumaFilter.cpp
Filters/StackBlurFilter.cpp Filters/StackBlurFilter.cpp
FontCascadeList.cpp
Font/BitmapFont.cpp Font/BitmapFont.cpp
Font/Emoji.cpp Font/Emoji.cpp
Font/Font.cpp Font/Font.cpp

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/FontCascadeList.h>
namespace Gfx {
void FontCascadeList::add(NonnullRefPtr<Font> font)
{
m_fonts.append({ move(font), {} });
}
void FontCascadeList::add(NonnullRefPtr<Font> font, Vector<UnicodeRange> 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;
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Font/Font.h>
#include <LibGfx/Font/UnicodeRange.h>
namespace Gfx {
class FontCascadeList : public RefCounted<FontCascadeList> {
public:
static NonnullRefPtr<FontCascadeList> 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> font);
void add(NonnullRefPtr<Font> font, Vector<UnicodeRange> 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> font;
Optional<Vector<UnicodeRange>> unicode_ranges;
};
private:
Vector<Entry> m_fonts;
};
}

View file

@ -138,8 +138,8 @@ Length::ResolutionContext Length::ResolutionContext::for_layout_node(Layout::Nod
VERIFY(root_element->layout_node()); VERIFY(root_element->layout_node());
return Length::ResolutionContext { return Length::ResolutionContext {
.viewport_rect = node.navigable()->viewport_rect(), .viewport_rect = node.navigable()->viewport_rect(),
.font_metrics = { node.computed_values().font_size(), node.font().pixel_metrics(), 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()->font().pixel_metrics(), root_element->layout_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 { FontMetrics font_metrics {
layout_node.computed_values().font_size(), layout_node.computed_values().font_size(),
layout_node.font().pixel_metrics(), layout_node.first_available_font().pixel_metrics(),
layout_node.line_height() layout_node.line_height()
}; };
FontMetrics root_font_metrics { FontMetrics root_font_metrics {
root_element->layout_node()->computed_values().font_size(), 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() root_element->layout_node()->line_height()
}; };

View file

@ -91,15 +91,18 @@ StyleComputer::~StyleComputer() = default;
class StyleComputer::FontLoader : public ResourceClient { class StyleComputer::FontLoader : public ResourceClient {
public: public:
explicit FontLoader(StyleComputer& style_computer, FlyString family_name, Vector<AK::URL> urls) explicit FontLoader(StyleComputer& style_computer, FlyString family_name, Vector<Gfx::UnicodeRange> unicode_ranges, Vector<AK::URL> urls)
: m_style_computer(style_computer) : m_style_computer(style_computer)
, m_family_name(move(family_name)) , m_family_name(move(family_name))
, m_unicode_ranges(move(unicode_ranges))
, m_urls(move(urls)) , m_urls(move(urls))
{ {
} }
virtual ~FontLoader() override { } virtual ~FontLoader() override { }
Vector<Gfx::UnicodeRange> const& unicode_ranges() const { return m_unicode_ranges; }
virtual void resource_did_load() override virtual void resource_did_load() override
{ {
auto result = try_load_font(); auto result = try_load_font();
@ -183,6 +186,7 @@ private:
StyleComputer& m_style_computer; StyleComputer& m_style_computer;
FlyString m_family_name; FlyString m_family_name;
Vector<Gfx::UnicodeRange> m_unicode_ranges;
RefPtr<Gfx::VectorFont> m_vector_font; RefPtr<Gfx::VectorFont> m_vector_font;
Vector<AK::URL> m_urls; Vector<AK::URL> m_urls;
@ -191,14 +195,21 @@ private:
struct StyleComputer::MatchingFontCandidate { struct StyleComputer::MatchingFontCandidate {
FontFaceKey key; FontFaceKey key;
Variant<FontLoader*, Gfx::Typeface const*> loader_or_typeface; Variant<FontLoaderList*, Gfx::Typeface const*> loader_or_typeface;
[[nodiscard]] RefPtr<Gfx::Font const> font_with_point_size(float point_size) const [[nodiscard]] RefPtr<Gfx::FontCascadeList const> font_with_point_size(float point_size) const
{ {
if (auto* loader = loader_or_typeface.get_pointer<FontLoader*>(); loader) { RefPtr<Gfx::FontCascadeList> font_list = Gfx::FontCascadeList::create();
return (*loader)->font_with_point_size(point_size); if (auto* loader_list = loader_or_typeface.get_pointer<FontLoaderList*>(); 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<Gfx::Typeface const*>()->get_font(point_size);
font_list->add(*loader_or_typeface.get<Gfx::Typeface const*>()->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 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()) }; 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.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); 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; return font_metrics;
} }
RefPtr<Gfx::Font const> StyleComputer::find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive) RefPtr<Gfx::FontCascadeList const> StyleComputer::find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive)
{ {
using Fn = AK::Function<bool(MatchingFontCandidate const&)>; using Fn = AK::Function<bool(MatchingFontCandidate const&)>;
auto pred = inclusive ? Fn([&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight >= target_weight; }) auto pred = inclusive ? Fn([&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight >= target_weight; })
@ -1550,7 +1561,7 @@ RefPtr<Gfx::Font const> StyleComputer::find_matching_font_weight_ascending(Vecto
return {}; return {};
} }
RefPtr<Gfx::Font const> StyleComputer::find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive) RefPtr<Gfx::FontCascadeList const> StyleComputer::find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive)
{ {
using Fn = AK::Function<bool(MatchingFontCandidate const&)>; using Fn = AK::Function<bool(MatchingFontCandidate const&)>;
auto pred = inclusive ? Fn([&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight <= target_weight; }) auto pred = inclusive ? Fn([&](auto const& matching_font_candidate) { return matching_font_candidate.key.weight <= target_weight; })
@ -1565,14 +1576,14 @@ RefPtr<Gfx::Font const> 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 // 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. // FIXME: This should be replaced by the full CSS font selection algorithm.
RefPtr<Gfx::Font const> StyleComputer::font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const RefPtr<Gfx::FontCascadeList const> 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 // 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. // narrows the set to a single face using other font properties in the order given below.
Vector<MatchingFontCandidate> matching_family_fonts; Vector<MatchingFontCandidate> matching_family_fonts;
for (auto const& font_key_and_loader : m_loaded_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)) 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<FontLoaderList*>(&font_key_and_loader.value));
} }
Gfx::FontDatabase::the().for_each_typeface_with_family_name(key.family_name.to_string(), [&](Gfx::Typeface const& typeface) { Gfx::FontDatabase::the().for_each_typeface_with_family_name(key.family_name.to_string(), [&](Gfx::Typeface const& typeface) {
matching_family_fonts.empend( matching_family_fonts.empend(
@ -1634,7 +1645,7 @@ RefPtr<Gfx::Font const> StyleComputer::font_matching_algorithm(FontFaceKey const
return {}; return {};
} }
RefPtr<Gfx::Font const> StyleComputer::compute_font_for_style_values(DOM::Element const* element, Optional<CSS::Selector::PseudoElement> 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<Gfx::FontCascadeList const> StyleComputer::compute_font_for_style_values(DOM::Element const* element, Optional<CSS::Selector::PseudoElement> 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); auto* parent_element = element_to_inherit_style_from(element, pseudo_element);
@ -1649,7 +1660,7 @@ RefPtr<Gfx::Font const> StyleComputer::compute_font_for_style_values(DOM::Elemen
auto parent_line_height = parent_or_root_element_line_height(element, pseudo_element); auto parent_line_height = parent_or_root_element_line_height(element, pseudo_element);
Gfx::FontPixelMetrics font_pixel_metrics; Gfx::FontPixelMetrics font_pixel_metrics;
if (parent_element && parent_element->computed_css_values()) 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 else
font_pixel_metrics = Platform::FontPlugin::the().default_font().pixel_metrics(); font_pixel_metrics = Platform::FontPlugin::the().default_font().pixel_metrics();
auto parent_font_size = [&]() -> CSSPixels { auto parent_font_size = [&]() -> CSSPixels {
@ -1746,7 +1757,7 @@ RefPtr<Gfx::Font const> StyleComputer::compute_font_for_style_values(DOM::Elemen
// and smaller may compute the font size to the previous entry in the table. // and smaller may compute the font size to the previous entry in the table.
if (identifier == CSS::ValueID::Smaller || identifier == CSS::ValueID::Larger) { if (identifier == CSS::ValueID::Smaller || identifier == CSS::ValueID::Larger) {
if (parent_element && parent_element->computed_css_values()) { 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); font_size_in_px *= get_absolute_size_mapping(identifier);
@ -1787,7 +1798,7 @@ RefPtr<Gfx::Font const> StyleComputer::compute_font_for_style_values(DOM::Elemen
float const font_size_in_pt = font_size_in_px * 0.75f; float const font_size_in_pt = font_size_in_px * 0.75f;
auto find_font = [&](FlyString const& family) -> RefPtr<Gfx::Font const> { auto find_font = [&](FlyString const& family) -> RefPtr<Gfx::FontCascadeList const> {
font_selector = { family, font_size_in_pt, weight, width, slope }; font_selector = { family, font_size_in_pt, weight, width, slope };
FontFaceKey key { FontFaceKey key {
@ -1796,25 +1807,29 @@ RefPtr<Gfx::Font const> StyleComputer::compute_font_for_style_values(DOM::Elemen
.slope = slope, .slope = slope,
}; };
auto result = Gfx::FontCascadeList::create();
if (auto it = m_loaded_fonts.find(key); it != m_loaded_fonts.end()) { if (auto it = m_loaded_fonts.find(key); it != m_loaded_fonts.end()) {
auto& loader = *it->value; auto const& loaders = it->value;
if (auto found_font = loader.font_with_point_size(font_size_in_pt)) for (auto const& loader : loaders) {
return found_font; 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; return found_font;
}
if (auto found_font = font_matching_algorithm(key, font_size_in_pt)) if (auto found_font = Gfx::FontDatabase::the().get(family, font_size_in_pt, weight, width, slope, Gfx::Font::AllowInexactSizeMatch::Yes)) {
return found_font; result->add(*found_font);
return result;
if (auto found_font = Gfx::FontDatabase::the().get(family, font_size_in_pt, weight, width, slope, Gfx::Font::AllowInexactSizeMatch::Yes)) }
return found_font;
return {}; return {};
}; };
auto find_generic_font = [&](ValueID font_id) -> RefPtr<Gfx::Font const> { auto find_generic_font = [&](ValueID font_id) -> RefPtr<Gfx::FontCascadeList const> {
Platform::GenericFont generic_font {}; Platform::GenericFont generic_font {};
switch (font_id) { switch (font_id) {
case ValueID::Monospace: case ValueID::Monospace:
@ -1849,40 +1864,38 @@ RefPtr<Gfx::Font const> StyleComputer::compute_font_for_style_values(DOM::Elemen
return find_font(Platform::FontPlugin::the().generic_font_name(generic_font)); return find_font(Platform::FontPlugin::the().generic_font_name(generic_font));
}; };
RefPtr<Gfx::Font const> found_font; auto font_list = Gfx::FontCascadeList::create();
if (font_family.is_value_list()) { if (font_family.is_value_list()) {
auto const& family_list = static_cast<StyleValueList const&>(font_family).values(); auto const& family_list = static_cast<StyleValueList const&>(font_family).values();
for (auto const& family : family_list) { for (auto const& family : family_list) {
RefPtr<Gfx::FontCascadeList const> other_font_list;
if (family->is_identifier()) { 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()) { } 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()) { } 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) if (other_font_list)
break; font_list->extend(*other_font_list);
} }
} else if (font_family.is_identifier()) { } 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()) { } 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()) { } 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) { auto found_font = StyleProperties::font_fallback(monospace, bold);
found_font = StyleProperties::font_fallback(monospace, bold); if (auto scaled_fallback_font = found_font->with_size(font_size_in_pt)) {
if (found_font) { font_list->add(*scaled_fallback_font);
if (auto scaled_fallback_font = found_font->with_size(font_size_in_pt))
found_font = scaled_fallback_font;
}
} }
m_font_cache.set(font_selector, *found_font); return font_list;
return found_font;
} }
void StyleComputer::compute_font(StyleProperties& style, DOM::Element const* element, Optional<CSS::Selector::PseudoElement> pseudo_element) const void StyleComputer::compute_font(StyleProperties& style, DOM::Element const* element, Optional<CSS::Selector::PseudoElement> 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_weight = style.property(CSS::PropertyID::FontWeight);
auto font_stretch = style.property(CSS::PropertyID::FontStretch); 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<Gfx::Font const> 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::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_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<HTML::HTMLHtmlElement>(*element)) { if (element && is<HTML::HTMLHtmlElement>(*element)) {
const_cast<StyleComputer&>(*this).m_root_element_font_metrics = calculate_root_element_font_metrics(style); const_cast<StyleComputer&>(*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(); auto const* computed_values = parent_element->computed_css_values();
if (!computed_values) if (!computed_values)
return m_root_element_font_metrics.line_height; 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(); auto parent_font_size = computed_values->property(CSS::PropertyID::FontSize)->as_length().length();
// FIXME: Can the parent font size be non-absolute here? // 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; 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 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 }; 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), .weight = font_face.weight().value_or(0),
.slope = font_face.slope().value_or(0), .slope = font_face.slope().value_or(0),
}; };
if (m_loaded_fonts.contains(key))
continue;
Vector<AK::URL> urls; Vector<AK::URL> urls;
for (auto& source : font_face.sources()) { for (auto& source : font_face.sources()) {
@ -2381,8 +2396,15 @@ void StyleComputer::load_fonts_from_sheet(CSSStyleSheet const& sheet)
if (urls.is_empty()) if (urls.is_empty())
continue; continue;
auto loader = make<FontLoader>(const_cast<StyleComputer&>(*this), font_face.font_family(), move(urls)); auto loader = make<FontLoader>(const_cast<StyleComputer&>(*this), font_face.font_family(), font_face.unicode_ranges(), move(urls));
const_cast<StyleComputer&>(*this).m_loaded_fonts.set(key, move(loader)); auto maybe_font_loaders_list = const_cast<StyleComputer&>(*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<StyleComputer&>(*this).m_loaded_fonts.set(key, move(loaders));
}
} }
} }

View file

@ -74,7 +74,7 @@ public:
void load_fonts_from_sheet(CSSStyleSheet const&); void load_fonts_from_sheet(CSSStyleSheet const&);
RefPtr<Gfx::Font const> compute_font_for_style_values(DOM::Element const* element, Optional<CSS::Selector::PseudoElement> 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<Gfx::FontCascadeList const> compute_font_for_style_values(DOM::Element const* element, Optional<CSS::Selector::PseudoElement> 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 { struct AnimationKey {
CSS::CSSStyleDeclaration const* source_declaration; CSS::CSSStyleDeclaration const* source_declaration;
@ -123,9 +123,9 @@ private:
ErrorOr<RefPtr<StyleProperties>> compute_style_impl(DOM::Element&, Optional<CSS::Selector::PseudoElement>, ComputeStyleMode) const; ErrorOr<RefPtr<StyleProperties>> compute_style_impl(DOM::Element&, Optional<CSS::Selector::PseudoElement>, ComputeStyleMode) const;
ErrorOr<void> compute_cascaded_values(StyleProperties&, DOM::Element&, Optional<CSS::Selector::PseudoElement>, bool& did_match_any_pseudo_element_rules, ComputeStyleMode) const; ErrorOr<void> compute_cascaded_values(StyleProperties&, DOM::Element&, Optional<CSS::Selector::PseudoElement>, bool& did_match_any_pseudo_element_rules, ComputeStyleMode) const;
static RefPtr<Gfx::Font const> find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive); static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
static RefPtr<Gfx::Font const> find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive); static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
RefPtr<Gfx::Font const> font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const; RefPtr<Gfx::FontCascadeList const> font_matching_algorithm(FontFaceKey const& key, float font_size_in_pt) const;
void compute_font(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const; void compute_font(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
void compute_math_depth(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const; void compute_math_depth(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
void compute_defaulted_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const; void compute_defaulted_values(StyleProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement>) const;
@ -186,7 +186,8 @@ private:
mutable FontCache m_font_cache; mutable FontCache m_font_cache;
HashMap<FontFaceKey, NonnullOwnPtr<FontLoader>> m_loaded_fonts; using FontLoaderList = Vector<NonnullOwnPtr<FontLoader>>;
HashMap<FontFaceKey, FontLoaderList> m_loaded_fonts;
Length::FontMetrics m_default_font_metrics; Length::FontMetrics m_default_font_metrics;
Length::FontMetrics m_root_element_font_metrics; Length::FontMetrics m_root_element_font_metrics;

View file

@ -191,7 +191,7 @@ CSSPixels StyleProperties::line_height(Layout::Node const& layout_node) const
auto line_height = property(CSS::PropertyID::LineHeight); auto line_height = property(CSS::PropertyID::LineHeight);
if (line_height->is_identifier() && line_height->to_identifier() == ValueID::Normal) 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()) { if (line_height->is_length()) {
auto line_height_length = line_height->as_length().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(); auto resolved = line_height->as_calculated().resolve_number();
if (!resolved.has_value()) { if (!resolved.has_value()) {
dbgln("FIXME: Failed to resolve calc() line-height (number): {}", line_height->as_calculated().to_string()); 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); 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); auto resolved = line_height->as_calculated().resolve_length(layout_node);
if (!resolved.has_value()) { if (!resolved.has_value()) {
dbgln("FIXME: Failed to resolve calc() line-height: {}", line_height->as_calculated().to_string()); 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 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<int> StyleProperties::z_index() const Optional<int> StyleProperties::z_index() const

View file

@ -9,6 +9,7 @@
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <AK/NonnullRefPtr.h> #include <AK/NonnullRefPtr.h>
#include <LibGfx/Font/Font.h> #include <LibGfx/Font/Font.h>
#include <LibGfx/FontCascadeList.h>
#include <LibGfx/Forward.h> #include <LibGfx/Forward.h>
#include <LibWeb/CSS/ComputedValues.h> #include <LibWeb/CSS/ComputedValues.h>
#include <LibWeb/CSS/LengthBox.h> #include <LibWeb/CSS/LengthBox.h>
@ -22,8 +23,6 @@ public:
static NonnullRefPtr<StyleProperties> create() { return adopt_ref(*new StyleProperties); } static NonnullRefPtr<StyleProperties> create() { return adopt_ref(*new StyleProperties); }
NonnullRefPtr<StyleProperties> clone() const;
template<typename Callback> template<typename Callback>
inline void for_each_property(Callback callback) const inline void for_each_property(Callback callback) const
{ {
@ -128,15 +127,17 @@ public:
float stroke_opacity() const; float stroke_opacity() const;
Optional<CSS::FillRule> fill_rule() const; Optional<CSS::FillRule> 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); VERIFY(m_font_list);
return *m_font; return *m_font_list;
} }
void set_computed_font(NonnullRefPtr<Gfx::Font const> font) void set_computed_font_list(NonnullRefPtr<Gfx::FontCascadeList> 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; 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<CSS::ShadowData> shadow(CSS::PropertyID, Layout::Node const&) const; Vector<CSS::ShadowData> shadow(CSS::PropertyID, Layout::Node const&) const;
int m_math_depth { InitialValues::math_depth() }; int m_math_depth { InitialValues::math_depth() };
mutable RefPtr<Gfx::Font const> m_font; mutable RefPtr<Gfx::FontCascadeList> m_font_list;
}; };
} }

View file

@ -492,7 +492,7 @@ static Element::RequiredInvalidationAfterStyleChange compute_required_invalidati
{ {
Element::RequiredInvalidationAfterStyleChange invalidation; 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; invalidation.relayout = true;
for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) { for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) {

View file

@ -58,7 +58,8 @@ public:
auto& font_stretch = *font_style_value.longhand(CSS::PropertyID::FontStretch); auto& font_stretch = *font_style_value.longhand(CSS::PropertyID::FontStretch);
auto& font_size = *font_style_value.longhand(CSS::PropertyID::FontSize); auto& font_size = *font_style_value.longhand(CSS::PropertyID::FontSize);
auto& font_family = *font_style_value.longhand(CSS::PropertyID::FontFamily); 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; } Bindings::CanvasTextAlign text_align() const { return my_drawing_state().text_align; }

View file

@ -1093,17 +1093,17 @@ void BlockFormattingContext::layout_list_item_marker(ListItemBox const& list_ite
image_height = list_style_image->natural_height().value_or(0); 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(""); auto marker_text = marker.text().value_or("");
if (marker_text.is_empty()) { if (marker_text.is_empty()) {
marker_state.set_content_width(image_width + default_marker_width); marker_state.set_content_width(image_width + default_marker_width);
} else { } 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_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; auto final_marker_width = marker_state.content_width() + default_marker_width;

View file

@ -25,8 +25,8 @@ void ButtonBox::prepare_for_replaced_layout()
// value attribute. This is not the case with <button />, which contains // value attribute. This is not the case with <button />, which contains
// its contents normally. // its contents normally.
if (is<HTML::HTMLInputElement>(dom_node())) { if (is<HTML::HTMLInputElement>(dom_node())) {
set_natural_width(CSSPixels::nearest_value_for(font().width(static_cast<HTML::HTMLInputElement&>(dom_node()).value()))); set_natural_width(CSSPixels::nearest_value_for(first_available_font().width(static_cast<HTML::HTMLInputElement&>(dom_node()).value())));
set_natural_height(font().pixel_size_rounded_up()); set_natural_height(first_available_font().pixel_size_rounded_up());
} }
} }

View file

@ -1720,7 +1720,7 @@ CSSPixels FormattingContext::box_baseline(Box const& box) const
return box.computed_values().font_size(); return box.computed_values().font_size();
case CSS::VerticalAlign::TextBottom: case CSS::VerticalAlign::TextBottom:
// TextTop: Align the bottom of the box with the bottom of the parent's content area (see 10.6.1). // TextTop: Align the bottom of the box with the bottom of the parent's content area (see 10.6.1).
return box_state.content_height() - CSSPixels::nearest_value_for(box.containing_block()->font().pixel_metrics().descent * 2); return box_state.content_height() - CSSPixels::nearest_value_for(box.containing_block()->first_available_font().pixel_metrics().descent * 2);
default: default:
break; break;
} }

View file

@ -190,14 +190,14 @@ Optional<InlineLevelIterator::Item> InlineLevelIterator::next_without_lookahead(
Vector<Gfx::DrawGlyphOrEmoji> glyph_run; Vector<Gfx::DrawGlyphOrEmoji> glyph_run;
float glyph_run_width = 0; float glyph_run_width = 0;
Gfx::for_each_glyph_position( Gfx::for_each_glyph_position(
{ 0, 0 }, chunk.view, text_node.font(), [&](Gfx::DrawGlyphOrEmoji const& glyph_or_emoji) { { 0, 0 }, chunk.view, text_node.first_available_font(), [&](Gfx::DrawGlyphOrEmoji const& glyph_or_emoji) {
glyph_run.append(glyph_or_emoji); glyph_run.append(glyph_or_emoji);
return IterationDecision::Continue; return IterationDecision::Continue;
}, },
Gfx::IncludeLeftBearing::No, glyph_run_width); Gfx::IncludeLeftBearing::No, glyph_run_width);
if (!m_text_node_context->is_last_chunk) if (!m_text_node_context->is_last_chunk)
glyph_run_width += text_node.font().glyph_spacing(); glyph_run_width += text_node.first_available_font().glyph_spacing();
CSSPixels chunk_width = CSSPixels::nearest_value_for(glyph_run_width); CSSPixels chunk_width = CSSPixels::nearest_value_for(glyph_run_width);

View file

@ -70,7 +70,8 @@ void LineBox::trim_trailing_whitespace()
if (!is_ascii_space(last_character)) if (!is_ascii_space(last_character))
break; break;
int last_character_width = last_fragment->layout_node().font().glyph_width(last_character); // FIXME: Use fragment's glyph run to determine the width of the last character.
int last_character_width = last_fragment->layout_node().first_available_font().glyph_width(last_character);
last_fragment->m_length -= 1; last_fragment->m_length -= 1;
last_fragment->set_width(last_fragment->width() - last_character_width); last_fragment->set_width(last_fragment->width() - last_character_width);
m_width -= last_character_width; m_width -= last_character_width;

View file

@ -47,7 +47,7 @@ int LineBoxFragment::text_index_at(CSSPixels x) const
if (!is<TextNode>(layout_node())) if (!is<TextNode>(layout_node()))
return 0; return 0;
auto& layout_text = verify_cast<TextNode>(layout_node()); auto& layout_text = verify_cast<TextNode>(layout_node());
auto& font = layout_text.font(); auto& font = layout_text.first_available_font();
Utf8View view(text()); Utf8View view(text());
CSSPixels relative_x = x - absolute_x(); CSSPixels relative_x = x - absolute_x();

View file

@ -192,7 +192,7 @@ void LineBuilder::update_last_line()
} }
auto strut_baseline = [&] { auto strut_baseline = [&] {
auto& font = m_context.containing_block().font(); auto& font = m_context.containing_block().first_available_font();
auto const line_height = m_context.containing_block().line_height(); auto const line_height = m_context.containing_block().line_height();
auto const font_metrics = font.pixel_metrics(); auto const font_metrics = font.pixel_metrics();
auto const typographic_height = CSSPixels::nearest_value_for(font_metrics.ascent + font_metrics.descent); auto const typographic_height = CSSPixels::nearest_value_for(font_metrics.ascent + font_metrics.descent);
@ -204,7 +204,7 @@ void LineBuilder::update_last_line()
auto line_box_baseline = [&] { auto line_box_baseline = [&] {
CSSPixels line_box_baseline = strut_baseline; CSSPixels line_box_baseline = strut_baseline;
for (auto& fragment : line_box.fragments()) { for (auto& fragment : line_box.fragments()) {
auto const& font = fragment.layout_node().font(); auto const& font = fragment.layout_node().first_available_font();
auto const line_height = fragment.layout_node().line_height(); auto const line_height = fragment.layout_node().line_height();
auto const font_metrics = font.pixel_metrics(); auto const font_metrics = font.pixel_metrics();
auto const typographic_height = CSSPixels::nearest_value_for(font_metrics.ascent + font_metrics.descent); auto const typographic_height = CSSPixels::nearest_value_for(font_metrics.ascent + font_metrics.descent);
@ -301,7 +301,7 @@ void LineBuilder::update_last_line()
top_of_inline_box = (fragment.offset().y() - fragment_box_state.margin_box_top()); top_of_inline_box = (fragment.offset().y() - fragment_box_state.margin_box_top());
bottom_of_inline_box = (fragment.offset().y() + fragment_box_state.content_height() + fragment_box_state.margin_box_bottom()); bottom_of_inline_box = (fragment.offset().y() + fragment_box_state.content_height() + fragment_box_state.margin_box_bottom());
} else { } else {
auto font_metrics = fragment.layout_node().font().pixel_metrics(); auto font_metrics = fragment.layout_node().first_available_font().pixel_metrics();
auto typographic_height = CSSPixels::nearest_value_for(font_metrics.ascent + font_metrics.descent); auto typographic_height = CSSPixels::nearest_value_for(font_metrics.ascent + font_metrics.descent);
auto leading = fragment.layout_node().line_height() - typographic_height; auto leading = fragment.layout_node().line_height() - typographic_height;
auto half_leading = leading / 2; auto half_leading = leading / 2;

View file

@ -349,7 +349,7 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
// NOTE: We have to be careful that font-related properties get set in the right order. // NOTE: We have to be careful that font-related properties get set in the right order.
// m_font is used by Length::to_px() when resolving sizes against this layout node. // m_font is used by Length::to_px() when resolving sizes against this layout node.
// That's why it has to be set before everything else. // That's why it has to be set before everything else.
m_font = computed_style.computed_font(); m_font_list = computed_style.computed_font_list();
computed_values.set_font_size(computed_style.property(CSS::PropertyID::FontSize)->as_length().length().to_px(*this)); computed_values.set_font_size(computed_style.property(CSS::PropertyID::FontSize)->as_length().length().to_px(*this));
computed_values.set_font_weight(round_to<int>(computed_style.property(CSS::PropertyID::FontWeight)->as_number().number())); computed_values.set_font_weight(round_to<int>(computed_style.property(CSS::PropertyID::FontWeight)->as_number().number()));
m_line_height = computed_style.line_height(*this); m_line_height = computed_style.line_height(*this);
@ -930,7 +930,7 @@ JS::NonnullGCPtr<NodeWithStyle> NodeWithStyle::create_anonymous_wrapper() const
{ {
auto wrapper = heap().allocate_without_realm<BlockContainer>(const_cast<DOM::Document&>(document()), nullptr, m_computed_values.clone_inherited_values()); auto wrapper = heap().allocate_without_realm<BlockContainer>(const_cast<DOM::Document&>(document()), nullptr, m_computed_values.clone_inherited_values());
static_cast<CSS::MutableComputedValues&>(wrapper->m_computed_values).set_display(CSS::Display(CSS::DisplayOutside::Block, CSS::DisplayInside::Flow)); static_cast<CSS::MutableComputedValues&>(wrapper->m_computed_values).set_display(CSS::Display(CSS::DisplayOutside::Block, CSS::DisplayInside::Flow));
wrapper->m_font = m_font; wrapper->m_font_list = m_font_list;
wrapper->m_line_height = m_line_height; wrapper->m_line_height = m_line_height;
return *wrapper; return *wrapper;
} }

View file

@ -143,7 +143,8 @@ public:
bool can_contain_boxes_with_position_absolute() const; bool can_contain_boxes_with_position_absolute() const;
Gfx::Font const& font() const; Gfx::FontCascadeList const& font_list() const;
Gfx::Font const& first_available_font() const;
Gfx::Font const& scaled_font(PaintContext&) const; Gfx::Font const& scaled_font(PaintContext&) const;
Gfx::Font const& scaled_font(float scale_factor) const; Gfx::Font const& scaled_font(float scale_factor) const;
@ -217,10 +218,11 @@ public:
void apply_style(const CSS::StyleProperties&); void apply_style(const CSS::StyleProperties&);
Gfx::Font const& font() const { return *m_font; } Gfx::Font const& first_available_font() const;
Gfx::FontCascadeList const& font_list() const { return *m_font_list; }
CSSPixels line_height() const { return m_line_height; } CSSPixels line_height() const { return m_line_height; }
void set_line_height(CSSPixels line_height) { m_line_height = line_height; } void set_line_height(CSSPixels line_height) { m_line_height = line_height; }
void set_font(Gfx::Font const& font) { m_font = font; } void set_font_list(Gfx::FontCascadeList const& font_list) { m_font_list = font_list; }
Vector<CSS::BackgroundLayerData> const& background_layers() const { return computed_values().background_layers(); } Vector<CSS::BackgroundLayerData> const& background_layers() const { return computed_values().background_layers(); }
const CSS::AbstractImageStyleValue* list_style_image() const { return m_list_style_image; } const CSS::AbstractImageStyleValue* list_style_image() const { return m_list_style_image; }
@ -238,7 +240,7 @@ private:
void reset_table_box_computed_values_used_by_wrapper_to_init_values(); void reset_table_box_computed_values_used_by_wrapper_to_init_values();
CSS::ComputedValues m_computed_values; CSS::ComputedValues m_computed_values;
RefPtr<Gfx::Font const> m_font; RefPtr<Gfx::FontCascadeList const> m_font_list;
CSSPixels m_line_height { 0 }; CSSPixels m_line_height { 0 };
RefPtr<CSS::AbstractImageStyleValue const> m_list_style_image; RefPtr<CSS::AbstractImageStyleValue const> m_list_style_image;
}; };
@ -275,13 +277,20 @@ inline bool Node::has_style_or_parent_with_style() const
return m_has_style || (parent() != nullptr && parent()->has_style_or_parent_with_style()); return m_has_style || (parent() != nullptr && parent()->has_style_or_parent_with_style());
} }
inline Gfx::Font const& Node::font() const inline Gfx::Font const& Node::first_available_font() const
{ {
VERIFY(has_style_or_parent_with_style()); VERIFY(has_style_or_parent_with_style());
if (m_has_style) if (m_has_style)
return static_cast<NodeWithStyle const*>(this)->font(); return static_cast<NodeWithStyle const*>(this)->first_available_font();
return parent()->font(); return parent()->first_available_font();
}
inline Gfx::FontCascadeList const& Node::font_list() const
{
VERIFY(has_style_or_parent_with_style());
if (m_has_style)
return static_cast<NodeWithStyle const*>(this)->font_list();
return parent()->font_list();
} }
inline Gfx::Font const& Node::scaled_font(PaintContext& context) const inline Gfx::Font const& Node::scaled_font(PaintContext& context) const
@ -291,7 +300,7 @@ inline Gfx::Font const& Node::scaled_font(PaintContext& context) const
inline Gfx::Font const& Node::scaled_font(float scale_factor) const inline Gfx::Font const& Node::scaled_font(float scale_factor) const
{ {
return document().style_computer().font_cache().scaled_font(font(), scale_factor); return document().style_computer().font_cache().scaled_font(first_available_font(), scale_factor);
} }
inline const CSS::ImmutableComputedValues& Node::computed_values() const inline const CSS::ImmutableComputedValues& Node::computed_values() const
@ -322,4 +331,12 @@ inline NodeWithStyle* Node::parent()
return static_cast<NodeWithStyle*>(TreeNode<Node>::parent()); return static_cast<NodeWithStyle*>(TreeNode<Node>::parent());
} }
inline Gfx::Font const& NodeWithStyle::first_available_font() const
{
// https://drafts.csswg.org/css-fonts/#first-available-font
// FIXME: Should be be the first font for which the character U+0020 (space) instead of
// any first font in the list
return m_font_list->first();
}
} }

View file

@ -222,7 +222,7 @@ void SVGFormattingContext::run(Box const& box, LayoutMode layout_mode, Available
} else if (is<SVGTextBox>(descendant)) { } else if (is<SVGTextBox>(descendant)) {
auto& text_element = static_cast<SVG::SVGTextPositioningElement&>(dom_node); auto& text_element = static_cast<SVG::SVGTextPositioningElement&>(dom_node);
auto& font = graphics_box.font(); auto& font = graphics_box.first_available_font();
auto text_contents = text_element.text_contents(); auto text_contents = text_element.text_contents();
Utf8View text_utf8 { text_contents }; Utf8View text_utf8 { text_contents };
auto text_width = font.width(text_utf8); auto text_width = font.width(text_utf8);

View file

@ -427,7 +427,7 @@ ErrorOr<void> TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::
auto content_box_computed_values = parent.computed_values().clone_inherited_values(); auto content_box_computed_values = parent.computed_values().clone_inherited_values();
auto content_box_wrapper = parent.heap().template allocate_without_realm<BlockContainer>(parent.document(), nullptr, move(content_box_computed_values)); auto content_box_wrapper = parent.heap().template allocate_without_realm<BlockContainer>(parent.document(), nullptr, move(content_box_computed_values));
content_box_wrapper->set_font(parent.font()); content_box_wrapper->set_font_list(parent.font_list());
content_box_wrapper->set_line_height(parent.line_height()); content_box_wrapper->set_line_height(parent.line_height());
content_box_wrapper->set_children_are_inline(parent.children_are_inline()); content_box_wrapper->set_children_are_inline(parent.children_are_inline());
@ -630,7 +630,7 @@ static void wrap_in_anonymous(Vector<JS::Handle<Node>>& sequence, Node* nearest_
} }
wrapper->set_children_are_inline(parent.children_are_inline()); wrapper->set_children_are_inline(parent.children_are_inline());
wrapper->set_line_height(parent.line_height()); wrapper->set_line_height(parent.line_height());
wrapper->set_font(parent.font()); wrapper->set_font_list(parent.font_list());
if (nearest_sibling) if (nearest_sibling)
parent.insert_before(*wrapper, *nearest_sibling); parent.insert_before(*wrapper, *nearest_sibling);
else else
@ -717,7 +717,7 @@ Vector<JS::Handle<Box>> TreeBuilder::generate_missing_parents(NodeWithStyle& roo
parent.remove_child(*table_box); parent.remove_child(*table_box);
wrapper->append_child(*table_box); wrapper->append_child(*table_box);
wrapper->set_font(parent.font()); wrapper->set_font_list(parent.font_list());
wrapper->set_line_height(parent.line_height()); wrapper->set_line_height(parent.line_height());
if (nearest_sibling) if (nearest_sibling)
@ -753,7 +753,7 @@ static void fixup_row(Box& row_box, TableGrid const& table_grid, size_t row_inde
// Ensure that the cell (with zero content height) will have the same height as the row by setting vertical-align to middle. // Ensure that the cell (with zero content height) will have the same height as the row by setting vertical-align to middle.
cell_computed_values.set_vertical_align(CSS::VerticalAlign::Middle); cell_computed_values.set_vertical_align(CSS::VerticalAlign::Middle);
auto cell_box = row_box.heap().template allocate_without_realm<BlockContainer>(row_box.document(), nullptr, cell_computed_values); auto cell_box = row_box.heap().template allocate_without_realm<BlockContainer>(row_box.document(), nullptr, cell_computed_values);
cell_box->set_font(row_box.font()); cell_box->set_font_list(row_box.font_list());
cell_box->set_line_height(row_box.line_height()); cell_box->set_line_height(row_box.line_height());
row_box.append_child(cell_box); row_box.append_child(cell_box);
} }

View file

@ -65,7 +65,7 @@ void ButtonPaintable::paint(PaintContext& context, PaintPhase phase) const
painter.draw_text( painter.draw_text(
text_rect.to_type<int>(), text_rect.to_type<int>(),
static_cast<HTML::HTMLInputElement const&>(dom_node).value(), static_cast<HTML::HTMLInputElement const&>(dom_node).value(),
document().style_computer().font_cache().scaled_font(layout_box().font(), context.device_pixels_per_css_pixel()), document().style_computer().font_cache().scaled_font(layout_box().first_available_font(), context.device_pixels_per_css_pixel()),
Gfx::TextAlignment::Center, Gfx::TextAlignment::Center,
computed_values().color()); computed_values().color());
painter.restore(); painter.restore();

View file

@ -531,7 +531,7 @@ static void paint_cursor_if_needed(PaintContext& context, Layout::TextNode const
auto fragment_rect = fragment.absolute_rect(); auto fragment_rect = fragment.absolute_rect();
CSSPixelRect cursor_rect { CSSPixelRect cursor_rect {
fragment_rect.x() + CSSPixels::nearest_value_for(text_node.font().width(fragment.text().substring_view(0, text_node.browsing_context().cursor_position()->offset() - fragment.start()))), fragment_rect.x() + CSSPixels::nearest_value_for(text_node.first_available_font().width(fragment.text().substring_view(0, text_node.browsing_context().cursor_position()->offset() - fragment.start()))),
fragment_rect.top(), fragment_rect.top(),
1, 1,
fragment_rect.height() fragment_rect.height()
@ -545,7 +545,7 @@ static void paint_cursor_if_needed(PaintContext& context, Layout::TextNode const
static void paint_text_decoration(PaintContext& context, Layout::Node const& text_node, Layout::LineBoxFragment const& fragment) static void paint_text_decoration(PaintContext& context, Layout::Node const& text_node, Layout::LineBoxFragment const& fragment)
{ {
auto& painter = context.recording_painter(); auto& painter = context.recording_painter();
auto& font = fragment.layout_node().font(); auto& font = fragment.layout_node().first_available_font();
auto fragment_box = fragment.absolute_rect(); auto fragment_box = fragment.absolute_rect();
CSSPixels glyph_height = CSSPixels::nearest_value_for(font.pixel_size()); CSSPixels glyph_height = CSSPixels::nearest_value_for(font.pixel_size());
auto baseline = fragment_box.height() / 2 - (glyph_height + 4) / 2 + glyph_height; auto baseline = fragment_box.height() / 2 - (glyph_height + 4) / 2 + glyph_height;
@ -650,7 +650,7 @@ static void paint_text_fragment(PaintContext& context, Layout::TextNode const& t
} }
painter.draw_text_run(baseline_start.to_type<int>(), scaled_glyph_run, text_node.computed_values().color(), fragment_absolute_device_rect.to_type<int>()); painter.draw_text_run(baseline_start.to_type<int>(), scaled_glyph_run, text_node.computed_values().color(), fragment_absolute_device_rect.to_type<int>());
auto selection_rect = context.enclosing_device_rect(fragment.selection_rect(text_node.font())).to_type<int>(); auto selection_rect = context.enclosing_device_rect(fragment.selection_rect(text_node.first_available_font())).to_type<int>();
if (!selection_rect.is_empty()) { if (!selection_rect.is_empty()) {
painter.fill_rect(selection_rect, CSS::SystemColor::highlight()); painter.fill_rect(selection_rect, CSS::SystemColor::highlight());
RecordingPainterStateSaver saver(painter); RecordingPainterStateSaver saver(painter);
@ -720,7 +720,7 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
layer.spread_distance.to_px(layout_box()), layer.spread_distance.to_px(layout_box()),
ShadowPlacement::Outer); ShadowPlacement::Outer);
} }
context.recording_painter().set_font(fragment.layout_node().font()); context.recording_painter().set_font(fragment.layout_node().first_available_font());
paint_text_shadow(context, fragment, resolved_shadow_data); paint_text_shadow(context, fragment, resolved_shadow_data);
} }
} }