]> granicus.if.org Git - graphviz/commitdiff
tests: SVGAnalyzer: add handling of the 'fill-opacity' attribute
authorMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Wed, 10 Aug 2022 14:40:44 +0000 (16:40 +0200)
committerMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Mon, 5 Sep 2022 07:01:20 +0000 (09:01 +0200)
tests/CMakeLists.txt
tests/svg_analyzer.cpp
tests/svg_analyzer.h
tests/svg_analyzer_interface.h
tests/svg_element.cpp
tests/svg_element.h
tests/svgpp_context.cpp
tests/svgpp_context.h
tests/svgpp_document_traverser.cpp
tests/test_svg_analyzer_color.cpp
tests/test_svg_analyzer_fillcolor.cpp [new file with mode: 0644]

index 9e29151139b63cb28a07316bbc5ab66702c4d2e6..9d1b82692458465b9ef6c7b360eef8e0aa312a20 100644 (file)
@@ -94,4 +94,5 @@ CREATE_TEST(subgraph_layout)
 CREATE_TEST(subgraphs)
 CREATE_TEST(svg_analyzer)
 CREATE_TEST(svg_analyzer_color)
+CREATE_TEST(svg_analyzer_fillcolor)
 CREATE_TEST(svg_analyzer_penwidth)
index f46d96be27934e68417fea77d24501721f3c9669..f6f2df93fccdcef22a8263d9b672b7443136fe1a 100644 (file)
@@ -219,6 +219,10 @@ void SVGAnalyzer::set_fill(std::string_view fill) {
   current_element().attributes.fill = fill;
 }
 
+void SVGAnalyzer::set_fill_opacity(double fill_opacity) {
+  current_element().attributes.fill_opacity = fill_opacity;
+}
+
 void SVGAnalyzer::set_height(double height) {
   current_element().attributes.height = height;
 }
index db0c0fb9c7006376f1f295dc62fc17572492cab9..e2cd2e3f15a7a31a67e2c30f08442cf56018d94b 100644 (file)
@@ -40,6 +40,7 @@ public:
   void set_font_family(std::string_view font_family) override;
   void set_font_size(double font_size) override;
   void set_fill(std::string_view fill) override;
+  void set_fill_opacity(double fill_opacity) override;
   void set_height(double height) override;
   void set_id(std::string_view id) override;
   void set_rx(double rx) override;
index f2b44e3d470bdd4e6dc34af5149dc9e70b0aed31..2633a367c2bdc253f0fdf08c98fc4e186da8f10d 100644 (file)
@@ -36,6 +36,7 @@ public:
   virtual void set_font_family(std::string_view font_family) = 0;
   virtual void set_font_size(double font_size) = 0;
   virtual void set_fill(std::string_view fill) = 0;
+  virtual void set_fill_opacity(double fill_opacity) = 0;
   virtual void set_height(double height) = 0;
   virtual void set_id(std::string_view id) = 0;
   virtual void set_rx(double rx) = 0;
index 76a5b9ce323efad78464ce5adfc650d63649084a..b00bcf01609be5d5332aca4478a7e6464ef55049 100644 (file)
@@ -221,6 +221,21 @@ std::string SVG::SVGElement::id_attribute_to_string() const {
   return fmt::format(R"(id="{}")", attributes.id);
 }
 
+std::string SVG::SVGElement::fill_opacity_attribute_to_string() const {
+  if (attributes.fill_opacity == 1) {
+    // Graphviz doesn't set `fill-opacity` to 1 since that's the default
+    return "";
+  }
+
+  if (attributes.fill_opacity == 0) {
+    // Graphviz doesn't set `fill-opacity` to 0 since in that case it sets
+    // `fill` to "none" instead
+    return "";
+  }
+
+  return fmt::format(R"(fill-opacity="{}")", attributes.fill_opacity);
+}
+
 std::string SVG::SVGElement::points_attribute_to_string() const {
   std::string points_attribute_str = R"|(points=")|";
   const char *separator = "";
@@ -305,6 +320,7 @@ void SVG::SVGElement::to_string_impl(std::string &output,
   switch (type) {
   case SVG::SVGElementType::Ellipse:
     append_attribute(attributes_str, fill_attribute_to_string());
+    append_attribute(attributes_str, fill_opacity_attribute_to_string());
     append_attribute(attributes_str, stroke_attribute_to_string());
     append_attribute(attributes_str, stroke_width_attribute_to_string());
     append_attribute(attributes_str, stroke_opacity_attribute_to_string());
@@ -323,6 +339,7 @@ void SVG::SVGElement::to_string_impl(std::string &output,
     break;
   case SVG::SVGElementType::Path: {
     append_attribute(attributes_str, fill_attribute_to_string());
+    append_attribute(attributes_str, fill_opacity_attribute_to_string());
     append_attribute(attributes_str, stroke_attribute_to_string());
     append_attribute(attributes_str, stroke_width_attribute_to_string());
     append_attribute(attributes_str, stroke_opacity_attribute_to_string());
@@ -348,6 +365,7 @@ void SVG::SVGElement::to_string_impl(std::string &output,
   }
   case SVG::SVGElementType::Polygon:
     append_attribute(attributes_str, fill_attribute_to_string());
+    append_attribute(attributes_str, fill_opacity_attribute_to_string());
     append_attribute(attributes_str, stroke_attribute_to_string());
     append_attribute(attributes_str, stroke_width_attribute_to_string());
     append_attribute(attributes_str, stroke_opacity_attribute_to_string());
index 5dd4918cfd319baaf600367e1310a58fd47f7d05..da557aa130caed7b8c0c14b964d8d71011bc5a3f 100644 (file)
@@ -60,6 +60,7 @@ struct SVGAttributes {
   double cx;
   double cy;
   std::string fill;
+  double fill_opacity = 1;
   std::string font_family;
   double font_size;
   double height;
@@ -162,6 +163,7 @@ private:
                         const std::string &attribute) const;
   std::string id_attribute_to_string() const;
   std::string fill_attribute_to_string() const;
+  std::string fill_opacity_attribute_to_string() const;
   std::string points_attribute_to_string() const;
   std::string stroke_attribute_to_string() const;
   std::string stroke_opacity_attribute_to_string() const;
index de7b397e57f935ca9aa83e62f23e4dffa395cc7e..003f5916bd9ccc69fc567a92c5620426ad68e33b 100644 (file)
@@ -126,6 +126,10 @@ void SvgppContext::set(svgpp::tag::attribute::fill, color_t color,
   m_svg_analyzer->set_fill(to_color_string(color));
 }
 
+void SvgppContext::set(svgpp::tag::attribute::fill_opacity, const double v) {
+  m_svg_analyzer->set_fill_opacity(v);
+}
+
 void SvgppContext::set(svgpp::tag::attribute::stroke, svgpp::tag::value::none) {
   m_svg_analyzer->set_stroke("none");
 }
index ab346caa3e8bbafdc61b7e39c7ad9b066ada1df6..250cc43d0e809ff30b4177d2b060d1e228d7b721 100644 (file)
@@ -99,6 +99,8 @@ public:
     throw std::runtime_error{
         "this flavor of the 'fill' attribute is not yet implemented"};
   };
+
+  void set(svgpp::tag::attribute::fill_opacity, double v);
   void set(svgpp::tag::attribute::stroke, svgpp::tag::value::none);
   void set(svgpp::tag::attribute::stroke, svgpp::tag::value::currentColor);
   void set(svgpp::tag::attribute::stroke, color_t color,
index 1d386638c7651af67a56bbe8f64dcc329bf3e612..9d64213ac6a760ae7406876154a51333845f22aa 100644 (file)
@@ -35,6 +35,7 @@ void traverseDocumentWithSvgpp(SvgppContext &context, char *text) {
                         svgpp::tag::attribute::cy,             //
                         svgpp::tag::attribute::d,              //
                         svgpp::tag::attribute::fill,           //
+                        svgpp::tag::attribute::fill_opacity,   //
                         svgpp::tag::attribute::font_family,    //
                         svgpp::tag::attribute::font_size,      //
                         svgpp::tag::attribute::height,         //
index b4bce838f8e34b8fe1602b14d81af54efbd4f82a..685e600abb637b842a61b49c6579dd8e74d7ba32 100644 (file)
@@ -19,26 +19,8 @@ TEST_CASE("SvgAnalyzer color",
   INFO(fmt::format("Color: {}", color));
   const auto color_attr = color.empty() ? "" : fmt::format(" color={}", color);
 
-  // FIXME: Edge arrowheads use `color` also for fill when `fillcolor` is not
-  // set. This can result in `fill-opacity' being set which we do not yet
-  // support. We therefore temporarily avoid specifying an edge color when
-  // opacity is not 0 or 100 %.
-  const auto edge_color_attr =
-      !color.ends_with("00\"") && !color.ends_with("ff\"")
-          ? ""
-          : fmt::format(" color={}", color);
-
   auto dot = fmt::format("digraph g1 {{node [shape={}{}]; edge [{}]; a -> b}}",
-                         shape, color_attr, edge_color_attr);
+                         shape, color_attr, color_attr);
 
-  if (shape == "point" && !color.ends_with("00\"") &&
-      !color.ends_with("ff\"")) {
-    // FIXME: The `point` shape implicitly uses style="filled" and this in turn
-    // causes `color` to be used for fill when `fillcolor` is not set.
-    // This can result in `fill-opacity' being set which we do not yet
-    // support. We therefore avoid checking the SVG for the `point` shape when
-    // the opacity is not 0 or 100 %.
-  } else {
-    SVGAnalyzer::make_from_dot(dot).re_create_and_verify_svg();
-  }
+  SVGAnalyzer::make_from_dot(dot).re_create_and_verify_svg();
 }
diff --git a/tests/test_svg_analyzer_fillcolor.cpp b/tests/test_svg_analyzer_fillcolor.cpp
new file mode 100644 (file)
index 0000000..3f31f3b
--- /dev/null
@@ -0,0 +1,31 @@
+#include <string_view>
+
+#include <catch2/catch.hpp>
+#include <fmt/format.h>
+
+#include "svg_analyzer.h"
+#include "test_utilities.h"
+
+TEST_CASE("SvgAnalyzer fillcolor",
+          "Test that the SvgAnalyzer can recreate the original SVG with the "
+          "correct `fill` and `fill-opacity` attributes when the Graphviz "
+          "`fillcolor` attribute is used for nodes and edges ") {
+
+  const auto shape = GENERATE(from_range(all_node_shapes));
+  INFO(fmt::format("Shape: {}", shape));
+
+  const std::string_view fillcolor =
+      GENERATE("", "\"#10204000\"", "\"#10204080\"", "\"#102040ff\"");
+  INFO(fmt::format("Fillcolor: {}", fillcolor));
+  const auto fillcolor_attr =
+      fillcolor.empty() ? "" : fmt::format(" fillcolor={}", fillcolor);
+  const std::string_view node_style = fillcolor.empty() ? "" : "filled";
+  const auto node_style_attr =
+      node_style.empty() ? "" : fmt::format(" style={}", node_style);
+
+  auto dot =
+      fmt::format("digraph g1 {{node [shape={}{}{}]; edge [{}]; a -> b}}",
+                  shape, node_style_attr, fillcolor_attr, fillcolor_attr);
+
+  SVGAnalyzer::make_from_dot(dot).re_create_and_verify_svg();
+}