initial support for strict SVG parsing (WIP)

This commit is contained in:
artemp 2017-05-25 14:30:34 +02:00 committed by Artem Pavlenko
parent df0bbe404c
commit 3f591af871
5 changed files with 53 additions and 32 deletions

View file

@ -55,7 +55,7 @@ public:
inline bool is_uri(std::string const& path) { return is_svg_uri(path) || is_image_uri(path); }
bool is_svg_uri(std::string const& path);
bool is_image_uri(std::string const& path);
std::shared_ptr<marker const> find(std::string const& key, bool update_cache = false);
std::shared_ptr<marker const> find(std::string const& key, bool update_cache = false, bool strict = false);
void clear();
};

View file

@ -40,13 +40,14 @@ namespace mapnik { namespace svg {
{
using error_message_container = std::vector<std::string> ;
public:
explicit svg_parser(svg_converter_type & path);
explicit svg_parser(svg_converter_type & path, bool strict = false);
~svg_parser();
error_message_container const& error_messages() const;
bool parse(std::string const& filename);
bool parse_from_string(std::string const& svg);
svg_converter_type & path_;
bool is_defs_;
bool strict_;
std::map<std::string, gradient> gradient_map_;
std::pair<std::string, gradient> temporary_gradient_;
error_message_container error_messages_;

View file

@ -141,7 +141,7 @@ struct visitor_create_marker
} // end detail ns
std::shared_ptr<mapnik::marker const> marker_cache::find(std::string const& uri,
bool update_cache)
bool update_cache, bool strict)
{
if (uri.empty())
{
@ -174,15 +174,15 @@ std::shared_ptr<mapnik::marker const> marker_cache::find(std::string const& uri,
vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source());
svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes());
svg_parser p(svg);
svg_parser p(svg, strict);
if (!p.parse_from_string(known_svg_string))
if (!p.parse_from_string(known_svg_string) && !strict)
{
for (auto const& msg : p.error_messages())
{
MAPNIK_LOG_ERROR(marker_cache) << "SVG PARSING ERROR:\"" << msg << "\"";
}
return std::make_shared<mapnik::marker const>(mapnik::marker_null());
//return std::make_shared<mapnik::marker const>(mapnik::marker_null());
}
//svg.arrange_orientations();
double lox,loy,hix,hiy;
@ -214,16 +214,16 @@ std::shared_ptr<mapnik::marker const> marker_cache::find(std::string const& uri,
vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source());
svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes());
svg_parser p(svg);
svg_parser p(svg, strict);
if (!p.parse(uri))
if (!p.parse(uri) && !strict)
{
for (auto const& msg : p.error_messages())
{
MAPNIK_LOG_ERROR(marker_cache) << "SVG PARSING ERROR:\"" << msg << "\"";
}
return std::make_shared<mapnik::marker const>(mapnik::marker_null());
//return std::make_shared<mapnik::marker const>(mapnik::marker_null());
}
//svg.arrange_orientations();
double lox,loy,hix,hiy;

View file

@ -115,6 +115,15 @@ BOOST_SPIRIT_DEFINE(key, value, key_value, key_value_sequence_ordered);
}}
namespace {
template <typename T>
void on_error(T & error_messages, std::string const& msg, bool strict)
{
if (strict) throw std::runtime_error(msg);
else error_messages.emplace_back(msg);
}
}
template <typename T>
mapnik::color parse_color(T & error_messages, const char* str)
{
@ -125,7 +134,7 @@ mapnik::color parse_color(T & error_messages, const char* str)
}
catch (mapnik::config_error const& ex)
{
error_messages.emplace_back(ex.what());
on_error(error_messages, ex.what(), false);
}
return c;
}
@ -144,7 +153,7 @@ double parse_double(T & error_messages, const char* str)
double val = 0.0;
if (!parse(str, str + std::strlen(str), double_, val))
{
error_messages.emplace_back("Failed to parse double: \"" + std::string(str) + "\"");
on_error(error_messages,"Failed to parse double: \"" + std::string(str) + "\"", false);
}
return val;
}
@ -178,12 +187,12 @@ double parse_svg_value(T & error_messages, const char* str, bool & is_percent)
x3::lit('%')[apply_percent]),
x3::space))
{
error_messages.emplace_back("Failed to parse SVG value: '" + std::string(str) + "'");
on_error(error_messages,"Failed to parse SVG value: '" + std::string(str) + "'", false);
}
else if (cur != end)
{
error_messages.emplace_back("Failed to parse SVG value: '" + std::string(str) +
"', trailing garbage: '" + cur + "'");
on_error(error_messages,"Failed to parse SVG value: '" + std::string(str) +
"', trailing garbage: '" + cur + "'", false);
}
return val;
}
@ -198,7 +207,7 @@ bool parse_viewbox(T & error_messages, const char* str, V & viewbox)
x3::double_ > -x3::lit(',') >
x3::double_, x3::space, viewbox))
{
error_messages.emplace_back("failed to parse SVG viewbox from " + std::string(str));
on_error(error_messages,"failed to parse SVG viewbox from " + std::string(str), false);
return false;
}
return true;
@ -261,7 +270,7 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
else
{
parser.path_.push_attr();
parse_attr(parser, node);
//parse_attr(parser, node);
if (parser.path_.display())
{
if (std::strcmp(name, "path") == 0)
@ -299,6 +308,7 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
else
{
//std::cerr << "unprocessed node <--[" << node->name() << "]\n";
on_error(parser.error_messages_, std::string("Unsupported element:\"") + node->name(), parser.strict_);
}
}
parser.path_.pop_attr();
@ -387,7 +397,7 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
{
std::stringstream ss;
ss << "Failed to find gradient fill: " << id;
parser.error_messages_.push_back(ss.str());
on_error(parser.error_messages_, ss.str(), parser.strict_);
}
}
else
@ -424,7 +434,7 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
{
std::stringstream ss;
ss << "Failed to find gradient stroke: " << id;
parser.error_messages_.push_back(ss.str());
on_error(parser.error_messages_, ss.str(), parser.strict_);
}
}
else
@ -489,6 +499,10 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
{
parser.path_.display(false);
}
else
{
on_error(parser.error_messages_, std::string("Unsupported attribute:\"") + name, parser.strict_);
}
}
void parse_attr(svg_parser & parser, rapidxml::xml_node<char> const* node)
@ -576,12 +590,12 @@ void parse_path(svg_parser & parser, rapidxml::xml_node<char> const* node)
if (id_attr == nullptr) id_attr = node->first_attribute("id");
if (id_attr)
{
parser.error_messages_.push_back(std::string("unable to parse invalid svg <path> with id '")
+ id_attr->value() + "'");
on_error(parser.error_messages_, std::string("unable to parse invalid svg <path> with id '")
+ id_attr->value() + "'", parser.strict_);
}
else
{
parser.error_messages_.push_back(std::string("unable to parse invalid svg <path>"));
on_error(parser.error_messages_, std::string("unable to parse invalid svg <path>"), parser.strict_);
}
}
parser.path_.end_path();
@ -597,7 +611,7 @@ void parse_polygon(svg_parser & parser, rapidxml::xml_node<char> const* node)
parser.path_.begin_path();
if (!mapnik::svg::parse_points(attr->value(), parser.path_))
{
parser.error_messages_.push_back(std::string("Failed to parse <polygon> 'points'"));
on_error(parser.error_messages_, std::string("Failed to parse <polygon> 'points'"), parser.strict_);
}
parser.path_.close_subpath();
parser.path_.end_path();
@ -612,7 +626,7 @@ void parse_polyline(svg_parser & parser, rapidxml::xml_node<char> const* node)
parser.path_.begin_path();
if (!mapnik::svg::parse_points(attr->value(), parser.path_))
{
parser.error_messages_.push_back(std::string("Failed to parse <polyline> 'points'"));
on_error(parser.error_messages_, std::string("Failed to parse <polyline> 'points'"), parser.strict_);
}
parser.path_.end_path();
}
@ -917,7 +931,7 @@ bool parse_common_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
{
std::stringstream ss;
ss << "Failed to find linked gradient " << linkid;
parser.error_messages_.push_back(ss.str());
on_error(parser.error_messages_, ss.str(), parser.strict_);
return false;
}
}
@ -981,8 +995,7 @@ void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
{
fy = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
}
else
fy = cy;
else fy = cy;
attr = node->first_attribute("r");
if (attr != nullptr)
@ -1049,9 +1062,10 @@ void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
}
svg_parser::svg_parser(svg_converter<svg_path_adapter,
agg::pod_bvector<mapnik::svg::path_attributes> > & path)
agg::pod_bvector<mapnik::svg::path_attributes> > & path, bool strict)
: path_(path),
is_defs_(false) {}
is_defs_(false),
strict_(strict) {}
svg_parser::~svg_parser() {}

View file

@ -144,6 +144,7 @@ int main (int argc,char** argv)
bool verbose = false;
bool auto_open = false;
bool strict = false;
int status = 0;
std::vector<std::string> svg_files;
mapnik::logger::instance().set_severity(mapnik::logger::error);
@ -156,18 +157,19 @@ int main (int argc,char** argv)
("version,V","print version string")
("verbose,v","verbose output")
("open","automatically open the file after rendering (os x only)")
("strict","enables strict SVG parsing")
("svg",po::value<std::vector<std::string> >(),"svg file to read")
;
po::positional_options_description p;
p.add("svg",-1);
p.add("svg", -1);
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
po::notify(vm);
if (vm.count("version"))
{
std::clog <<"version " << MAPNIK_VERSION_STRING << std::endl;
std::clog << "version " << MAPNIK_VERSION_STRING << std::endl;
return 1;
}
@ -187,6 +189,11 @@ int main (int argc,char** argv)
auto_open = true;
}
if (vm.count("strict"))
{
strict = true;
}
if (vm.count("svg"))
{
svg_files=vm["svg"].as< std::vector<std::string> >();
@ -211,8 +218,7 @@ int main (int argc,char** argv)
{
std::clog << "found: " << svg_name << "\n";
}
std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false, strict);
main_marker_visitor visitor(svg_name, verbose, auto_open);
status = mapnik::util::apply_visitor(visitor, *marker);
}