/* * Copyright (c) 2022, kleines Filmröllchen * * SPDX-License-Identifier: BSD-2-Clause */ #include "SlideObject.h" #include "Presentation.h" #include #include #include #include static DeprecatedString to_css_length(float design_value, Presentation const& presentation) { float length_in_vw = design_value / static_cast(presentation.normative_size().width()) * 100.0f; return DeprecatedString::formatted("{}vw", length_in_vw); } ErrorOr> 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 object; if (type == "text"sv) object = TRY(try_make_ref_counted(Index { slide_index, frame })); else if (type == "image"sv) object = TRY(try_make_ref_counted(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 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 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 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 {}; }