diff --git a/deps/agg/src/agg_bezier_arc.cpp b/deps/agg/src/agg_bezier_arc.cpp index bb5ffc464..08ec67cd7 100644 --- a/deps/agg/src/agg_bezier_arc.cpp +++ b/deps/agg/src/agg_bezier_arc.cpp @@ -22,7 +22,6 @@ #include #include "agg_bezier_arc.h" - namespace agg { @@ -144,7 +143,7 @@ void bezier_arc_svg::init(double x0, double y0, m_radii_ok = true; if(rx < 0.0) rx = -rx; - if(ry < 0.0) ry = -rx; + if(ry < 0.0) ry = -ry; // Calculate the middle point between // the current and the final points @@ -178,7 +177,6 @@ void bezier_arc_svg::init(double x0, double y0, pry = ry * ry; if(radii_check > 10.0) m_radii_ok = false; } - // Calculate (cx1, cy1) //------------------------ double sign = (large_arc_flag == sweep_flag) ? -1.0 : 1.0; @@ -222,15 +220,15 @@ void bezier_arc_svg::init(double x0, double y0, if(v < -1.0) v = -1.0; if(v > 1.0) v = 1.0; double sweep_angle = sign * std::acos(v); + if (std::fabs(sweep_angle) < pi * 1e-6) m_radii_ok = false; if(!sweep_flag && sweep_angle > 0) { sweep_angle -= pi * 2.0; } - else - if (sweep_flag && sweep_angle < 0) - { - sweep_angle += pi * 2.0; - } + else if (sweep_flag && sweep_angle < 0) + { + sweep_angle += pi * 2.0; + } // We can now build and transform the resulting arc //------------------------ diff --git a/include/mapnik/svg/svg_path_adapter.hpp b/include/mapnik/svg/svg_path_adapter.hpp index 19a8d1dbd..d3df61b04 100644 --- a/include/mapnik/svg/svg_path_adapter.hpp +++ b/include/mapnik/svg/svg_path_adapter.hpp @@ -444,12 +444,13 @@ void path_adapter::curve3(double x_to, double y_to) { double x0; double y0; - if(is_vertex(vertices_.last_vertex(&x0, &y0))) + unsigned last_cmd = last_vertex(&x0, &y0); + if (is_vertex(last_cmd)) { double x_ctrl; double y_ctrl; - unsigned cmd = vertices_.prev_vertex(&x_ctrl, &y_ctrl); - if(is_curve(cmd)) + unsigned prev_cmd = prev_vertex(&x_ctrl, &y_ctrl); + if (is_curve(last_cmd) && is_curve(prev_cmd)) { x_ctrl = x0 + x0 - x_ctrl; y_ctrl = y0 + y0 - y_ctrl; @@ -491,9 +492,7 @@ void path_adapter::curve4_rel(double dx_ctrl1, double dy_ctrl1, rel_to_abs(&dx_ctrl1, &dy_ctrl1); rel_to_abs(&dx_ctrl2, &dy_ctrl2); rel_to_abs(&dx_to, &dy_to); - vertices_.add_vertex(dx_ctrl1, dy_ctrl1, path_cmd_curve4); - vertices_.add_vertex(dx_ctrl2, dy_ctrl2, path_cmd_curve4); - vertices_.add_vertex(dx_to, dy_to, path_cmd_curve4); + curve4(dx_ctrl1, dy_ctrl1, dx_ctrl2, dy_ctrl2, dx_to, dy_to); } //------------------------------------------------------------------------ @@ -503,12 +502,13 @@ void path_adapter::curve4(double x_ctrl2, double y_ctrl2, { double x0; double y0; - if(is_vertex(last_vertex(&x0, &y0))) + unsigned last_cmd = last_vertex(&x0, &y0); + if (is_vertex(last_cmd)) { double x_ctrl1; double y_ctrl1; - unsigned cmd = prev_vertex(&x_ctrl1, &y_ctrl1); - if(is_curve(cmd)) + unsigned prev_cmd = prev_vertex(&x_ctrl1, &y_ctrl1); + if (is_curve(last_cmd) && is_curve(prev_cmd)) { x_ctrl1 = x0 + x0 - x_ctrl1; y_ctrl1 = y0 + y0 - y_ctrl1; diff --git a/include/mapnik/svg/svg_path_grammar_x3_def.hpp b/include/mapnik/svg/svg_path_grammar_x3_def.hpp index 89fcffb7d..b4dc73d23 100644 --- a/include/mapnik/svg/svg_path_grammar_x3_def.hpp +++ b/include/mapnik/svg/svg_path_grammar_x3_def.hpp @@ -83,9 +83,9 @@ auto const curve4 = [] (auto const& ctx) auto const& p1 = boost::fusion::at_c<1>(attr); auto const& p2 = boost::fusion::at_c<2>(attr); extract_path(ctx).curve4(std::get<0>(p0),std::get<1>(p0), - std::get<0>(p1),std::get<1>(p1), - std::get<0>(p2),std::get<1>(p2), - x3::get(ctx)); + std::get<0>(p1),std::get<1>(p1), + std::get<0>(p2),std::get<1>(p2), + x3::get(ctx)); }; auto const curve4_smooth = [] (auto const& ctx) @@ -94,8 +94,8 @@ auto const curve4_smooth = [] (auto const& ctx) auto const& p0 = boost::fusion::at_c<0>(attr); auto const& p1 = boost::fusion::at_c<1>(attr); extract_path(ctx).curve4(std::get<0>(p0),std::get<1>(p0), - std::get<0>(p1),std::get<1>(p1), - x3::get(ctx)); + std::get<0>(p1),std::get<1>(p1), + x3::get(ctx)); }; auto const curve3 = [] (auto const& ctx) @@ -104,15 +104,16 @@ auto const curve3 = [] (auto const& ctx) auto const& p0 = boost::fusion::at_c<0>(attr); auto const& p1 = boost::fusion::at_c<1>(attr); extract_path(ctx).curve3(std::get<0>(p0),std::get<1>(p0), - std::get<0>(p1),std::get<1>(p1), - x3::get(ctx)); + std::get<0>(p1),std::get<1>(p1), + x3::get(ctx)); }; auto const curve3_smooth = [] (auto const& ctx) { auto const& attr = _attr(ctx); - extract_path(ctx).curve3(std::get<0>(attr),std::get<1>(attr), - x3::get(ctx)); + extract_path(ctx).curve3(std::get<0>(attr), + std::get<1>(attr), + x3::get(ctx)); }; @@ -125,10 +126,10 @@ auto const arc_to = [] (auto & ctx) bool sweep_flag = boost::fusion::at_c<3>(attr); auto const& v = boost::fusion::at_c<4>(attr); extract_path(ctx).arc_to(std::get<0>(p), std::get<1>(p), - util::radians(angle), - large_arc_flag, sweep_flag, - std::get<0>(v),std::get<1>(v), - x3::get(ctx)); + util::radians(angle), + large_arc_flag, sweep_flag, + std::get<0>(v),std::get<1>(v), + x3::get(ctx)); }; auto const close_path = [] (auto const& ctx) diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index fed882f52..4959c2dab 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -602,7 +602,7 @@ void parse_fill(svg_parser& parser, char const* value) traverse_tree(parser, gradient_node); if (parser.gradient_map_.count(id) > 0) { - parser.path_.add_stroke_gradient(parser.gradient_map_[id]); + parser.path_.add_fill_gradient(parser.gradient_map_[id]); } else { @@ -1360,7 +1360,6 @@ void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node const* } } parser.gradient_map_[id] = gr; - //MAPNIK_LOG_DEBUG(svg_parser) << "Found Radial Gradient: " << " " << cx << " " << cy << " " << fx << " " << fy << " " << r; } void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node const* node) diff --git a/test/data-visual b/test/data-visual index 901ee22d4..54a2c2595 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 901ee22d4cdbe1a7caff3f92e10f38ec784b9caa +Subproject commit 54a2c2595c81e838810e413d42a57f09fcf41331 diff --git a/test/unit/svg/svg_path_parser_test.cpp b/test/unit/svg/svg_path_parser_test.cpp index e877f6063..7352a7a47 100644 --- a/test/unit/svg/svg_path_parser_test.cpp +++ b/test/unit/svg/svg_path_parser_test.cpp @@ -161,4 +161,33 @@ TEST_CASE("SVG path parser") { std::make_tuple(1050, 125, 2)}; test_path_parser(str, expected); } + + SECTION("Quadratic Bézier") + { + std::string str = "M200,300 Q400,50 600,300 T1000,300"; + + std::vector> expected = { + std::make_tuple(200, 300, 1), + std::make_tuple(400, 50, 3), + std::make_tuple(600, 300, 3), + std::make_tuple(800, 550, 3), + std::make_tuple(1000, 300, 3)}; + test_path_parser(str, expected); + } + + SECTION("Cubic Bézier") + { + std::string str = "M100,200 C100,100 250,100 250,200S400,300 400,200"; + + std::vector> expected = { + std::make_tuple(100, 200, 1), + std::make_tuple(100, 100, 4), + std::make_tuple(250, 100, 4), + std::make_tuple(250, 200, 4), + std::make_tuple(250, 300, 4), + std::make_tuple(400, 300, 4), + std::make_tuple(400, 200, 4)}; + + test_path_parser(str, expected); + } } diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp index 61b5ba0d0..e2b07358d 100644 --- a/utils/svg2png/svg2png.cpp +++ b/utils/svg2png/svg2png.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -54,9 +55,11 @@ struct main_marker_visitor { main_marker_visitor(std::string const& svg_name, + double scale_factor, bool verbose, bool auto_open) : svg_name_(svg_name), + scale_factor_(scale_factor), verbose_(verbose), auto_open_(auto_open) {} @@ -71,17 +74,33 @@ struct main_marker_visitor double opacity = 1; double w, h; std::tie(w, h) = marker.dimensions(); + + + double svg_width = w * scale_factor_; + double svg_height = h * scale_factor_; + + int output_width = static_cast(std::round(svg_width)); + int output_height = static_cast(std::round(svg_height)); if (verbose_) { - std::clog << "found width of '" << w << "' and height of '" << h << "'\n"; + std::clog << "Found width of '" << w << "' and height of '" << h << "'\n"; + auto b = marker.bounding_box(); + std::clog << "SVG BBOX:" << b << std::endl; + std::clog << "Output image dimensions:[" << output_width << "," << output_height << "]" << std::endl; } - mapnik::image_rgba8 im(static_cast(w + 0.5), static_cast(h + 0.5)); + mapnik::image_rgba8 im(output_width, output_height, true, true); agg::rendering_buffer buf(im.bytes(), im.width(), im.height(), im.row_size()); pixfmt pixf(buf); renderer_base renb(pixf); - mapnik::box2d const& bbox = {0, 0, w, h}; - agg::trans_affine mtx = {}; + mapnik::box2d const& bbox = {0, 0, svg_width, svg_height}; + // center the svg marker on '0,0' + agg::trans_affine mtx = agg::trans_affine_translation(-0.5 * w, -0.5 * h); + // Scale the image + mtx.scale(scale_factor_); + // render the marker at the center of the marker box + mtx.translate(0.5 * svg_width, 0.5 * svg_height); + mapnik::svg::vertex_stl_adapter stl_storage(marker.get_data()->source()); mapnik::svg::svg_path_adapter svg_path(stl_storage); mapnik::svg::renderer_agg< @@ -124,6 +143,7 @@ struct main_marker_visitor private: std::string svg_name_; + double scale_factor_ = 1.0; bool verbose_; bool auto_open_; }; @@ -138,7 +158,8 @@ int main (int argc,char** argv) int status = 0; std::vector svg_files; mapnik::logger::instance().set_severity(mapnik::logger::error); - + double scale_factor = 1.0; + std::string usage = "Usage: svg2png [options] "; try { po::options_description desc("svg2png utility"); @@ -148,6 +169,7 @@ int main (int argc,char** argv) ("verbose,v","verbose output") ("open,o","automatically open the file after rendering (os x only)") ("strict,s","enables strict SVG parsing") + ("scale-factor", po::value(), "provide scaling factor (default: 1.0)") ("svg",po::value >(),"svg file to read") ; @@ -166,6 +188,7 @@ int main (int argc,char** argv) if (vm.count("help")) { std::clog << desc << std::endl; + std::clog << usage << std::endl; return 1; } @@ -183,24 +206,26 @@ int main (int argc,char** argv) { strict = true; } - + if (vm.count("scale-factor")) + { + scale_factor = vm["scale-factor"].as(); + } if (vm.count("svg")) { svg_files=vm["svg"].as< std::vector >(); } else { - std::clog << "please provide an svg file!" << std::endl; + std::clog << usage << std::endl; return -1; } std::vector::const_iterator itr = svg_files.begin(); if (itr == svg_files.end()) { - std::clog << "no svg files to render" << std::endl; + std::clog << usage << std::endl; return 0; } - while (itr != svg_files.end()) { std::string svg_name (*itr++); @@ -209,7 +234,7 @@ int main (int argc,char** argv) std::clog << "found: " << svg_name << "\n"; } std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false, strict); - main_marker_visitor visitor(svg_name, verbose, auto_open); + main_marker_visitor visitor(svg_name, scale_factor, verbose, auto_open); status = mapnik::util::apply_visitor(visitor, *marker); } }