CREATE_TEST(subgraph_layout)
CREATE_TEST(subgraphs)
CREATE_TEST(svg_analyzer)
+CREATE_TEST(svg_analyzer_penwidth)
current_element().attributes.stroke = stroke;
}
+void SVGAnalyzer::set_stroke_width(double stroke_width) {
+ current_element().attributes.stroke_width = stroke_width;
+}
+
void SVGAnalyzer::set_id(std::string_view id) {
current_element().attributes.id = id;
}
void set_ry(double ry) override;
void set_class(std::string_view) override;
void set_stroke(std::string_view stroke) 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;
void set_text_anchor(std::string_view text_anchor) override;
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_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;
virtual void set_transform(double a, double b, double c, double d, double e,
stroke_to_graphviz_color(attributes.stroke));
}
+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
+ return "";
+ }
+
+ return fmt::format(R"(stroke-width="{}")", attributes.stroke_width);
+}
+
std::string SVG::SVGElement::to_string(std::size_t indent_size = 2) const {
std::string output;
output += R"(<?xml version="1.0" encoding="UTF-8" standalone="no"?>)"
case SVG::SVGElementType::Ellipse:
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());
attributes_str +=
fmt::format(R"( cx="{}" cy="{}" rx="{}" ry="{}")", attributes.cx,
attributes.cy, attributes.rx, attributes.ry);
case SVG::SVGElementType::Path: {
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());
attributes_str += R"|( d=")|";
auto command = 'M';
for (const auto &point : path_points) {
case SVG::SVGElementType::Polygon:
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, 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, points_attribute_to_string());
break;
case SVG::SVGElementType::Svg:
double rx;
double ry;
std::string stroke;
+ double stroke_width = 1;
std::string text_anchor;
std::optional<SVGMatrix> transform;
SVGRect viewBox;
std::string fill_attribute_to_string() const;
std::string points_attribute_to_string() const;
std::string stroke_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;
void to_string_impl(std::string &output, std::size_t indent_size,
m_svg_analyzer->set_stroke(to_color_string(color));
}
+void SvgppContext::set(svgpp::tag::attribute::stroke_width, const double v) {
+ m_svg_analyzer->set_stroke_width(v);
+}
+
void SvgppContext::transform_matrix(const boost::array<double, 6> &matrix) {
double a = matrix.at(0);
double b = matrix.at(1);
throw std::runtime_error{
"this flavor of the 'stroke' attribute is not yet implemented"};
};
+ 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);
void set(svgpp::tag::attribute::rx rx, double v);
+// The pre-processed boost::mpl::set allows only 20 elements, but we need more
+#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
+#define BOOST_MPL_LIMIT_SET_SIZE 40
+
#include <rapidxml_ns/rapidxml_ns.hpp>
#include <svgpp/policy/xml/rapidxml_ns.hpp>
#include <svgpp/svgpp.hpp>
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::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_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<
--- /dev/null
+#include <catch2/catch.hpp>
+#include <fmt/format.h>
+
+#include "svg_analyzer.h"
+#include "test_utilities.h"
+
+TEST_CASE("SvgAnalyzer penwidth",
+ "Test that the SvgAnalyzer can recreate the original SVG with the "
+ "correct `stroke-width` attribute when the Graphviz `penwidth` "
+ "attribute is used for nodes and edges") {
+
+ const auto shape = GENERATE(from_range(all_node_shapes));
+ INFO(fmt::format("Shape: {}", shape));
+
+ const auto penwidth = GENERATE(0.5, 1.0, 2.0);
+ INFO(fmt::format("Node and edge penwidth: {}", penwidth));
+
+ auto dot = fmt::format(
+ "digraph g1 {{node [shape={} penwidth={}]; edge [penwidth={}]; a -> b}}",
+ shape, penwidth, penwidth);
+
+ SVGAnalyzer::make_from_dot(dot).re_create_and_verify_svg();
+}