• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CyberZHG / SVGDiagram / 20020810855

08 Dec 2025 07:58AM UTC coverage: 96.996% (-0.02%) from 97.017%
20020810855

push

github

web-flow
Use unit 'point' for all the functions (#3)

26 of 29 new or added lines in 3 files covered. (89.66%)

3 existing lines in 1 file now uncovered.

1356 of 1398 relevant lines covered (97.0%)

1110.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.4
/src/svg_draw.cpp
1
#include "svg_draw.h"
2
#include "svg_text_size.h"
3
#include "attribute_utils.h"
4

5
#include <format>
6
#include <ranges>
7
#include <regex>
8
#include <algorithm>
9
using namespace std;
10
using namespace svg_diagram;
11

12
SVGDrawBoundingBox::SVGDrawBoundingBox(double x1, double y1, double x2, double y2) {
1,122✔
13
    if (x1 > x2) {
1,122✔
14
        swap(x1, x2);
41✔
15
    }
16
    if (y1 > y2) {
1,122✔
17
        swap(y1, y2);
29✔
18
    }
19
    this->x1 = x1;
1,122✔
20
    this->x2 = x2;
1,122✔
21
    this->y1 = y1;
1,122✔
22
    this->y2 = y2;
1,122✔
23
}
1,122✔
24

25
void SVGDraw::setAttribute(const string_view& key, const string& value) {
2,223✔
26
    _attributes[key] = value;
2,223✔
27
}
2,223✔
28

29
void SVGDraw::addAttributesToXMLElement(const XMLElement::ChildType& element) const {
1,452✔
30
    auto keys_view = _attributes | std::views::keys;
1,452✔
31
    std::vector<string> keys(keys_view.begin(), keys_view.end());
1,452✔
32
    ranges::sort(keys);
1,452✔
33
    for (const auto& key : keys) {
3,697✔
34
        if (const auto& value = _attributes.at(key); !value.empty()) {
2,245✔
35
            element->addAttribute(key, value);
2,169✔
36
        }
37
    }
38
}
1,452✔
39

40
SVGDrawComment::SVGDrawComment(const string& comment) {
318✔
41
    this->comment = comment;
318✔
42
}
318✔
43

44
XMLElement::ChildrenType SVGDrawComment::generateXMLElements() const {
319✔
45
    return {make_shared<XMLElementComment>(comment)};
957✔
46
}
319✔
47

48
SVGDrawBoundingBox SVGDrawComment::boundingBox() const {
×
49
    return {};
×
50
}
51

52
bool SVGDrawComment::hasEntity() const {
319✔
53
    return false;
319✔
54
}
55

56
SVGDrawGroup::SVGDrawGroup(vector<unique_ptr<SVGDraw>>& draws) {
×
57
    this->addChildren(draws);
×
58
}
×
59

60
void SVGDrawGroup::addChild(unique_ptr<SVGDraw> child) {
317✔
61
    children.emplace_back(std::move(child));
317✔
62
}
317✔
63

64
void SVGDrawGroup::addChildren(vector<unique_ptr<SVGDraw>>& draws) {
317✔
65
    for (auto& child : draws) {
1,096✔
66
        children.emplace_back(std::move(child));
779✔
67
    }
68
}
317✔
69

70
XMLElement::ChildrenType SVGDrawGroup::generateXMLElements() const {
317✔
71
    const auto groupElement = make_shared<XMLElement>("g");
317✔
72
    addAttributesToXMLElement(groupElement);
317✔
73
    for (const auto& child : children) {
1,413✔
74
        groupElement->addChildren(child->generateXMLElements());
1,096✔
75
    }
76
    return {groupElement};
1,268✔
77
}
634✔
78

79
SVGDrawBoundingBox SVGDrawGroup::boundingBox() const {
317✔
80
    double xMin = 0.0, yMin = 0.0, xMax = 1.0, yMax = 1.0;
317✔
81
    bool first = true;
317✔
82
    for (const auto& child : children) {
1,413✔
83
        if (child->hasEntity()) {
1,096✔
84
            const auto [x1, y1, x2, y2] = child->boundingBox();
779✔
85
            if (first) {
779✔
86
                first = false;
317✔
87
                xMin = x1, yMin = y1;
317✔
88
                xMax = x2, yMax = y2;
317✔
89
            } else {
90
                xMin = min(xMin, x1);
462✔
91
                xMax = max(xMax, x2);
462✔
92
                yMin = min(yMin, y1);
462✔
93
                yMax = max(yMax, y2);
462✔
94
            }
95
        }
96
    }
97
    return {xMin, yMin, xMax, yMax};
634✔
98
}
99

100
bool SVGDrawGroup::hasEntity() const {
317✔
101
    for (const auto& child : children) {
634✔
102
        if (child->hasEntity()) {
634✔
103
            return true;
317✔
104
        }
105
    }
106
    return false;
×
107
}
108

109
SVGDrawTitle::SVGDrawTitle(const string& title) {
317✔
110
    this->title = title;
317✔
111
}
317✔
112

113
XMLElement::ChildrenType SVGDrawTitle::generateXMLElements() const {
317✔
114
    const auto titleElement = make_shared<XMLElement>("title");
317✔
115
    addAttributesToXMLElement(titleElement);
317✔
116
    titleElement->setContent(title);
317✔
117
    return {titleElement};
1,268✔
118
}
634✔
119

120
SVGDrawBoundingBox SVGDrawTitle::boundingBox() const {
×
121
    return {};
×
122
}
123

124
bool SVGDrawTitle::hasEntity() const {
634✔
125
    return false;
634✔
126
}
127

128
void SVGDrawAttribute::setFill(const string& value) {
545✔
129
    setAttribute(SVG_ATTR_KEY_FILL, value);
545✔
130
}
545✔
131

132
void SVGDrawAttribute::setStroke(const string& value) {
538✔
133
    setAttribute(SVG_ATTR_KEY_STROKE, value);
538✔
134
}
538✔
135

UNCOV
136
void SVGDrawAttribute::setStrokeWidth(const string& value) {
×
UNCOV
137
    setAttribute(SVG_ATTR_KEY_STROKE_WIDTH, value);
×
UNCOV
138
}
×
139

140
void SVGDrawAttribute::setStrokeWidth(const double value) {
13✔
141
    setAttribute(SVG_ATTR_KEY_STROKE_WIDTH, format("{}", value));
13✔
142
}
13✔
143

144
bool SVGDrawEntity::hasEntity() const {
1,122✔
145
    return true;
1,122✔
146
}
147

148
SVGDrawNode::SVGDrawNode(const double cx, const double cy, const double width, const double height) {
116✔
149
    this->cx = cx;
116✔
150
    this->cy = cy;
116✔
151
    this->width = width;
116✔
152
    this->height = height;
116✔
153
}
116✔
154

155
SVGDrawBoundingBox SVGDrawNode::boundingBox() const {
116✔
156
    const double halfWidth = width / 2.0;
116✔
157
    const double halfHeight = height / 2.0;
116✔
158
    return {cx - halfWidth, cy - halfHeight, cx + halfWidth, cy + halfHeight};
116✔
159
}
160

161
SVGDrawText::SVGDrawText() {
×
162
    setFont("Times,serif", 14);
×
163
}
×
164

165
SVGDrawText::SVGDrawText(const double x, const double y, const string& text) {
244✔
166
    cx = x;
244✔
167
    cy = y;
244✔
168
    this->text = text;
244✔
169
    setFont("Times,serif", 14);
244✔
170
}
244✔
171

172
void SVGDrawText::setFont(const string& fontFamily, double fontSize) {
244✔
173
    setAttribute(SVG_ATTR_KEY_FONT_FAMILY, fontFamily);
244✔
174
    setAttribute(SVG_ATTR_KEY_FONT_SIZE, format("{}", fontSize));
244✔
175
}
244✔
176

177
XMLElement::ChildrenType SVGDrawText::generateXMLElements() const {
250✔
178
    auto splitLines = [](const string& s) -> vector<string> {
250✔
179
        regex re("\r\n|\r|\n");
250✔
180
        sregex_token_iterator it(s.begin(), s.end(), re, -1);
250✔
181
        sregex_token_iterator end;
250✔
182
        return {it, end};
1,000✔
183
    };
250✔
184
    const auto textElement = make_shared<XMLElement>("text");
250✔
185
    textElement->addAttribute("x", cx);
500✔
186
    textElement->addAttribute("y", cy);
500✔
187
    textElement->addAttribute("text-anchor", "middle");
1,000✔
188
    textElement->addAttribute("dominant-baseline", "central");
1,000✔
189
    addAttributesToXMLElement(textElement);
250✔
190
    if (const auto lines = splitLines(text); lines.size() == 1) {
250✔
191
        textElement->setContent(text);
231✔
192
    } else {
193
        XMLElement::ChildrenType spans;
19✔
194
        for (int i = 0; i < static_cast<int>(lines.size()); ++i) {
66✔
195
            double dy = SVGTextSize::DEFAULT_APPROXIMATION_HEIGHT_SCALE + SVGTextSize::DEFAULT_APPROXIMATION_LINE_SPACING_SCALE;
47✔
196
            if (i == 0) {
47✔
197
                dy = -(static_cast<double>(lines.size()) - 1) / 2 * dy;
19✔
198
            }
199
            const auto tspanElement = make_shared<XMLElement>("tspan");
47✔
200
            tspanElement->addAttribute("x", cx);
94✔
201
            tspanElement->addAttribute("dy", format("{}em", dy));
141✔
202
            tspanElement->setContent(lines[i]);
47✔
203
            spans.emplace_back(tspanElement);
47✔
204
        }
47✔
205
        textElement->addChildren(spans);
19✔
206
    }
269✔
207
    return {textElement};
1,000✔
208
}
500✔
209

210
SVGDrawBoundingBox SVGDrawText::boundingBox() const {
247✔
211
    const SVGTextSize textSize;
247✔
212
    const auto fontSize = stod(_attributes.at(SVG_ATTR_KEY_FONT_SIZE));
247✔
213
    const auto fontFamily = _attributes.at(SVG_ATTR_KEY_FONT_FAMILY);
247✔
214
    const auto [width, height] = textSize.computeTextSize(text, fontSize, fontFamily);
247✔
215
    return {cx - width / 2.0, cy - height / 2.0, cx + width / 2.0, cy + height / 2.0};
494✔
216
}
247✔
217

218
SVGDrawCircle::SVGDrawCircle(const double x, const double y, const double radius) {
110✔
219
    cx = x;
110✔
220
    cy = y;
110✔
221
    width = height = radius * 2;
110✔
222
}
110✔
223

224
XMLElement::ChildrenType SVGDrawCircle::generateXMLElements() const {
124✔
225
    const double radius = min(width, height) / 2;
124✔
226
    const auto circleElement = make_shared<XMLElement>("circle");
124✔
227
    circleElement->addAttribute("cx", cx);
248✔
228
    circleElement->addAttribute("cy", cy);
248✔
229
    circleElement->addAttribute("r", radius);
248✔
230
    addAttributesToXMLElement(circleElement);
124✔
231
    return {circleElement};
496✔
232
}
248✔
233

234
SVGDrawBoundingBox SVGDrawCircle::boundingBox() const {
117✔
235
    const double radius = min(width, height) / 2;
117✔
236
    return {cx - radius, cy - radius, cx + radius, cy + radius};
117✔
237
}
238

239
XMLElement::ChildrenType SVGDrawRect::generateXMLElements() const {
92✔
240
    const double x = cx - width / 2;
92✔
241
    const double y = cy - height / 2;
92✔
242
    const auto rectElement = make_shared<XMLElement>("rect");
92✔
243
    rectElement->addAttribute("x", x);
184✔
244
    rectElement->addAttribute("y", y);
184✔
245
    rectElement->addAttribute("width", width);
184✔
246
    rectElement->addAttribute("height", height);
184✔
247
    addAttributesToXMLElement(rectElement);
92✔
248
    return {rectElement};
368✔
249
}
184✔
250

251
XMLElement::ChildrenType SVGDrawEllipse::generateXMLElements() const {
24✔
252
    const double rx = width / 2;
24✔
253
    const double ry = height / 2;
24✔
254
    const auto ellipseElement = make_shared<XMLElement>("ellipse");
24✔
255
    ellipseElement->addAttribute("cx", cx);
48✔
256
    ellipseElement->addAttribute("cy", cy);
48✔
257
    ellipseElement->addAttribute("rx", rx);
48✔
258
    ellipseElement->addAttribute("ry", ry);
48✔
259
    addAttributesToXMLElement(ellipseElement);
24✔
260
    return {ellipseElement};
96✔
261
}
48✔
262

263
SVGDrawPolygon::SVGDrawPolygon(const vector<pair<double, double>>& points) {
132✔
264
    this->points = points;
132✔
265
}
132✔
266

267
XMLElement::ChildrenType SVGDrawPolygon::generateXMLElements() const {
132✔
268
    const auto polygonElement = make_shared<XMLElement>("polygon");
132✔
269
    string path;
132✔
270
    if (!points.empty()) {
132✔
271
        path += format("{},{}", points[0].first, points[0].second);
132✔
272
        for (size_t i = 1; i < points.size(); ++i) {
528✔
273
            path += format(" {},{}", points[i].first, points[i].second);
396✔
274
        }
275
    }
276
    polygonElement->addAttribute("points", path);
264✔
277
    addAttributesToXMLElement(polygonElement);
132✔
278
    return {polygonElement};
528✔
279
}
264✔
280

281
SVGDrawBoundingBox SVGDrawPolygon::boundingBox() const {
132✔
282
    if (points.empty()) {
132✔
283
        return {};
×
284
    }
285
    double xMin = points[0].first, yMin = points[0].second;
132✔
286
    double xMax = points[0].first, yMax = points[0].second;
132✔
287
    for (size_t i = 1; i < points.size(); ++i) {
528✔
288
        xMin = min(xMin, points[i].first);
396✔
289
        xMax = max(xMax, points[i].first);
396✔
290
        yMin = min(yMin, points[i].second);
396✔
291
        yMax = max(yMax, points[i].second);
396✔
292
    }
293
    return {xMin, yMin, xMax, yMax};
132✔
294
}
295

296
SVGDrawLine::SVGDrawLine(const double x1, const double y1, const double x2, const double y2) {
103✔
297
    this->x1 = x1;
103✔
298
    this->y1 = y1;
103✔
299
    this->x2 = x2;
103✔
300
    this->y2 = y2;
103✔
301
}
103✔
302

303
XMLElement::ChildrenType SVGDrawLine::generateXMLElements() const {
109✔
304
    const auto lineElement = make_shared<XMLElement>("line");
109✔
305
    lineElement->addAttribute("x1", x1);
218✔
306
    lineElement->addAttribute("y1", y1);
218✔
307
    lineElement->addAttribute("x2", x2);
218✔
308
    lineElement->addAttribute("y2", y2);
218✔
309
    addAttributesToXMLElement(lineElement);
109✔
310
    return {lineElement};
436✔
311
}
218✔
312

313
SVGDrawBoundingBox SVGDrawLine::boundingBox() const {
106✔
314
    return {x1, y1, x2, y2};
106✔
315
}
316

317
SVGDrawPath::SVGDrawPath(const string& d) {
87✔
318
    this->d = d;
87✔
319
}
87✔
320

321
XMLElement::ChildrenType SVGDrawPath::generateXMLElements() const {
87✔
322
    const auto commands = AttributeUtils::parseDCommands(d);
87✔
323
    string reformat;
87✔
324
    for (int i = 0; i < static_cast<int>(commands.size()); ++i) {
390✔
325
        const auto& [command, parameters] = commands[i];
303✔
326
        if (i > 0) {
303✔
327
            reformat += ' ';
216✔
328
        }
329
        reformat += command;
303✔
330
        for (const auto& parameter : parameters) {
1,773✔
331
            reformat += format(" {}", parameter);
1,470✔
332
        }
333
    }
334
    const auto pathElement = make_shared<XMLElement>("path");
87✔
335
    pathElement->addAttribute("d", reformat);
174✔
336
    addAttributesToXMLElement(pathElement);
87✔
337
    return {pathElement};
348✔
338
}
174✔
339

340
SVGDrawBoundingBox SVGDrawPath::boundingBox() const {
87✔
341
    double xMin = 0.0, yMin = 0.0, xMax = 0.0, yMax = 0.0;
87✔
342
    const auto commands = AttributeUtils::parseDCommands(d);
87✔
343
    if (const auto points = AttributeUtils::computeDPathPoints(commands); !points.empty()) {
87✔
344
        xMin = xMax = points[0].first;
87✔
345
        yMin = yMax = points[0].second;
87✔
346
        for (const auto&[x, y] : points) {
822✔
347
            xMin = min(xMin, x);
735✔
348
            yMin = min(yMin, y);
735✔
349
            xMax = max(xMax, x);
735✔
350
            yMax = max(yMax, y);
735✔
351
        }
352
    }
87✔
353
    return {xMin, yMin, xMax, yMax};
174✔
354
}
87✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc