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

@@ -971,6 +971,12 @@ TextAnchor ComputedProperties::text_anchor() const
return keyword_to_text_anchor(value.to_keyword()).release_value();
}
Optional<BaselineMetric> ComputedProperties::dominant_baseline() const
{
auto const& value = property(PropertyID::DominantBaseline);
return keyword_to_baseline_metric(value.to_keyword());
}
TextAlign ComputedProperties::text_align() const
{
auto const& value = property(PropertyID::TextAlign);

View File

@@ -95,6 +95,7 @@ public:
ColorInterpolation color_interpolation() const;
PreferredColorScheme color_scheme(PreferredColorScheme, Optional<Vector<String> const&> document_supported_schemes) const;
TextAnchor text_anchor() const;
Optional<BaselineMetric> dominant_baseline() const;
TextAlign text_align() const;
TextJustify text_justify() const;
TextOverflow text_overflow() const;

View File

@@ -260,6 +260,7 @@ public:
static TransformBox transform_box() { return TransformBox::ViewBox; }
static TransformStyle transform_style() { return TransformStyle::Flat; }
static Direction direction() { return Direction::Ltr; }
static Optional<BaselineMetric> dominant_baseline() { return {}; }
static UnicodeBidi unicode_bidi() { return UnicodeBidi::Normal; }
static WritingMode writing_mode() { return WritingMode::HorizontalTb; }
static UserSelect user_select() { return UserSelect::Auto; }
@@ -594,6 +595,7 @@ public:
ObjectFit object_fit() const { return m_noninherited.object_fit; }
Position object_position() const { return m_noninherited.object_position; }
Direction direction() const { return m_inherited.direction; }
Optional<BaselineMetric> dominant_baseline() const { return m_inherited.dominant_baseline; }
UnicodeBidi unicode_bidi() const { return m_noninherited.unicode_bidi; }
WritingMode writing_mode() const { return m_inherited.writing_mode; }
UserSelect user_select() const { return m_noninherited.user_select; }
@@ -749,6 +751,7 @@ protected:
ListStyleType list_style_type { InitialValues::list_style_type() };
QuotesData quotes { InitialValues::quotes() };
Direction direction { InitialValues::direction() };
Optional<BaselineMetric> dominant_baseline { InitialValues::dominant_baseline() };
WritingMode writing_mode { InitialValues::writing_mode() };
FillRule fill_rule { InitialValues::fill_rule() };
StrokeLinecap stroke_linecap { InitialValues::stroke_linecap() };
@@ -1078,6 +1081,7 @@ public:
void set_object_fit(ObjectFit value) { m_noninherited.object_fit = value; }
void set_object_position(Position value) { m_noninherited.object_position = move(value); }
void set_direction(Direction value) { m_inherited.direction = value; }
void set_dominant_baseline(Optional<BaselineMetric> value) { m_inherited.dominant_baseline = value; }
void set_unicode_bidi(UnicodeBidi value) { m_noninherited.unicode_bidi = value; }
void set_writing_mode(WritingMode value) { m_inherited.writing_mode = value; }
void set_user_select(UserSelect value) { m_noninherited.user_select = value; }

View File

@@ -127,6 +127,16 @@
"padding-box",
"text"
],
"baseline-metric": [
"text-bottom",
"alphabetic",
"ideographic",
"middle",
"central",
"mathematical",
"hanging",
"text-top"
],
"border-collapse": [
"separate",
"collapse"

View File

@@ -130,6 +130,7 @@
"captiontext",
"cell",
"center",
"central",
"checkbox",
"circle",
"clip",
@@ -269,6 +270,7 @@
"inactivecaption",
"inactivecaptiontext",
"increasing",
"ideographic",
"incremental",
"infinite",
"infinity",
@@ -348,6 +350,7 @@
"match-source",
"math",
"math-auto",
"mathematical",
"max-content",
"medium",
"menu",

View File

@@ -1820,6 +1820,18 @@
"display-legacy"
]
},
"dominant-baseline": {
"animation-type": "discrete",
"inherited": true,
"initial": "auto",
"requires-computation": "never",
"valid-types": [
"baseline-metric"
],
"valid-identifiers": [
"auto"
]
},
"empty-cells": {
"animation-type": "discrete",
"inherited": true,

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);

View File

@@ -71,6 +71,7 @@ static ReadonlySpan<NamedPropertyID> attribute_style_properties()
NamedPropertyID(CSS::PropertyID::Cy, { SVG::TagNames::circle, SVG::TagNames::ellipse }),
NamedPropertyID(CSS::PropertyID::Direction),
NamedPropertyID(CSS::PropertyID::Display),
NamedPropertyID(CSS::PropertyID::DominantBaseline),
NamedPropertyID(CSS::PropertyID::FillOpacity),
NamedPropertyID(CSS::PropertyID::FillRule),
NamedPropertyID(CSS::PropertyID::Filter),

View File

@@ -0,0 +1,64 @@
Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children: not-inline
BlockContainer <html> at [0,0] [0+0+0 800 0+0+0] [0+0+0 416 0+0+0] [BFC] children: not-inline
BlockContainer <body> at [8,8] [8+0+0 784 0+0+8] [8+0+0 400 0+0+8] children: inline
frag 0 from SVGSVGBox start: 0, length: 0, rect: [8,8 400x400] baseline: 400
SVGSVGBox <svg> at [8,8] [0+0+0 400 0+0+0] [0+0+0 400 0+0+0] [SVG] children: inline
TextNode <#text> (not painted)
SVGGeometryBox <line> at [7.75,57.75] [0+0+0 400.5 0+0+0] [0+0+0 0.5 0+0+0] children: not-inline
TextNode <#text> (not painted)
SVGGeometryBox <line> at [7.75,107.75] [0+0+0 400.5 0+0+0] [0+0+0 0.5 0+0+0] children: not-inline
TextNode <#text> (not painted)
SVGGeometryBox <line> at [7.75,157.75] [0+0+0 400.5 0+0+0] [0+0+0 0.5 0+0+0] children: not-inline
TextNode <#text> (not painted)
SVGGeometryBox <line> at [7.75,207.75] [0+0+0 400.5 0+0+0] [0+0+0 0.5 0+0+0] children: not-inline
TextNode <#text> (not painted)
SVGGeometryBox <line> at [7.75,257.75] [0+0+0 400.5 0+0+0] [0+0+0 0.5 0+0+0] children: not-inline
TextNode <#text> (not painted)
SVGGeometryBox <line> at [7.75,307.75] [0+0+0 400.5 0+0+0] [0+0+0 0.5 0+0+0] children: not-inline
TextNode <#text> (not painted)
SVGGeometryBox <line> at [7.75,357.75] [0+0+0 400.5 0+0+0] [0+0+0 0.5 0+0+0] children: not-inline
TextNode <#text> (not painted)
SVGTextBox <text> at [18.171875,44.90625] [0+0+0 44.4375 0+0+0] [0+0+0 14 0+0+0] children: inline
TextNode <#text> (not painted)
TextNode <#text> (not painted)
SVGTextBox <text> at [18.171875,92.96875] [0+0+0 100.59375 0+0+0] [0+0+0 15.703125 0+0+0] children: inline
TextNode <#text> (not painted)
TextNode <#text> (not painted)
SVGTextBox <text> at [19.015625,150.90625] [0+0+0 68.890625 0+0+0] [0+0+0 14.03125 0+0+0] children: inline
TextNode <#text> (not painted)
TextNode <#text> (not painted)
SVGTextBox <text> at [19.3125,198.328125] [0+0+0 54.46875 0+0+0] [0+0+0 15.46875 0+0+0] children: inline
TextNode <#text> (not painted)
TextNode <#text> (not painted)
SVGTextBox <text> at [18.859375,255.75] [0+0+0 70.625 0+0+0] [0+0+0 17.515625 0+0+0] children: inline
TextNode <#text> (not painted)
TextNode <#text> (not painted)
SVGTextBox <text> at [19.265625,288.953125] [0+0+0 110.5 0+0+0] [0+0+0 17.515625 0+0+0] children: inline
TextNode <#text> (not painted)
TextNode <#text> (not painted)
SVGTextBox <text> at [19.3125,350.953125] [0+0+0 126.59375 0+0+0] [0+0+0 15.46875 0+0+0] children: inline
TextNode <#text> (not painted)
TextNode <#text> (not painted)
TextNode <#text> (not painted)
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x416]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x400]
SVGSVGPaintable (SVGSVGBox<svg>) [8,8 400x400] overflow: [8,8 400.25x400]
SVGPathPaintable (SVGGeometryBox<line>) [7.75,57.75 400.5x0.5]
SVGPathPaintable (SVGGeometryBox<line>) [7.75,107.75 400.5x0.5]
SVGPathPaintable (SVGGeometryBox<line>) [7.75,157.75 400.5x0.5]
SVGPathPaintable (SVGGeometryBox<line>) [7.75,207.75 400.5x0.5]
SVGPathPaintable (SVGGeometryBox<line>) [7.75,257.75 400.5x0.5]
SVGPathPaintable (SVGGeometryBox<line>) [7.75,307.75 400.5x0.5]
SVGPathPaintable (SVGGeometryBox<line>) [7.75,357.75 400.5x0.5]
SVGPathPaintable (SVGTextBox<text>) [18.171875,44.90625 44.4375x14]
SVGPathPaintable (SVGTextBox<text>) [18.171875,92.96875 100.59375x15.703125]
SVGPathPaintable (SVGTextBox<text>) [19.015625,150.90625 68.890625x14.03125]
SVGPathPaintable (SVGTextBox<text>) [19.3125,198.328125 54.46875x15.46875]
SVGPathPaintable (SVGTextBox<text>) [18.859375,255.75 70.625x17.515625]
SVGPathPaintable (SVGTextBox<text>) [19.265625,288.953125 110.5x17.515625]
SVGPathPaintable (SVGTextBox<text>) [19.3125,350.953125 126.59375x15.46875]
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
SC for BlockContainer<HTML> [0,0 800x416] [children: 0] (z-index: auto)

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<svg viewBox="0 0 400 400" width="400" height="400" xmlns="http://www.w3.org/2000/svg">
<line x1="0" y1="50" x2="400" y2="50" stroke="gray" stroke-width="0.5"/>
<line x1="0" y1="100" x2="400" y2="100" stroke="gray" stroke-width="0.5"/>
<line x1="0" y1="150" x2="400" y2="150" stroke="gray" stroke-width="0.5"/>
<line x1="0" y1="200" x2="400" y2="200" stroke="gray" stroke-width="0.5"/>
<line x1="0" y1="250" x2="400" y2="250" stroke="gray" stroke-width="0.5"/>
<line x1="0" y1="300" x2="400" y2="300" stroke="gray" stroke-width="0.5"/>
<line x1="0" y1="350" x2="400" y2="350" stroke="gray" stroke-width="0.5"/>
<text x="10" y="50" font-size="20" font-family="SerenitySans" dominant-baseline="auto">auto</text>
<text x="10" y="100" font-size="20" font-family="SerenitySans" dominant-baseline="alphabetic">alphabetic</text>
<text x="10" y="150" font-size="20" font-family="SerenitySans" dominant-baseline="central">central</text>
<text x="10" y="200" font-size="20" font-family="SerenitySans" dominant-baseline="middle">middle</text>
<text x="10" y="250" font-size="20" font-family="SerenitySans" dominant-baseline="hanging">hanging</text>
<text x="10" y="300" font-size="20" font-family="SerenitySans" dominant-baseline="ideographic">ideographic</text>
<text x="10" y="350" font-size="20" font-family="SerenitySans" dominant-baseline="mathematical">mathematical</text>
</svg>

View File

@@ -12,6 +12,7 @@ All properties associated with getComputedStyle(document.body):
"color-scheme",
"cursor",
"direction",
"dominant-baseline",
"empty-cells",
"fill",
"fill-opacity",

View File

@@ -443,6 +443,8 @@ All supported properties and their default values exposed from CSSStylePropertie
'cy': '0px'
'direction': 'ltr'
'display': 'block'
'dominantBaseline': 'auto'
'dominant-baseline': 'auto'
'emptyCells': 'show'
'empty-cells': 'show'
'fill': 'rgb(0, 0, 0)'

View File

@@ -10,6 +10,7 @@ color-interpolation: srgb
color-scheme: normal
cursor: auto
direction: ltr
dominant-baseline: auto
empty-cells: show
fill: rgb(0, 0, 0)
fill-opacity: 1
@@ -102,7 +103,7 @@ background-position-x: 0%
background-position-y: 0%
background-repeat: repeat
background-size: auto
block-size: 1560px
block-size: 1575px
border-block-end-color: rgb(0, 0, 0)
border-block-end-style: none
border-block-end-width: 0px
@@ -188,7 +189,7 @@ grid-row-start: auto
grid-template-areas: none
grid-template-columns: none
grid-template-rows: none
height: 2850px
height: 2865px
inline-size: 784px
inset-block-end: auto
inset-block-start: auto

View File

@@ -1,8 +1,8 @@
Harness status: OK
Found 285 tests
Found 286 tests
281 Pass
282 Pass
4 Fail
Pass accent-color
Pass border-collapse
@@ -15,6 +15,7 @@ Pass color-interpolation
Pass color-scheme
Pass cursor
Pass direction
Pass dominant-baseline
Pass empty-cells
Pass fill
Pass fill-opacity

View File

@@ -0,0 +1,14 @@
Harness status: OK
Found 9 tests
9 Pass
Pass Property dominant-baseline value 'auto'
Pass Property dominant-baseline value 'text-bottom'
Pass Property dominant-baseline value 'alphabetic'
Pass Property dominant-baseline value 'ideographic'
Pass Property dominant-baseline value 'middle'
Pass Property dominant-baseline value 'central'
Pass Property dominant-baseline value 'mathematical'
Pass Property dominant-baseline value 'hanging'
Pass Property dominant-baseline value 'text-top'

View File

@@ -0,0 +1,9 @@
Harness status: OK
Found 4 tests
4 Pass
Pass e.style['dominant-baseline'] = "normal" should not set the property value
Pass e.style['dominant-baseline'] = "none" should not set the property value
Pass e.style['dominant-baseline'] = "alphabetic, ideographic" should not set the property value
Pass e.style['dominant-baseline'] = "middle central" should not set the property value

View File

@@ -0,0 +1,14 @@
Harness status: OK
Found 9 tests
9 Pass
Pass e.style['dominant-baseline'] = "auto" should set the property value
Pass e.style['dominant-baseline'] = "text-bottom" should set the property value
Pass e.style['dominant-baseline'] = "alphabetic" should set the property value
Pass e.style['dominant-baseline'] = "ideographic" should set the property value
Pass e.style['dominant-baseline'] = "middle" should set the property value
Pass e.style['dominant-baseline'] = "central" should set the property value
Pass e.style['dominant-baseline'] = "mathematical" should set the property value
Pass e.style['dominant-baseline'] = "hanging" should set the property value
Pass e.style['dominant-baseline'] = "text-top" should set the property value

View File

@@ -1,8 +1,8 @@
Harness status: OK
Found 48 tests
Found 49 tests
48 Pass
49 Pass
Pass clip-path presentation attribute supported on an irrelevant element
Pass clip-rule presentation attribute supported on an irrelevant element
Pass color presentation attribute supported on an irrelevant element
@@ -10,6 +10,7 @@ Pass color-interpolation presentation attribute supported on an irrelevant eleme
Pass cursor presentation attribute supported on an irrelevant element
Pass direction presentation attribute supported on an irrelevant element
Pass display presentation attribute supported on an irrelevant element
Pass dominant-baseline presentation attribute supported on an irrelevant element
Pass fill presentation attribute supported on an irrelevant element
Pass fill-opacity presentation attribute supported on an irrelevant element
Pass fill-rule presentation attribute supported on an irrelevant element

View File

@@ -1,8 +1,8 @@
Harness status: OK
Found 58 tests
Found 59 tests
57 Pass
58 Pass
1 Fail
Pass clip-path presentation attribute supported on a relevant element
Pass clip-rule presentation attribute supported on a relevant element
@@ -13,6 +13,7 @@ Pass cx presentation attribute supported on a relevant element
Pass cy presentation attribute supported on a relevant element
Pass direction presentation attribute supported on a relevant element
Pass display presentation attribute supported on a relevant element
Pass dominant-baseline presentation attribute supported on a relevant element
Pass fill presentation attribute supported on a relevant element
Pass fill-opacity presentation attribute supported on a relevant element
Pass fill-rule presentation attribute supported on a relevant element

View File

@@ -1,8 +1,8 @@
Harness status: OK
Found 48 tests
Found 49 tests
48 Pass
49 Pass
Pass clip-path presentation attribute supported on an unknown SVG element
Pass clip-rule presentation attribute supported on an unknown SVG element
Pass color presentation attribute supported on an unknown SVG element
@@ -10,6 +10,7 @@ Pass color-interpolation presentation attribute supported on an unknown SVG elem
Pass cursor presentation attribute supported on an unknown SVG element
Pass direction presentation attribute supported on an unknown SVG element
Pass display presentation attribute supported on an unknown SVG element
Pass dominant-baseline presentation attribute supported on an unknown SVG element
Pass fill presentation attribute supported on an unknown SVG element
Pass fill-opacity presentation attribute supported on an unknown SVG element
Pass fill-rule presentation attribute supported on an unknown SVG element

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Inline Layout: getComputedStyle().dominantBaseline</title>
<link rel="help" href="https://drafts.csswg.org/css-inline-3/#dominant-baseline-property">
<meta name="assert" content="dominant-baseline computed value is as specified.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/computed-testcommon.js"></script>
</head>
<body>
<div id="target"></div>
<script>
test_computed_value("dominant-baseline", "auto");
test_computed_value("dominant-baseline", "text-bottom");
test_computed_value("dominant-baseline", "alphabetic");
test_computed_value("dominant-baseline", "ideographic");
test_computed_value("dominant-baseline", "middle");
test_computed_value("dominant-baseline", "central");
test_computed_value("dominant-baseline", "mathematical");
test_computed_value("dominant-baseline", "hanging");
test_computed_value("dominant-baseline", "text-top");
</script>
</body>
</html>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Inline Layout: parsing dominant-baseline with invalid values</title>
<link rel="help" href="https://drafts.csswg.org/css-inline-3/#dominant-baseline-property">
<meta name="assert" content="dominant-baseline supports only the grammar 'auto | text-bottom | alphabetic | ideographic | middle | central | mathematical | hanging | text-top'.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/parsing-testcommon.js"></script>
</head>
<body>
<script>
test_invalid_value("dominant-baseline", "normal");
test_invalid_value("dominant-baseline", "none");
test_invalid_value("dominant-baseline", "alphabetic, ideographic");
test_invalid_value("dominant-baseline", "middle central");
</script>
</body>
</html>

View File

@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Inline Layout: parsing dominant-baseline with valid values</title>
<link rel="help" href="https://drafts.csswg.org/css-inline-3/#dominant-baseline-property">
<meta name="assert" content="dominant-baseline supports the full grammar 'auto | text-bottom | alphabetic | ideographic | middle | central | mathematical | hanging | text-top'.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/parsing-testcommon.js"></script>
</head>
<body>
<script>
test_valid_value("dominant-baseline", "auto");
test_valid_value("dominant-baseline", "text-bottom");
test_valid_value("dominant-baseline", "alphabetic");
test_valid_value("dominant-baseline", "ideographic");
test_valid_value("dominant-baseline", "middle");
test_valid_value("dominant-baseline", "central");
test_valid_value("dominant-baseline", "mathematical");
test_valid_value("dominant-baseline", "hanging");
test_valid_value("dominant-baseline", "text-top");
</script>
</body>
</html>