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