Serenity Operating System
at master 224 lines 10 kB view raw
1/* 2 * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibWeb/Bindings/Intrinsics.h> 8#include <LibWeb/SVG/AttributeNames.h> 9#include <LibWeb/SVG/AttributeParser.h> 10#include <LibWeb/SVG/SVGAnimatedLength.h> 11#include <LibWeb/SVG/SVGLength.h> 12#include <LibWeb/SVG/SVGRectElement.h> 13 14namespace Web::SVG { 15 16SVGRectElement::SVGRectElement(DOM::Document& document, DOM::QualifiedName qualified_name) 17 : SVGGeometryElement(document, qualified_name) 18{ 19} 20 21JS::ThrowCompletionOr<void> SVGRectElement::initialize(JS::Realm& realm) 22{ 23 MUST_OR_THROW_OOM(Base::initialize(realm)); 24 set_prototype(&Bindings::ensure_web_prototype<Bindings::SVGRectElementPrototype>(realm, "SVGRectElement")); 25 26 return {}; 27} 28 29void SVGRectElement::parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value) 30{ 31 SVGGeometryElement::parse_attribute(name, value); 32 33 if (name == SVG::AttributeNames::x) { 34 m_x = AttributeParser::parse_coordinate(value); 35 m_path.clear(); 36 } else if (name == SVG::AttributeNames::y) { 37 m_y = AttributeParser::parse_coordinate(value); 38 m_path.clear(); 39 } else if (name == SVG::AttributeNames::width) { 40 m_width = AttributeParser::parse_positive_length(value); 41 m_path.clear(); 42 } else if (name == SVG::AttributeNames::height) { 43 m_height = AttributeParser::parse_positive_length(value); 44 m_path.clear(); 45 } else if (name == SVG::AttributeNames::rx) { 46 m_radius_x = AttributeParser::parse_length(value); 47 m_path.clear(); 48 } else if (name == SVG::AttributeNames::ry) { 49 m_radius_y = AttributeParser::parse_length(value); 50 m_path.clear(); 51 } 52} 53 54Gfx::Path& SVGRectElement::get_path() 55{ 56 if (m_path.has_value()) 57 return m_path.value(); 58 59 float width = m_width.value_or(0); 60 float height = m_height.value_or(0); 61 float x = m_x.value_or(0); 62 float y = m_y.value_or(0); 63 64 Gfx::Path path; 65 // If width or height is zero, rendering is disabled. 66 if (width == 0 && height == 0) { 67 m_path = move(path); 68 return m_path.value(); 69 } 70 71 auto corner_radii = calculate_used_corner_radius_values(); 72 float rx = corner_radii.width(); 73 float ry = corner_radii.height(); 74 75 // 1. perform an absolute moveto operation to location (x+rx,y); 76 path.move_to({ x + rx, y }); 77 78 // 2, perform an absolute horizontal lineto with parameter x+width-rx; 79 path.horizontal_line_to(x + width - rx); 80 81 // 3. if both rx and ry are greater than zero, 82 // perform an absolute elliptical arc operation to coordinate (x+width,y+ry), 83 // where rx and ry are used as the equivalent parameters to the elliptical arc command, 84 // the x-axis-rotation and large-arc-flag are set to zero, 85 // the sweep-flag is set to one; 86 double x_axis_rotation = 0; 87 bool large_arc_flag = false; 88 bool sweep_flag = true; 89 if (rx > 0 && ry > 0) 90 path.elliptical_arc_to({ x + width, y + ry }, corner_radii, x_axis_rotation, large_arc_flag, sweep_flag); 91 92 // 4. perform an absolute vertical lineto parameter y+height-ry; 93 path.vertical_line_to(y + height - ry); 94 95 // 5. if both rx and ry are greater than zero, 96 // perform an absolute elliptical arc operation to coordinate (x+width-rx,y+height), 97 // using the same parameters as previously; 98 if (rx > 0 && ry > 0) 99 path.elliptical_arc_to({ x + width - rx, y + height }, corner_radii, x_axis_rotation, large_arc_flag, sweep_flag); 100 101 // 6. perform an absolute horizontal lineto parameter x+rx; 102 path.horizontal_line_to(x + rx); 103 104 // 7. if both rx and ry are greater than zero, 105 // perform an absolute elliptical arc operation to coordinate (x,y+height-ry), 106 // using the same parameters as previously; 107 if (rx > 0 && ry > 0) 108 path.elliptical_arc_to({ x, y + height - ry }, corner_radii, x_axis_rotation, large_arc_flag, sweep_flag); 109 110 // 8. perform an absolute vertical lineto parameter y+ry 111 path.vertical_line_to(y + ry); 112 113 // 9. if both rx and ry are greater than zero, 114 // perform an absolute elliptical arc operation with a segment-completing close path operation, 115 // using the same parameters as previously. 116 if (rx > 0 && ry > 0) 117 path.elliptical_arc_to({ x + rx, y }, corner_radii, x_axis_rotation, large_arc_flag, sweep_flag); 118 119 m_path = move(path); 120 return m_path.value(); 121} 122 123Gfx::FloatSize SVGRectElement::calculate_used_corner_radius_values() const 124{ 125 // 1. Let rx and ry be length values. 126 float rx = 0; 127 float ry = 0; 128 129 // 2. If neither ‘rx’ nor ‘ry’ are properly specified, then set both rx and ry to 0. (This will result in square corners.) 130 if (!m_radius_x.has_value() && !m_radius_y.has_value()) { 131 rx = 0; 132 ry = 0; 133 } 134 // 3. Otherwise, if a properly specified value is provided for ‘rx’, but not for ‘ry’, then set both rx and ry to the value of ‘rx’. 135 else if (m_radius_x.has_value()) { 136 rx = m_radius_x.value(); 137 ry = m_radius_x.value(); 138 } 139 // 4. Otherwise, if a properly specified value is provided for ‘ry’, but not for ‘rx’, then set both rx and ry to the value of ‘ry’. 140 else if (m_radius_y.has_value()) { 141 rx = m_radius_y.value(); 142 ry = m_radius_y.value(); 143 } 144 // 5. Otherwise, both ‘rx’ and ‘ry’ were specified properly. Set rx to the value of ‘rx’ and ry to the value of ‘ry’. 145 else { 146 rx = m_radius_x.value(); 147 ry = m_radius_y.value(); 148 } 149 150 // 6. If rx is greater than half of ‘width’, then set rx to half of ‘width’. 151 auto half_width = m_width.value_or(0) / 2; 152 if (rx > half_width) 153 rx = half_width; 154 155 // 7. If ry is greater than half of ‘height’, then set ry to half of ‘height’. 156 auto half_height = m_height.value_or(0) / 2; 157 if (ry > half_height) 158 ry = half_height; 159 160 // 8. The effective values of ‘rx’ and ‘ry’ are rx and ry, respectively. 161 return { rx, ry }; 162} 163 164// https://www.w3.org/TR/SVG11/shapes.html#RectElementXAttribute 165JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::x() const 166{ 167 // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). 168 // FIXME: Create a proper animated value when animations are supported. 169 auto base_length = SVGLength::create(realm(), 0, m_x.value_or(0)).release_value_but_fixme_should_propagate_errors(); 170 auto anim_length = SVGLength::create(realm(), 0, m_x.value_or(0)).release_value_but_fixme_should_propagate_errors(); 171 return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)).release_value_but_fixme_should_propagate_errors(); 172} 173 174// https://www.w3.org/TR/SVG11/shapes.html#RectElementYAttribute 175JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::y() const 176{ 177 // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). 178 // FIXME: Create a proper animated value when animations are supported. 179 auto base_length = SVGLength::create(realm(), 0, m_y.value_or(0)).release_value_but_fixme_should_propagate_errors(); 180 auto anim_length = SVGLength::create(realm(), 0, m_y.value_or(0)).release_value_but_fixme_should_propagate_errors(); 181 return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)).release_value_but_fixme_should_propagate_errors(); 182} 183 184// https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute 185JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::width() const 186{ 187 // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). 188 // FIXME: Create a proper animated value when animations are supported. 189 auto base_length = SVGLength::create(realm(), 0, m_width.value_or(0)).release_value_but_fixme_should_propagate_errors(); 190 auto anim_length = SVGLength::create(realm(), 0, m_width.value_or(0)).release_value_but_fixme_should_propagate_errors(); 191 return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)).release_value_but_fixme_should_propagate_errors(); 192} 193 194// https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute 195JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::height() const 196{ 197 // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). 198 // FIXME: Create a proper animated value when animations are supported. 199 auto base_length = SVGLength::create(realm(), 0, m_height.value_or(0)).release_value_but_fixme_should_propagate_errors(); 200 auto anim_length = SVGLength::create(realm(), 0, m_height.value_or(0)).release_value_but_fixme_should_propagate_errors(); 201 return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)).release_value_but_fixme_should_propagate_errors(); 202} 203 204// https://www.w3.org/TR/SVG11/shapes.html#RectElementRXAttribute 205JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::rx() const 206{ 207 // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). 208 // FIXME: Create a proper animated value when animations are supported. 209 auto base_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0)).release_value_but_fixme_should_propagate_errors(); 210 auto anim_length = SVGLength::create(realm(), 0, m_radius_x.value_or(0)).release_value_but_fixme_should_propagate_errors(); 211 return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)).release_value_but_fixme_should_propagate_errors(); 212} 213 214// https://www.w3.org/TR/SVG11/shapes.html#RectElementRYAttribute 215JS::NonnullGCPtr<SVGAnimatedLength> SVGRectElement::ry() const 216{ 217 // FIXME: Populate the unit type when it is parsed (0 here is "unknown"). 218 // FIXME: Create a proper animated value when animations are supported. 219 auto base_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0)).release_value_but_fixme_should_propagate_errors(); 220 auto anim_length = SVGLength::create(realm(), 0, m_radius_y.value_or(0)).release_value_but_fixme_should_propagate_errors(); 221 return SVGAnimatedLength::create(realm(), move(base_length), move(anim_length)).release_value_but_fixme_should_propagate_errors(); 222} 223 224}