summaryrefslogtreecommitdiffstats
path: root/src/libserver/html/html_block.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/libserver/html/html_block.hxx')
-rw-r--r--src/libserver/html/html_block.hxx358
1 files changed, 358 insertions, 0 deletions
diff --git a/src/libserver/html/html_block.hxx b/src/libserver/html/html_block.hxx
new file mode 100644
index 0000000..f9b5184
--- /dev/null
+++ b/src/libserver/html/html_block.hxx
@@ -0,0 +1,358 @@
+/*-
+ * Copyright 2021 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef RSPAMD_HTML_BLOCK_HXX
+#define RSPAMD_HTML_BLOCK_HXX
+#pragma once
+
+#include "libserver/css/css_value.hxx"
+#include <cmath>
+
+namespace rspamd::html {
+
+/*
+ * Block tag definition
+ */
+struct html_block {
+ rspamd::css::css_color fg_color;
+ rspamd::css::css_color bg_color;
+ std::int16_t height;
+ std::int16_t width;
+ rspamd::css::css_display_value display;
+ std::int8_t font_size;
+
+ unsigned fg_color_mask : 2;
+ unsigned bg_color_mask : 2;
+ unsigned height_mask : 2;
+ unsigned width_mask : 2;
+ unsigned font_mask : 2;
+ unsigned display_mask : 2;
+ unsigned visibility_mask : 2;
+
+ constexpr static const auto unset = 0;
+ constexpr static const auto inherited = 1;
+ constexpr static const auto implicit = 1;
+ constexpr static const auto set = 3;
+ constexpr static const auto invisible_flag = 1;
+ constexpr static const auto transparent_flag = 2;
+
+ /* Helpers to set mask when setting the elements */
+ auto set_fgcolor(const rspamd::css::css_color &c, int how = html_block::set) -> void
+ {
+ fg_color = c;
+ fg_color_mask = how;
+ }
+ auto set_bgcolor(const rspamd::css::css_color &c, int how = html_block::set) -> void
+ {
+ bg_color = c;
+ bg_color_mask = how;
+ }
+ auto set_height(float h, bool is_percent = false, int how = html_block::set) -> void
+ {
+ h = is_percent ? (-h) : h;
+ if (h < INT16_MIN) {
+ /* Negative numbers encode percents... */
+ height = -100;
+ }
+ else if (h > INT16_MAX) {
+ height = INT16_MAX;
+ }
+ else {
+ height = h;
+ }
+ height_mask = how;
+ }
+
+ auto set_width(float w, bool is_percent = false, int how = html_block::set) -> void
+ {
+ w = is_percent ? (-w) : w;
+ if (w < INT16_MIN) {
+ width = INT16_MIN;
+ }
+ else if (w > INT16_MAX) {
+ width = INT16_MAX;
+ }
+ else {
+ width = w;
+ }
+ width_mask = how;
+ }
+
+ auto set_display(bool v, int how = html_block::set) -> void
+ {
+ if (v) {
+ display = rspamd::css::css_display_value::DISPLAY_INLINE;
+ }
+ else {
+ display = rspamd::css::css_display_value::DISPLAY_HIDDEN;
+ }
+ display_mask = how;
+ }
+
+ auto set_display(rspamd::css::css_display_value v, int how = html_block::set) -> void
+ {
+ display = v;
+ display_mask = how;
+ }
+
+ auto set_font_size(float fs, bool is_percent = false, int how = html_block::set) -> void
+ {
+ fs = is_percent ? (-fs) : fs;
+ if (fs < INT8_MIN) {
+ font_size = -100;
+ }
+ else if (fs > INT8_MAX) {
+ font_size = INT8_MAX;
+ }
+ else {
+ font_size = fs;
+ }
+ font_mask = how;
+ }
+
+private:
+ template<typename T, typename MT>
+ static constexpr auto simple_prop(MT mask_val, MT other_mask, T &our_val,
+ T other_val) -> MT
+ {
+ if (other_mask && other_mask > mask_val) {
+ our_val = other_val;
+ mask_val = html_block::inherited;
+ }
+
+ return mask_val;
+ }
+
+ /* Sizes propagation logic
+ * We can have multiple cases:
+ * 1) Our size is > 0 and we can use it as is
+ * 2) Parent size is > 0 and our size is undefined, so propagate parent
+ * 3) Parent size is < 0 and our size is undefined - propagate parent
+ * 4) Parent size is > 0 and our size is < 0 - multiply parent by abs(ours)
+ * 5) Parent size is undefined and our size is < 0 - tricky stuff, assume some defaults
+ */
+ template<typename T, typename MT>
+ static constexpr auto size_prop(MT mask_val, MT other_mask, T &our_val,
+ T other_val, T default_val) -> MT
+ {
+ if (mask_val) {
+ /* We have our value */
+ if (our_val < 0) {
+ if (other_mask > 0) {
+ if (other_val >= 0) {
+ our_val = other_val * (-our_val / 100.0);
+ }
+ else {
+ our_val *= (-other_val / 100.0);
+ }
+ }
+ else {
+ /* Parent value is not defined and our value is relative */
+ our_val = default_val * (-our_val / 100.0);
+ }
+ }
+ else if (other_mask && other_mask > mask_val) {
+ our_val = other_val;
+ mask_val = html_block::inherited;
+ }
+ }
+ else {
+ /* We propagate parent if defined */
+ if (other_mask && other_mask > mask_val) {
+ our_val = other_val;
+ mask_val = html_block::inherited;
+ }
+ /* Otherwise do nothing */
+ }
+
+ return mask_val;
+ }
+
+public:
+ /**
+ * Propagate values from the block if they are not defined by the current block
+ * @param other
+ * @return
+ */
+ auto propagate_block(const html_block &other) -> void
+ {
+ fg_color_mask = html_block::simple_prop(fg_color_mask, other.fg_color_mask,
+ fg_color, other.fg_color);
+ bg_color_mask = html_block::simple_prop(bg_color_mask, other.bg_color_mask,
+ bg_color, other.bg_color);
+ display_mask = html_block::simple_prop(display_mask, other.display_mask,
+ display, other.display);
+
+ height_mask = html_block::size_prop(height_mask, other.height_mask,
+ height, other.height, static_cast<std::int16_t>(800));
+ width_mask = html_block::size_prop(width_mask, other.width_mask,
+ width, other.width, static_cast<std::int16_t>(1024));
+ font_mask = html_block::size_prop(font_mask, other.font_mask,
+ font_size, other.font_size, static_cast<std::int8_t>(10));
+ }
+
+ /*
+ * Set block overriding all inherited values
+ */
+ auto set_block(const html_block &other) -> void
+ {
+ constexpr auto set_value = [](auto mask_val, auto other_mask, auto &our_val,
+ auto other_val) constexpr -> int {
+ if (other_mask && mask_val != html_block::set) {
+ our_val = other_val;
+ mask_val = other_mask;
+ }
+
+ return mask_val;
+ };
+
+ fg_color_mask = set_value(fg_color_mask, other.fg_color_mask, fg_color, other.fg_color);
+ bg_color_mask = set_value(bg_color_mask, other.bg_color_mask, bg_color, other.bg_color);
+ display_mask = set_value(display_mask, other.display_mask, display, other.display);
+ height_mask = set_value(height_mask, other.height_mask, height, other.height);
+ width_mask = set_value(width_mask, other.width_mask, width, other.width);
+ font_mask = set_value(font_mask, other.font_mask, font_size, other.font_size);
+ }
+
+ auto compute_visibility(void) -> void
+ {
+ if (display_mask) {
+ if (display == css::css_display_value::DISPLAY_HIDDEN) {
+ visibility_mask = html_block::invisible_flag;
+
+ return;
+ }
+ }
+
+ if (font_mask) {
+ if (font_size == 0) {
+ visibility_mask = html_block::invisible_flag;
+
+ return;
+ }
+ }
+
+ auto is_similar_colors = [](const rspamd::css::css_color &fg, const rspamd::css::css_color &bg) -> bool {
+ constexpr const auto min_visible_diff = 0.1f;
+ auto diff_r = ((float) fg.r - bg.r);
+ auto diff_g = ((float) fg.g - bg.g);
+ auto diff_b = ((float) fg.b - bg.b);
+ auto ravg = ((float) fg.r + bg.r) / 2.0f;
+
+ /* Square diffs */
+ diff_r *= diff_r;
+ diff_g *= diff_g;
+ diff_b *= diff_b;
+
+ auto diff = std::sqrt(2.0f * diff_r + 4.0f * diff_g + 3.0f * diff_b +
+ (ravg * (diff_r - diff_b) / 256.0f)) /
+ 256.0f;
+
+ return diff < min_visible_diff;
+ };
+ /* Check if we have both bg/fg colors */
+ if (fg_color_mask && bg_color_mask) {
+ if (fg_color.alpha < 10) {
+ /* Too transparent */
+ visibility_mask = html_block::transparent_flag;
+
+ return;
+ }
+
+ if (bg_color.alpha > 10) {
+ if (is_similar_colors(fg_color, bg_color)) {
+ visibility_mask = html_block::transparent_flag;
+ return;
+ }
+ }
+ }
+ else if (fg_color_mask) {
+ /* Merely fg color */
+ if (fg_color.alpha < 10) {
+ /* Too transparent */
+ visibility_mask = html_block::transparent_flag;
+
+ return;
+ }
+
+ /* Implicit fg color */
+ if (is_similar_colors(fg_color, rspamd::css::css_color::white())) {
+ visibility_mask = html_block::transparent_flag;
+ return;
+ }
+ }
+ else if (bg_color_mask) {
+ if (bg_color.alpha > 10) {
+ if (is_similar_colors(rspamd::css::css_color::black(), bg_color)) {
+ visibility_mask = html_block::transparent_flag;
+ return;
+ }
+ }
+ }
+
+ visibility_mask = html_block::unset;
+ }
+
+ constexpr auto is_visible(void) const -> bool
+ {
+ return visibility_mask == html_block::unset;
+ }
+
+ constexpr auto is_transparent(void) const -> bool
+ {
+ return visibility_mask == html_block::transparent_flag;
+ }
+
+ constexpr auto has_display(int how = html_block::set) const -> bool
+ {
+ return display_mask >= how;
+ }
+
+ /**
+ * Returns a default html block for root HTML element
+ * @return
+ */
+ static auto default_html_block(void) -> html_block
+ {
+ return html_block{.fg_color = rspamd::css::css_color::black(),
+ .bg_color = rspamd::css::css_color::white(),
+ .height = 0,
+ .width = 0,
+ .display = rspamd::css::css_display_value::DISPLAY_INLINE,
+ .font_size = 12,
+ .fg_color_mask = html_block::inherited,
+ .bg_color_mask = html_block::inherited,
+ .height_mask = html_block::unset,
+ .width_mask = html_block::unset,
+ .font_mask = html_block::unset,
+ .display_mask = html_block::inherited,
+ .visibility_mask = html_block::unset};
+ }
+ /**
+ * Produces html block with no defined values allocated from the pool
+ * @param pool
+ * @return
+ */
+ static auto undefined_html_block_pool(rspamd_mempool_t *pool) -> html_block *
+ {
+ auto *bl = rspamd_mempool_alloc0_type(pool, html_block);
+
+ return bl;
+ }
+};
+
+}// namespace rspamd::html
+
+#endif//RSPAMD_HTML_BLOCK_HXX