]> granicus.if.org Git - graphviz/commitdiff
tests: SvgAnalyzer: add internal re-creation of the SVG element hierarchy
authorMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Thu, 21 Jul 2022 11:16:41 +0000 (13:16 +0200)
committerMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Tue, 16 Aug 2022 10:21:45 +0000 (12:21 +0200)
This will be used in upcoming commits in this series to store
attributes on and to re-create the original SVG from. Also, an
upcoming commit series will extend the SVG analyzer to be aware of
Graphviz graphs, nodes and edges and will use this hierarchy and the
associated attributes to be able to answer questions about their
properties.

tests/CMakeLists.txt
tests/svg_analyzer.cpp
tests/svg_analyzer.h
tests/svg_analyzer_interface.h
tests/svg_element.cpp [new file with mode: 0644]
tests/svg_element.h [new file with mode: 0644]
tests/svgpp_context.cpp

index 0a1b956c7c291f073a04396f682d59ed17860fa5..0ccd8cc982bf8b524738d910c12b1ef81f45dab5 100644 (file)
@@ -20,6 +20,8 @@ add_library(test_common SHARED
             svg_analyzer.cpp
             svg_analyzer.h
             svg_analyzer_interface.h
+            svg_element.cpp
+            svg_element.h
             ../cmd/dot/dot_builtins.cpp
 )
 set_target_properties(test_common PROPERTIES CXX_STANDARD 20)
index 2ac848a143df853c3427348b71fe5c11edeee31f..a69591a40217a2e374c40577ff01c2f7de7bc0bf 100644 (file)
@@ -1,28 +1,84 @@
+#include <stdexcept>
+
 #include "svg_analyzer.h"
 #include "svgpp_context.h"
 #include "svgpp_document_traverser.h"
 
-SVGAnalyzer::SVGAnalyzer(char *text) {
+SVGAnalyzer::SVGAnalyzer(char *text)
+    : m_svg(SVG::SVGElement(SVG::SVGElementType::Svg)) {
+  m_elements_in_process.push_back(&m_svg);
   SvgppContext context{this};
   traverseDocumentWithSvgpp(context, text);
+  if (m_elements_in_process.size() != 1) {
+    throw std::runtime_error{
+        "Wrong number of elements in process after traversing SVG document"};
+  }
 }
 
 void SVGAnalyzer::on_enter_element_svg() { m_num_svgs++; }
 
-void SVGAnalyzer::on_enter_element_g() { m_num_groups++; }
+void SVGAnalyzer::on_enter_element_g() {
+  enter_element(SVG::SVGElementType::Group);
+  m_num_groups++;
+}
 
-void SVGAnalyzer::on_enter_element_circle() { m_num_circles++; }
+void SVGAnalyzer::on_enter_element_circle() {
+  enter_element(SVG::SVGElementType::Circle);
+  m_num_circles++;
+}
 
-void SVGAnalyzer::on_enter_element_ellipse() { m_num_ellipses++; }
+void SVGAnalyzer::on_enter_element_ellipse() {
+  enter_element(SVG::SVGElementType::Ellipse);
+  m_num_ellipses++;
+}
 
-void SVGAnalyzer::on_enter_element_line() { m_num_lines++; }
+void SVGAnalyzer::on_enter_element_line() {
+  enter_element(SVG::SVGElementType::Line);
+  m_num_lines++;
+}
 
-void SVGAnalyzer::on_enter_element_path() { m_num_paths++; }
+void SVGAnalyzer::on_enter_element_path() {
+  enter_element(SVG::SVGElementType::Path);
+  m_num_paths++;
+}
 
-void SVGAnalyzer::on_enter_element_polygon() { m_num_polygons++; }
+void SVGAnalyzer::on_enter_element_polygon() {
+  enter_element(SVG::SVGElementType::Polygon);
+  m_num_polygons++;
+}
 
-void SVGAnalyzer::on_enter_element_polyline() { m_num_polylines++; }
+void SVGAnalyzer::on_enter_element_polyline() {
+  enter_element(SVG::SVGElementType::Polyline);
+  m_num_polylines++;
+}
 
-void SVGAnalyzer::on_enter_element_rect() { m_num_rects++; }
+void SVGAnalyzer::on_enter_element_rect() {
+  enter_element(SVG::SVGElementType::Rect);
+  m_num_rects++;
+}
+
+void SVGAnalyzer::on_enter_element_title() {
+  enter_element(SVG::SVGElementType::Title);
+  m_num_titles++;
+}
+
+void SVGAnalyzer::on_exit_element() { m_elements_in_process.pop_back(); }
 
-void SVGAnalyzer::on_enter_element_title() { m_num_titles++; }
+SVG::SVGElement &SVGAnalyzer::current_element() {
+  if (m_elements_in_process.empty()) {
+    throw std::runtime_error{"No current element"};
+  }
+  return *m_elements_in_process.back();
+}
+
+void SVGAnalyzer::enter_element(SVG::SVGElementType type) {
+  if (m_elements_in_process.empty()) {
+    throw std::runtime_error{
+        "No element is currently being processed by the SVG++ document "
+        "traverser when entering a new element. Expecting at least the top "
+        "level 'svg' to be in process"};
+  }
+  auto &element = current_element();
+  element.children.emplace_back(type);
+  m_elements_in_process.push_back(&element.children.back());
+}
index 77aff3aa33772d11bec3c62d24e1bc37e440b165..124c1d7df77f4da7351f3b16adddd02975754223 100644 (file)
@@ -1,8 +1,10 @@
 #pragma once
 
 #include <cstddef>
+#include <vector>
 
 #include "svg_analyzer_interface.h"
+#include "svg_element.h"
 
 /**
  * @brief The SVGAnalyzer class analyzes the contents of an SVG document.
@@ -21,6 +23,7 @@ public:
   void on_enter_element_polyline() override;
   void on_enter_element_rect() override;
   void on_enter_element_title() override;
+  void on_exit_element() override;
   std::size_t num_svgs() const { return m_num_svgs; };
   std::size_t num_groups() const { return m_num_groups; };
   std::size_t num_circles() const { return m_num_circles; };
@@ -33,6 +36,16 @@ public:
   std::size_t num_titles() const { return m_num_titles; };
 
 private:
+  /// Get the current element being processed by the SVG document traverser
+  SVG::SVGElement &current_element();
+  /// Enter a new SVG element retrieved by the SVG document traverser into the
+  /// list of elements currently being processed
+  void enter_element(SVG::SVGElementType type);
+
+  /// A list of pointers to elements currently being processed by the SVG++
+  /// document traverser, in hierarchical order. The front element is the top
+  /// level SVG and the back element is the current element.
+  std::vector<SVG::SVGElement *> m_elements_in_process;
   std::size_t m_num_svgs = 1; // the top level svg is implicit. See
                               // https://github.com/svgpp/svgpp/issues/98
   std::size_t m_num_groups = 0;
@@ -44,4 +57,6 @@ private:
   std::size_t m_num_polylines = 0;
   std::size_t m_num_rects = 0;
   std::size_t m_num_titles = 0;
+  /// The top level SVG `svg` element corresponding to the Graphviz graph
+  SVG::SVGElement m_svg;
 };
index a0a0bc431ea2043bd2a1c6d1e52b1a89ca2f5926..53f40f52caee7965101c6dedef7fbb9fdb96013a 100644 (file)
@@ -22,4 +22,5 @@ public:
   virtual void on_enter_element_polyline() = 0;
   virtual void on_enter_element_rect() = 0;
   virtual void on_enter_element_title() = 0;
+  virtual void on_exit_element() = 0;
 };
diff --git a/tests/svg_element.cpp b/tests/svg_element.cpp
new file mode 100644 (file)
index 0000000..719430c
--- /dev/null
@@ -0,0 +1,3 @@
+#include "svg_element.h"
+
+SVG::SVGElement::SVGElement(SVGElementType type) : type(type) {}
diff --git a/tests/svg_element.h b/tests/svg_element.h
new file mode 100644 (file)
index 0000000..0e8acc8
--- /dev/null
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <vector>
+
+namespace SVG {
+
+enum class SVGElementType {
+  Circle,
+  Ellipse,
+  Group,
+  Line,
+  Path,
+  Polygon,
+  Polyline,
+  Rect,
+  Svg,
+  Text,
+  Title,
+};
+
+/**
+ * @brief The SVGElement class represents an SVG element
+ */
+
+class SVGElement {
+public:
+  SVGElement() = delete;
+  explicit SVGElement(SVG::SVGElementType type);
+
+  std::vector<SVGElement> children;
+  /// The type of SVG element
+  const SVGElementType type;
+};
+
+} // namespace SVG
index b69f5cb98e9023773bce26aabcf0956f696f5197..9d13c607f3877947127c1b5c63a09cf404cbc67f 100644 (file)
@@ -46,7 +46,7 @@ void SvgppContext::on_enter_element(svgpp::tag::element::title) {
   m_svgAnalyzer->on_enter_element_title();
 }
 
-void SvgppContext::on_exit_element() {}
+void SvgppContext::on_exit_element() { m_svgAnalyzer->on_exit_element(); }
 
 void SvgppContext::path_move_to(double x, double y,
                                 svgpp::tag::coordinate::absolute c) {