]> granicus.if.org Git - graphviz/commitdiff
tests: SVGAnalyzer: add handling of the 'stroke-opacity' attribute
authorMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Wed, 10 Aug 2022 12:33:02 +0000 (14:33 +0200)
committerMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Mon, 5 Sep 2022 06:52:07 +0000 (08:52 +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 [new file with mode: 0644]

index 702ed9db30491238d4776a9443e1759576068289..603298ef74c71e7eee7ce06fc8c205d79d0d39a1 100644 (file)
@@ -91,4 +91,5 @@ CREATE_TEST(simple)
 CREATE_TEST(subgraph_layout)
 CREATE_TEST(subgraphs)
 CREATE_TEST(svg_analyzer)
+CREATE_TEST(svg_analyzer_color)
 CREATE_TEST(svg_analyzer_penwidth)
index 348275055997e27167f46039b684f41a2a3db17e..f46d96be27934e68417fea77d24501721f3c9669 100644 (file)
@@ -227,6 +227,10 @@ void SVGAnalyzer::set_stroke(std::string_view stroke) {
   current_element().attributes.stroke = stroke;
 }
 
+void SVGAnalyzer::set_stroke_opacity(double stroke_opacity) {
+  current_element().attributes.stroke_opacity = stroke_opacity;
+}
+
 void SVGAnalyzer::set_stroke_width(double stroke_width) {
   current_element().attributes.stroke_width = stroke_width;
 }
index f035e312d486700e0a4f3c9b8edecbdd6cfc9e37..db0c0fb9c7006376f1f295dc62fc17572492cab9 100644 (file)
@@ -46,6 +46,7 @@ public:
   void set_ry(double ry) override;
   void set_class(std::string_view) override;
   void set_stroke(std::string_view stroke) override;
+  void set_stroke_opacity(double stroke_opacity) override;
   void set_stroke_width(double stroke_width) override;
   void set_point(std::pair<double, double> point) override;
   void set_text(std::string_view text) override;
index 0487134cfe4a0fbd533ad6185efffe7d55f57e80..f2b44e3d470bdd4e6dc34af5149dc9e70b0aed31 100644 (file)
@@ -42,6 +42,7 @@ public:
   virtual void set_ry(double ry) = 0;
   virtual void set_point(std::pair<double, double> point) = 0;
   virtual void set_stroke(std::string_view stroke) = 0;
+  virtual void set_stroke_opacity(double stroke_opacity) = 0;
   virtual void set_stroke_width(double stroke_width) = 0;
   virtual void set_text(std::string_view text) = 0;
   virtual void set_text_anchor(std::string_view text_anchor) = 0;
index 94da9d57b86f5e2b0a2aaf82b1ae58ba62fa4055..dcd1267c69dba05cb5d16d713228c4e9ecc3db86 100644 (file)
@@ -217,6 +217,21 @@ std::string SVG::SVGElement::stroke_attribute_to_string() const {
                      stroke_to_graphviz_color(attributes.stroke));
 }
 
+std::string SVG::SVGElement::stroke_opacity_attribute_to_string() const {
+  if (attributes.stroke_opacity == 1) {
+    // Graphviz doesn't set `stroke-opacity` to 1 since that's the default
+    return "";
+  }
+
+  if (attributes.stroke_opacity == 0) {
+    // Graphviz doesn't set `stroke-opacity` to 0 since in that case it sets
+    // `stroke` to "none" instead
+    return "";
+  }
+
+  return fmt::format(R"(stroke-opacity="{}")", attributes.stroke_opacity);
+}
+
 std::string SVG::SVGElement::stroke_width_attribute_to_string() const {
   if (attributes.stroke_width == 1) {
     // Graphviz doesn't set `stroke-width` to 1 since that's the default
@@ -267,6 +282,7 @@ void SVG::SVGElement::to_string_impl(std::string &output,
     append_attribute(attributes_str, fill_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());
     attributes_str +=
         fmt::format(R"( cx="{}" cy="{}" rx="{}" ry="{}")", attributes.cx,
                     attributes.cy, attributes.rx, attributes.ry);
@@ -284,6 +300,7 @@ void SVG::SVGElement::to_string_impl(std::string &output,
     append_attribute(attributes_str, fill_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());
     attributes_str += R"|( d=")|";
     auto command = 'M';
     for (const auto &point : path_points) {
@@ -308,12 +325,14 @@ void SVG::SVGElement::to_string_impl(std::string &output,
     append_attribute(attributes_str, fill_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());
     append_attribute(attributes_str, points_attribute_to_string());
     break;
   case SVG::SVGElementType::Polyline:
     append_attribute(attributes_str, fill_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());
     append_attribute(attributes_str, points_attribute_to_string());
     break;
   case SVG::SVGElementType::Svg:
index b5e1ca2b3a96ed6d41d0383796680ceef0fd0514..d7cc6b70347727e817b4c91f136e6cf07e087d1b 100644 (file)
@@ -68,6 +68,7 @@ struct SVGAttributes {
   double rx;
   double ry;
   std::string stroke;
+  double stroke_opacity = 1;
   double stroke_width = 1;
   std::string text_anchor;
   std::optional<SVGMatrix> transform;
@@ -161,6 +162,7 @@ private:
   std::string fill_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;
   std::string stroke_width_attribute_to_string() const;
   std::string stroke_to_graphviz_color(const std::string &color) const;
   SVG::SVGRect text_bbox() const;
index 24d321fa52882116ca998cb05f0853f8b345a0dd..de7b397e57f935ca9aa83e62f23e4dffa395cc7e 100644 (file)
@@ -142,6 +142,10 @@ void SvgppContext::set(svgpp::tag::attribute::stroke,
   m_svg_analyzer->set_stroke(to_color_string(color));
 }
 
+void SvgppContext::set(svgpp::tag::attribute::stroke_opacity, const double v) {
+  m_svg_analyzer->set_stroke_opacity(v);
+}
+
 void SvgppContext::set(svgpp::tag::attribute::stroke_width, const double v) {
   m_svg_analyzer->set_stroke_width(v);
 }
index 69551426cd1e1685b91c4229947ceb21b06a6a3d..ab346caa3e8bbafdc61b7e39c7ad9b066ada1df6 100644 (file)
@@ -149,6 +149,7 @@ public:
     throw std::runtime_error{
         "this flavor of the 'stroke' attribute is not yet implemented"};
   };
+  void set(svgpp::tag::attribute::stroke_opacity, double v);
   void set(svgpp::tag::attribute::stroke_width, double v);
   void transform_matrix(const boost::array<double, 6> &matrix);
   void set(svgpp::tag::attribute::r r, double v);
index e02f69ea5603f4dd9b2213c1855155d210ad668e..1d386638c7651af67a56bbe8f64dcc329bf3e612 100644 (file)
@@ -30,26 +30,27 @@ void traverseDocumentWithSvgpp(SvgppContext &context, char *text) {
 
     using processed_attributes_t =
         boost::mpl::set<svgpp::traits::shapes_attributes_by_element,
-                        svgpp::tag::attribute::class_,       //
-                        svgpp::tag::attribute::cx,           //
-                        svgpp::tag::attribute::cy,           //
-                        svgpp::tag::attribute::d,            //
-                        svgpp::tag::attribute::fill,         //
-                        svgpp::tag::attribute::font_family,  //
-                        svgpp::tag::attribute::font_size,    //
-                        svgpp::tag::attribute::height,       //
-                        svgpp::tag::attribute::id,           //
-                        svgpp::tag::attribute::points,       //
-                        svgpp::tag::attribute::rx,           //
-                        svgpp::tag::attribute::ry,           //
-                        svgpp::tag::attribute::stroke,       //
-                        svgpp::tag::attribute::stroke_width, //
-                        svgpp::tag::attribute::text_anchor,  //
-                        svgpp::tag::attribute::transform,    //
-                        svgpp::tag::attribute::viewBox,      //
-                        svgpp::tag::attribute::width,        //
-                        svgpp::tag::attribute::x,            //
-                        svgpp::tag::attribute::y             //
+                        svgpp::tag::attribute::class_,         //
+                        svgpp::tag::attribute::cx,             //
+                        svgpp::tag::attribute::cy,             //
+                        svgpp::tag::attribute::d,              //
+                        svgpp::tag::attribute::fill,           //
+                        svgpp::tag::attribute::font_family,    //
+                        svgpp::tag::attribute::font_size,      //
+                        svgpp::tag::attribute::height,         //
+                        svgpp::tag::attribute::id,             //
+                        svgpp::tag::attribute::points,         //
+                        svgpp::tag::attribute::rx,             //
+                        svgpp::tag::attribute::ry,             //
+                        svgpp::tag::attribute::stroke,         //
+                        svgpp::tag::attribute::stroke_opacity, //
+                        svgpp::tag::attribute::stroke_width,   //
+                        svgpp::tag::attribute::text_anchor,    //
+                        svgpp::tag::attribute::transform,      //
+                        svgpp::tag::attribute::viewBox,        //
+                        svgpp::tag::attribute::width,          //
+                        svgpp::tag::attribute::x,              //
+                        svgpp::tag::attribute::y               //
                         >::type;
 
     svgpp::document_traversal<
diff --git a/tests/test_svg_analyzer_color.cpp b/tests/test_svg_analyzer_color.cpp
new file mode 100644 (file)
index 0000000..b4bce83
--- /dev/null
@@ -0,0 +1,44 @@
+#include <string_view>
+
+#include <catch2/catch.hpp>
+#include <fmt/format.h>
+
+#include "svg_analyzer.h"
+#include "test_utilities.h"
+
+TEST_CASE("SvgAnalyzer color",
+          "Test that the SvgAnalyzer can recreate the original "
+          "SVG with the correct `stroke` and `stroke-opacity` attributes when "
+          "the Graphviz `color` 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 color =
+      GENERATE("", "\"#10204000\"", "\"#10204080\"", "\"#102040ff\"");
+  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);
+
+  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();
+  }
+}