…lines 1–17 unchanged (17 lines)
18#include <LibWeb/Layout/DominantBaseline.h>
19#include <LibWeb/Layout/SVGClipBox.h>
20#include <LibWeb/Layout/SVGFormattingContext.h>
21#include <LibWeb/Layout/SVGGeometryBox.h>
22#include <LibWeb/Layout/SVGImageBox.h>
23#include <LibWeb/Layout/SVGMaskBox.h>
24#include <LibWeb/Layout/SVGPatternBox.h>
25#include <LibWeb/Layout/Viewport.h>
26#include <LibWeb/SVG/SVGAElement.h>
27#include <LibWeb/SVG/SVGClipPathElement.h>
28#include <LibWeb/SVG/SVGForeignObjectElement.h>
29#include <LibWeb/SVG/SVGGElement.h>
30#include <LibWeb/SVG/SVGImageElement.h>
…lines 31–200 unchanged (170 lines)
201 // I'm not sure if this is good or bad, but our viewport transform logic depends on it.
202 svg_box_state.set_has_definite_width(true);
203 svg_box_state.set_has_definite_height(true);
204
205 auto* dom_node = context_box().dom_node();
206 VERIFY(dom_node);
207 Optional<SVG::ViewBox> active_view_box;
208 if (auto* svg_graphics_element = as_if<SVG::SVGGraphicsElement>(*dom_node))
209 active_view_box = svg_graphics_element->active_view_box();
210 else if (auto* svg_fit_to_view_box = as_if<SVG::SVGFitToViewBox>(*dom_node))
211 active_view_box = svg_fit_to_view_box->view_box();
212 // https://svgwg.org/svg2-draft/coords.html#ViewBoxAttribute
213 if (active_view_box.has_value()) {
214 if (active_view_box->width < 0 || active_view_box->height < 0) {
215 // A negative value for <width> or <height> is an error and invalidates the ‘viewBox’ attribute.
216 active_view_box = {};
217 } else if (active_view_box->width == 0 || active_view_box->height == 0) {
…lines 218–455 unchanged (238 lines)
456}
457
458Gfx::AffineTransform SVGFormattingContext::get_parent_svg_transform(SVGGraphicsBox const& box) const
459{
460 // Mask/clip boxes are transform boundaries — the target's transform is applied separately at paint time.
461 for (auto* ancestor = box.parent(); ancestor; ancestor = ancestor->parent()) {
462 if (is<SVGMaskBox>(*ancestor) || is<SVGClipBox>(*ancestor) || is<SVGPatternBox>(*ancestor))
463 return {};
464 if (auto const* svg_graphics_ancestor = as_if<SVGGraphicsBox>(*ancestor)) {
465 auto const& ancestor_state = m_state.get(*svg_graphics_ancestor);
466 if (ancestor_state.computed_svg_transforms().has_value())
467 return ancestor_state.computed_svg_transforms()->svg_transform();
468 }
…lines 469–491 unchanged (23 lines)
492 if (auto* mask_box = graphics_box.first_child_of_type<SVGMaskBox>())
493 layout_mask_or_clip(*mask_box);
494
495 if (auto* clip_box = graphics_box.first_child_of_type<SVGClipBox>())
496 layout_mask_or_clip(*clip_box);
497
498 graphics_box.for_each_child_of_type<SVGPatternBox>([&](auto const& pattern_box) {
499 layout_mask_or_clip(pattern_box);
500 return IterationDecision::Continue;
501 });
502}
503
504void SVGFormattingContext::layout_image_element(SVGImageBox const& image_box)
505{
506 auto& box_state = m_state.get_mutable(image_box);
507 VERIFY(box_state.computed_svg_transforms().has_value());
…lines 508–522 unchanged (15 lines)
523{
524 SVG::SVGUnits content_units {};
525 if (is<SVGMaskBox>(mask_or_clip))
526 content_units = static_cast<SVGMaskBox const&>(mask_or_clip).dom_node().mask_content_units();
527 else if (is<SVGClipBox>(mask_or_clip))
528 content_units = static_cast<SVGClipBox const&>(mask_or_clip).dom_node().clip_path_units();
529 else if (is<SVGPatternBox>(mask_or_clip))
530 content_units = static_cast<SVGPatternBox const&>(mask_or_clip).dom_node().pattern_content_units();
531 else
532 VERIFY_NOT_REACHED();
533 // FIXME: Somehow limit <clipPath> contents to: shape elements, <text>, and <use>.
534 auto& layout_state = m_state.get_mutable(mask_or_clip);
535 auto parent_viewbox_transform = m_current_viewbox_transform;
536
537 auto const* pattern_box = as_if<SVGPatternBox>(mask_or_clip);
538 if (pattern_box && pattern_box->dom_node().view_box().has_value()) {
539 auto const& pattern = pattern_box->dom_node();
540 if (pattern.pattern_units() == SVG::SVGUnits::UserSpaceOnUse) {
541 layout_state.set_content_width(CSSPixels::nearest_value_for(pattern.pattern_width().resolve_relative_to(m_viewport_size.width().to_float())));
542 layout_state.set_content_height(CSSPixels::nearest_value_for(pattern.pattern_height().resolve_relative_to(m_viewport_size.height().to_float())));
543 } else {
544 auto& parent_node_state = m_state.get(*mask_or_clip.parent());
545 layout_state.set_content_width(CSSPixels::nearest_value_for(pattern.pattern_width().value() * parent_node_state.content_width().to_double()));
546 layout_state.set_content_height(CSSPixels::nearest_value_for(pattern.pattern_height().value() * parent_node_state.content_height().to_double()));
547 parent_viewbox_transform = Gfx::AffineTransform {}.translate(parent_node_state.offset.to_type<float>());
548 }
549 } else if (content_units == SVG::SVGUnits::ObjectBoundingBox) {
550 auto& parent_node_state = m_state.get(*mask_or_clip.parent());
551 layout_state.set_content_width(parent_node_state.content_width());
552 layout_state.set_content_height(parent_node_state.content_height());
553 // https://svgwg.org/svg2-draft/pservers.html#PatternElementPatternContentUnitsAttribute
554 parent_viewbox_transform = Gfx::AffineTransform {}.translate(parent_node_state.offset.to_type<float>());
555 if (pattern_box)
556 parent_viewbox_transform.scale(parent_node_state.content_width().to_float(), parent_node_state.content_height().to_float());
557 } else {
558 layout_state.set_content_width(m_viewport_size.width());
559 layout_state.set_content_height(m_viewport_size.height());
560 }
561 // Pretend masks/clips are a viewport so we can scale the contents depending on the `contentUnits`.
562 SVGFormattingContext nested_context(m_state, m_layout_mode, mask_or_clip, this, parent_viewbox_transform);
…lines 563–566 unchanged (4 lines)
567
568void SVGFormattingContext::layout_container_element(SVGBox const& container)
569{
570 auto& box_state = m_state.get_mutable(container);
571 Gfx::BoundingBox<CSSPixels> bounding_box;
572 container.for_each_child_of_type<Box>([&](Box const& child) {
573 // Masks/clips/patterns do not change the bounding box of their parents.
574 if (is<SVGMaskBox>(child) || is<SVGClipBox>(child) || is<SVGPatternBox>(child))
575 return IterationDecision::Continue;
576 layout_svg_element(child);
577 auto& child_state = m_state.get(child);
578 bounding_box.add_point(child_state.offset);
579 bounding_box.add_point(child_state.offset.translated(child_state.content_width(), child_state.content_height()));
580 return IterationDecision::Continue;
…lines 581–591 unchanged (11 lines)