Compare commits

...

7 commits

Author SHA1 Message Date
Artem Pavlenko
d02f6e4e1a Merge branch 'master' into viewbox-refactor 2023-01-18 09:57:59 +00:00
Artem Pavlenko
aa9af233a3 SVG unit tests - update bounding box values 2023-01-18 09:46:46 +00:00
Artem Pavlenko
442c98d3b4 Fix compiler warning (consistent class or struct declarations) 2023-01-18 09:42:42 +00:00
Artem Pavlenko
9e96740114 Merge branch 'master' into viewbox-refactor 2022-12-19 12:38:36 +00:00
Artem Pavlenko
97857f48b6 clang-format 2022-12-19 12:33:41 +00:00
Artem Pavlenko
53288c5dd2 SVG parser - only set SVG dimensions top level <svg> element 2022-12-19 12:29:04 +00:00
Artem Pavlenko
32263cde89 SVG parser - simplify viewport/viewBox logic + use functions e.g 'premultiply' rather than operator*= for clarity. 2022-12-19 11:07:03 +00:00
4 changed files with 64 additions and 61 deletions

View file

@ -106,12 +106,12 @@ class MAPNIK_DECL svg_parser : private util::noncopyable
bool strict_; bool strict_;
bool ignore_; bool ignore_;
bool css_style_; bool css_style_;
bool dimensions_ = false;
std::map<std::string, gradient> gradient_map_; std::map<std::string, gradient> gradient_map_;
std::map<std::string, boost::property_tree::detail::rapidxml::xml_node<char> const*> node_cache_; std::map<std::string, boost::property_tree::detail::rapidxml::xml_node<char> const*> node_cache_;
mapnik::css_data css_data_; mapnik::css_data css_data_;
boost::optional<viewbox> vbox_{}; boost::optional<viewbox> vbox_{};
double normalized_diagonal_ = 0.0; double normalized_diagonal_ = 0.0;
agg::trans_affine viewbox_tr_{};
std::deque<double> font_sizes_{}; std::deque<double> font_sizes_{};
error_handler err_handler_; error_handler err_handler_;
}; };

View file

@ -64,7 +64,7 @@ auto const matrix_action = [](auto const& ctx) {
auto d = boost::fusion::at_c<3>(attr); auto d = boost::fusion::at_c<3>(attr);
auto e = boost::fusion::at_c<4>(attr); auto e = boost::fusion::at_c<4>(attr);
auto f = boost::fusion::at_c<5>(attr); auto f = boost::fusion::at_c<5>(attr);
tr = agg::trans_affine(a, b, c, d, e, f) * tr; tr.premultiply(agg::trans_affine(a, b, c, d, e, f));
}; };
auto const rotate_action = [](auto const& ctx) { auto const rotate_action = [](auto const& ctx) {
@ -75,14 +75,14 @@ auto const rotate_action = [](auto const& ctx) {
auto cy = boost::fusion::at_c<2>(attr) ? *boost::fusion::at_c<2>(attr) : 0.0; auto cy = boost::fusion::at_c<2>(attr) ? *boost::fusion::at_c<2>(attr) : 0.0;
if (cx == 0.0 && cy == 0.0) if (cx == 0.0 && cy == 0.0)
{ {
tr = agg::trans_affine_rotation(agg::deg2rad(a)) * tr; tr.premultiply(agg::trans_affine_rotation(agg::deg2rad(a)));
} }
else else
{ {
agg::trans_affine t = agg::trans_affine_translation(-cx, -cy); agg::trans_affine t = agg::trans_affine_translation(-cx, -cy);
t *= agg::trans_affine_rotation(agg::deg2rad(a)); t *= agg::trans_affine_rotation(agg::deg2rad(a));
t *= agg::trans_affine_translation(cx, cy); t *= agg::trans_affine_translation(cx, cy);
tr = t * tr; tr.premultiply(t);
} }
}; };
@ -92,9 +92,9 @@ auto const translate_action = [](auto const& ctx) {
auto tx = boost::fusion::at_c<0>(attr); auto tx = boost::fusion::at_c<0>(attr);
auto ty = boost::fusion::at_c<1>(attr); auto ty = boost::fusion::at_c<1>(attr);
if (ty) if (ty)
tr = agg::trans_affine_translation(tx, *ty) * tr; tr.premultiply(agg::trans_affine_translation(tx, *ty));
else else
tr = agg::trans_affine_translation(tx, 0.0) * tr; tr.premultiply(agg::trans_affine_translation(tx, 0.0));
}; };
auto const scale_action = [](auto const& ctx) { auto const scale_action = [](auto const& ctx) {
@ -103,21 +103,21 @@ auto const scale_action = [](auto const& ctx) {
auto sx = boost::fusion::at_c<0>(attr); auto sx = boost::fusion::at_c<0>(attr);
auto sy = boost::fusion::at_c<1>(attr); auto sy = boost::fusion::at_c<1>(attr);
if (sy) if (sy)
tr = agg::trans_affine_scaling(sx, *sy) * tr; tr.premultiply(agg::trans_affine_scaling(sx, *sy));
else else
tr = agg::trans_affine_scaling(sx, sx) * tr; tr.premultiply(agg::trans_affine_scaling(sx, sx));
}; };
auto const skewX_action = [](auto const& ctx) { auto const skewX_action = [](auto const& ctx) {
auto& tr = extract_transform(ctx); auto& tr = extract_transform(ctx);
auto skew_x = _attr(ctx); auto skew_x = _attr(ctx);
tr = agg::trans_affine_skewing(agg::deg2rad(skew_x), 0.0) * tr; tr.premultiply(agg::trans_affine_skewing(agg::deg2rad(skew_x), 0.0));
}; };
auto const skewY_action = [](auto const& ctx) { auto const skewY_action = [](auto const& ctx) {
auto& tr = extract_transform(ctx); auto& tr = extract_transform(ctx);
auto skew_y = _attr(ctx); auto skew_y = _attr(ctx);
tr = agg::trans_affine_skewing(0.0, agg::deg2rad(skew_y)) * tr; tr.premultiply(agg::trans_affine_skewing(0.0, agg::deg2rad(skew_y)));
}; };
// rules // rules

View file

@ -540,6 +540,15 @@ void traverse_tree(svg_parser& parser, rapidxml::xml_node<char> const* node)
process_css(parser, node); process_css(parser, node);
parse_attr(parser, node); parse_attr(parser, node);
} }
break;
case "svg"_case:
if (node->first_node() != nullptr)
{
parser.path_.push_attr();
parse_attr(parser, node);
parse_dimensions(parser, node);
}
break; break;
case "use"_case: case "use"_case:
parser.path_.push_attr(); parser.path_.push_attr();
@ -642,38 +651,26 @@ void parse_element(svg_parser& parser, char const* name, rapidxml::xml_node<char
switch (name_to_int(name)) switch (name_to_int(name))
{ {
case "path"_case: case "path"_case:
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_path(parser, node); parse_path(parser, node);
break; break;
case "polygon"_case: case "polygon"_case:
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_polygon(parser, node); parse_polygon(parser, node);
break; break;
case "polyline"_case: case "polyline"_case:
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_polyline(parser, node); parse_polyline(parser, node);
break; break;
case "line"_case: case "line"_case:
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_line(parser, node); parse_line(parser, node);
break; break;
case "rect"_case: case "rect"_case:
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_rect(parser, node); parse_rect(parser, node);
break; break;
case "circle"_case: case "circle"_case:
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_circle(parser, node); parse_circle(parser, node);
break; break;
case "ellipse"_case: case "ellipse"_case:
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_ellipse(parser, node); parse_ellipse(parser, node);
break; break;
case "svg"_case:
parser.path_.push_attr();
parse_dimensions(parser, node);
parse_attr(parser, node);
break;
default: default:
handle_unsupported(parser, unsupported_elements, name, "element"); handle_unsupported(parser, unsupported_elements, name, "element");
break; break;
@ -893,7 +890,6 @@ void parse_dimensions(svg_parser& parser, rapidxml::xml_node<char> const* node)
viewbox vbox = {0, 0, 0, 0}; viewbox vbox = {0, 0, 0, 0};
bool has_percent_height = true; bool has_percent_height = true;
bool has_percent_width = true; bool has_percent_width = true;
auto const* width_attr = node->first_attribute("width"); auto const* width_attr = node->first_attribute("width");
if (width_attr) if (width_attr)
{ {
@ -933,58 +929,56 @@ void parse_dimensions(svg_parser& parser, rapidxml::xml_node<char> const* node)
double sx = width / vbox.width; double sx = width / vbox.width;
double sy = height / vbox.height; double sy = height / vbox.height;
double scale = preserve_aspect_ratio.second ? std::min(sx, sy) : std::max(sx, sy); double scale = preserve_aspect_ratio.second ? std::min(sx, sy) : std::max(sx, sy);
switch (preserve_aspect_ratio.first) switch (preserve_aspect_ratio.first)
{ {
case none: case none:
t = agg::trans_affine_scaling(sx, sy) * t; t.premultiply(agg::trans_affine_scaling(sx, sy));
break; break;
case xMinYMin: case xMinYMin:
t = agg::trans_affine_scaling(scale, scale) * t; t.premultiply(agg::trans_affine_scaling(scale, scale));
break; break;
case xMinYMid: case xMinYMid:
t = agg::trans_affine_scaling(scale, scale) * t; t.premultiply(agg::trans_affine_scaling(scale, scale));
t = agg::trans_affine_translation(0, -0.5 * (vbox.height - height / scale)) * t; t.premultiply(agg::trans_affine_translation(0, -0.5 * (vbox.height - height / scale)));
break; break;
case xMinYMax: case xMinYMax:
t = agg::trans_affine_scaling(scale, scale) * t; t.premultiply(agg::trans_affine_scaling(scale, scale));
t = agg::trans_affine_translation(0, -1.0 * (vbox.height - height / scale)) * t; t.premultiply(agg::trans_affine_translation(0, -1.0 * (vbox.height - height / scale)));
break; break;
case xMidYMin: case xMidYMin:
t = agg::trans_affine_scaling(scale, scale) * t; t.premultiply(agg::trans_affine_scaling(scale, scale));
t = agg::trans_affine_translation(-0.5 * (vbox.width - width / scale), 0.0) * t; t.premultiply(agg::trans_affine_translation(-0.5 * (vbox.width - width / scale), 0.0));
break; break;
case xMidYMid: // (the default) case xMidYMid: // (the default)
t = agg::trans_affine_scaling(scale, scale) * t; t.premultiply(agg::trans_affine_scaling(scale, scale));
t = agg::trans_affine_translation(-0.5 * (vbox.width - width / scale), t.premultiply(agg::trans_affine_translation(-0.5 * (vbox.width - width / scale),
-0.5 * (vbox.height - height / scale)) * -0.5 * (vbox.height - height / scale)));
t;
break; break;
case xMidYMax: case xMidYMax:
t = agg::trans_affine_scaling(scale, scale) * t; t.premultiply(agg::trans_affine_scaling(scale, scale));
t = agg::trans_affine_translation(-0.5 * (vbox.width - width / scale), t.premultiply(agg::trans_affine_translation(-0.5 * (vbox.width - width / scale),
-1.0 * (vbox.height - height / scale)) * -1.0 * (vbox.height - height / scale)));
t;
break; break;
case xMaxYMin: case xMaxYMin:
t = agg::trans_affine_scaling(scale, scale) * t; t.premultiply(agg::trans_affine_scaling(scale, scale));
t = agg::trans_affine_translation(-1.0 * (vbox.width - width / scale), 0.0) * t; t.premultiply(agg::trans_affine_translation(-1.0 * (vbox.width - width / scale), 0.0));
break; break;
case xMaxYMid: case xMaxYMid:
t = agg::trans_affine_scaling(scale, scale) * t; t.premultiply(agg::trans_affine_scaling(scale, scale));
t = agg::trans_affine_translation(-1.0 * (vbox.width - width / scale), t.premultiply(agg::trans_affine_translation(-1.0 * (vbox.width - width / scale),
-0.5 * (vbox.height - height / scale)) * -0.5 * (vbox.height - height / scale)));
t;
break; break;
case xMaxYMax: case xMaxYMax:
t = agg::trans_affine_scaling(scale, scale) * t; t.premultiply(agg::trans_affine_scaling(scale, scale));
t = agg::trans_affine_translation(-1.0 * (vbox.width - width / scale), t.premultiply(agg::trans_affine_translation(-1.0 * (vbox.width - width / scale),
-1.0 * (vbox.height - height / scale)) * -1.0 * (vbox.height - height / scale)));
t;
break; break;
}; };
t.premultiply(agg::trans_affine_translation(-vbox.x0, -vbox.y0));
parser.path_.transform().premultiply(t);
} }
t = agg::trans_affine_translation(-vbox.x0, -vbox.y0) * t;
parser.viewbox_tr_ = t;
} }
else if (width == 0 || height == 0 || has_percent_width || has_percent_height) else if (width == 0 || height == 0 || has_percent_width || has_percent_height)
{ {
@ -1004,7 +998,11 @@ void parse_dimensions(svg_parser& parser, rapidxml::xml_node<char> const* node)
parser.path_.set_dimensions(0, 0); parser.path_.set_dimensions(0, 0);
return; return;
} }
if (!parser.dimensions_)
{
parser.dimensions_ = true;
parser.path_.set_dimensions(width, height); parser.path_.set_dimensions(width, height);
}
} }
void parse_path(svg_parser& parser, rapidxml::xml_node<char> const* node) void parse_path(svg_parser& parser, rapidxml::xml_node<char> const* node)

View file

@ -442,8 +442,8 @@ TEST_CASE("SVG parser")
REQUIRE(marker->is<mapnik::marker_svg>()); REQUIRE(marker->is<mapnik::marker_svg>());
mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker); mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
auto bbox = svg.bounding_box(); auto bbox = svg.bounding_box();
// REQUIRE(bbox == mapnik::box2d<double>(0.3543307086614174,0.3543307086614174, REQUIRE(bbox == mapnik::box2d<double>(0.3779527559055118,0.3779527559055118,
// 424.8425196850394059,141.3779527559055396)); 453.1653543307086807,150.8031496062992005));
auto storage = svg.get_data(); auto storage = svg.get_data();
REQUIRE(storage); REQUIRE(storage);
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source()); mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
@ -478,7 +478,8 @@ TEST_CASE("SVG parser")
REQUIRE(marker->is<mapnik::marker_svg>()); REQUIRE(marker->is<mapnik::marker_svg>());
mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker); mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
auto bbox = svg.bounding_box(); auto bbox = svg.bounding_box();
// REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,1199.0,399.0)); REQUIRE(bbox == mapnik::box2d<double>(0.3779527559055118,0.3779527559055118,
453.1653543307086807,150.8031496062992005));
auto storage = svg.get_data(); auto storage = svg.get_data();
REQUIRE(storage); REQUIRE(storage);
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source()); mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
@ -517,7 +518,8 @@ TEST_CASE("SVG parser")
REQUIRE(marker->is<mapnik::marker_svg>()); REQUIRE(marker->is<mapnik::marker_svg>());
mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker); mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
auto bbox = svg.bounding_box(); auto bbox = svg.bounding_box();
// REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,1199.0,399.0)); REQUIRE(bbox == mapnik::box2d<double>(0.3779527559055118,0.3779527559055118,
453.1653543307086807,150.8031496062992005));
auto storage = svg.get_data(); auto storage = svg.get_data();
REQUIRE(storage); REQUIRE(storage);
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source()); mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
@ -555,7 +557,8 @@ TEST_CASE("SVG parser")
REQUIRE(marker->is<mapnik::marker_svg>()); REQUIRE(marker->is<mapnik::marker_svg>());
mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker); mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
auto bbox = svg.bounding_box(); auto bbox = svg.bounding_box();
// REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,799.0,599.0)); REQUIRE(bbox == mapnik::box2d<double>(75.7795275590551114,0.1889763779527559,
226.5826771653543119,113.1968503937007853));
auto storage = svg.get_data(); auto storage = svg.get_data();
REQUIRE(storage); REQUIRE(storage);
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source()); mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
@ -651,7 +654,8 @@ TEST_CASE("SVG parser")
REQUIRE(marker->is<mapnik::marker_svg>()); REQUIRE(marker->is<mapnik::marker_svg>());
mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker); mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
auto bbox = svg.bounding_box(); auto bbox = svg.bounding_box();
// REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,699.0,199.0)); REQUIRE(bbox == mapnik::box2d<double>(0.3779527559055118,0.3779527559055118,
264.1889763779527698,75.2125984251968447));
auto storage = svg.get_data(); auto storage = svg.get_data();
REQUIRE(storage); REQUIRE(storage);
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source()); mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
@ -701,7 +705,8 @@ TEST_CASE("SVG parser")
REQUIRE(marker->is<mapnik::marker_svg>()); REQUIRE(marker->is<mapnik::marker_svg>());
mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker); mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
auto bbox = svg.bounding_box(); auto bbox = svg.bounding_box();
// REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,799.0,599.0)); REQUIRE(bbox == mapnik::box2d<double>(75.7795275590551114,0.1889763779527559,
226.5826771653543119,113.1968503937007853));
auto storage = svg.get_data(); auto storage = svg.get_data();
REQUIRE(storage); REQUIRE(storage);
@ -722,7 +727,7 @@ TEST_CASE("SVG parser")
REQUIRE(marker->is<mapnik::marker_svg>()); REQUIRE(marker->is<mapnik::marker_svg>());
mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker); mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
auto bbox = svg.bounding_box(); auto bbox = svg.bounding_box();
// REQUIRE(bbox == mapnik::box2d<double>(20,20,460,230)); REQUIRE(bbox == mapnik::box2d<double>(20,20,460,230));
auto storage = svg.get_data(); auto storage = svg.get_data();
REQUIRE(storage); REQUIRE(storage);
@ -741,7 +746,7 @@ TEST_CASE("SVG parser")
REQUIRE(marker->is<mapnik::marker_svg>()); REQUIRE(marker->is<mapnik::marker_svg>());
mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker); mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
auto bbox = svg.bounding_box(); auto bbox = svg.bounding_box();
// REQUIRE(bbox == mapnik::box2d<double>(0,0,200,200)); REQUIRE(bbox == mapnik::box2d<double>(0,0,200,200));
auto storage = svg.get_data(); auto storage = svg.get_data();
REQUIRE(storage); REQUIRE(storage);