mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:52:45 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			164 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include "SlideObject.h"
 | |
| #include "Presentation.h"
 | |
| #include <AK/JsonObject.h>
 | |
| #include <AK/URL.h>
 | |
| #include <LibGfx/Font/FontStyleMapping.h>
 | |
| #include <LibGfx/Rect.h>
 | |
| 
 | |
| static DeprecatedString to_css_length(float design_value, Presentation const& presentation)
 | |
| {
 | |
|     float length_in_vw = design_value / static_cast<float>(presentation.normative_size().width()) * 100.0f;
 | |
|     return DeprecatedString::formatted("{}vw", length_in_vw);
 | |
| }
 | |
| 
 | |
| ErrorOr<NonnullRefPtr<SlideObject>> SlideObject::parse_slide_object(JsonObject const& slide_object_json, unsigned slide_index)
 | |
| {
 | |
|     auto frame = slide_object_json.get_u32("frame"sv).value_or(0);
 | |
|     auto maybe_type = slide_object_json.get_deprecated_string("type"sv);
 | |
|     if (!maybe_type.has_value())
 | |
|         return Error::from_string_view("Slide object must have a type"sv);
 | |
| 
 | |
|     auto type = maybe_type.value();
 | |
|     RefPtr<SlideObject> object;
 | |
|     if (type == "text"sv)
 | |
|         object = TRY(try_make_ref_counted<Text>(Index { slide_index, frame }));
 | |
|     else if (type == "image"sv)
 | |
|         object = TRY(try_make_ref_counted<Image>(Index { slide_index, frame }));
 | |
|     else
 | |
|         return Error::from_string_view("Unsupported slide object type"sv);
 | |
| 
 | |
|     slide_object_json.for_each_member([&](auto const& key, auto const& value) {
 | |
|         object->set_property(key, value);
 | |
|     });
 | |
| 
 | |
|     return object.release_nonnull();
 | |
| }
 | |
| 
 | |
| void SlideObject::set_property(StringView name, JsonValue value)
 | |
| {
 | |
|     if (name == "rect"sv) {
 | |
|         if (value.is_array() && value.as_array().size() == 4) {
 | |
|             Gfx::IntRect rect;
 | |
|             rect.set_x(value.as_array()[0].to_i32());
 | |
|             rect.set_y(value.as_array()[1].to_i32());
 | |
|             rect.set_width(value.as_array()[2].to_i32());
 | |
|             rect.set_height(value.as_array()[3].to_i32());
 | |
|             m_rect = rect;
 | |
|         }
 | |
|     }
 | |
|     m_properties.set(name, move(value));
 | |
| }
 | |
| 
 | |
| void GraphicsObject::set_property(StringView name, JsonValue value)
 | |
| {
 | |
|     if (name == "color"sv) {
 | |
|         if (auto color = Gfx::Color::from_string(value.to_deprecated_string()); color.has_value()) {
 | |
|             m_color = color.release_value();
 | |
|         }
 | |
|     }
 | |
|     SlideObject::set_property(name, move(value));
 | |
| }
 | |
| 
 | |
| void Text::set_property(StringView name, JsonValue value)
 | |
| {
 | |
|     if (name == "text"sv) {
 | |
|         m_text = value.to_deprecated_string();
 | |
|     } else if (name == "font"sv) {
 | |
|         m_font_family = value.to_deprecated_string();
 | |
|     } else if (name == "font-weight"sv) {
 | |
|         m_font_weight = Gfx::name_to_weight(value.to_deprecated_string());
 | |
|     } else if (name == "font-size"sv) {
 | |
|         m_font_size_in_pt = value.to_float();
 | |
|     } else if (name == "text-alignment"sv) {
 | |
|         m_text_align = value.to_deprecated_string();
 | |
|     }
 | |
|     GraphicsObject::set_property(name, move(value));
 | |
| }
 | |
| 
 | |
| void Image::set_property(StringView name, JsonValue value)
 | |
| {
 | |
|     if (name == "path"sv) {
 | |
|         m_src = value.to_deprecated_string();
 | |
|     } else if (name == "scaling-mode"sv) {
 | |
|         if (value.to_deprecated_string() == "nearest-neighbor"sv)
 | |
|             m_image_rendering = "crisp-edges"sv;
 | |
|         else if (value.to_deprecated_string() == "smooth-pixels"sv)
 | |
|             m_image_rendering = "pixelated"sv;
 | |
|     }
 | |
|     SlideObject::set_property(name, move(value));
 | |
| }
 | |
| 
 | |
| ErrorOr<HTMLElement> Text::render(Presentation const& presentation) const
 | |
| {
 | |
|     HTMLElement div;
 | |
|     div.tag_name = "div"sv;
 | |
|     TRY(div.attributes.try_set("class"sv, DeprecatedString::formatted("frame slide{}-frame{}", m_slide_index, m_frame_index)));
 | |
|     div.style.set("color"sv, m_color.to_deprecated_string());
 | |
|     div.style.set("font-family"sv, DeprecatedString::formatted("'{}'", m_font_family));
 | |
|     div.style.set("font-size"sv, to_css_length(m_font_size_in_pt * 1.33333333f, presentation));
 | |
|     div.style.set("font-weight"sv, DeprecatedString::number(m_font_weight));
 | |
|     div.style.set("text-align"sv, m_text_align);
 | |
|     div.style.set("white-space"sv, "pre-wrap"sv);
 | |
|     div.style.set("width"sv, to_css_length(m_rect.width(), presentation));
 | |
|     div.style.set("height"sv, to_css_length(m_rect.height(), presentation));
 | |
|     div.style.set("position"sv, "absolute"sv);
 | |
|     div.style.set("left"sv, to_css_length(m_rect.left(), presentation));
 | |
|     div.style.set("top"sv, to_css_length(m_rect.top(), presentation));
 | |
|     div.inner_text = m_text;
 | |
|     return div;
 | |
| }
 | |
| 
 | |
| ErrorOr<HTMLElement> Image::render(Presentation const& presentation) const
 | |
| {
 | |
|     HTMLElement img;
 | |
|     img.tag_name = "img"sv;
 | |
|     img.attributes.set("src"sv, URL::create_with_file_scheme(m_src).to_deprecated_string());
 | |
|     img.style.set("image-rendering"sv, m_image_rendering);
 | |
|     if (m_rect.width() > m_rect.height())
 | |
|         img.style.set("height"sv, "100%"sv);
 | |
|     else
 | |
|         img.style.set("width"sv, "100%"sv);
 | |
| 
 | |
|     HTMLElement image_wrapper;
 | |
|     image_wrapper.tag_name = "div"sv;
 | |
|     TRY(image_wrapper.attributes.try_set("class"sv, DeprecatedString::formatted("frame slide{}-frame{}", m_slide_index, m_frame_index)));
 | |
|     image_wrapper.children.append(move(img));
 | |
|     image_wrapper.style.set("position"sv, "absolute"sv);
 | |
|     image_wrapper.style.set("left"sv, to_css_length(m_rect.left(), presentation));
 | |
|     image_wrapper.style.set("top"sv, to_css_length(m_rect.top(), presentation));
 | |
|     image_wrapper.style.set("width"sv, to_css_length(m_rect.width(), presentation));
 | |
|     image_wrapper.style.set("height"sv, to_css_length(m_rect.height(), presentation));
 | |
|     image_wrapper.style.set("text-align"sv, "center"sv);
 | |
|     return image_wrapper;
 | |
| }
 | |
| 
 | |
| ErrorOr<void> HTMLElement::serialize(StringBuilder& builder) const
 | |
| {
 | |
|     TRY(builder.try_appendff("<{}", tag_name));
 | |
|     for (auto const& [key, value] : attributes) {
 | |
|         // FIXME: Escape the value string as necessary.
 | |
|         TRY(builder.try_appendff(" {}='{}'", key, value));
 | |
|     }
 | |
|     if (!style.is_empty()) {
 | |
|         TRY(builder.try_append(" style=\""sv));
 | |
|         for (auto const& [key, value] : style) {
 | |
|             // FIXME: Escape the value string as necessary.
 | |
|             TRY(builder.try_appendff(" {}: {};", key, value));
 | |
|         }
 | |
|         TRY(builder.try_append("\""sv));
 | |
|     }
 | |
|     TRY(builder.try_append(">"sv));
 | |
|     if (!inner_text.is_empty())
 | |
|         TRY(builder.try_append(inner_text));
 | |
|     for (auto const& child : children) {
 | |
|         TRY(child.serialize(builder));
 | |
|     }
 | |
|     TRY(builder.try_appendff("</{}>", tag_name));
 | |
|     return {};
 | |
| }
 | 
