SVGDecodedImageData.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /*
  2. * Copyright (c) 2023, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGfx/Bitmap.h>
  7. #include <LibWeb/DOM/Document.h>
  8. #include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
  9. #include <LibWeb/HTML/BrowsingContext.h>
  10. #include <LibWeb/HTML/DocumentState.h>
  11. #include <LibWeb/HTML/NavigationParams.h>
  12. #include <LibWeb/HTML/Parser/HTMLParser.h>
  13. #include <LibWeb/HTML/TraversableNavigable.h>
  14. #include <LibWeb/Layout/Viewport.h>
  15. #include <LibWeb/Page/Page.h>
  16. #include <LibWeb/Painting/PaintContext.h>
  17. #include <LibWeb/Painting/ViewportPaintable.h>
  18. #include <LibWeb/SVG/SVGDecodedImageData.h>
  19. #include <LibWeb/SVG/SVGSVGElement.h>
  20. namespace Web::SVG {
  21. class SVGDecodedImageData::SVGPageClient final : public PageClient {
  22. public:
  23. explicit SVGPageClient(Page& host_page)
  24. : m_host_page(host_page)
  25. {
  26. }
  27. virtual ~SVGPageClient() override = default;
  28. Page& m_host_page;
  29. Page* m_svg_page { nullptr };
  30. virtual Page& page() override { return *m_svg_page; }
  31. virtual Page const& page() const override { return *m_svg_page; }
  32. virtual bool is_connection_open() const override { return false; }
  33. virtual Gfx::Palette palette() const override { return m_host_page.client().palette(); }
  34. virtual DevicePixelRect screen_rect() const override { return {}; }
  35. virtual double device_pixels_per_css_pixel() const override { return 1.0; }
  36. virtual CSS::PreferredColorScheme preferred_color_scheme() const override { return m_host_page.client().preferred_color_scheme(); }
  37. virtual void request_file(FileRequest) override { }
  38. virtual void paint(DevicePixelRect const&, Gfx::Bitmap&) override { }
  39. };
  40. ErrorOr<NonnullRefPtr<SVGDecodedImageData>> SVGDecodedImageData::create(Page& host_page, AK::URL const& url, ByteBuffer data)
  41. {
  42. auto page_client = make<SVGPageClient>(host_page);
  43. auto page = make<Page>(*page_client);
  44. page_client->m_svg_page = page.ptr();
  45. JS::NonnullGCPtr<HTML::Navigable> navigable = page->top_level_traversable();
  46. auto response = Fetch::Infrastructure::Response::create(navigable->vm());
  47. response->url_list().append(url);
  48. HTML::NavigationParams navigation_params {
  49. .id = {},
  50. .request = nullptr,
  51. .response = response,
  52. .origin = HTML::Origin {},
  53. .policy_container = HTML::PolicyContainer {},
  54. .final_sandboxing_flag_set = HTML::SandboxingFlagSet {},
  55. .cross_origin_opener_policy = HTML::CrossOriginOpenerPolicy {},
  56. .coop_enforcement_result = HTML::CrossOriginOpenerPolicyEnforcementResult {},
  57. .reserved_environment = {},
  58. .browsing_context = nullptr,
  59. .navigable = navigable,
  60. };
  61. auto document = DOM::Document::create_and_initialize(DOM::Document::Type::HTML, "text/html", navigation_params).release_value_but_fixme_should_propagate_errors();
  62. navigable->set_ongoing_navigation({});
  63. navigable->active_session_history_entry()->document_state->set_document(document);
  64. auto parser = HTML::HTMLParser::create_with_uncertain_encoding(document, data);
  65. parser->run(document->url());
  66. // Perform some DOM surgery to make the SVG root element be the first child of the Document.
  67. // FIXME: This is a huge hack until we figure out how to actually parse separate SVG files.
  68. auto* svg_root = document->body()->first_child_of_type<SVG::SVGSVGElement>();
  69. if (!svg_root)
  70. return Error::from_string_literal("SVGDecodedImageData: Invalid SVG input");
  71. svg_root->remove();
  72. document->remove_all_children();
  73. MUST(document->append_child(*svg_root));
  74. return adopt_nonnull_ref_or_enomem(new (nothrow) SVGDecodedImageData(move(page), move(page_client), move(document), move(svg_root)));
  75. }
  76. SVGDecodedImageData::SVGDecodedImageData(NonnullOwnPtr<Page> page, NonnullOwnPtr<SVGPageClient> page_client, JS::Handle<DOM::Document> document, JS::Handle<SVG::SVGSVGElement> root_element)
  77. : m_page(move(page))
  78. , m_page_client(move(page_client))
  79. , m_document(move(document))
  80. , m_root_element(move(root_element))
  81. {
  82. }
  83. SVGDecodedImageData::~SVGDecodedImageData() = default;
  84. void SVGDecodedImageData::render(Gfx::IntSize size) const
  85. {
  86. VERIFY(m_document->navigable());
  87. m_document->navigable()->set_viewport_rect({ 0, 0, size.width(), size.height() });
  88. m_document->update_layout();
  89. Gfx::Painter painter(*m_bitmap);
  90. PaintContext context(painter, m_page_client->palette(), m_page_client->device_pixels_per_css_pixel());
  91. m_document->paintable()->paint_all_phases(context);
  92. }
  93. RefPtr<Gfx::Bitmap const> SVGDecodedImageData::bitmap(size_t, Gfx::IntSize size) const
  94. {
  95. if (size.is_empty())
  96. return nullptr;
  97. if (m_bitmap && m_bitmap->size() == size)
  98. return m_bitmap;
  99. m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, size).release_value_but_fixme_should_propagate_errors();
  100. render(size);
  101. return m_bitmap;
  102. }
  103. Optional<CSSPixels> SVGDecodedImageData::intrinsic_width() const
  104. {
  105. // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
  106. m_document->update_style();
  107. auto const* root_element_style = m_root_element->computed_css_values();
  108. VERIFY(root_element_style);
  109. auto const& width_value = root_element_style->size_value(CSS::PropertyID::Width);
  110. if (width_value.is_length() && width_value.length().is_absolute())
  111. return width_value.length().absolute_length_to_px();
  112. return {};
  113. }
  114. Optional<CSSPixels> SVGDecodedImageData::intrinsic_height() const
  115. {
  116. // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
  117. m_document->update_style();
  118. auto const* root_element_style = m_root_element->computed_css_values();
  119. VERIFY(root_element_style);
  120. auto const& height_value = root_element_style->size_value(CSS::PropertyID::Height);
  121. if (height_value.is_length() && height_value.length().is_absolute())
  122. return height_value.length().absolute_length_to_px();
  123. return {};
  124. }
  125. Optional<CSSPixelFraction> SVGDecodedImageData::intrinsic_aspect_ratio() const
  126. {
  127. // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
  128. auto width = intrinsic_width();
  129. auto height = intrinsic_height();
  130. if (width.has_value() && height.has_value())
  131. return *width / *height;
  132. if (auto const& viewbox = m_root_element->view_box(); viewbox.has_value())
  133. return CSSPixels::nearest_value_for(viewbox->width) / CSSPixels::nearest_value_for(viewbox->height);
  134. return {};
  135. }
  136. }