1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- mode: C++ -*-
3 //
4 // Copyright (C) 2013-2020 Red Hat, Inc.
5
6 #include <stdexcept>
7 #include <fstream>
8
9 #include "abg-internal.h"
10 // <headers defining libabigail's API go under here>
11 ABG_BEGIN_EXPORT_DECLARATIONS
12
13 #include "abg-viz-svg.h"
14
15 ABG_END_EXPORT_DECLARATIONS
16 // </headers defining libabigail's API>
17
18 namespace abigail
19 {
20
21 void
write()22 svg::write()
23 {
24 try
25 {
26 std::string filename(_M_title + ".svg");
27 std::ofstream f(filename);
28 if (!f.is_open() || !f.good())
29 throw std::runtime_error("abigail::svg::write fail");
30
31 f << _M_sstream.str() << std::endl;
32 }
33 catch(std::exception& e)
34 {
35 throw e;
36 }
37 }
38
39 // SVG element beginning boilerplate.
40 // Variable: units, x=0, y=0, width, height
41 void
start_element()42 svg::start_element()
43 {
44 const std::string start = R"_delimiter_(<?xml version="1.0" encoding="utf-8"?>
45 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
46 <svg version="1.1"
47 id="svg2" xml:space="preserve"
48 xmlns:dc="http://purl.org/dc/elements/1.1/"
49 xmlns:cc="http://creativecommons.org/ns#"
50 xmlns:svg="http://www.w3.org/2000/svg"
51 xmlns="http://www.w3.org/2000/svg"
52 xmlns:xlink="http://www.w3.org/1999/xlink"
53 )_delimiter_";
54
55 const std::string units("__units");
56 const std::string width("__width");
57 const std::string height("__height");
58
59 std::string strip = R"_delimiter_(x="0__units" y="0__units"
60 width="__width__units" height="__height__units"
61 viewBox="0 0 __width __height" enable-background="new 0 0 __width __height">
62 )_delimiter_";
63
64 string_replace(strip, units, units_to_string(_M_canvas._M_units));
65 string_replace(strip, width, std::to_string(_M_canvas._M_width));
66 string_replace(strip, height, std::to_string(_M_canvas._M_height));
67
68 _M_sstream << start;
69 _M_sstream << strip << std::endl;
70 }
71
72 // SVG element end boilerplate.
73 void
74 svg::finish_element()
75 {
76 _M_sstream << "</svg>" << std::endl;
77 }
78
79 void
80 svg::add_title()
81 {
82 _M_sstream << "<title>" << _M_title << "</title>" << std::endl;
83 }
84
85 // Column labels
86 // Variable: x, y
87 void
88 svg::add_y_label()
89 {
90 unsigned int xcur = 0;
91 const unsigned int padding = 10;
92 const std::string x("__x");
93 const std::string y("__y");
94 const std::string label("__label");
95 const std::string style("__style");
96 const std::string offset("OFFSET");
97 const std::string size("SIZE");
98 const std::string align("ALIGN");
99
100 // Base text element.
101 std::string text_strip = R"_delimiter_(<text x="__x" y="__y" transform="rotate(270 __x __y)" __style>__label</text>
102 )_delimiter_";
103
104 // These parts are the same for every text element ...
105 string_replace(text_strip, y, std::to_string(_M_y_origin - padding));
106 string_replace(text_strip, style, _M_typo.to_attribute(typography::start));
107
108 // ... just the label and the x position in the center of the current column.
109 xcur = _M_x_origin + (.5 * _M_x_space);
110 std::string offset_strip = text_strip;
111 string_replace(offset_strip, x, std::to_string(xcur));
112 string_replace(offset_strip, label, offset);
113
114 xcur += _M_x_space;
115 std::string size_strip = text_strip;
116 string_replace(size_strip, x, std::to_string(xcur));
117 string_replace(size_strip, label, size);
118
119 xcur += _M_x_space;
120 std::string align_strip = text_strip;
121 string_replace(align_strip, x, std::to_string(xcur));
122 string_replace(align_strip, label, align);
123
124 _M_sstream << "<g><!-- vertical labels -->" << std::endl;
125 _M_sstream << offset_strip;
126 _M_sstream << size_strip;
127 _M_sstream << align_strip;
128 _M_sstream << "</g>" << std::endl;
129 }
130
131 // Draws in 4 vertical hairlines.
132 // Variable: x, y, _M_y_size, _M_y_space
133 void
134 svg::add_y_lines()
135 {
136 unsigned int xcur = 0;
137 const unsigned int yend = _M_y_origin + _M_y_size * _M_y_space;
138 const std::string x("__x");
139 const std::string y1("__y1");
140 const std::string y2("__y2");
141
142 std::string strip = R"_delimiter_(<path stroke="black" stroke-width="1" d="M __x __y1 L __x __y2"/>
143 )_delimiter_";
144
145 // These parts are the same for every text element ...
146 string_replace(strip, y1, std::to_string(_M_y_origin - _M_y_space));
147 string_replace(strip, y2, std::to_string(yend));
148
149 xcur = _M_x_origin;
150 std::string strip_1 = strip;
151 string_replace(strip_1, x, std::to_string(xcur));
152
153 xcur += _M_x_space;
154 std::string strip_2 = strip;
155 string_replace(strip_2, x, std::to_string(xcur));
156
157 xcur += _M_x_space;
158 std::string strip_3 = strip;
159 string_replace(strip_3, x, std::to_string(xcur));
160
161 xcur += _M_x_space;
162 std::string strip_4 = strip;
163 string_replace(strip_4, x, std::to_string(xcur));
164
165
166 _M_sstream << "<g><!-- vertical lines -->" << std::endl;
167 _M_sstream << strip_1;
168 _M_sstream << strip_2;
169 _M_sstream << strip_3;
170 _M_sstream << strip_4;
171 _M_sstream << "</g>" << std::endl;
172 }
173
174 // Add in a row of data.
175 // Columns assumed to be: offset, size, align, data member name/label
176 // Variable: x, y, row type,
177 void
178 svg::add_y_row(const row& __r)
179 {
180 // Background rectangles are horizontally-oriented on column and row
181 // boundaries, and span the second to third column.
182 unsigned int xcur = 0;
183 std::string chroma;
184 const unsigned int ycur = _M_y_origin + (_M_y_size * _M_y_space) + (.5 * _M_y_space);
185 const std::string x("__x");
186 const std::string y("__y");
187 const std::string name("__name");
188 const std::string style("__style");
189 const std::string color("__color");
190 const std::string width("__width");
191 const std::string height("__height");
192 const std::string val("__val");
193
194 std::string rect_strip = R"_delimiter_(<rect x="__x" y="__y" fill="__color" stroke="__color" stroke-width="1" width="__width" height="__height"/>
195 )_delimiter_";
196
197 xcur = _M_x_origin + _M_x_space;
198 chroma = color_to_string(__r._M_style._M_fill_color);
199 string_replace(rect_strip, x, std::to_string(xcur));
200 string_replace(rect_strip, y, std::to_string(ycur - (.5 * _M_y_space)));
201 string_replace(rect_strip, width, std::to_string(_M_x_space * 2));
202 string_replace(rect_strip, height, std::to_string(_M_y_space));
203 string_replace(rect_strip, color, chroma);
204
205
206 // Text template for each bit of data.
207 std::string text_strip = R"_delimiter_(<text x="__x" y="__y" fill="__color" __style>__val</text>
208 )_delimiter_";
209
210 // Column 1 offset
211 // Optional offset, if not a primary type row.
212 std::string offset_strip(text_strip);
213 xcur = _M_x_origin + (.5 * _M_x_space);
214 chroma = color_to_string(abigail::color::black);
215 string_replace(offset_strip, x, std::to_string(xcur));
216 string_replace(offset_strip, y, std::to_string(ycur));
217 string_replace(offset_strip, val, std::to_string(__r._M_offset));
218 string_replace(offset_strip, style, _M_typo.to_attribute(typography::middle));
219 string_replace(offset_strip, color, chroma);
220
221
222 // Column 2 size
223 std::string size_strip(text_strip);
224 xcur += _M_x_space;
225 chroma = color_to_string(__r._M_style._M_text_color);
226 string_replace(size_strip, x, std::to_string(xcur));
227 string_replace(size_strip, y, std::to_string(ycur));
228 string_replace(size_strip, val, std::to_string(__r._M_size));
229 string_replace(size_strip, style, _M_typo.to_attribute(typography::middle));
230 string_replace(size_strip, color, chroma);
231
232
233 // Column 3 align
234 std::string align_strip(text_strip);
235 xcur += _M_x_space;
236 string_replace(align_strip, x, std::to_string(xcur));
237 string_replace(align_strip, y, std::to_string(ycur));
238 string_replace(align_strip, val, std::to_string(__r._M_align));
239 string_replace(align_strip, style, _M_typo.to_attribute(typography::middle));
240 string_replace(align_strip, color, chroma);
241
242
243 // Column 4 data member id
244 const unsigned int padding = 10;
245 std::string name_strip(text_strip);
246 xcur = _M_x_origin + (_M_x_size * _M_x_space) + padding;
247 chroma = color_to_string(abigail::color::black);
248 string_replace(name_strip, x, std::to_string(xcur));
249 string_replace(name_strip, y, std::to_string(ycur));
250 string_replace(name_strip, val, __r._M_id);
251 string_replace(name_strip, style, _M_typo.to_attribute(typography::start));
252 string_replace(name_strip, color, chroma);
253
254
255 // Write out stripped strings.
256 _M_sstream << "<g><!-- row " << _M_y_size << " -->" << std::endl;
257 _M_sstream << rect_strip;
258 _M_sstream << offset_strip;
259 _M_sstream << size_strip;
260 _M_sstream << align_strip;
261 _M_sstream << name_strip;
262 _M_sstream << "</g>" << std::endl;
263
264 ++_M_y_size;
265 }
266
267
268 }//end namespace abigail
269