From ed648ced467d37b54fa933323671050e6b0d2042 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 23 Dec 2016 16:32:05 +0100 Subject: [PATCH] SVG transform - implement in terms of boost::spirit::x3 --- include/mapnik/svg/svg_grammar_config_x3.hpp | 4 + .../mapnik/svg/svg_path_grammar_x3_def.hpp | 7 +- include/mapnik/svg/svg_path_parser.hpp | 4 +- include/mapnik/svg/svg_transform_grammar.hpp | 56 ------ .../mapnik/svg/svg_transform_grammar_impl.hpp | 171 ------------------ .../mapnik/svg/svg_transform_grammar_x3.hpp | 43 +++++ .../svg/svg_transform_grammar_x3_def.hpp | 163 +++++++++++++++++ src/svg/svg_transform_parser.cpp | 31 +++- 8 files changed, 236 insertions(+), 243 deletions(-) delete mode 100644 include/mapnik/svg/svg_transform_grammar.hpp delete mode 100644 include/mapnik/svg/svg_transform_grammar_impl.hpp create mode 100644 include/mapnik/svg/svg_transform_grammar_x3.hpp create mode 100644 include/mapnik/svg/svg_transform_grammar_x3_def.hpp diff --git a/include/mapnik/svg/svg_grammar_config_x3.hpp b/include/mapnik/svg/svg_grammar_config_x3.hpp index 87444b0ca..3666cebb9 100644 --- a/include/mapnik/svg/svg_grammar_config_x3.hpp +++ b/include/mapnik/svg/svg_grammar_config_x3.hpp @@ -33,6 +33,7 @@ namespace mapnik { namespace svg { namespace grammar { class relative_tag; class svg_path_tag; +class svg_transform_tag; namespace x3 = boost::spirit::x3; using space_type = x3::standard::space_type; @@ -42,6 +43,9 @@ using phrase_parse_context_type = x3::phrase_parse_context::type; using svg_parse_context_type = x3::with_context const, x3::with_context const, phrase_parse_context_type>::type>::type; + +inline double deg2rad(double deg) {return (M_PI * deg) / 180.0;} + }}} #endif // MAPNIK_SVG_GRAMMAR_CONFIG_X3_HPP diff --git a/include/mapnik/svg/svg_path_grammar_x3_def.hpp b/include/mapnik/svg/svg_path_grammar_x3_def.hpp index d3d685ce7..b1bed97aa 100644 --- a/include/mapnik/svg/svg_path_grammar_x3_def.hpp +++ b/include/mapnik/svg/svg_path_grammar_x3_def.hpp @@ -102,10 +102,6 @@ auto const curve3_smooth = [] (auto const& ctx) x3::get(ctx)); }; -inline double deg2rad(double deg) -{ - return (M_PI * deg) / 180.0; -} auto const arc_to = [] (auto & ctx) { @@ -136,10 +132,11 @@ auto const absolute = [] (auto const& ctx) x3::get(ctx).get() = false; }; +// exported rules svg_path_grammar_type const svg_path = "SVG Path"; - svg_points_grammar_type const svg_points = "SVG_Points"; +// rules auto const coord = x3::rule{} = double_ > -lit(',') > double_; auto const svg_points_def = coord[move_to] // move_to diff --git a/include/mapnik/svg/svg_path_parser.hpp b/include/mapnik/svg/svg_path_parser.hpp index bb0ec8c12..444badbf5 100644 --- a/include/mapnik/svg/svg_path_parser.hpp +++ b/include/mapnik/svg/svg_path_parser.hpp @@ -39,12 +39,12 @@ template bool parse_points(const char* wkt, PathType& p); template -bool MAPNIK_DECL parse_svg_transform(const char* wkt, TransformType& tr); +bool parse_svg_transform(const char* wkt, TransformType& tr); // extern template bool MAPNIK_DECL parse_path(const char*, svg_converter_type&); extern template bool MAPNIK_DECL parse_points(const char*, svg_converter_type&); -extern template bool MAPNIK_DECL parse_svg_transform(const char*, svg_converter_type&); +extern template bool MAPNIK_DECL parse_svg_transform(const char*, agg::trans_affine&); } } diff --git a/include/mapnik/svg/svg_transform_grammar.hpp b/include/mapnik/svg/svg_transform_grammar.hpp deleted file mode 100644 index 18fce6bb6..000000000 --- a/include/mapnik/svg/svg_transform_grammar.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2016 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#ifndef MAPNIK_SVG_TRANSFORM_GRAMMAR_HPP -#define MAPNIK_SVG_TRANSFORM_GRAMMAR_HPP - -// mapnik -#include - -#pragma GCC diagnostic push -#include -#include -#pragma GCC diagnostic pop - -namespace mapnik { namespace svg { - -using namespace boost::spirit; - -template -struct svg_transform_grammar : qi::grammar -{ - // ctor - svg_transform_grammar(); - // rules - qi::rule start; - qi::rule transform_; - qi::rule matrix; - qi::rule translate; - qi::rule scale; - qi::rule, void(TransformType&), SkipType> rotate; - qi::rule skewX; - qi::rule skewY; -}; - -}} - -#endif // MAPNIK_SVG_TRANSFORM_GRAMMAR_HPP diff --git a/include/mapnik/svg/svg_transform_grammar_impl.hpp b/include/mapnik/svg/svg_transform_grammar_impl.hpp deleted file mode 100644 index 39c731928..000000000 --- a/include/mapnik/svg/svg_transform_grammar_impl.hpp +++ /dev/null @@ -1,171 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2016 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -// NOTE: This is an implementation header file and is only meant to be included -// from implementation files. It therefore doesn't have an include guard. -// mapnik -#include - -#pragma GCC diagnostic push -#include -#include -#include -#include -#pragma GCC diagnostic pop - -#pragma GCC diagnostic push -#include -#include -#pragma GCC diagnostic pop - -namespace mapnik { namespace svg { - -using namespace boost::spirit; -using namespace boost::fusion; -using namespace boost::phoenix; - -inline double deg2rad(double d) -{ - return M_PI * d / 180.0; -} - -struct process_matrix -{ - using result_type = void; - template - void operator () (TransformType & tr, double a, double b, double c, double d, double e, double f) const - { - tr = agg::trans_affine(a,b,c,d,e,f) * tr; - } -}; - -struct process_rotate -{ - using result_type = void; - - template - void operator () (TransformType & tr, T0 a, T1 cx, T2 cy) const - { - if (cx == 0.0 && cy == 0.0) - { - tr = agg::trans_affine_rotation(deg2rad(a)) * tr; - } - else - { - agg::trans_affine t = agg::trans_affine_translation(-cx,-cy); - t *= agg::trans_affine_rotation(deg2rad(a)); - t *= agg::trans_affine_translation(cx, cy); - tr = t * tr; - } - } -}; - -struct process_translate -{ - using result_type = void; - template - void operator () (TransformType & tr, T0 tx, T1 ty) const - { - if (ty) tr = agg::trans_affine_translation(tx,*ty) * tr; - else tr = agg::trans_affine_translation(tx,0.0) * tr; - } -}; - -struct process_scale -{ - using result_type = void; - template - void operator () (TransformType & tr, T0 sx, T1 sy) const - { - if (sy) tr = agg::trans_affine_scaling(sx,*sy) * tr; - else tr = agg::trans_affine_scaling(sx,sx) * tr; - } -}; - - -struct process_skew -{ - using result_type = void; - - template - void operator () (TransformType & tr, T0 skew_x, T1 skew_y) const - { - tr = agg::trans_affine_skewing(deg2rad(skew_x),deg2rad(skew_y)) * tr; - } -}; - -template -svg_transform_grammar::svg_transform_grammar() - : svg_transform_grammar::base_type(start) -{ - qi::_1_type _1; - qi::_2_type _2; - qi::_3_type _3; - qi::_4_type _4; - qi::_5_type _5; - qi::_6_type _6; - qi::_a_type _a; - qi::_b_type _b; - qi::_c_type _c; - qi::_r1_type _r1; - qi::lit_type lit; - qi::double_type double_; - qi::no_case_type no_case; - - // actions - function matrix_action; - function rotate_action; - function translate_action; - function scale_action; - function skew_action; - - start = +transform_(_r1) ; - - transform_ = matrix(_r1) | rotate(_r1) | translate(_r1) | scale(_r1) | rotate(_r1) | skewX(_r1) | skewY (_r1) ; - - matrix = no_case[lit("matrix")] >> lit('(') - >> (double_ >> -lit(',') - >> double_ >> -lit(',') - >> double_ >> -lit(',') - >> double_ >> -lit(',') - >> double_ >> -lit(',') - >> double_)[matrix_action(_r1, _1, _2, _3, _4, _5, _6)] >> lit(')'); - - translate = no_case[lit("translate")] - >> lit('(') >> (double_ >> -lit(',') >> -double_)[translate_action(_r1, _1, _2)] >> lit(')'); - - scale = no_case[lit("scale")] - >> lit('(') >> (double_ >> -lit(',') >> -double_)[scale_action(_r1, _1, _2)] >> lit(')'); - - rotate = no_case[lit("rotate")] - >> lit('(') - >> double_[_a = _1] >> -lit(',') - >> -(double_ [_b = _1] >> -lit(',') >> double_[_c = _1]) - >> lit(')') [ rotate_action(_r1, _a,_b,_c)]; - - skewX = no_case[lit("skewX")] >> lit('(') >> double_ [ skew_action(_r1, _1, 0.0)] >> lit(')'); - - skewY = no_case[lit("skewY")] >> lit('(') >> double_ [ skew_action(_r1, 0.0, _1)] >> lit(')'); - -} - -}} diff --git a/include/mapnik/svg/svg_transform_grammar_x3.hpp b/include/mapnik/svg/svg_transform_grammar_x3.hpp new file mode 100644 index 000000000..c0fb12915 --- /dev/null +++ b/include/mapnik/svg/svg_transform_grammar_x3.hpp @@ -0,0 +1,43 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2016 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_SVG_TRANSFORM_GRAMMAR_X3_HPP +#define MAPNIK_SVG_TRANSFORM_GRAMMAR_X3_HPP + +// mapnik +#include + +namespace mapnik { namespace svg { namespace grammar { + +using namespace boost::spirit::x3; + +using svg_transform_grammar_type = x3::rule; + +BOOST_SPIRIT_DECLARE(svg_transform_grammar_type); + +} + +grammar::svg_transform_grammar_type const& svg_transform_grammar(); + +}} + +#endif // MAPNIK_SVG_TRANSFORM_GRAMMAR_X3_HPP diff --git a/include/mapnik/svg/svg_transform_grammar_x3_def.hpp b/include/mapnik/svg/svg_transform_grammar_x3_def.hpp new file mode 100644 index 000000000..47b6030ce --- /dev/null +++ b/include/mapnik/svg/svg_transform_grammar_x3_def.hpp @@ -0,0 +1,163 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2016 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_SVG_TRANSFORM_GRAMMAR_X3_DEF_HPP +#define MAPNIK_SVG_TRANSFORM_GRAMMAR_X3_DEF_HPP + +// mapnik +#include +// boost::fusion +#pragma GCC diagnostic push +#include +#include +#pragma GCC diagnostic pop +// agg +#pragma GCC diagnostic push +#include +#include +#pragma GCC diagnostic pop + +namespace mapnik { namespace svg { namespace grammar { + +namespace x3 = boost::spirit::x3; + +using x3::lit; +using x3::double_; +using x3::no_case; + +auto const matrix_action = [] (auto const& ctx) +{ + auto & tr = x3::get(ctx).get(); + auto const& attr = _attr(ctx); + auto a = boost::fusion::at_c<0>(attr); + auto b = boost::fusion::at_c<1>(attr); + auto c = boost::fusion::at_c<2>(attr); + auto d = boost::fusion::at_c<3>(attr); + auto e = boost::fusion::at_c<4>(attr); + auto f = boost::fusion::at_c<5>(attr); + tr = agg::trans_affine(a, b, c, d, e, f) * tr; +}; + +auto const rotate_action = [] (auto const& ctx) +{ + auto & tr = x3::get(ctx).get(); + auto const& attr = _attr(ctx); + auto a = boost::fusion::at_c<0>(attr); + auto cx = boost::fusion::at_c<1>(attr) ? *boost::fusion::at_c<1>(attr) : 0.0; + auto cy = boost::fusion::at_c<2>(attr) ? *boost::fusion::at_c<2>(attr) : 0.0; + if (cx == 0.0 && cy == 0.0) + { + tr = agg::trans_affine_rotation(deg2rad(a)) * tr; + } + else + { + agg::trans_affine t = agg::trans_affine_translation(-cx,-cy); + t *= agg::trans_affine_rotation(deg2rad(a)); + t *= agg::trans_affine_translation(cx, cy); + tr = t * tr; + } +}; + +auto const translate_action = [] (auto const& ctx) +{ + auto & tr = x3::get(ctx).get(); + auto const& attr = _attr(ctx); + auto tx = boost::fusion::at_c<0>(attr); + auto ty = boost::fusion::at_c<1>(attr); + if (ty) tr = agg::trans_affine_translation(tx, *ty) * tr; + else tr = agg::trans_affine_translation(tx,0.0) * tr; +}; + +auto const scale_action = [] (auto const& ctx) +{ + auto & tr = x3::get(ctx).get(); + auto const& attr = _attr(ctx); + auto sx = boost::fusion::at_c<0>(attr); + auto sy = boost::fusion::at_c<1>(attr); + if (sy) tr = agg::trans_affine_scaling(sx, *sy) * tr; + else tr = agg::trans_affine_scaling(sx, sx) * tr; +}; + +auto const skewX_action = [] (auto const& ctx) +{ + auto & tr = x3::get(ctx).get(); + auto skew_x = _attr(ctx); + tr = agg::trans_affine_skewing(deg2rad(skew_x), 0.0) * tr; +}; + +auto const skewY_action = [] (auto const& ctx) +{ + auto & tr = x3::get(ctx).get(); + auto skew_y= _attr(ctx); + tr = agg::trans_affine_skewing(0.0, deg2rad(skew_y)) * tr; +}; + +//exported rule +svg_transform_grammar_type const svg_transform = "SVG Transform"; +// rules +auto const matrix = x3::rule {} = no_case[lit("matrix")] + > lit('(') > (double_ > -lit(',') + > double_ > -lit(',') + > double_ > -lit(',') + > double_ > -lit(',') + > double_ > -lit(',') + > double_)[matrix_action] + > lit(')'); + +auto const translate = x3::rule {} = no_case[lit("translate")] + > lit('(') > (double_ > -lit(',') >> -double_)[translate_action] > lit(')'); + +auto const scale = x3::rule {} = no_case[lit("scale")] + > lit('(') > (double_ > -lit(',') > -double_)[scale_action] > lit(')'); + +auto const rotate = x3::rule {} = no_case[lit("rotate")] + > lit('(') + > (double_ > -(lit(',') > double_) > -(lit(',') > double_))[rotate_action] + > lit(')') ; + +auto const skewX = x3::rule {} = no_case[lit("skewX")] + > lit('(') > double_ [ skewX_action] > lit(')'); +auto const skewY = x3::rule {} = no_case[lit("skewY")] + > lit('(') > double_ [ skewY_action] > lit(')'); + +auto const transform = x3::rule {} = matrix | rotate | translate | scale /*| rotate*/ | skewX | skewY ; + +auto const svg_transform_def = +transform ; + +#pragma GCC diagnostic push +#include +BOOST_SPIRIT_DEFINE( + svg_transform + ); +#pragma GCC diagnostic pop + +} + +grammar::svg_transform_grammar_type const& svg_transform_grammar() +{ + return grammar::svg_transform; +} + +}} + + +#endif // MAPNIK_SVG_TRANSFORM_GRAMMAR_X3_HPP diff --git a/src/svg/svg_transform_parser.cpp b/src/svg/svg_transform_parser.cpp index 78ac34609..46aca7fd6 100644 --- a/src/svg/svg_transform_parser.cpp +++ b/src/svg/svg_transform_parser.cpp @@ -22,24 +22,37 @@ // mapnik #include -#include +#include // stl #include #include -namespace mapnik { -namespace svg { +namespace mapnik { namespace svg { -template -bool parse_svg_transform(const char* wkt, TransformType& tr) +template +bool parse_svg_transform(const char* wkt, Transform& tr) { using namespace boost::spirit; - using iterator_type = const char*; - using skip_type = ascii::space_type; - static const svg_transform_grammar g; + using iterator_type = char const*; iterator_type first = wkt; iterator_type last = wkt + std::strlen(wkt); - return qi::phrase_parse(first, last, (g)(boost::phoenix::ref(tr)), skip_type()); + using space_type = mapnik::svg::grammar::space_type; + auto const grammar = x3::with(std::ref(tr)) + [mapnik::svg::svg_transform_grammar()]; + + try + { + if (!x3::phrase_parse(first, last, grammar, space_type()) + || first != last) + { + throw std::runtime_error("Failed to parse svg-transform"); + } + } + catch (...) + { + return false; + } + return true; } template bool MAPNIK_DECL parse_svg_transform(const char*, agg::trans_affine&);