]> granicus.if.org Git - graphviz/commitdiff
tests: SVGAnalyzer: add support for retrieving the Graphviz edge fillcolor
authorMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Tue, 23 Aug 2022 09:25:17 +0000 (11:25 +0200)
committerMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Mon, 5 Sep 2022 07:01:20 +0000 (09:01 +0200)
tests/CMakeLists.txt
tests/graphviz_edge.cpp
tests/graphviz_edge.h
tests/svg_element.cpp
tests/svg_element.h
tests/test_edge_fillcolor.cpp [new file with mode: 0644]
tests/test_utilities.cpp
tests/test_utilities.h

index 55f63ae056d7dcc8011c417c3bfded994e976d99..f4a49b059405c032b933c5dd83c3050500b300bd 100644 (file)
@@ -79,6 +79,7 @@ endmacro()
 CREATE_TEST(AGraph_construction)
 CREATE_TEST(clusters)
 CREATE_TEST(edge_color)
+CREATE_TEST(edge_fillcolor)
 CREATE_TEST(edge_penwidth)
 CREATE_TEST(engines)
 CREATE_TEST(GVContext_construction)
index 88eb1ef7d1ef1f7c5e4995d85872f8dbeaf62e69..03c1a9aa72ada1879314d8cc54efc3fc5af8c784 100644 (file)
@@ -1,12 +1,25 @@
 #include <string>
 
 #include "graphviz_edge.h"
+#include "svg_element.h"
 
 GraphvizEdge::GraphvizEdge(SVG::SVGElement &svg_g_element)
     : m_edgeop(svg_g_element.graphviz_id), m_svg_g_element(svg_g_element) {}
 
 std::string_view GraphvizEdge::edgeop() const { return m_edgeop; }
 
+std::string GraphvizEdge::fillcolor() const {
+  const auto fill = m_svg_g_element.attribute_from_subtree<std::string>(
+      &SVG::SVGAttributes::fill, &SVG::SVGElement::is_closed_shape_element, "");
+  const auto fill_opacity = m_svg_g_element.attribute_from_subtree<double>(
+      &SVG::SVGAttributes::fill_opacity,
+      &SVG::SVGElement::is_closed_shape_element, 1);
+  if (fill.empty() && fill_opacity == 1) {
+    return "";
+  }
+  return SVG::to_dot_color(fill, fill_opacity);
+}
+
 const SVG::SVGElement &GraphvizEdge::svg_g_element() const {
   return m_svg_g_element;
 }
index 6840127d72dc77d5aab0784a71b84c55534f5345..743fca1608d51e4bd9e13f9e35e845671335b6d6 100644 (file)
@@ -25,6 +25,9 @@ public:
   /// Return the 'edgeop' according to the DOT language specification. Note that
   /// this is not the same as the 'id' attribute of an edge.
   std::string_view edgeop() const;
+  /// Return the edge's `fillcolor` attribute in RGB hex format if the opacity
+  /// is 100 % or in RGBA hex format otherwise.
+  std::string fillcolor() const;
   /// Return the edge's `penwidth` attribute
   double penwidth() const;
   /// Return a non-mutable reference to the SVG `g` element corresponding to the
index b00bcf01609be5d5332aca4478a7e6464ef55049..5870b3da198d9adc2a609b27a9e6008659d3b554 100644 (file)
@@ -16,6 +16,18 @@ static double px_to_pt(double px) {
   return px * 3 / 4;
 }
 
+bool SVG::SVGElement::is_closed_shape_element() const {
+  switch (type) {
+  case SVG::SVGElementType::Circle:
+  case SVG::SVGElementType::Ellipse:
+  case SVG::SVGElementType::Polygon:
+  case SVG::SVGElementType::Rect:
+    return true;
+  default:
+    return false;
+  }
+}
+
 bool SVG::SVGElement::is_shape_element() const {
   switch (type) {
   case SVG::SVGElementType::Circle:
index da557aa130caed7b8c0c14b964d8d71011bc5a3f..18b83f1e4bc1886c7d604a2f7d5ebd709a45a9d1 100644 (file)
@@ -135,6 +135,7 @@ public:
   /// throw an exception unless the `throw_if_bbox_not_defined` parameter is
   /// `false`.
   SVG::SVGRect bbox(bool throw_if_bbox_not_defined = true);
+  bool is_closed_shape_element() const;
   bool is_shape_element() const;
   std::string to_string(std::size_t indent_size) const;
 
diff --git a/tests/test_edge_fillcolor.cpp b/tests/test_edge_fillcolor.cpp
new file mode 100644 (file)
index 0000000..5a7afd5
--- /dev/null
@@ -0,0 +1,50 @@
+#include <string_view>
+
+#include <catch2/catch.hpp>
+#include <fmt/format.h>
+
+#include "svg_analyzer.h"
+#include "test_utilities.h"
+
+TEST_CASE("Edge color",
+          "Test that the Graphviz `color` attribute is used to set the "
+          "`stroke` and `stroke-opacity` attributes correctly for edges in the "
+          "generated SVG") {
+
+  const auto primitive_arrow_shape =
+      GENERATE(from_range(all_primitive_arrow_shapes));
+  INFO(fmt::format("Primitive arrow shape: {}", primitive_arrow_shape));
+
+  const auto edge_rgb_fillcolor = GENERATE("#000000", "#ffffff", "#5580aa");
+  const auto opacity = GENERATE(0, 100, 255);
+  const auto edge_rgba_fillcolor =
+      fmt::format("{}{:02x}", edge_rgb_fillcolor, opacity);
+  INFO(fmt::format("Edge fillcolor: {}", edge_rgba_fillcolor));
+
+  auto dot =
+      fmt::format("digraph g1 {{edge [arrowhead={} fillcolor=\"{}\"]; a -> b}}",
+                  primitive_arrow_shape, edge_rgba_fillcolor);
+
+  const auto engine = "dot";
+  auto svg_analyzer = SVGAnalyzer::make_from_dot(dot, engine);
+
+  const auto expected_edge_fillcolor = [&]() -> const std::string {
+    if (primitive_arrow_shapes_without_closed_svg_shape.contains(
+            primitive_arrow_shape)) {
+      return "";
+    }
+    if (opacity == 255) {
+      return edge_rgb_fillcolor;
+    }
+    if (opacity == 0) {
+      return "#00000000"; // transparent/none
+    }
+    return edge_rgba_fillcolor;
+  }();
+
+  for (const auto &graph : svg_analyzer.graphs()) {
+    for (const auto &edge : graph.edges()) {
+      CHECK(edge.fillcolor() == expected_edge_fillcolor);
+    }
+  }
+}
index 161531420db68156ead5588960b7f3a8d94f64ff..da7bb6eb728794baae56f0450a081cab6b002513 100644 (file)
@@ -184,5 +184,9 @@ const std::unordered_set<std::string_view> all_primitive_arrow_shapes = {
     "box", "crow", "curve",  "diamond", "dot", "icurve",
     "inv", "none", "normal", "tee",     "vee"};
 
+const std::unordered_set<std::string_view>
+    primitive_arrow_shapes_without_closed_svg_shape = {"curve", "icurve",
+                                                       "none"};
+
 const std::unordered_set<std::string_view> all_rank_directions = {"TB", "BT",
                                                                   "LR", "RL"};
index d3f4c7d431ac9868d6f1cfc994ec9ae983780ab9..887bb7a4d4276f96c03bb00f267029eae66d81f8 100644 (file)
@@ -29,6 +29,8 @@ extern const std::unordered_set<std::string_view>
 extern const std::unordered_set<std::string_view>
     primitive_polygon_and_polyline_arrow_shapes;
 extern const std::unordered_set<std::string_view> all_primitive_arrow_shapes;
+extern const std::unordered_set<std::string_view>
+    primitive_arrow_shapes_without_closed_svg_shape;
 
 /// rank directions
 extern const std::unordered_set<std::string_view> all_rank_directions;