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

CyberZHG / SVGDiagram / 20161633397

12 Dec 2025 09:00AM UTC coverage: 96.218% (+0.8%) from 95.382%
20161633397

Pull #5

github

web-flow
Merge 97700b239 into e57fdfa95
Pull Request #5: Draw subgraph

206 of 210 new or added lines in 2 files covered. (98.1%)

1603 of 1666 relevant lines covered (96.22%)

1070.4 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,233✔
13
    if (x1 > x2) {
1,233✔
14
        swap(x1, x2);
44✔
15
    }
16
    if (y1 > y2) {
1,233✔
17
        swap(y1, y2);
29✔
18
    }
19
    this->x1 = x1;
1,233✔
20
    this->x2 = x2;
1,233✔
21
    this->y1 = y1;
1,233✔
22
    this->y2 = y2;
1,233✔
23
}
1,233✔
24

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

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

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

44
XMLElement::ChildrenType SVGDrawComment::generateXMLElements() const {
338✔
45
    return {make_shared<XMLElementComment>(comment)};
1,014✔
46
}
338✔
47

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

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

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

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

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

70
XMLElement::ChildrenType SVGDrawGroup::generateXMLElements() const {
346✔
71
    const auto groupElement = make_shared<XMLElement>("g");
346✔
72
    addAttributesToXMLElement(groupElement);
346✔
73
    for (const auto& child : children) {
1,527✔
74
        groupElement->addChildren(child->generateXMLElements());
1,181✔
75
    }
76
    return {groupElement};
1,384✔
77
}
692✔
78

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

313
SVGDrawBoundingBox SVGDrawLine::boundingBox() const {
112✔
314
    return {x1, y1, x2, y2};
112✔
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 TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc