// SPDX-License-Identifier: GPL-2.0-or-later /* * Authors: * Martin Owens * * Copyright (C) 2022 Martin Owens * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include #include #include #include #include #include "svg/svg-box.h" #include "util/units.h" // Match a side to it's fallback index, // top->bottom, top->right, right->left #define FALLBACK(i) ((i - 2 >= 0) ? (i - 2) : 0) /** * An svg box is a type of css/html type which contains up to 4 svg lengths. * Usally representing widths, margins, padding of the html box model. */ SVGBox::SVGBox() {} /** * Read in the value, may be an array of four */ bool SVGBox::read(const std::string &value) { return fromString(value, ""); } /** * Update box with em, ex and percentage scaling. */ void SVGBox::update(double em, double ex, double width, double height) { _value[0].update(em, ex, height); _value[1].update(em, ex, width); _value[2].update(em, ex, height); _value[3].update(em, ex, width); } /** * Write out the values into a compact form. */ std::string SVGBox::write() const { return toString(""); } /** * Write as specific unit for user display */ std::string SVGBox::toString(const std::string &unit, std::optional precision, bool add_unit) const { std::string ret = ""; bool write = false; for (int i = 3; i >= 0; i--) { SVGLength val = _value[i]; SVGLength fallback = _value[FALLBACK(i)]; if (i == BOX_TOP || (val != fallback) || write) { if (unit.size()) { ret = std::string(val.toString(unit, precision, add_unit)) + " " + ret; } else { ret = std::string(val.write()) + " " + ret; } write = true; } } ret.pop_back(); return ret; } /** * Set the svg box from user input, with a default unit */ bool SVGBox::fromString(const std::string &value, const std::string &unit) { if (!value.size()) return false; // A. Split by spaces. std::vector elements = Glib::Regex::split_simple("\\s*[,\\s]\\s*", value); // Take item zero for (int i = 0; i < 4; i++) { if ((i == BOX_TOP || (int)elements.size() >= i+1) && elements[i].size() > 0) { if (!fromString((BoxSide)i, elements[i], unit)) { return false; // One position failed. } } else { _value[i] = _value[FALLBACK(i)]; } } _is_set = true; return true; } /** * Parse a single side from a string and unit combo (pass through to SVGLength.fromString) * * @param side - The side of the box to set * @param value - The string value entered by the user * @param unit - The default units the context is using * @param confine - If true, other sides of the same original value will also change. */ bool SVGBox::fromString(BoxSide side, const std::string &value, const std::string &unit) { if (unit.size()) { return _value[side].fromString(value, unit); } return _value[side].read(value.c_str()); } /** * Returns true if the box is set, but all values are zero */ bool SVGBox::isZero() const { return _value[0] == 0.0 && _value[1] == 0.0 && _value[2] == 0.0 && _value[3] == 0.0; } /** * Set values into this box model. */ void SVGBox::set(double top, double right, double bottom, double left) { set(BOX_TOP, top); set(BOX_RIGHT, right); set(BOX_BOTTOM, bottom); set(BOX_LEFT, left); } /** * Set the value of the side, retaining it's original unit. * * confine - If true, will set any OTHER sides which are the same. */ void SVGBox::set(BoxSide side, double px, bool confine) { // Unit gets destroyed here delibrately. Units are not ok in the svg. SVGLength original = _value[side]; for (int i = 0; i < 4; i++) { if (i == (int)side || (confine && _value[i] == original)) { _value[i].set(SVGLength::PX, px, px); } } _is_set = true; } void SVGBox::unset() { _is_set = false; } void SVGBox::readOrUnset(gchar const *value) { if (!value || !read(value)) { unset(); } } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :