SVG parser - add support for <use> element (ref #763).

This commit is contained in:
artemp 2017-06-08 11:10:53 +02:00 committed by Artem Pavlenko
parent a4e8603af1
commit 53a69463ea
2 changed files with 74 additions and 15 deletions

View file

@ -72,9 +72,8 @@ public:
bool is_defs_;
bool strict_;
std::map<std::string, gradient> gradient_map_;
std::map<std::string, boost::property_tree::detail::rapidxml::xml_node<char> const*> unresolved_gradient_map_;
std::map<std::string, boost::property_tree::detail::rapidxml::xml_node<char> const*> node_cache_;
error_handler err_handler_;
};
}}

View file

@ -78,6 +78,7 @@ namespace rapidxml = boost::property_tree::detail::rapidxml;
bool traverse_tree(svg_parser& parser, rapidxml::xml_node<char> const* node);
void end_element(svg_parser& parser, rapidxml::xml_node<char> const* node);
void parse_path(svg_parser& parser, rapidxml::xml_node<char> const* node);
void parse_use(svg_parser& parser, rapidxml::xml_node<char> const* node);
void parse_dimensions(svg_parser& parser, rapidxml::xml_node<char> const* node);
void parse_polygon(svg_parser& parser, rapidxml::xml_node<char> const* node);
void parse_polyline(svg_parser& parser, rapidxml::xml_node<char> const* node);
@ -113,6 +114,19 @@ BOOST_SPIRIT_DEFINE(key, value, key_value, key_value_sequence_ordered);
}}
boost::property_tree::detail::rapidxml::xml_attribute<char> const * parse_id(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
auto const* id_attr = node->first_attribute("xml:id");
if (id_attr == nullptr) id_attr = node->first_attribute("id");
if (id_attr && parser.node_cache_.count(id_attr->value()) == 0)
{
parser.node_cache_.emplace(id_attr->value(), node);
}
return id_attr;
}
template <typename T>
mapnik::color parse_color(T & err_handler, const char* str)
{
@ -156,6 +170,7 @@ double parse_svg_value(T & err_handler, const char* str, bool & is_percent)
x3::symbols<double> units;
units.add
("px", 1.0)
("em", 1.0) // (?)
("pt", DPI/72.0)
("pc", DPI/6.0)
("mm", DPI/25.4)
@ -248,12 +263,22 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
if (node->first_node() != nullptr)
{
parser.path_.push_attr();
parse_id(parser, node);
parse_attr(parser, node);
}
}
else if (std::strcmp(name, "use") == 0)
{
parser.path_.push_attr();
parse_id(parser, node);
parse_attr(parser, node);
parse_use(parser, node);
parser.path_.pop_attr();
}
else
{
parser.path_.push_attr();
parse_id(parser, node);
parse_attr(parser, node);
if (parser.path_.display())
{
@ -291,12 +316,18 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
}
else
{
//parser.err_handler().on_error(std::string("Unsupported element:\"") + node->name());
parser.err_handler().on_error(std::string("Unsupported element:\"") + node->name());
}
}
parser.path_.pop_attr();
}
}
else
{
// save node for later
parse_id(parser, node);
}
for (auto const* child = node->first_node();
child; child = child->next_sibling())
@ -372,10 +403,10 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
{
parser.path_.add_fill_gradient(parser.gradient_map_[id]);
}
else if (parser.unresolved_gradient_map_.count(id) > 0)
else if (parser.node_cache_.count(id) > 0)
{
// try parsing again
auto const* gradient_node = parser.unresolved_gradient_map_[id];
auto const* gradient_node = parser.node_cache_[id];
traverse_tree(parser, gradient_node);
if (parser.gradient_map_.count(id) > 0)
{
@ -425,10 +456,10 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
{
parser.path_.add_stroke_gradient(parser.gradient_map_[id]);
}
else if (parser.unresolved_gradient_map_.count(id) > 0)
else if (parser.node_cache_.count(id) > 0)
{
// try parsing again
auto const* gradient_node = parser.unresolved_gradient_map_[id];
auto const* gradient_node = parser.node_cache_[id];
traverse_tree(parser, gradient_node);
if (parser.gradient_map_.count(id) > 0)
{
@ -597,8 +628,7 @@ void parse_path(svg_parser & parser, rapidxml::xml_node<char> const* node)
if (!mapnik::svg::parse_path(value, parser.path_))
{
auto const* id_attr = node->first_attribute("xml:id");
if (id_attr == nullptr) id_attr = node->first_attribute("id");
auto const* id_attr = parse_id(parser, node);
if (id_attr)
{
parser.err_handler().on_error(std::string("unable to parse invalid svg <path> with id '")
@ -614,6 +644,39 @@ void parse_path(svg_parser & parser, rapidxml::xml_node<char> const* node)
}
}
void parse_use(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
auto * attr = node->first_attribute("xlink:href");
if (attr)
{
auto const* value = attr->value();
if (std::strlen(value) > 1 && value[0] == '#')
{
std::string id(&value[1]);
if (parser.node_cache_.count(id) > 0)
{
auto const* base_node = parser.node_cache_[id];
double x = 0.0;
double y = 0.0;
bool percent = false;
attr = node->first_attribute("x");
if (attr != nullptr)
{
x = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
attr = node->first_attribute("y");
if (attr != nullptr)
{
y = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
parser.path_.transform().premultiply(agg::trans_affine_translation(x, y));
traverse_tree(parser, base_node);
}
}
}
}
void parse_polygon(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
auto const* attr = node->first_attribute("points");
@ -926,7 +989,7 @@ bool parse_common_gradient(svg_parser & parser, std::string const& id, mapnik::g
else
{
// save node for later
parser.unresolved_gradient_map_.emplace(id, node);
parser.node_cache_.emplace(id, node);
return false;
}
}
@ -957,10 +1020,8 @@ bool parse_common_gradient(svg_parser & parser, std::string const& id, mapnik::g
void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
auto * attr = node->first_attribute("xml:id");
if (attr == nullptr) attr = node->first_attribute("id");
auto * attr = parse_id(parser, node);
if (attr == nullptr) return;
std::string id = attr->value();
mapnik::gradient gr;
@ -1028,8 +1089,7 @@ void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
auto * attr = node->first_attribute("xml:id");
if (attr == nullptr) attr = node->first_attribute("id");
auto const* attr = parse_id(parser, node);
if (attr == nullptr) return;
std::string id = attr->value();