LibWeb: Implement dominant-baseline for SVG text

This property determines the default baseline used to align content
within the given box.
This commit is contained in:
Tim Ledbetter
2026-02-25 20:46:37 +00:00
committed by Jelle Raaijmakers
parent 90a211bf47
commit f05bc7c0cd
Notes: github-actions[bot] 2026-02-26 08:24:27 +00:00
25 changed files with 311 additions and 10 deletions

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2026, Tim Ledbetter <tim.ledbetter@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Font/Font.h>
#include <LibWeb/CSS/ComputedValues.h>
namespace Web::Layout {
// https://drafts.csswg.org/css-inline/#dominant-baseline-property
static float dominant_baseline_offset(CSS::BaselineMetric metric, Gfx::FontPixelMetrics const& font_metrics)
{
switch (metric) {
case CSS::BaselineMetric::Central:
return (font_metrics.ascent - font_metrics.descent) / 2;
case CSS::BaselineMetric::Middle:
return font_metrics.x_height / 2;
case CSS::BaselineMetric::Hanging:
// FIXME: Read the hanging baseline from the font's BASE table.
return font_metrics.ascent * 0.8f;
case CSS::BaselineMetric::Ideographic:
// FIXME: Read the ideographic baseline from the font's BASE table.
return -font_metrics.descent;
case CSS::BaselineMetric::Mathematical:
// FIXME: Read the math baseline from the font's BASE table.
return font_metrics.ascent * 0.5f;
case CSS::BaselineMetric::TextTop:
case CSS::BaselineMetric::TextBottom:
// FIXME: Support text-top and text-bottom.
case CSS::BaselineMetric::Alphabetic:
return 0;
}
VERIFY_NOT_REACHED();
}
static CSS::BaselineMetric resolve_dominant_baseline_metric(CSS::ComputedValues const& computed_values)
{
auto dominant_baseline = computed_values.dominant_baseline();
if (dominant_baseline.has_value())
return *dominant_baseline;
// https://drafts.csswg.org/css-inline/#valdef-dominant-baseline-auto
// Equivalent to alphabetic in horizontal writing modes and in vertical writing modes when text-orientation is
// sideways. Equivalent to central in vertical writing modes when text-orientation is mixed or upright.
// FIXME: Take text-orientation into account once it is implemented.
switch (computed_values.writing_mode()) {
case CSS::WritingMode::HorizontalTb:
case CSS::WritingMode::SidewaysRl:
case CSS::WritingMode::SidewaysLr:
return CSS::BaselineMetric::Alphabetic;
case CSS::WritingMode::VerticalRl:
case CSS::WritingMode::VerticalLr:
return CSS::BaselineMetric::Central;
}
VERIFY_NOT_REACHED();
}
}

View File

@@ -893,6 +893,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
computed_values.set_stop_opacity(computed_style.stop_opacity());
computed_values.set_text_anchor(computed_style.text_anchor());
computed_values.set_dominant_baseline(computed_style.dominant_baseline());
// FIXME: Support calc()
if (auto const& column_count = computed_style.property(CSS::PropertyID::ColumnCount); column_count.is_integer())

View File

@@ -15,6 +15,7 @@
#include <LibGfx/TextLayout.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/Layout/BlockFormattingContext.h>
#include <LibWeb/Layout/DominantBaseline.h>
#include <LibWeb/Layout/SVGClipBox.h>
#include <LibWeb/Layout/SVGFormattingContext.h>
#include <LibWeb/Layout/SVGGeometryBox.h>
@@ -381,6 +382,9 @@ Gfx::Path SVGFormattingContext::compute_path_for_text(SVGTextBox const& text_box
VERIFY_NOT_REACHED();
}
auto baseline_metric = resolve_dominant_baseline_metric(text_box.computed_values());
text_offset.translate_by(0, dominant_baseline_offset(baseline_metric, font.pixel_metrics()));
Gfx::Path path;
path.move_to(text_offset);
path.text(text_contents, font);