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 is_defs_;
bool strict_; bool strict_;
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*> unresolved_gradient_map_; std::map<std::string, boost::property_tree::detail::rapidxml::xml_node<char> const*> node_cache_;
error_handler err_handler_; 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); 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 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_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_dimensions(svg_parser& parser, rapidxml::xml_node<char> const* node);
void parse_polygon(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); 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> template <typename T>
mapnik::color parse_color(T & err_handler, const char* str) 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; x3::symbols<double> units;
units.add units.add
("px", 1.0) ("px", 1.0)
("em", 1.0) // (?)
("pt", DPI/72.0) ("pt", DPI/72.0)
("pc", DPI/6.0) ("pc", DPI/6.0)
("mm", DPI/25.4) ("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) if (node->first_node() != nullptr)
{ {
parser.path_.push_attr(); parser.path_.push_attr();
parse_id(parser, node);
parse_attr(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 else
{ {
parser.path_.push_attr(); parser.path_.push_attr();
parse_id(parser, node);
parse_attr(parser, node); parse_attr(parser, node);
if (parser.path_.display()) if (parser.path_.display())
{ {
@ -291,12 +316,18 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
} }
else 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(); parser.path_.pop_attr();
} }
} }
else
{
// save node for later
parse_id(parser, node);
}
for (auto const* child = node->first_node(); for (auto const* child = node->first_node();
child; child = child->next_sibling()) 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]); 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 // 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); traverse_tree(parser, gradient_node);
if (parser.gradient_map_.count(id) > 0) 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]); 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 // 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); traverse_tree(parser, gradient_node);
if (parser.gradient_map_.count(id) > 0) 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_)) if (!mapnik::svg::parse_path(value, parser.path_))
{ {
auto const* id_attr = node->first_attribute("xml:id"); auto const* id_attr = parse_id(parser, node);
if (id_attr == nullptr) id_attr = node->first_attribute("id");
if (id_attr) if (id_attr)
{ {
parser.err_handler().on_error(std::string("unable to parse invalid svg <path> with id '") 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) void parse_polygon(svg_parser & parser, rapidxml::xml_node<char> const* node)
{ {
auto const* attr = node->first_attribute("points"); 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 else
{ {
// save node for later // save node for later
parser.unresolved_gradient_map_.emplace(id, node); parser.node_cache_.emplace(id, node);
return false; 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) void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const* node)
{ {
auto * attr = node->first_attribute("xml:id"); auto * attr = parse_id(parser, node);
if (attr == nullptr) attr = node->first_attribute("id");
if (attr == nullptr) return; if (attr == nullptr) return;
std::string id = attr->value(); std::string id = attr->value();
mapnik::gradient gr; 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) void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node<char> const* node)
{ {
auto * attr = node->first_attribute("xml:id"); auto const* attr = parse_id(parser, node);
if (attr == nullptr) attr = node->first_attribute("id");
if (attr == nullptr) return; if (attr == nullptr) return;
std::string id = attr->value(); std::string id = attr->value();