From d0fb2d796ea2c8a8f5671ff4c4443e4c7570c9ea Mon Sep 17 00:00:00 2001 From: Magnus Jacobsson Date: Wed, 10 Aug 2022 14:33:02 +0200 Subject: [PATCH] tests: SVGAnalyzer: add handling of the 'stroke-opacity' attribute --- tests/CMakeLists.txt | 1 + tests/svg_analyzer.cpp | 4 +++ tests/svg_analyzer.h | 1 + tests/svg_analyzer_interface.h | 1 + tests/svg_element.cpp | 19 +++++++++++++ tests/svg_element.h | 2 ++ tests/svgpp_context.cpp | 4 +++ tests/svgpp_context.h | 1 + tests/svgpp_document_traverser.cpp | 41 ++++++++++++++-------------- tests/test_svg_analyzer_color.cpp | 44 ++++++++++++++++++++++++++++++ 10 files changed, 98 insertions(+), 20 deletions(-) create mode 100644 tests/test_svg_analyzer_color.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 702ed9db3..603298ef7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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) diff --git a/tests/svg_analyzer.cpp b/tests/svg_analyzer.cpp index 348275055..f46d96be2 100644 --- a/tests/svg_analyzer.cpp +++ b/tests/svg_analyzer.cpp @@ -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; } diff --git a/tests/svg_analyzer.h b/tests/svg_analyzer.h index f035e312d..db0c0fb9c 100644 --- a/tests/svg_analyzer.h +++ b/tests/svg_analyzer.h @@ -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 point) override; void set_text(std::string_view text) override; diff --git a/tests/svg_analyzer_interface.h b/tests/svg_analyzer_interface.h index 0487134cf..f2b44e3d4 100644 --- a/tests/svg_analyzer_interface.h +++ b/tests/svg_analyzer_interface.h @@ -42,6 +42,7 @@ public: virtual void set_ry(double ry) = 0; virtual void set_point(std::pair 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; diff --git a/tests/svg_element.cpp b/tests/svg_element.cpp index 94da9d57b..dcd1267c6 100644 --- a/tests/svg_element.cpp +++ b/tests/svg_element.cpp @@ -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: diff --git a/tests/svg_element.h b/tests/svg_element.h index b5e1ca2b3..d7cc6b703 100644 --- a/tests/svg_element.h +++ b/tests/svg_element.h @@ -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 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; diff --git a/tests/svgpp_context.cpp b/tests/svgpp_context.cpp index 24d321fa5..de7b397e5 100644 --- a/tests/svgpp_context.cpp +++ b/tests/svgpp_context.cpp @@ -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); } diff --git a/tests/svgpp_context.h b/tests/svgpp_context.h index 69551426c..ab346caa3 100644 --- a/tests/svgpp_context.h +++ b/tests/svgpp_context.h @@ -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 &matrix); void set(svgpp::tag::attribute::r r, double v); diff --git a/tests/svgpp_document_traverser.cpp b/tests/svgpp_document_traverser.cpp index e02f69ea5..1d386638c 100644 --- a/tests/svgpp_document_traverser.cpp +++ b/tests/svgpp_document_traverser.cpp @@ -30,26 +30,27 @@ void traverseDocumentWithSvgpp(SvgppContext &context, char *text) { using processed_attributes_t = boost::mpl::set::type; svgpp::document_traversal< diff --git a/tests/test_svg_analyzer_color.cpp b/tests/test_svg_analyzer_color.cpp new file mode 100644 index 000000000..b4bce838f --- /dev/null +++ b/tests/test_svg_analyzer_color.cpp @@ -0,0 +1,44 @@ +#include + +#include +#include + +#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(); + } +} -- 2.40.0