SVG path parser - boost::spirit::x3 based implementation
This commit is contained in:
parent
9d0096eff6
commit
30749031cf
5 changed files with 266 additions and 178 deletions
|
@ -1,61 +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_PATH_GRAMMAR_HPP
|
||||
#define MAPNIK_SVG_PATH_GRAMMAR_HPP
|
||||
|
||||
// spirit
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace mapnik { namespace svg {
|
||||
|
||||
using namespace boost::spirit;
|
||||
|
||||
template <typename Iterator, typename PathType, typename SkipType>
|
||||
struct svg_path_grammar : qi::grammar<Iterator, void(PathType&), SkipType>
|
||||
{
|
||||
// ctor
|
||||
svg_path_grammar();
|
||||
// rules
|
||||
qi::rule<Iterator, void(PathType&), SkipType> start;
|
||||
qi::rule<Iterator, void(PathType&), SkipType> cmd;
|
||||
qi::rule<Iterator, void(PathType&), SkipType> drawto_cmd;
|
||||
qi::rule<Iterator, qi::locals<bool>, void(PathType&), SkipType> M; // M,m
|
||||
qi::rule<Iterator, qi::locals<bool>, void(PathType&), SkipType> L; // L,l
|
||||
qi::rule<Iterator, qi::locals<bool>, void(PathType&), SkipType> H; // H,h
|
||||
qi::rule<Iterator, qi::locals<bool>, void(PathType&), SkipType> V; // V,v
|
||||
qi::rule<Iterator, qi::locals<bool>, void(PathType&), SkipType> C; // C,c
|
||||
qi::rule<Iterator, qi::locals<bool>, void(PathType&), SkipType> S; // S,s
|
||||
qi::rule<Iterator, qi::locals<bool>, void(PathType&), SkipType> Q; // Q,q
|
||||
qi::rule<Iterator, qi::locals<bool>, void(PathType&), SkipType> T; // T,t
|
||||
qi::rule<Iterator, qi::locals<bool>, void(PathType&), SkipType> A; // A,a
|
||||
qi::rule<Iterator, void(PathType&), SkipType> Z; // Z,z
|
||||
qi::rule<Iterator, boost::fusion::vector2<double, double>(), SkipType> coord;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
|
||||
#endif // MAPNIK_SVG_PATH_GRAMMAR_HPP
|
|
@ -1,111 +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 <mapnik/svg/svg_path_grammar.hpp>
|
||||
#include <mapnik/svg/svg_path_commands.hpp>
|
||||
|
||||
// spirit
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
#include <boost/spirit/include/phoenix_function.hpp>
|
||||
#include <boost/spirit/include/phoenix_operator.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace mapnik { namespace svg {
|
||||
|
||||
using namespace boost::spirit;
|
||||
using namespace boost::phoenix;
|
||||
|
||||
|
||||
template <typename Iterator, typename PathType, typename SkipType>
|
||||
svg_path_grammar<Iterator, PathType, SkipType>::svg_path_grammar()
|
||||
: svg_path_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::_a_type _a;
|
||||
qi::lit_type lit;
|
||||
qi::_r1_type _r1;
|
||||
qi::double_type double_;
|
||||
qi::int_type int_;
|
||||
qi::no_case_type no_case;
|
||||
|
||||
// commands
|
||||
function<move_to> move_to_;
|
||||
function<hline_to> hline_to_;
|
||||
function<vline_to> vline_to_;
|
||||
function<line_to> line_to_;
|
||||
function<curve4> curve4_;
|
||||
function<curve4_smooth> curve4_smooth_;
|
||||
function<curve3> curve3_;
|
||||
function<curve3_smooth> curve3_smooth_;
|
||||
function<arc_to> arc_to_;
|
||||
function<close> close_;
|
||||
//
|
||||
start = +cmd(_r1);
|
||||
|
||||
cmd = M(_r1) >> *drawto_cmd(_r1);
|
||||
|
||||
drawto_cmd = L(_r1) | H(_r1) | V(_r1) | C(_r1) | S(_r1) | Q(_r1) | T(_r1) | A(_r1) | Z(_r1);
|
||||
|
||||
M = (lit('M')[_a = false] | lit('m')[_a = true]) >> coord[move_to_(_r1, _1, _a)] // move_to
|
||||
>> *(-lit(',') >> coord[line_to_(_r1, _1, _a)]); // *line_to
|
||||
|
||||
H = (lit('H')[_a = false] | lit('h')[_a = true])
|
||||
>> (double_[ hline_to_(_r1, _1,_a) ] % -lit(',')) ; // +hline_to
|
||||
|
||||
V = (lit('V')[_a = false] | lit('v')[_a = true])
|
||||
>> (double_ [ vline_to_(_r1, _1,_a) ] % -lit(',')); // +vline_to
|
||||
|
||||
L = (lit('L')[_a = false] | lit('l')[_a = true])
|
||||
>> (coord [ line_to_(_r1, _1, _a) ] % -lit(',')); // +line_to
|
||||
|
||||
C = (lit('C')[_a = false] | lit('c')[_a = true])
|
||||
>> ((coord >> -lit(',') >> coord >> -lit(',') >> coord)[curve4_(_r1, _1, _2, _3, _a)] % -lit(',')); // +curve4
|
||||
|
||||
S = (lit('S')[_a = false] | lit('s')[_a = true])
|
||||
>> ((coord >> -lit(',') >> coord) [ curve4_smooth_(_r1, _1,_2,_a) ] % -lit(',')); // +curve4_smooth (smooth curveto)
|
||||
|
||||
Q = (lit('Q')[_a = false] | lit('q')[_a = true])
|
||||
>> ((coord >> -lit(',') >> coord) [ curve3_(_r1, _1,_2,_a) ] % -lit(',')); // +curve3 (quadratic-bezier-curveto)
|
||||
|
||||
T = (lit('T')[_a = false] | lit('t')[_a = true])
|
||||
>> ((coord ) [ curve3_smooth_(_r1, _1,_a) ] % -lit(',')); // +curve3_smooth (smooth-quadratic-bezier-curveto)
|
||||
|
||||
A = (lit('A')[_a = false] | lit('a')[_a = true])
|
||||
>> ((coord >> -lit(',') >> double_ >> -lit(',') >> int_ >> -lit(',') >> int_ >> -lit(',') >> coord)
|
||||
[arc_to_(_r1, _1, _2, _3, _4, _5, _a)] % -lit(',')); // arc_to;
|
||||
|
||||
Z = no_case[lit('z')] [close_(_r1)]; // close path
|
||||
|
||||
coord = double_ >> -lit(',') >> double_;
|
||||
}
|
||||
|
||||
} // namespace svg
|
||||
} // namespace mapnik
|
48
include/mapnik/svg/svg_path_grammar_x3.hpp
Normal file
48
include/mapnik/svg/svg_path_grammar_x3.hpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* 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_PATH_GRAMMAR_X3_HPP
|
||||
#define MAPNIK_SVG_PATH_GRAMMAR_X3_HPP
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace mapnik { namespace svg { namespace grammar {
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
class relative_tag;
|
||||
class svg_path_tag;
|
||||
|
||||
using svg_path_grammar_type = x3::rule<class svg_rule_tag>;
|
||||
|
||||
BOOST_SPIRIT_DECLARE(svg_path_grammar_type);
|
||||
|
||||
}
|
||||
|
||||
grammar::svg_path_grammar_type svg_path_grammar();
|
||||
|
||||
}}
|
||||
|
||||
#endif // MAPNIK_SVG_PATH_GRAMMAR_X3_HPP
|
195
include/mapnik/svg/svg_path_grammar_x3_def.hpp
Normal file
195
include/mapnik/svg/svg_path_grammar_x3_def.hpp
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* 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_PATH_GRAMMAR_X3_DEF_HPP
|
||||
#define MAPNIK_SVG_PATH_GRAMMAR_X3_DEF_HPP
|
||||
|
||||
#include <mapnik/global.hpp>
|
||||
#include <mapnik/config.hpp>
|
||||
#include <mapnik/svg/svg_path_grammar_x3.hpp>
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
#include <boost/fusion/adapted/std_tuple.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace mapnik { namespace svg { namespace grammar {
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
using x3::lit;
|
||||
using x3::double_;
|
||||
using x3::int_;
|
||||
using x3::no_case;
|
||||
|
||||
using coord_type = std::tuple<double,double>;
|
||||
|
||||
auto const move_to = [] (auto const& ctx)
|
||||
{
|
||||
x3::get<svg_path_tag>(ctx).get().move_to(std::get<0>(_attr(ctx)), std::get<1>(_attr(ctx)), x3::get<relative_tag>(ctx));
|
||||
};
|
||||
|
||||
auto const line_to = [] (auto const & ctx)
|
||||
{
|
||||
x3::get<svg_path_tag>(ctx).get().line_to(std::get<0>(_attr(ctx)), std::get<1>(_attr(ctx)), x3::get<relative_tag>(ctx));
|
||||
};
|
||||
|
||||
auto const hline_to = [] (auto const& ctx)
|
||||
{
|
||||
x3::get<svg_path_tag>(ctx).get().hline_to(_attr(ctx), x3::get<relative_tag>(ctx));
|
||||
};
|
||||
|
||||
auto const vline_to = [] (auto const& ctx)
|
||||
{
|
||||
x3::get<svg_path_tag>(ctx).get().vline_to(_attr(ctx), x3::get<relative_tag>(ctx));
|
||||
};
|
||||
|
||||
auto const curve4 = [] (auto const& ctx)
|
||||
{
|
||||
auto const& attr = _attr(ctx);
|
||||
auto const& p0 = boost::fusion::at_c<0>(attr);
|
||||
auto const& p1 = boost::fusion::at_c<1>(attr);
|
||||
auto const& p2 = boost::fusion::at_c<2>(attr);
|
||||
x3::get<svg_path_tag>(ctx).get().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<relative_tag>(ctx));
|
||||
};
|
||||
|
||||
auto const curve4_smooth = [] (auto const& ctx)
|
||||
{
|
||||
auto const& attr = _attr(ctx);
|
||||
auto const& p0 = boost::fusion::at_c<0>(attr);
|
||||
auto const& p1 = boost::fusion::at_c<1>(attr);
|
||||
x3::get<svg_path_tag>(ctx).get().curve4(std::get<0>(p0),std::get<1>(p0),
|
||||
std::get<0>(p1),std::get<1>(p1),
|
||||
x3::get<relative_tag>(ctx));
|
||||
};
|
||||
|
||||
auto const curve3 = [] (auto const& ctx)
|
||||
{
|
||||
auto const& attr = _attr(ctx);
|
||||
auto const& p0 = boost::fusion::at_c<0>(attr);
|
||||
auto const& p1 = boost::fusion::at_c<1>(attr);
|
||||
x3::get<svg_path_tag>(ctx).get().curve3(std::get<0>(p0),std::get<1>(p0),
|
||||
std::get<0>(p1),std::get<1>(p1),
|
||||
x3::get<relative_tag>(ctx));
|
||||
};
|
||||
|
||||
auto const curve3_smooth = [] (auto const& ctx)
|
||||
{
|
||||
auto const& attr = _attr(ctx);
|
||||
x3::get<svg_path_tag>(ctx).get().curve3(std::get<0>(attr),std::get<1>(attr),
|
||||
x3::get<relative_tag>(ctx));
|
||||
};
|
||||
|
||||
inline double deg2rad(double deg)
|
||||
{
|
||||
return (M_PI * deg) / 180.0;
|
||||
}
|
||||
|
||||
auto const arc_to = [] (auto & ctx)
|
||||
{
|
||||
auto const& attr = _attr(ctx);
|
||||
auto const& p = boost::fusion::at_c<0>(attr);
|
||||
double angle = boost::fusion::at_c<1>(attr);
|
||||
int large_arc_flag = boost::fusion::at_c<2>(attr);
|
||||
int sweep_flag = boost::fusion::at_c<3>(attr);
|
||||
auto const& v = boost::fusion::at_c<4>(attr);
|
||||
x3::get<svg_path_tag>(ctx).get().arc_to(std::get<0>(p),std::get<1>(p),
|
||||
deg2rad(angle), large_arc_flag, sweep_flag,
|
||||
std::get<0>(v),std::get<1>(v),
|
||||
x3::get<relative_tag>(ctx));
|
||||
};
|
||||
|
||||
auto const close_path = [] (auto const& ctx)
|
||||
{
|
||||
x3::get<svg_path_tag>(ctx).get().close_subpath();
|
||||
};
|
||||
|
||||
auto const relative = [] (auto const& ctx)
|
||||
{
|
||||
x3::get<relative_tag>(ctx).get() = true;
|
||||
};
|
||||
|
||||
auto const absolute = [] (auto const& ctx)
|
||||
{
|
||||
x3::get<relative_tag>(ctx).get() = false;
|
||||
};
|
||||
|
||||
svg_path_grammar_type const svg_path = "SVG Path";
|
||||
|
||||
auto const coord = x3::rule<class coord_tag, coord_type>{} = double_ > -lit(',') > double_;
|
||||
|
||||
auto const M = x3::rule<class M_tag> {} = (lit('M')[absolute] | lit('m')[relative])
|
||||
> coord[move_to] // move_to
|
||||
> *(-lit(',') >> coord[line_to]); // *line_to
|
||||
|
||||
auto const H = x3::rule<class H_tag> {} = (lit('H')[absolute] | lit('h')[relative])
|
||||
> (double_[ hline_to] % -lit(',')) ; // +hline_to
|
||||
|
||||
auto const V = x3::rule<class V_tag> {} = (lit('V')[absolute] | lit('v')[relative])
|
||||
> (double_[ vline_to] % -lit(',')) ; // +vline_to
|
||||
|
||||
auto const L = x3::rule<class L_tag> {} = (lit('L')[absolute] | lit('l')[relative])
|
||||
> (coord [line_to] % -lit(',')); // +line_to
|
||||
|
||||
auto const C = x3::rule<class C_tag> {} = (lit('C')[absolute] | lit('c')[relative])
|
||||
> ((coord > -lit(',') > coord > -lit(',') > coord)[curve4] % -lit(',')); // +curve4
|
||||
|
||||
auto const S = x3::rule<class S_tag> {} = (lit('S')[absolute] | lit('s')[relative])
|
||||
> ((coord > -lit(',') > coord) [curve4_smooth] % -lit(',')); // +curve4_smooth (smooth curveto)
|
||||
|
||||
auto const Q = x3::rule<class Q_tag> {} = (lit('Q')[absolute] | lit('q')[relative])
|
||||
> ((coord > -lit(',') > coord) [curve3] % -lit(',')); // +curve3 (quadratic-bezier-curveto)
|
||||
|
||||
auto const T = x3::rule<class T_tag> {} = (lit('T')[absolute] | lit('t')[relative])
|
||||
> ((coord ) [curve3_smooth] % -lit(',')); // +curve3_smooth (smooth-quadratic-bezier-curveto)
|
||||
|
||||
auto const A = x3::rule<class A_tag> {} = (lit('A')[absolute] | lit('a')[relative])
|
||||
> ((coord > -lit(',') > double_ > -lit(',') > int_ > -lit(',') > int_ > -lit(',') > coord)
|
||||
[arc_to] % -lit(',')); // arc_to;
|
||||
|
||||
auto const Z = x3::rule<class Z_tag>{} = no_case[lit('z')] [close_path]; // close path
|
||||
|
||||
auto const drawto_cmd = x3::rule<class drawto_cmd_tag> {} = L | H | V | C | S | Q | T | A | Z;
|
||||
|
||||
auto const cmd = x3::rule<class cmd_tag> {} = M > *drawto_cmd ;
|
||||
|
||||
auto const svg_path_def = +cmd;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
BOOST_SPIRIT_DEFINE(
|
||||
svg_path
|
||||
);
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
}
|
||||
|
||||
grammar::svg_path_grammar_type svg_path_grammar()
|
||||
{
|
||||
return grammar::svg_path;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
#endif // MAPNIK_SVG_PATH_GRAMMAR_X3_HPP
|
|
@ -23,7 +23,7 @@
|
|||
// mapnik
|
||||
|
||||
#include <mapnik/svg/svg_path_parser.hpp>
|
||||
#include <mapnik/svg/svg_path_grammar_impl.hpp>
|
||||
#include <mapnik/svg/svg_path_grammar_x3_def.hpp>
|
||||
// stl
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
@ -35,14 +35,31 @@ template <typename PathType>
|
|||
bool parse_path(const char* wkt, PathType& p)
|
||||
{
|
||||
using namespace boost::spirit;
|
||||
using iterator_type = const char*;
|
||||
using skip_type = ascii::space_type;
|
||||
static const svg_path_grammar<iterator_type, PathType, skip_type> g;
|
||||
using iterator_type = char const*;
|
||||
using skip_type = x3::ascii::space_type;
|
||||
skip_type space;
|
||||
iterator_type first = wkt;
|
||||
iterator_type last = wkt + std::strlen(wkt);
|
||||
bool status = qi::phrase_parse(first, last, (g)(boost::phoenix::ref(p)), skip_type());
|
||||
return (status && (first == last));
|
||||
bool relative = false;
|
||||
auto const grammar = x3::with<mapnik::svg::grammar::svg_path_tag>(std::ref(p))
|
||||
[ x3::with<mapnik::svg::grammar::relative_tag>(std::ref(relative))
|
||||
[mapnik::svg::svg_path_grammar()]];
|
||||
|
||||
try
|
||||
{
|
||||
if (!x3::phrase_parse(first, last, grammar, space)
|
||||
|| first != last)
|
||||
{
|
||||
throw std::runtime_error("Failed to parse svg-path");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool MAPNIK_DECL parse_path<svg_converter_type>(const char*, svg_converter_type&);
|
||||
|
||||
} // namespace svg
|
||||
|
|
Loading…
Reference in a new issue