#include <stdexcept>
+#include <fmt/format.h>
+
#include "svg_analyzer.h"
#include "svgpp_context.h"
#include "svgpp_document_traverser.h"
throw std::runtime_error{
"Wrong number of elements in process after traversing SVG document"};
}
+ m_elements_in_process.pop_back();
+ retrieve_graphviz_components();
+}
+
+const std::vector<GraphvizGraph> &SVGAnalyzer::graphs() const {
+ return m_graphs;
}
void SVGAnalyzer::on_enter_element_svg() { m_num_svgs++; }
current_element().attributes.class_ = class_;
}
+void SVGAnalyzer::retrieve_graphviz_components() {
+ retrieve_graphviz_components_impl(m_svg);
+}
+
+void SVGAnalyzer::retrieve_graphviz_components_impl(
+ SVG::SVGElement &svg_element) {
+ if (svg_element.type == SVG::SVGElementType::Group) {
+ // The SVG 'class' attribute determines which type of Graphviz element a 'g'
+ // element corresponds to
+ const auto class_ = svg_element.attributes.class_;
+ if (class_ == "graph") {
+ m_graphs.emplace_back(svg_element);
+ } else if (class_ == "node") {
+ if (m_graphs.empty()) {
+ throw std::runtime_error{
+ fmt::format("No graph to add node {} to", svg_element.graphviz_id)};
+ }
+ m_graphs.back().add_node(svg_element);
+ } else if (class_ == "edge") {
+ if (m_graphs.empty()) {
+ throw std::runtime_error{
+ fmt::format("No graph to add edge {} to", svg_element.graphviz_id)};
+ }
+ m_graphs.back().add_edge(svg_element);
+ } else if (class_ == "cluster") {
+ // ignore of now
+ } else {
+ throw std::runtime_error("Unknown class" + std::string{class_});
+ }
+ }
+
+ for (auto &child : svg_element.children) {
+ retrieve_graphviz_components_impl(child);
+ }
+}
+
void SVGAnalyzer::set_cx(double cx) { current_element().attributes.cx = cx; }
void SVGAnalyzer::set_cy(double cy) { current_element().attributes.cy = cy; }
#include <string_view>
#include <vector>
+#include "graphviz_graph.h"
#include "svg_analyzer_interface.h"
#include "svg_element.h"
/**
- * @brief The SVGAnalyzer class analyzes the contents of an SVG document.
+ * @brief The SVGAnalyzer class analyzes the contents of an SVG document and
+ * identifies Graphviz graphs, nodes and edges.
*/
class SVGAnalyzer : public ISVGAnalyzer {
public:
SVGAnalyzer(char *text);
+ /// Return a non-mutable reference to the list of Graphviz graphs
+ const std::vector<GraphvizGraph> &graphs() const;
void on_enter_element_svg() override;
void on_enter_element_g() override;
void on_enter_element_circle() override;
/// Get the parent element of the current element being processed by the SVG
/// document traverser
SVG::SVGElement &parent_element();
+ /// Traverses the processed SVG element hierarchy, identifies nodes and edges,
+ /// creates objects representing them and stores them in lists
+ void retrieve_graphviz_components();
+ void retrieve_graphviz_components_impl(SVG::SVGElement &parent_svg_element);
/// A list of pointers to elements currently being processed by the SVG++
/// document traverser, in hierarchical order. The front element is the top
std::size_t m_num_rects = 0;
std::size_t m_num_texts = 0;
std::size_t m_num_titles = 0;
+ /// A list of Graphviz recreated graphs
+ std::vector<GraphvizGraph> m_graphs;
/// The top level SVG `svg` element corresponding to the Graphviz graph
SVG::SVGElement m_svg;
};
#include <boost/algorithm/string.hpp>
+#include <boost/range/adaptor/indexed.hpp>
#include <catch2/catch.hpp>
#include <fmt/format.h>
+#include "graphviz_graph.h"
#include "svg_analyzer.h"
#include <cgraph++/AGraph.h>
#include <gvc++/GVContext.h>
const std::size_t expected_num_nodes = 2;
const std::size_t expected_num_edges = 1;
- {
+ CHECK(svgAnalyzer.graphs().size() == expected_num_graphs);
+ for (const auto &graph : svgAnalyzer.graphs()) {
+ CHECK(graph.svg_g_element().type == SVG::SVGElementType::Group);
+ CHECK(graph.svg_g_element().attributes.class_ == "graph");
+ CHECK(graph.svg_g_element().graphviz_id == "g1");
+
+ CHECK(graph.nodes().size() == expected_num_nodes);
+ for (const auto &node_it : graph.nodes() | boost::adaptors::indexed(0)) {
+ const auto node = node_it.value();
+ const auto i = node_it.index();
+ CHECK(node.svg_g_element().type == SVG::SVGElementType::Group);
+ CHECK(node.svg_g_element().attributes.class_ == "node");
+ const auto node_id = i == 0 ? "a" : "b";
+ CHECK(node.svg_g_element().graphviz_id == node_id);
+ }
+
+ CHECK(graph.edges().size() == expected_num_edges);
+ for (const auto &edge : graph.edges()) {
+ CHECK(edge.svg_g_element().type == SVG::SVGElementType::Group);
+ CHECK(edge.svg_g_element().attributes.class_ == "edge");
+ CHECK(edge.svg_g_element().graphviz_id == "a->b");
+ }
+
const std::size_t expected_num_svgs = expected_num_graphs;
const std::size_t expected_num_groups =
expected_num_graphs + expected_num_nodes + expected_num_edges;