# separate test can be as small as possible
add_library(test_common STATIC
catch2_main.cpp
+ svgpp_context.cpp
+ svgpp_context.h
+ svgpp_document_traverser.cpp
+ svgpp_document_traverser.h
+ svg_analyzer.cpp
+ svg_analyzer.h
+ svg_analyzer_interface.h
../cmd/dot/dot_builtins.c
)
set_target_properties(test_common PROPERTIES CXX_STANDARD 20)
--- /dev/null
+#include "svg_analyzer.h"
+#include "svgpp_context.h"
+#include "svgpp_document_traverser.h"
+
+SVGAnalyzer::SVGAnalyzer(char *text) {
+ SvgppContext context{this};
+ traverseDocumentWithSvgpp(context, text);
+}
+
+void SVGAnalyzer::on_enter_element_svg() { m_num_svgs++; }
+
+void SVGAnalyzer::on_enter_element_g() { m_num_groups++; }
+
+void SVGAnalyzer::on_enter_element_circle() { m_num_circles++; }
+
+void SVGAnalyzer::on_enter_element_ellipse() { m_num_ellipses++; }
+
+void SVGAnalyzer::on_enter_element_line() { m_num_lines++; }
+
+void SVGAnalyzer::on_enter_element_path() { m_num_paths++; }
+
+void SVGAnalyzer::on_enter_element_polygon() { m_num_polygons++; }
+
+void SVGAnalyzer::on_enter_element_polyline() { m_num_polylines++; }
+
+void SVGAnalyzer::on_enter_element_rect() { m_num_rects++; }
+
+void SVGAnalyzer::on_enter_element_title() { m_num_titles++; }
+
+void SVGAnalyzer::on_enter_element_unknown() { m_num_unknowns++; }
--- /dev/null
+#pragma once
+
+#include <cstddef>
+
+#include "svg_analyzer_interface.h"
+
+/**
+ * @brief The SVGAnalyzer class analyzes the contents of an SVG document.
+ */
+
+class SVGAnalyzer : public ISVGAnalyzer {
+public:
+ SVGAnalyzer(char *text);
+ void on_enter_element_svg() override;
+ void on_enter_element_g() override;
+ void on_enter_element_circle() override;
+ void on_enter_element_ellipse() override;
+ void on_enter_element_line() override;
+ void on_enter_element_path() override;
+ void on_enter_element_polygon() override;
+ void on_enter_element_polyline() override;
+ void on_enter_element_rect() override;
+ void on_enter_element_title() override;
+ void on_enter_element_unknown() 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; };
+ std::size_t num_ellipses() const { return m_num_ellipses; };
+ std::size_t num_lines() const { return m_num_lines; };
+ std::size_t num_paths() const { return m_num_paths; };
+ std::size_t num_polygons() const { return m_num_polygons; };
+ std::size_t num_polylines() const { return m_num_polylines; };
+ std::size_t num_rects() const { return m_num_rects; };
+ std::size_t num_titles() const { return m_num_titles; };
+ std::size_t num_unknowns() const { return m_num_unknowns; };
+
+private:
+ 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;
+ std::size_t m_num_circles = 0;
+ std::size_t m_num_ellipses = 0;
+ std::size_t m_num_lines = 0;
+ std::size_t m_num_paths = 0;
+ std::size_t m_num_polygons = 0;
+ std::size_t m_num_polylines = 0;
+ std::size_t m_num_rects = 0;
+ std::size_t m_num_titles = 0;
+ std::size_t m_num_unknowns = 0;
+};
--- /dev/null
+#pragma once
+
+/**
+ * @brief The ISVGAnalyzer class is an interface class declaring
+ * callbacks that can be implemented by an SVGAnalyzer class. Its
+ * purpose is to isolate the SVGAnalyzer class from the SvgppContext
+ * class to avoid recompiling the SvgppContext class when the
+ * SVGAnalyzer class is changed, which is expected to happen
+ * frequently.
+ */
+
+class ISVGAnalyzer {
+public:
+ virtual ~ISVGAnalyzer() = default;
+ virtual void on_enter_element_svg() = 0;
+ virtual void on_enter_element_g() = 0;
+ virtual void on_enter_element_circle() = 0;
+ virtual void on_enter_element_ellipse() = 0;
+ virtual void on_enter_element_line() = 0;
+ virtual void on_enter_element_path() = 0;
+ virtual void on_enter_element_polygon() = 0;
+ virtual void on_enter_element_polyline() = 0;
+ virtual void on_enter_element_rect() = 0;
+ virtual void on_enter_element_title() = 0;
+ virtual void on_enter_element_unknown() = 0;
+};
--- /dev/null
+#include <any>
+
+#include "svg_analyzer_interface.h"
+#include "svgpp_context.h"
+
+SvgppContext::SvgppContext(ISVGAnalyzer *svgAnalyzer)
+ : m_svgAnalyzer(svgAnalyzer){};
+
+void SvgppContext::on_enter_element(svgpp::tag::element::svg e) {
+ (void)e;
+ m_svgAnalyzer->on_enter_element_svg();
+}
+
+void SvgppContext::on_enter_element(svgpp::tag::element::g e) {
+ (void)e;
+ m_svgAnalyzer->on_enter_element_g();
+}
+
+void SvgppContext::on_enter_element(svgpp::tag::element::circle e) {
+ (void)e;
+ m_svgAnalyzer->on_enter_element_circle();
+}
+
+void SvgppContext::on_enter_element(svgpp::tag::element::ellipse e) {
+ (void)e;
+ m_svgAnalyzer->on_enter_element_ellipse();
+}
+
+void SvgppContext::on_enter_element(svgpp::tag::element::line e) {
+ (void)e;
+ m_svgAnalyzer->on_enter_element_line();
+}
+
+void SvgppContext::on_enter_element(svgpp::tag::element::path e) {
+ (void)e;
+ m_svgAnalyzer->on_enter_element_path();
+}
+
+void SvgppContext::on_enter_element(svgpp::tag::element::polygon e) {
+ (void)e;
+ m_svgAnalyzer->on_enter_element_polygon();
+}
+
+void SvgppContext::on_enter_element(svgpp::tag::element::polyline e) {
+ (void)e;
+ m_svgAnalyzer->on_enter_element_polyline();
+}
+
+void SvgppContext::on_enter_element(svgpp::tag::element::rect e) {
+ (void)e;
+ m_svgAnalyzer->on_enter_element_rect();
+}
+
+void SvgppContext::on_enter_element(svgpp::tag::element::title e) {
+ (void)e;
+ m_svgAnalyzer->on_enter_element_title();
+}
+
+void SvgppContext::on_enter_element(svgpp::tag::element::any) {
+ m_svgAnalyzer->on_enter_element_unknown();
+}
+
+void SvgppContext::on_exit_element() {}
+
+void SvgppContext::path_move_to(double x, double y,
+ svgpp::tag::coordinate::absolute c) {
+ (void)x;
+ (void)y;
+ (void)c;
+}
+
+void SvgppContext::path_line_to(double x, double y,
+ svgpp::tag::coordinate::absolute c) {
+ (void)x;
+ (void)y;
+ (void)c;
+}
+
+void SvgppContext::path_cubic_bezier_to(double x1, double y1, double x2,
+ double y2, double x, double y,
+ svgpp::tag::coordinate::absolute c) {
+ (void)x1;
+ (void)y1;
+ (void)x2;
+ (void)y2;
+ (void)x;
+ (void)y;
+ (void)c;
+}
+
+void SvgppContext::path_quadratic_bezier_to(
+ double x1, double y1, double x, double y,
+ svgpp::tag::coordinate::absolute c) {
+ (void)x1;
+ (void)y1;
+ (void)x;
+ (void)y;
+ (void)c;
+}
+
+void SvgppContext::path_elliptical_arc_to(double rx, double ry,
+ double x_axis_rotation,
+ bool large_arc_flag, bool sweep_flag,
+ double x, double y,
+ svgpp::tag::coordinate::absolute c) {
+ (void)rx;
+ (void)ry;
+ (void)x_axis_rotation;
+ (void)large_arc_flag;
+ (void)sweep_flag;
+ (void)x;
+ (void)y;
+ (void)c;
+}
+
+void SvgppContext::path_close_subpath() {}
+
+void SvgppContext::path_exit() {}
+
+void SvgppContext::set(svgpp::tag::attribute::cy a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::cx a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::r a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::rx a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::ry a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::x1 a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::y1 a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::x2 a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::y2 a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::x a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::y a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::width a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set(svgpp::tag::attribute::height a, const double v) {
+ (void)a;
+ (void)v;
+}
+
+void SvgppContext::set_impl(svgpp::tag::attribute::points &points,
+ const std::any &range) {
+ (void)points;
+ (void)range;
+ // ignore for now
+}
+
+void SvgppContext::set_text_impl(const std::any &range) {
+ (void)range;
+ // ignore for now
+}
--- /dev/null
+#pragma once
+
+#include <any>
+
+#include <svgpp/svgpp.hpp>
+
+class ISVGAnalyzer;
+
+/**
+ * @brief The SvgppContext class provides a context containing SVG element
+ * callbacks for the SVG++ parser which is called from the SVG document
+ * traverser and forwards these callbacks to the SVG analyzer. It's separated
+ * from the SVG analyzer to avoid the very time consuming recompilation of the
+ * SVG document traverser when then SVG analyzer header is changed, which is
+ * expected to happen often as new functionality is added.
+ *
+ * Most of this is taken from
+ * http://svgpp.org/lesson01.html#handling-shapes-geometry.
+ */
+
+class SvgppContext {
+public:
+ SvgppContext(ISVGAnalyzer *svgAnalyzer);
+ void on_enter_element(svgpp::tag::element::svg e);
+ void on_enter_element(svgpp::tag::element::g e);
+ void on_enter_element(svgpp::tag::element::circle e);
+ void on_enter_element(svgpp::tag::element::ellipse e);
+ void on_enter_element(svgpp::tag::element::line e);
+ void on_enter_element(svgpp::tag::element::path e);
+ void on_enter_element(svgpp::tag::element::polygon e);
+ void on_enter_element(svgpp::tag::element::polyline e);
+ void on_enter_element(svgpp::tag::element::rect e);
+ void on_enter_element(svgpp::tag::element::title e);
+ void on_enter_element(svgpp::tag::element::any e);
+ void on_exit_element();
+ void path_move_to(double x, double y, svgpp::tag::coordinate::absolute);
+ void path_line_to(double x, double y, svgpp::tag::coordinate::absolute);
+ void path_cubic_bezier_to(double x1, double y1, double x2, double y2,
+ double x, double y,
+ svgpp::tag::coordinate::absolute);
+ void path_quadratic_bezier_to(double x1, double y1, double x, double y,
+ svgpp::tag::coordinate::absolute);
+ void path_elliptical_arc_to(double rx, double ry, double x_axis_rotation,
+ bool large_arc_flag, bool sweep_flag, double x,
+ double y, svgpp::tag::coordinate::absolute);
+ void path_close_subpath();
+ void path_exit();
+ void set(svgpp::tag::attribute::cy cy, const double v);
+ void set(svgpp::tag::attribute::cx cx, const double v);
+ void set(svgpp::tag::attribute::r r, const double v);
+ void set(svgpp::tag::attribute::rx rx, const double v);
+ void set(svgpp::tag::attribute::ry ry, const double v);
+ void set(svgpp::tag::attribute::x1 x1, const double v);
+ void set(svgpp::tag::attribute::y1 y1, const double v);
+ void set(svgpp::tag::attribute::x2 x2, const double v);
+ void set(svgpp::tag::attribute::y2 y2, const double v);
+ template <typename Range>
+ void set(svgpp::tag::attribute::points points, const Range &range) {
+ set_impl(points, range);
+ }
+ void set(svgpp::tag::attribute::x a, const double v);
+ void set(svgpp::tag::attribute::y y, const double v);
+ void set(svgpp::tag::attribute::width width, const double v);
+ void set(svgpp::tag::attribute::height height, const double v);
+ template <class Range> void set_text(const Range &range) {
+ set_text_impl(range);
+ }
+
+private:
+ void set_impl(svgpp::tag::attribute::points &points, const std::any &range);
+ void set_text_impl(const std::any &range);
+
+private:
+ ISVGAnalyzer *m_svgAnalyzer = nullptr;
+};
--- /dev/null
+#include <rapidxml_ns/rapidxml_ns.hpp>
+#include <svgpp/policy/xml/rapidxml_ns.hpp>
+#include <svgpp/svgpp.hpp>
+
+#include "svgpp_context.h"
+#include "svgpp_document_traverser.h"
+
+// most of this is taken from
+// http://svgpp.org/lesson01.html#handling-shapes-geometry
+
+void traverseDocumentWithSvgpp(SvgppContext &context, char *text) {
+ rapidxml_ns::xml_document<> doc;
+ doc.parse<0>(text);
+ if (rapidxml_ns::xml_node<> *svg_element = doc.first_node("svg")) {
+ const rapidxml_ns::xml_node<> *xml_root_element = svg_element;
+
+ using processed_elements_t = boost::mpl::set<
+ // SVG Structural Elements
+ svgpp::tag::element::svg, svgpp::tag::element::g,
+ // SVG Shape Elements
+ svgpp::tag::element::circle, svgpp::tag::element::ellipse,
+ svgpp::tag::element::line, svgpp::tag::element::path,
+ svgpp::tag::element::polygon, svgpp::tag::element::polyline,
+ svgpp::tag::element::rect, svgpp::tag::element::title>::type;
+
+ svgpp::document_traversal<
+ svgpp::processed_elements<processed_elements_t>,
+ svgpp::processed_attributes<
+ svgpp::traits::shapes_attributes_by_element>,
+ svgpp::basic_shapes_policy<svgpp::policy::basic_shapes::raw>>::
+ load_document(xml_root_element, context);
+ }
+}
--- /dev/null
+#pragma once
+
+/**
+ * @brief The traverseDocumentWithSvgpp function traverses the SVG
+ * document through the SVG++ document loader and provides it with a
+ * context containing callbacks for handling SVG elements. It is
+ * separated from the SvgppContext class to reduce the compile time of
+ * the SvgppContext class.
+ */
+
+class SvgppContext;
+
+void traverseDocumentWithSvgpp(SvgppContext &context, char *text);