summaryrefslogtreecommitdiffstats
path: root/src/svg/svg-box.cpp
blob: bf434e4bbdcbb9702f74a79ddc5265d3f9db0b54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Authors:
 *   Martin Owens <doctormo@geek-2.com>
 *
 * Copyright (C) 2022 Martin Owens
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include <cstring>
#include <string>
#include <iostream>

#include <glib.h>
#include <glibmm/regex.h>

#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<unsigned int> 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<Glib::ustring> 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 :