fix viewBox coordinate system logic + apply transform in <use> element

This commit is contained in:
artemp 2017-06-21 17:24:08 +02:00 committed by Artem Pavlenko
parent 3fbce913d0
commit 3edcd4afa2

View file

@ -61,8 +61,8 @@ struct viewbox
{ {
double x0; double x0;
double y0; double y0;
double x1; double width;
double y1; double height;
}; };
}} }}
@ -70,8 +70,8 @@ BOOST_FUSION_ADAPT_STRUCT (
mapnik::svg::viewbox, mapnik::svg::viewbox,
(double, x0) (double, x0)
(double, y0) (double, y0)
(double, x1) (double, width)
(double, y1) (double, height)
) )
@ -368,24 +368,31 @@ void parse_element(svg_parser & parser, char const* name, rapidxml::xml_node<cha
switch (name_to_int(name)) switch (name_to_int(name))
{ {
case name_to_int("path"): case name_to_int("path"):
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_path(parser, node); parse_path(parser, node);
break; break;
case name_to_int("polygon"): case name_to_int("polygon"):
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_polygon(parser, node); parse_polygon(parser, node);
break; break;
case name_to_int("polyline"): case name_to_int("polyline"):
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_polyline(parser, node); parse_polyline(parser, node);
break; break;
case name_to_int("line"): case name_to_int("line"):
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_line(parser, node); parse_line(parser, node);
break; break;
case name_to_int("rect"): case name_to_int("rect"):
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_rect(parser, node); parse_rect(parser, node);
break; break;
case name_to_int("circle"): case name_to_int("circle"):
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_circle(parser, node); parse_circle(parser, node);
break; break;
case name_to_int("ellipse"): case name_to_int("ellipse"):
parser.path_.transform().multiply(parser.viewbox_tr_);
parse_ellipse(parser, node); parse_ellipse(parser, node);
break; break;
case name_to_int("svg"): case name_to_int("svg"):
@ -615,28 +622,49 @@ void parse_dimensions(svg_parser & parser, rapidxml::xml_node<char> const* node)
{ {
height = parse_svg_value(parser.err_handler(), height_attr->value(), has_percent_height); height = parse_svg_value(parser.err_handler(), height_attr->value(), has_percent_height);
} }
auto const* viewbox_attr = node->first_attribute("viewBox"); auto const* viewbox_attr = node->first_attribute("viewBox");
if (viewbox_attr) if (viewbox_attr)
{ {
has_viewbox = parse_viewbox(parser.err_handler(), viewbox_attr->value(), vbox); has_viewbox = parse_viewbox(parser.err_handler(), viewbox_attr->value(), vbox);
} if (width > 0 && height > 0 && vbox.width > 0 && vbox.height > 0)
{
agg::trans_affine t{};
double sx = width / vbox.width;
double sy = height / vbox.height;
double scale = std::min(sx, sy);
// xMidYMid (the default)
t = agg::trans_affine_scaling(scale, scale) * t;
double ratio1 = vbox.width / vbox.height;
double ratio2 = width / height;
if (ratio1 > ratio2)
{
t = agg::trans_affine_translation(0, -0.5 * (vbox.height - height / scale)) * t;
}
else if (ratio2 > ratio1)
{
t = agg::trans_affine_translation(-0.5 * (vbox.width - width / scale), 0) * t;
}
t = agg::trans_affine_translation(-vbox.x0, -vbox.y0) * t;
parser.viewbox_tr_ = t;
}
}
if (has_percent_width && !has_percent_height && has_viewbox) if (has_percent_width && !has_percent_height && has_viewbox)
{ {
aspect_ratio = vbox.x1 / vbox.y1; aspect_ratio = vbox.width / vbox.height;
width = aspect_ratio * height; width = aspect_ratio * height;
} }
else if (!has_percent_width && has_percent_height && has_viewbox) else if (!has_percent_width && has_percent_height && has_viewbox)
{ {
aspect_ratio = vbox.x1 / vbox.y1; aspect_ratio = vbox.width/vbox.height;
height = height / aspect_ratio; height = height / aspect_ratio;
} }
else if (has_percent_width && has_percent_height && has_viewbox) else if (has_percent_width && has_percent_height && has_viewbox)
{ {
width = vbox.x1; width = vbox.width;
height = vbox.y1; height = vbox.height;
} }
parser.path_.set_dimensions(width, height); parser.path_.set_dimensions(width, height);
} }
@ -701,26 +729,31 @@ void parse_use(svg_parser & parser, rapidxml::xml_node<char> const* node)
if (attr != nullptr) if (attr != nullptr)
{ {
w = parse_svg_value(parser.err_handler(), attr->value(), percent); w = parse_svg_value(parser.err_handler(), attr->value(), percent);
if (percent) w *= parser.path_.width();
} }
attr = node->first_attribute("height"); attr = node->first_attribute("height");
if (attr) if (attr)
{ {
h = parse_svg_value(parser.err_handler(), attr->value(), percent); h = parse_svg_value(parser.err_handler(), attr->value(), percent);
if (percent) h *= parser.path_.height();
} }
agg::trans_affine t{}; agg::trans_affine t{};
if (w != 0.0 && h != 0.0)
if (!node->first_attribute("transform") && w != 0.0 && h != 0.0)
{ {
if(w < 0.0) if (w < 0.0)
{ {
parser.err_handler().on_error("parse_use: Invalid width"); parser.err_handler().on_error("parse_use: Invalid width");
} }
else if(h < 0.0) else if (h < 0.0)
{ {
parser.err_handler().on_error("parse_use: Invalid height"); parser.err_handler().on_error("parse_use: Invalid height");
} }
double scale = std::min(double(w/parser.path_.width()), double(h/parser.path_.height())); double scale = std::min(double(w / parser.path_.width()), double(h / parser.path_.height()));
t *= agg::trans_affine_scaling(scale); t *= agg::trans_affine_scaling(scale);
} }
t *= agg::trans_affine_translation(x, y); t *= agg::trans_affine_translation(x, y);
parser.path_.transform().premultiply(t); parser.path_.transform().premultiply(t);
traverse_tree(parser, base_node); traverse_tree(parser, base_node);