SVG rendering improvements:

* process `font-size` attributes and use them to scale `em` units
* fix default `color-stop` value
* store `viewBox` and use it to calculate `%` values correctly
* add more absolute/relative values
* default `DPI`:96
* default `font-size`: 10
* process style attributes on `<svg>` element
* use viewBox `width`, `height` and `normalized_diagonal` for relevant length values
* apply scale factor to gradient transforms
This commit is contained in:
Artem Pavlenko 2021-04-13 09:19:33 +01:00
parent 9c2132f895
commit f293371a9d
2 changed files with 167 additions and 90 deletions

View file

@ -34,6 +34,9 @@
// stl // stl
#include <map> #include <map>
#include <algorithm> #include <algorithm>
#include <deque>
// boost
#include <boost/optional.hpp>
namespace boost { namespace property_tree { namespace detail { namespace rapidxml { namespace boost { namespace property_tree { namespace detail { namespace rapidxml {
template <typename T> class xml_node; template <typename T> class xml_node;
@ -41,6 +44,14 @@ template <typename T> class xml_node;
namespace mapnik { namespace svg { namespace mapnik { namespace svg {
struct viewbox
{
double x0;
double y0;
double width;
double height;
};
class svg_parser_error_handler class svg_parser_error_handler
{ {
using error_message_container = std::vector<std::string> ; using error_message_container = std::vector<std::string> ;
@ -88,7 +99,10 @@ public:
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_{};
double normalized_diagonal_ = 0.0;
agg::trans_affine viewbox_tr_{}; agg::trans_affine viewbox_tr_{};
std::deque<double> font_sizes_{};
error_handler err_handler_; error_handler err_handler_;
}; };

View file

@ -39,10 +39,6 @@ MAPNIK_DISABLE_WARNING_PUSH
#include "agg_rounded_rect.h" #include "agg_rounded_rect.h"
#include "agg_span_gradient.h" #include "agg_span_gradient.h"
#include "agg_color_rgba.h" #include "agg_color_rgba.h"
MAPNIK_DISABLE_WARNING_POP
#include <mapnik/warning.hpp>
MAPNIK_DISABLE_WARNING_PUSH
#include <mapnik/warning_ignore.hpp> #include <mapnik/warning_ignore.hpp>
#include <boost/spirit/home/x3.hpp> #include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/struct.hpp> #include <boost/fusion/adapted/struct.hpp>
@ -62,14 +58,6 @@ namespace mapnik { namespace svg {
using util::name_to_int; using util::name_to_int;
using util::operator"" _case; using util::operator"" _case;
struct viewbox
{
double x0;
double y0;
double width;
double height;
};
}} }}
BOOST_FUSION_ADAPT_STRUCT ( BOOST_FUSION_ADAPT_STRUCT (
@ -118,7 +106,7 @@ static std::array<unsigned, 9> const unsupported_elements
"pattern"_case} "pattern"_case}
}; };
static std::array<unsigned, 43> const unsupported_attributes static std::array<unsigned, 42> const unsupported_attributes
{ {"alignment-baseline"_case, { {"alignment-baseline"_case,
"baseline-shift"_case, "baseline-shift"_case,
"clip"_case, "clip"_case,
@ -136,7 +124,6 @@ static std::array<unsigned, 43> const unsupported_attributes
"flood-color"_case, "flood-color"_case,
"flood-opacity"_case, "flood-opacity"_case,
"font-family"_case, "font-family"_case,
"font-size"_case,
"font-size-adjust"_case, "font-size-adjust"_case,
"font-stretch"_case, "font-stretch"_case,
"font-style"_case, "font-style"_case,
@ -257,31 +244,70 @@ double parse_double(T & err_handler, const char* str)
} }
// https://www.w3.org/TR/SVG/coords.html#Units // https://www.w3.org/TR/SVG/coords.html#Units
template <typename T, int DPI = 90> template <typename T>
double parse_svg_value(T & err_handler, const char* str, bool & is_percent) double parse_svg_value(T & parser, char const* str, bool & is_percent)
{ {
namespace x3 = boost::spirit::x3; namespace x3 = boost::spirit::x3;
double val = 0.0; double val = 0.0;
css_unit_value units; css_unit_value units;
const char* cur = str; // phrase_parse mutates the first iterator const char* cur = str; // phrase_parse mutates the first iterator
const char* end = str + std::strlen(str); const char* end = str + std::strlen(str);
double font_size = parser.font_sizes_.back();
auto apply_value = [&](auto const& ctx) { val = _attr(ctx); is_percent = false; }; auto apply_value = [&](auto const& ctx) { val = _attr(ctx); is_percent = false; };
auto apply_units = [&](auto const& ctx) { val *= _attr(ctx); }; auto apply_units = [&](auto const& ctx) { val *= _attr(ctx); };
auto apply_em = [&](auto const& ctx) { val *= font_size;};
auto apply_percent = [&](auto const& ctx) { val *= 0.01; is_percent = true; }; auto apply_percent = [&](auto const& ctx) { val *= 0.01; is_percent = true; };
if (!x3::phrase_parse(cur, end, if (!x3::phrase_parse(cur, end,
x3::double_[apply_value] x3::double_[apply_value]
> - (units[apply_units] > - (units[apply_units]
|
x3::lit("em")[apply_em]
| |
x3::lit('%')[apply_percent]), x3::lit('%')[apply_percent]),
x3::space) || (cur != end)) x3::space) || (cur != end))
{ {
err_handler.on_error("SVG parse error: failed to parse <number> with value \"" + std::string(str) + "\""); parser.err_handler().on_error("SVG parse error: failed to parse <number> with value \"" + std::string(str) + "\"");
} }
return val; return val;
} }
template <typename T>
double parse_font_size(T & parser, char const* str)
{
namespace x3 = boost::spirit::x3;
double val;
css_unit_value units;
css_absolute_size absolute;
css_relative_size relative;
std::size_t size = parser.font_sizes_.size();
double parent_font_size = size > 1 ? parser.font_sizes_[size - 2] : 10.0 ;
const char* cur = str; // phrase_parse mutates the first iterator
const char* end = str + std::strlen(str);
auto apply_value = [&](auto const& ctx) { val = _attr(ctx);};
auto apply_relative =[&](auto const& ctx) { val = parent_font_size * _attr(ctx);};
auto apply_units = [&](auto const& ctx) { val *= _attr(ctx); };
auto apply_percent = [&](auto const& ctx) { val = val * parent_font_size / 100.0;};
auto apply_em = [&](auto const& ctx) { val = val * parent_font_size;};
if (!x3::phrase_parse(cur, end,
absolute[apply_value]
|
relative[apply_relative]
|
x3::double_[apply_value]
> -(units[apply_units]
| x3::lit("em")[apply_em]
| x3::lit('%')[apply_percent]),
x3::space) || (cur != end))
{
parser.err_handler().on_error("SVG parse error: failed to parse <font-size> with value \"" + std::string(str) + "\"");
}
parser.font_sizes_.back() = val;
return val;
}
template <typename T, typename V> template <typename T, typename V>
bool parse_viewbox(T & err_handler, char const* str, V & viewbox) bool parse_viewbox(T & err_handler, char const* str, V & viewbox)
{ {
@ -371,7 +397,6 @@ void process_css(svg_parser & parser, rapidxml::xml_node<char> const* node)
auto itr = css.find(element_name); auto itr = css.find(element_name);
if (itr != css.end()) if (itr != css.end())
{ {
//std::cerr << "-> element key:" << element_name << std::endl;
for (auto const& def : std::get<1>(*itr)) for (auto const& def : std::get<1>(*itr))
{ {
style[std::get<0>(def)] = std::get<1>(def); style[std::get<0>(def)] = std::get<1>(def);
@ -396,8 +421,6 @@ void process_css(svg_parser & parser, rapidxml::xml_node<char> const* node)
auto range = css.equal_range(solitary_class_key); auto range = css.equal_range(solitary_class_key);
for (auto itr = range.first; itr != range.second; ++itr) for (auto itr = range.first; itr != range.second; ++itr)
{ {
//std::cerr << "<" << element_name << ">";
//std::cerr << "--> solitary class key:" << solitary_class_key << std::endl;
for (auto const& def : std::get<1>(*itr)) for (auto const& def : std::get<1>(*itr))
{ {
style[std::get<0>(def)] = std::get<1>(def); style[std::get<0>(def)] = std::get<1>(def);
@ -407,8 +430,6 @@ void process_css(svg_parser & parser, rapidxml::xml_node<char> const* node)
range = css.equal_range(class_key); range = css.equal_range(class_key);
for (auto itr = range.first; itr != range.second; ++itr) for (auto itr = range.first; itr != range.second; ++itr)
{ {
//std::cerr << "<" << element_name << ">";
//std::cerr << "---> class key:" << class_key << std::endl;
for (auto const& def : std::get<1>(*itr)) for (auto const& def : std::get<1>(*itr))
{ {
style[std::get<0>(def)] = std::get<1>(def); style[std::get<0>(def)] = std::get<1>(def);
@ -416,10 +437,6 @@ void process_css(svg_parser & parser, rapidxml::xml_node<char> const* node)
} }
} }
} }
//else
//{
// std::cerr << "Failed to parse styles..." << std::endl;
//}
} }
} }
auto const* id_attr = node->first_attribute("id"); auto const* id_attr = node->first_attribute("id");
@ -432,8 +449,6 @@ void process_css(svg_parser & parser, rapidxml::xml_node<char> const* node)
itr = css.find(id_key); itr = css.find(id_key);
if (itr != css.end()) if (itr != css.end())
{ {
//std::cerr << "<" << element_name << ">";
//std::cerr << "----> ID key:" << id_key << std::endl;
for (auto const& def : std::get<1>(*itr)) for (auto const& def : std::get<1>(*itr))
{ {
style[std::get<0>(def)] = std::get<1>(def); style[std::get<0>(def)] = std::get<1>(def);
@ -449,7 +464,6 @@ void process_css(svg_parser & parser, rapidxml::xml_node<char> const* node)
{ {
auto const& r = std::get<1>(def); auto const& r = std::get<1>(def);
std::string val{r.begin(), r.end()}; std::string val{r.begin(), r.end()};
//std::cerr << "PARSE ATTR:" << std::get<0>(def) << ":" << val << std::endl;
parse_attr(parser, std::get<0>(def).c_str(), val.c_str()); parse_attr(parser, std::get<0>(def).c_str(), val.c_str());
} }
} }
@ -459,10 +473,12 @@ void traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
{ {
if (parser.ignore_) return; if (parser.ignore_) return;
auto name = name_to_int(node->name()); auto name = name_to_int(node->name());
switch (node->type()) switch (node->type())
{ {
case rapidxml::node_element: case rapidxml::node_element:
{ {
parser.font_sizes_.push_back(parser.font_sizes_.back());
switch(name) switch(name)
{ {
case "defs"_case: case "defs"_case:
@ -574,6 +590,7 @@ void 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)
{ {
parser.font_sizes_.pop_back();
auto name = name_to_int(node->name()); auto name = name_to_int(node->name());
if (!parser.is_defs_ && (name == "g"_case)) if (!parser.is_defs_ && (name == "g"_case))
{ {
@ -582,6 +599,13 @@ void end_element(svg_parser & parser, rapidxml::xml_node<char> const* node)
parser.path_.pop_attr(); parser.path_.pop_attr();
} }
} }
else if (name == "svg"_case)
{
if (node->first_node() != nullptr)
{
parser.path_.pop_attr();
}
}
else if (name == "defs"_case) else if (name == "defs"_case)
{ {
if (node->first_node() != nullptr) if (node->first_node() != nullptr)
@ -629,7 +653,9 @@ void parse_element(svg_parser & parser, char const* name, rapidxml::xml_node<cha
parse_ellipse(parser, node); parse_ellipse(parser, node);
break; break;
case "svg"_case: case "svg"_case:
parser.path_.push_attr();
parse_dimensions(parser, node); parse_dimensions(parser, node);
parse_attr(parser, node);
break; break;
default: default:
handle_unsupported(parser, unsupported_elements, name, "element"); handle_unsupported(parser, unsupported_elements, name, "element");
@ -762,9 +788,13 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
parse_stroke(parser, value); parse_stroke(parser, value);
break; break;
case "stroke-width"_case: case "stroke-width"_case:
bool percent; {
parser.path_.stroke_width(parse_svg_value(parser.err_handler(), value, percent)); bool percent = false;
double stroke_width = parse_svg_value(parser, value, percent);
if (percent && parser.vbox_) stroke_width *= parser.normalized_diagonal_;
parser.path_.stroke_width(stroke_width);
break; break;
}
case "stroke-opacity"_case: case "stroke-opacity"_case:
parser.path_.stroke_opacity(parse_double(parser.err_handler(), value)); parser.path_.stroke_opacity(parse_double(parser.err_handler(), value));
break; break;
@ -805,6 +835,11 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
parser.path_.display(false); parser.path_.display(false);
} }
break; break;
case "font-size"_case:
{
double font_size = parse_font_size(parser, value);
break;
}
default: default:
handle_unsupported(parser, unsupported_attributes, name, "attribute"); handle_unsupported(parser, unsupported_attributes, name, "attribute");
break; break;
@ -847,22 +882,24 @@ void parse_dimensions(svg_parser & parser, rapidxml::xml_node<char> const* node)
auto const* width_attr = node->first_attribute("width"); auto const* width_attr = node->first_attribute("width");
if (width_attr) if (width_attr)
{ {
width = parse_svg_value(parser.err_handler(), width_attr->value(), has_percent_width); width = parse_svg_value(parser, width_attr->value(), has_percent_width);
} }
auto const* height_attr = node->first_attribute("height"); auto const* height_attr = node->first_attribute("height");
if (height_attr) if (height_attr)
{ {
height = parse_svg_value(parser.err_handler(), height_attr->value(), has_percent_height); height = parse_svg_value(parser, height_attr->value(), has_percent_height);
} }
parser.vbox_ = viewbox{0, 0, width, height} ;
auto const* viewbox_attr = node->first_attribute("viewBox"); auto const* viewbox_attr = node->first_attribute("viewBox");
if (viewbox_attr && parse_viewbox(parser.err_handler(), viewbox_attr->value(), vbox)) if (viewbox_attr && parse_viewbox(parser.err_handler(), viewbox_attr->value(), vbox))
{ {
agg::trans_affine t{};
parser.vbox_ = vbox;
parser.normalized_diagonal_ = std::sqrt(vbox.width * vbox.width + vbox.height * vbox.height)/std::sqrt(2.0);
if (!has_percent_width && !has_percent_height) if (!has_percent_width && !has_percent_height)
{ {
if (width > 0 && height > 0 && vbox.width > 0 && vbox.height > 0) if (width > 0 && height > 0 && vbox.width > 0 && vbox.height > 0)
{ {
agg::trans_affine t{};
std::pair<unsigned,bool> preserve_aspect_ratio {xMidYMid, true}; std::pair<unsigned,bool> preserve_aspect_ratio {xMidYMid, true};
auto const* aspect_ratio_attr = node->first_attribute("preserveAspectRatio"); auto const* aspect_ratio_attr = node->first_attribute("preserveAspectRatio");
if (aspect_ratio_attr) if (aspect_ratio_attr)
@ -918,9 +955,6 @@ void parse_dimensions(svg_parser & parser, rapidxml::xml_node<char> const* node)
-1.0 * (vbox.height - height / scale)) * t; -1.0 * (vbox.height - height / scale)) * t;
break; break;
}; };
t = agg::trans_affine_translation(-vbox.x0, -vbox.y0) * t;
parser.viewbox_tr_ = t;
} }
} }
if (has_percent_width && !has_percent_height) if (has_percent_width && !has_percent_height)
@ -938,6 +972,8 @@ void parse_dimensions(svg_parser & parser, rapidxml::xml_node<char> const* node)
width = vbox.width; width = vbox.width;
height = vbox.height; height = vbox.height;
} }
t = agg::trans_affine_translation(-vbox.x0, -vbox.y0) * t;
parser.viewbox_tr_ = t;
} }
parser.path_.set_dimensions(width, height); parser.path_.set_dimensions(width, height);
} }
@ -990,25 +1026,25 @@ void parse_use(svg_parser & parser, rapidxml::xml_node<char> const* node)
attr = node->first_attribute("x"); attr = node->first_attribute("x");
if (attr != nullptr) if (attr != nullptr)
{ {
x = parse_svg_value(parser.err_handler(), attr->value(), percent); x = parse_svg_value(parser, attr->value(), percent);
} }
attr = node->first_attribute("y"); attr = node->first_attribute("y");
if (attr != nullptr) if (attr != nullptr)
{ {
y = parse_svg_value(parser.err_handler(), attr->value(), percent); y = parse_svg_value(parser, attr->value(), percent);
} }
attr = node->first_attribute("width"); attr = node->first_attribute("width");
if (attr != nullptr) if (attr != nullptr)
{ {
w = parse_svg_value(parser.err_handler(), attr->value(), percent); w = parse_svg_value(parser, attr->value(), percent);
if (percent) w *= parser.path_.width(); 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, attr->value(), percent);
if (percent) h *= parser.path_.height(); if (percent) h *= parser.path_.height();
} }
if (w < 0.0) if (w < 0.0)
@ -1073,19 +1109,32 @@ void parse_line(svg_parser & parser, rapidxml::xml_node<char> const* node)
double y1 = 0.0; double y1 = 0.0;
double x2 = 0.0; double x2 = 0.0;
double y2 = 0.0; double y2 = 0.0;
bool percent; bool percent = false;
auto const* x1_attr = node->first_attribute("x1"); auto const* x1_attr = node->first_attribute("x1");
if (x1_attr) x1 = parse_svg_value(parser.err_handler(), x1_attr->value(), percent); if (x1_attr)
{
x1 = parse_svg_value(parser, x1_attr->value(), percent);
if (percent && parser.vbox_) x1 *= parser.vbox_->width;
}
auto const* y1_attr = node->first_attribute("y1"); auto const* y1_attr = node->first_attribute("y1");
if (y1_attr) y1 = parse_svg_value(parser.err_handler(), y1_attr->value(), percent); if (y1_attr)
{
y1 = parse_svg_value(parser, y1_attr->value(), percent);
if (percent && parser.vbox_) y1 *= parser.vbox_->height;
}
auto const* x2_attr = node->first_attribute("x2"); auto const* x2_attr = node->first_attribute("x2");
if (x2_attr) x2 = parse_svg_value(parser.err_handler(), x2_attr->value(), percent); if (x2_attr)
{
x2 = parse_svg_value(parser, x2_attr->value(), percent);
if (percent && parser.vbox_) x2 *= parser.vbox_->width;
}
auto const* y2_attr = node->first_attribute("y2"); auto const* y2_attr = node->first_attribute("y2");
if (y2_attr) y2 = parse_svg_value(parser.err_handler(), y2_attr->value(), percent); if (y2_attr)
{
y2 = parse_svg_value(parser, y2_attr->value(), percent);
if (percent && parser.vbox_) y2 *= parser.vbox_->height;
}
parser.path_.begin_path(); parser.path_.begin_path();
parser.path_.move_to(x1, y1); parser.path_.move_to(x1, y1);
parser.path_.line_to(x2, y2); parser.path_.line_to(x2, y2);
@ -1097,23 +1146,26 @@ void parse_circle(svg_parser & parser, rapidxml::xml_node<char> const* node)
double cx = 0.0; double cx = 0.0;
double cy = 0.0; double cy = 0.0;
double r = 0.0; double r = 0.0;
bool percent; bool percent = false;
auto * attr = node->first_attribute("cx"); auto * attr = node->first_attribute("cx");
if (attr != nullptr) if (attr != nullptr)
{ {
cx = parse_svg_value(parser.err_handler(), attr->value(), percent); cx = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) cx *= parser.vbox_->width;
} }
attr = node->first_attribute("cy"); attr = node->first_attribute("cy");
if (attr != nullptr) if (attr != nullptr)
{ {
cy = parse_svg_value(parser.err_handler(), attr->value(), percent); cy = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) cy *= parser.vbox_->height;
} }
attr = node->first_attribute("r"); attr = node->first_attribute("r");
if (attr != nullptr) if (attr != nullptr)
{ {
r = parse_svg_value(parser.err_handler(), attr->value(), percent); r = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) r *= parser.normalized_diagonal_;
} }
parser.path_.begin_path(); parser.path_.begin_path();
@ -1140,29 +1192,33 @@ void parse_ellipse(svg_parser & parser, rapidxml::xml_node<char> const * node)
double cy = 0.0; double cy = 0.0;
double rx = 0.0; double rx = 0.0;
double ry = 0.0; double ry = 0.0;
bool percent; bool percent = false;
auto * attr = node->first_attribute("cx"); auto * attr = node->first_attribute("cx");
if (attr != nullptr) if (attr != nullptr)
{ {
cx = parse_svg_value(parser.err_handler(), attr->value(), percent); cx = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) cx *= parser.vbox_->width;
} }
attr = node->first_attribute("cy"); attr = node->first_attribute("cy");
if (attr) if (attr)
{ {
cy = parse_svg_value(parser.err_handler(), attr->value(), percent); cy = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) cy *= parser.vbox_->height;
} }
attr = node->first_attribute("rx"); attr = node->first_attribute("rx");
if (attr != nullptr) if (attr != nullptr)
{ {
rx = parse_svg_value(parser.err_handler(), attr->value(), percent); rx = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) rx *= parser.normalized_diagonal_;
} }
attr = node->first_attribute("ry"); attr = node->first_attribute("ry");
if (attr != nullptr) if (attr != nullptr)
{ {
ry = parse_svg_value(parser.err_handler(), attr->value(), percent); ry = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) ry *= parser.normalized_diagonal_;
} }
if (rx != 0.0 && ry != 0.0) if (rx != 0.0 && ry != 0.0)
@ -1198,35 +1254,40 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node<char> const* node)
double h = 0.0; double h = 0.0;
double rx = 0.0; double rx = 0.0;
double ry = 0.0; double ry = 0.0;
bool percent; bool percent = false;
auto * attr = node->first_attribute("x"); auto * attr = node->first_attribute("x");
if (attr != nullptr) if (attr != nullptr)
{ {
x = parse_svg_value(parser.err_handler(), attr->value(), percent); x = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) x *= parser.vbox_->width;
} }
attr = node->first_attribute("y"); attr = node->first_attribute("y");
if (attr != nullptr) if (attr != nullptr)
{ {
y = parse_svg_value(parser.err_handler(), attr->value(), percent); y = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) y *= parser.vbox_->height;
} }
attr = node->first_attribute("width"); attr = node->first_attribute("width");
if (attr != nullptr) if (attr != nullptr)
{ {
w = parse_svg_value(parser.err_handler(), attr->value(), percent); w = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) w *= parser.vbox_->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, attr->value(), percent);
if (percent && parser.vbox_) h *= parser.vbox_->height;
} }
bool rounded = true; bool rounded = true;
attr = node->first_attribute("rx"); attr = node->first_attribute("rx");
if (attr != nullptr) if (attr != nullptr)
{ {
rx = parse_svg_value(parser.err_handler(), attr->value(), percent); rx = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) rx *= parser.vbox_->width;
if ( rx > 0.5 * w ) rx = 0.5 * w; if ( rx > 0.5 * w ) rx = 0.5 * w;
} }
else rounded = false; else rounded = false;
@ -1234,7 +1295,8 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node<char> const* node)
attr = node->first_attribute("ry"); attr = node->first_attribute("ry");
if (attr != nullptr) if (attr != nullptr)
{ {
ry = parse_svg_value(parser.err_handler(), attr->value(), percent); ry = parse_svg_value(parser, attr->value(), percent);
if (percent && parser.vbox_) ry *= parser.vbox_->height;
if ( ry > 0.5 * h ) ry = 0.5 * h; if ( ry > 0.5 * h ) ry = 0.5 * h;
if (!rounded) if (!rounded)
{ {
@ -1300,14 +1362,14 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node<char> const* node)
void parse_gradient_stop(svg_parser & parser, mapnik::gradient& gr, rapidxml::xml_node<char> const* node) void parse_gradient_stop(svg_parser & parser, mapnik::gradient& gr, rapidxml::xml_node<char> const* node)
{ {
double offset = 0.0; double offset = 0.0;
mapnik::color stop_color; mapnik::color stop_color{0,0,0,255};
double opacity = 1.0; double opacity = 1.0;
auto * attr = node->first_attribute("offset"); auto * attr = node->first_attribute("offset");
if (attr != nullptr) if (attr != nullptr)
{ {
bool percent = false; bool percent = false;
offset = parse_svg_value(parser.err_handler(),attr->value(), percent); offset = parse_svg_value(parser,attr->value(), percent);
} }
attr = node->first_attribute("style"); attr = node->first_attribute("style");
@ -1406,39 +1468,38 @@ void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
double fx = 0.0; double fx = 0.0;
double fy = 0.0; double fy = 0.0;
double r = 0.5; double r = 0.5;
bool has_percent=true; bool has_percent = false;
attr = node->first_attribute("cx"); attr = node->first_attribute("cx");
if (attr != nullptr) if (attr != nullptr)
{ {
cx = parse_svg_value(parser.err_handler(), attr->value(), has_percent); cx = parse_svg_value(parser, attr->value(), has_percent);
} }
attr = node->first_attribute("cy"); attr = node->first_attribute("cy");
if (attr != nullptr) if (attr != nullptr)
{ {
cy = parse_svg_value(parser.err_handler(), attr->value(), has_percent); cy = parse_svg_value(parser, attr->value(), has_percent);
} }
attr = node->first_attribute("fx"); attr = node->first_attribute("fx");
if (attr != nullptr) if (attr != nullptr)
{ {
fx = parse_svg_value(parser.err_handler(),attr->value(), has_percent); fx = parse_svg_value(parser,attr->value(), has_percent);
} }
else else fx = cx;
fx = cx;
attr = node->first_attribute("fy"); attr = node->first_attribute("fy");
if (attr != nullptr) if (attr != nullptr)
{ {
fy = parse_svg_value(parser.err_handler(), attr->value(), has_percent); fy = parse_svg_value(parser, attr->value(), has_percent);
} }
else fy = cy; else fy = cy;
attr = node->first_attribute("r"); attr = node->first_attribute("r");
if (attr != nullptr) if (attr != nullptr)
{ {
r = parse_svg_value(parser.err_handler(), attr->value(), has_percent); r = parse_svg_value(parser, attr->value(), has_percent);
} }
// this logic for detecting %'s will not support mixed coordinates. // this logic for detecting %'s will not support mixed coordinates.
if (has_percent && gr.get_units() == USER_SPACE_ON_USE) if (has_percent && gr.get_units() == USER_SPACE_ON_USE)
@ -1471,40 +1532,39 @@ void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
if (!parse_common_gradient(parser, id, gr, node)) return; if (!parse_common_gradient(parser, id, gr, node)) return;
double x1 = 0.0; double x1 = 0.0;
double x2 = 1.0;
double y1 = 0.0; double y1 = 0.0;
double x2 = 1.0;
double y2 = 0.0; double y2 = 0.0;
bool has_percent=true; bool has_percent = true;
attr = node->first_attribute("x1"); attr = node->first_attribute("x1");
if (attr != nullptr) if (attr != nullptr)
{ {
x1 = parse_svg_value(parser.err_handler(), attr->value(), has_percent); x1 = parse_svg_value(parser, attr->value(), has_percent);
}
attr = node->first_attribute("x2");
if (attr != nullptr)
{
x2 = parse_svg_value(parser.err_handler(), attr->value(), has_percent);
} }
attr = node->first_attribute("y1"); attr = node->first_attribute("y1");
if (attr != nullptr) if (attr != nullptr)
{ {
y1 = parse_svg_value(parser.err_handler(), attr->value(), has_percent); y1 = parse_svg_value(parser, attr->value(), has_percent);
}
attr = node->first_attribute("x2");
if (attr != nullptr)
{
x2 = parse_svg_value(parser, attr->value(), has_percent);
} }
attr = node->first_attribute("y2"); attr = node->first_attribute("y2");
if (attr != nullptr) if (attr != nullptr)
{ {
y2 = parse_svg_value(parser.err_handler(), attr->value(), has_percent); y2 = parse_svg_value(parser, attr->value(), has_percent);
} }
// this logic for detecting %'s will not support mixed coordinates. // this logic for detecting %'s will not support mixed coordinates.
if (has_percent && gr.get_units() == USER_SPACE_ON_USE) if (has_percent && gr.get_units() == USER_SPACE_ON_USE)
{ {
gr.set_units(USER_SPACE_ON_USE_BOUNDING_BOX); gr.set_units(USER_SPACE_ON_USE_BOUNDING_BOX);
} }
gr.set_gradient_type(LINEAR); gr.set_gradient_type(LINEAR);
gr.set_control_points(x1, y1, x2, y2); gr.set_control_points(x1, y1, x2, y2);
@ -1526,7 +1586,10 @@ svg_parser::svg_parser(svg_converter_type & path, bool strict)
is_defs_(false), is_defs_(false),
ignore_(false), ignore_(false),
css_style_(false), css_style_(false),
err_handler_(strict) {} err_handler_(strict)
{
font_sizes_.push_back(10.0);
}
svg_parser::~svg_parser() {} svg_parser::~svg_parser() {}