commit
1784c4f03e
18 changed files with 690 additions and 48 deletions
3
Makefile
3
Makefile
|
@ -28,7 +28,8 @@ src/json/libmapnik-json.a:
|
|||
src/renderer_common/render_thunk_extractor.os \
|
||||
src/json/libmapnik-json.a \
|
||||
src/wkt/libmapnik-wkt.a \
|
||||
src/css_color_grammar_x3.os \
|
||||
src/css/css_grammar_x3.os \
|
||||
src/css/css_color_grammar_x3.os \
|
||||
src/expression_grammar_x3.os \
|
||||
src/transform_expression_grammar_x3.os \
|
||||
src/image_filter_grammar_x3.os \
|
||||
|
|
|
@ -28,6 +28,7 @@ subdirglobs = [
|
|||
('.', 'mapnik/*.hpp'),
|
||||
('agg', '../deps/agg/include/agg*'),
|
||||
('cairo', 'mapnik/cairo/*.hpp'),
|
||||
('css', 'mapnik/css/*.hpp'),
|
||||
('csv', 'mapnik/csv/*.hpp'),
|
||||
('deps/mapbox', '../deps/mapbox/variant/include/mapbox/*.hpp'),
|
||||
('deps/mapbox', '../deps/mapbox/geometry/include/mapbox/*.hpp'),
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#ifndef MAPNIK_CSS_COLOR_GRAMMAR_X3_DEF_HPP
|
||||
#define MAPNIK_CSS_COLOR_GRAMMAR_X3_DEF_HPP
|
||||
|
||||
#include <mapnik/css_color_grammar_x3.hpp>
|
||||
#include <mapnik/css/css_color_grammar_x3.hpp>
|
||||
#include <mapnik/util/hsl.hpp>
|
||||
#include <mapnik/safe_cast.hpp>
|
||||
|
73
include/mapnik/css/css_grammar_x3.hpp
Normal file
73
include/mapnik/css/css_grammar_x3.hpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2020 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_CSS_GRAMMAR_X3_HPP
|
||||
#define MAPNIK_CSS_GRAMMAR_X3_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
using property_value_type = boost::iterator_range<char const*>;
|
||||
using css_key_value = std::pair<std::string, property_value_type>;
|
||||
using definition_type = std::vector<css_key_value>;
|
||||
using css_data = std::unordered_multimap<std::string, definition_type>;
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
namespace css_grammar
|
||||
{
|
||||
|
||||
class css_tag;
|
||||
class ident_tag;
|
||||
class skipper_tag;
|
||||
class classes_tag;
|
||||
//
|
||||
using ident_grammar_type = x3::rule<ident_tag, std::string>;
|
||||
using css_grammar_type = x3::rule<css_tag, css_data>;
|
||||
using css_skipper_type = x3::rule<skipper_tag, x3::unused_type const>;
|
||||
using css_classes_type = x3::rule<classes_tag, std::vector<std::string>>;
|
||||
|
||||
ident_grammar_type const ident{"IDENT"};
|
||||
css_grammar_type const css_grammar{"css"};
|
||||
css_skipper_type const css_skipper{"css skipper"};
|
||||
css_classes_type const css_classes{"css classes"};
|
||||
|
||||
BOOST_SPIRIT_DECLARE(ident_grammar_type,
|
||||
css_classes_type,
|
||||
css_grammar_type,
|
||||
css_skipper_type);
|
||||
|
||||
}
|
||||
|
||||
css_grammar::ident_grammar_type const ident_grammar();
|
||||
css_grammar::css_classes_type const classes();
|
||||
css_grammar::css_grammar_type const grammar();
|
||||
css_grammar::css_skipper_type const skipper();
|
||||
}
|
||||
|
||||
#endif // MAPNIK_CSS_GRAMMAR_X3_HPP
|
220
include/mapnik/css/css_grammar_x3_def.hpp
Normal file
220
include/mapnik/css/css_grammar_x3_def.hpp
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2020 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_CSS_GRAMMAR_X3_DEF_HPP
|
||||
#define MAPNIK_CSS_GRAMMAR_X3_DEF_HPP
|
||||
|
||||
#include <mapnik/css/css_grammar_x3.hpp>
|
||||
#include <mapnik/json/unicode_string_grammar_x3.hpp>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
#include <boost/fusion/adapted/std_pair.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
/*
|
||||
The grammar below is LL(1) (but note that most UA's should not use it directly, since it doesn't express the parsing conventions, only the CSS1 syntax). The format of the productions is optimized for human consumption and some shorthand notation beyond yacc [15] is used:
|
||||
|
||||
* : 0 or more
|
||||
+ : 1 or more
|
||||
? : 0 or 1
|
||||
| : separates alternatives
|
||||
[] : grouping
|
||||
|
||||
The productions are:
|
||||
|
||||
stylesheet
|
||||
: [CDO|CDC]* [ import [CDO|CDC]* ]* [ ruleset [CDO|CDC]* ]*
|
||||
;
|
||||
import
|
||||
: IMPORT_SYM [STRING|URL] ';' // E.g., @import url(fun.css);
|
||||
;
|
||||
unary_operator
|
||||
: '-' | '+'
|
||||
;
|
||||
operator
|
||||
: '/' | ',' | // empty
|
||||
;
|
||||
property
|
||||
: IDENT
|
||||
;
|
||||
ruleset
|
||||
: selector [ ',' selector ]*
|
||||
'{' declaration [ ';' declaration ]* '}'
|
||||
;
|
||||
selector
|
||||
: simple_selector+ [ pseudo_element | solitary_pseudo_element ]?
|
||||
| solitary_pseudo_element
|
||||
;
|
||||
// An "id" is an ID that is attached to an element type
|
||||
// on its left, as in: P#p007
|
||||
// A "solitary_id" is an ID that is not so attached,
|
||||
/ as in: #p007
|
||||
// Analogously for classes and pseudo-classes.
|
||||
|
||||
simple_selector
|
||||
: element_name id? class? pseudo_class? // eg: H1.subject
|
||||
| solitary_id class? pseudo_class? // eg: #xyz33
|
||||
| solitary_class pseudo_class? // eg: .author
|
||||
| solitary_pseudo_class // eg: :link
|
||||
;
|
||||
element_name
|
||||
: IDENT
|
||||
;
|
||||
pseudo_class // as in: A:link
|
||||
: LINK_PSCLASS_AFTER_IDENT
|
||||
| VISITED_PSCLASS_AFTER_IDENT
|
||||
| ACTIVE_PSCLASS_AFTER_IDENT
|
||||
;
|
||||
solitary_pseudo_class // as in: :link
|
||||
: LINK_PSCLASS
|
||||
| VISITED_PSCLASS
|
||||
| ACTIVE_PSCLASS
|
||||
;
|
||||
class // as in: P.note
|
||||
: CLASS_AFTER_IDENT
|
||||
;
|
||||
solitary_class // as in: .note
|
||||
: CLASS
|
||||
;
|
||||
pseudo_element // as in: P:first-line
|
||||
: FIRST_LETTER_AFTER_IDENT
|
||||
| FIRST_LINE_AFTER_IDENT
|
||||
;
|
||||
solitary_pseudo_element // as in: :first-line
|
||||
: FIRST_LETTER
|
||||
| FIRST_LINE
|
||||
;
|
||||
// There is a constraint on the id and solitary_id that the
|
||||
// part after the "#" must be a valid HTML ID value;
|
||||
// e.g., "#x77" is OK, but "#77" is not.
|
||||
|
||||
id
|
||||
: HASH_AFTER_IDENT
|
||||
;
|
||||
solitary_id
|
||||
: HASH
|
||||
;
|
||||
declaration
|
||||
: property ':' expr prio?
|
||||
| // empty // Prevents syntax errors...
|
||||
;
|
||||
prio
|
||||
: IMPORTANT_SYM // !important
|
||||
;
|
||||
expr
|
||||
: term [ operator term ]*
|
||||
;
|
||||
term
|
||||
: unary_operator?
|
||||
[ NUMBER | STRING | PERCENTAGE | LENGTH | EMS | EXS
|
||||
| IDENT | hexcolor | URL | RGB ]
|
||||
;
|
||||
// There is a constraint on the color that it must
|
||||
// have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
|
||||
// after the "#"; e.g., "#000" is OK, but "#abcd" is not.
|
||||
|
||||
hexcolor
|
||||
: HASH | HASH_AFTER_IDENT
|
||||
;
|
||||
|
||||
*/
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
namespace css_grammar {
|
||||
|
||||
using x3::lit;
|
||||
using x3::attr;
|
||||
using x3::no_case;
|
||||
using x3::no_skip;
|
||||
using x3::lexeme;
|
||||
using x3::alpha;
|
||||
using x3::alnum;
|
||||
using x3::char_;
|
||||
using x3::raw;
|
||||
using x3::standard::space;
|
||||
|
||||
// import unicode string rule
|
||||
namespace { auto const& css_string = mapnik::json::grammar::unicode_string; }
|
||||
|
||||
auto assign_def = [] (auto const& ctx)
|
||||
{
|
||||
for (auto const& k : std::get<0>(_attr(ctx)))
|
||||
{
|
||||
_val(ctx).emplace(k, std::get<1>(_attr(ctx)));
|
||||
}
|
||||
};
|
||||
|
||||
auto assign_key = [] (auto const& ctx)
|
||||
{
|
||||
_val(ctx).first = std::move(_attr(ctx));
|
||||
};
|
||||
|
||||
auto assign_value = [] (auto const& ctx) {_val(ctx).second = std::move(_attr(ctx));};
|
||||
|
||||
// rules
|
||||
x3::rule<class simple_selector_tag, std::string> const simple_selector {"Simple selector"};
|
||||
x3::rule<class selector_tag, std::vector<std::string>> const selector {"Selector"};
|
||||
x3::rule<class value_tag, property_value_type> const value {"Value"};
|
||||
x3::rule<class key_value_tag, css_key_value> const key_value {"CSS Key/Value"};
|
||||
x3::rule<class definition_tag, std::pair<std::vector<std::string>, definition_type>> const definition {"CSS Definition"};
|
||||
|
||||
auto const ident_def = alpha >> *(alnum | char_('-'));
|
||||
auto const simple_selector_def = lexeme[ident >> -((char_('.') | char_('#') | char_(':')) >> ident)]
|
||||
| lexeme[char_('#') >> ident >> -((char_('.') | char_(':')) >> ident)]
|
||||
| lexeme[char_('.') >> ident >> -(char_(':') >> ident)];
|
||||
|
||||
auto const selector_def = simple_selector % lit(',');
|
||||
auto const value_def = raw[lexeme[+~char_(";}")]];
|
||||
auto const key_value_def = lexeme[ident][assign_key] >> lit(':') >> value[assign_value] >> -lit(';');
|
||||
auto const definition_def = selector >> lit('{') >> *key_value >> lit('}');
|
||||
|
||||
auto const css_grammar_def = *definition[assign_def];
|
||||
auto const css_skipper_def = space | "/*" >> *(char_ - "*/") >> "*/";
|
||||
|
||||
auto const css_classes_def = +lexeme[ident];
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
BOOST_SPIRIT_DEFINE(
|
||||
ident,
|
||||
css_classes,
|
||||
simple_selector,
|
||||
selector,
|
||||
value,
|
||||
key_value,
|
||||
definition,
|
||||
css_grammar,
|
||||
css_skipper
|
||||
);
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
} //css_grammar
|
||||
} //mapnik
|
||||
|
||||
#endif //MAPNIK_CSS_GRAMMAR_X3_DEF_HPP
|
54
include/mapnik/css/css_unit_value.hpp
Normal file
54
include/mapnik/css/css_unit_value.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2020 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_CSS_UNIT_VALUE_HPP
|
||||
#define MAPNIK_CSS_UNIT_VALUE_HPP
|
||||
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
// units
|
||||
struct css_unit_value : x3::symbols<double>
|
||||
{
|
||||
const double DPI = 90;
|
||||
css_unit_value()
|
||||
{
|
||||
add
|
||||
("px", 1.0)
|
||||
("pt", DPI/72.0)
|
||||
("pc", DPI/6.0)
|
||||
("mm", DPI/25.4)
|
||||
("cm", DPI/2.54)
|
||||
("in", static_cast<double>(DPI))
|
||||
//("em", 1.0/16.0) // default pixel size for body (usually 16px)
|
||||
// ^^ this doesn't work currently as 'e' in 'em' interpreted as part of scientific notation.
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
} //mapnik
|
||||
|
||||
#endif //MAPNIK_CSS_GRAMMAR_X3_DEF_HPP
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
#include <mapnik/image_filter_grammar_x3.hpp>
|
||||
#include <mapnik/image_filter_types.hpp>
|
||||
#include <mapnik/css_color_grammar_x3.hpp>
|
||||
#include <mapnik/css/css_color_grammar_x3.hpp>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
// mapnik
|
||||
#include <mapnik/config.hpp>
|
||||
#include <mapnik/css/css_grammar_x3.hpp>
|
||||
#include <mapnik/svg/svg_path_attributes.hpp>
|
||||
#include <mapnik/svg/svg_converter.hpp>
|
||||
#include <mapnik/svg/svg_path_adapter.hpp>
|
||||
|
@ -83,8 +84,10 @@ public:
|
|||
bool is_defs_;
|
||||
bool strict_;
|
||||
bool ignore_;
|
||||
bool css_style_;
|
||||
std::map<std::string, gradient> gradient_map_;
|
||||
std::map<std::string, boost::property_tree::detail::rapidxml::xml_node<char> const*> node_cache_;
|
||||
mapnik::css_data css_data_;
|
||||
agg::trans_affine viewbox_tr_{};
|
||||
error_handler err_handler_;
|
||||
};
|
||||
|
|
|
@ -152,7 +152,6 @@ else: # unix, non-macos
|
|||
source = Split(
|
||||
"""
|
||||
expression_grammar_x3.cpp
|
||||
css_color_grammar_x3.cpp
|
||||
fs.cpp
|
||||
request.cpp
|
||||
well_known_srs.cpp
|
||||
|
@ -225,6 +224,8 @@ source = Split(
|
|||
raster_colorizer.cpp
|
||||
mapped_memory_cache.cpp
|
||||
marker_cache.cpp
|
||||
css/css_color_grammar_x3.cpp
|
||||
css/css_grammar_x3.cpp
|
||||
svg/svg_parser.cpp
|
||||
svg/svg_path_parser.cpp
|
||||
svg/svg_points_parser.cpp
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <mapnik/color.hpp>
|
||||
#include <mapnik/color_factory.hpp>
|
||||
#include <mapnik/config_error.hpp>
|
||||
#include <mapnik/css_color_grammar_x3.hpp>
|
||||
#include <mapnik/css/css_color_grammar_x3.hpp>
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <mapnik/css_color_grammar_x3_def.hpp>
|
||||
#include <mapnik/css/css_color_grammar_x3_def.hpp>
|
||||
#if BOOST_VERSION < 107000
|
||||
#include <mapnik/image_filter_types.hpp>
|
||||
#endif
|
56
src/css/css_grammar_x3.cpp
Normal file
56
src/css/css_grammar_x3.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2020 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
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include <mapnik/css/css_grammar_x3_def.hpp>
|
||||
|
||||
namespace mapnik { namespace css_grammar {
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
using iterator_type = char const*;
|
||||
using context_type = x3::phrase_parse_context<css_skipper_type>::type;
|
||||
|
||||
BOOST_SPIRIT_INSTANTIATE(ident_grammar_type, iterator_type, context_type);
|
||||
BOOST_SPIRIT_INSTANTIATE(css_classes_type, iterator_type, context_type);
|
||||
BOOST_SPIRIT_INSTANTIATE(css_grammar_type, iterator_type, context_type);
|
||||
BOOST_SPIRIT_INSTANTIATE(css_skipper_type, iterator_type, x3::unused_type);
|
||||
}
|
||||
|
||||
css_grammar::ident_grammar_type const ident_grammar()
|
||||
{
|
||||
return css_grammar::ident;
|
||||
}
|
||||
|
||||
css_grammar::css_classes_type const classes()
|
||||
{
|
||||
return css_grammar::css_classes;
|
||||
}
|
||||
|
||||
css_grammar::css_grammar_type const grammar()
|
||||
{
|
||||
return css_grammar::css_grammar;
|
||||
}
|
||||
|
||||
css_grammar::css_skipper_type const skipper()
|
||||
{
|
||||
return css_grammar::css_skipper;
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
*****************************************************************************/
|
||||
|
||||
#include <mapnik/debug.hpp>
|
||||
#include <mapnik/css/css_unit_value.hpp>
|
||||
#include <mapnik/color_factory.hpp>
|
||||
#include <mapnik/svg/svg_parser.hpp>
|
||||
#include <mapnik/svg/svg_path_parser.hpp>
|
||||
|
@ -57,6 +58,24 @@
|
|||
|
||||
namespace mapnik { namespace svg {
|
||||
|
||||
namespace {
|
||||
void print_css(mapnik::css_data & data)
|
||||
{
|
||||
for (auto const& kv : data)
|
||||
{
|
||||
std::cerr << std::get<0>(kv) << " {" << std::endl;
|
||||
for (auto const& def : std::get<1>(kv))
|
||||
{
|
||||
auto const& r = std::get<1>(def);
|
||||
std::cerr << " " << std::get<0>(def) << ":"
|
||||
<< std::string(r.begin(), r.end());
|
||||
}
|
||||
std::cerr << "}" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using util::name_to_int;
|
||||
using util::operator"" _case;
|
||||
|
||||
|
@ -259,17 +278,7 @@ double parse_svg_value(T & err_handler, const char* str, bool & is_percent)
|
|||
{
|
||||
namespace x3 = boost::spirit::x3;
|
||||
double val = 0.0;
|
||||
x3::symbols<double> units;
|
||||
units.add
|
||||
("px", 1.0)
|
||||
("pt", DPI/72.0)
|
||||
("pc", DPI/6.0)
|
||||
("mm", DPI/25.4)
|
||||
("cm", DPI/2.54)
|
||||
("in", static_cast<double>(DPI))
|
||||
//("em", 1.0/16.0) // default pixel size for body (usually 16px)
|
||||
// ^^ this doesn't work currently as 'e' in 'em' interpreted as part of scientific notation.
|
||||
;
|
||||
css_unit_value units;
|
||||
const char* cur = str; // phrase_parse mutates the first iterator
|
||||
const char* end = str + std::strlen(str);
|
||||
|
||||
|
@ -370,15 +379,107 @@ bool parse_id_from_url (char const* str, std::string & id)
|
|||
x3::space);
|
||||
}
|
||||
|
||||
void process_css(svg_parser & parser, rapidxml::xml_node<char> const* node)
|
||||
{
|
||||
auto const& css = parser.css_data_;
|
||||
std::unordered_map<std::string, mapnik::property_value_type> style;
|
||||
std::string element_name = node->name();
|
||||
auto itr = css.find(element_name);
|
||||
if (itr != css.end())
|
||||
{
|
||||
//std::cerr << "-> element key:" << element_name << std::endl;
|
||||
for (auto const& def : std::get<1>(*itr))
|
||||
{
|
||||
style[std::get<0>(def)] = std::get<1>(def);
|
||||
}
|
||||
}
|
||||
|
||||
auto const* class_attr = node->first_attribute("class");
|
||||
if (class_attr != nullptr)
|
||||
{
|
||||
auto const* value = class_attr->value();
|
||||
if (std::strlen(value) > 0)
|
||||
{
|
||||
char const* first = value;
|
||||
char const* last = first + std::strlen(value);
|
||||
std::vector<std::string> classes;
|
||||
bool result = boost::spirit::x3::phrase_parse(first, last, mapnik::classes(), mapnik::skipper(), classes);
|
||||
if (result)
|
||||
{
|
||||
for (auto const& class_name : classes)
|
||||
{
|
||||
std::string solitary_class_key = std::string(".") + class_name;
|
||||
auto range = css.equal_range(solitary_class_key);
|
||||
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))
|
||||
{
|
||||
style[std::get<0>(def)] = std::get<1>(def);
|
||||
}
|
||||
}
|
||||
std::string class_key = element_name + solitary_class_key;
|
||||
range = css.equal_range(class_key);
|
||||
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))
|
||||
{
|
||||
style[std::get<0>(def)] = std::get<1>(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//else
|
||||
//{
|
||||
// std::cerr << "Failed to parse styles..." << std::endl;
|
||||
//}
|
||||
}
|
||||
}
|
||||
auto const* id_attr = node->first_attribute("id");
|
||||
if (id_attr != nullptr)
|
||||
{
|
||||
auto const* value = id_attr->value();
|
||||
if (std::strlen(value) > 0)
|
||||
{
|
||||
std::string id_key = std::string("#") + value;
|
||||
itr = css.find(id_key);
|
||||
if (itr != css.end())
|
||||
{
|
||||
//std::cerr << "<" << element_name << ">";
|
||||
//std::cerr << "----> ID key:" << id_key << std::endl;
|
||||
for (auto const& def : std::get<1>(*itr))
|
||||
{
|
||||
style[std::get<0>(def)] = std::get<1>(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If style has properties, create or update style attribute
|
||||
if (style.size() > 0)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
for (auto const& def : style)
|
||||
{
|
||||
auto const& r = std::get<1>(def);
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
|
||||
{
|
||||
if (parser.ignore_) return;
|
||||
auto const* name = node->name();
|
||||
auto name = name_to_int(node->name());
|
||||
switch (node->type())
|
||||
{
|
||||
case rapidxml::node_element:
|
||||
{
|
||||
switch(name_to_int(name))
|
||||
switch(name)
|
||||
{
|
||||
case "defs"_case:
|
||||
{
|
||||
|
@ -407,19 +508,23 @@ void traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
|
|||
|
||||
if (!parser.is_defs_) // FIXME
|
||||
{
|
||||
switch (name_to_int(name))
|
||||
switch (name)
|
||||
{
|
||||
case "g"_case:
|
||||
if (node->first_node() != nullptr)
|
||||
{
|
||||
parser.path_.push_attr();
|
||||
parse_id(parser, node);
|
||||
if (parser.css_style_)
|
||||
process_css(parser, node);
|
||||
parse_attr(parser, node);
|
||||
}
|
||||
break;
|
||||
case "use"_case:
|
||||
parser.path_.push_attr();
|
||||
parse_id(parser, node);
|
||||
if (parser.css_style_)
|
||||
process_css(parser, node);
|
||||
parse_attr(parser, node);
|
||||
parse_use(parser, node);
|
||||
parser.path_.pop_attr();
|
||||
|
@ -427,10 +532,12 @@ void traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
|
|||
default:
|
||||
parser.path_.push_attr();
|
||||
parse_id(parser, node);
|
||||
if (parser.css_style_)
|
||||
process_css(parser, node);
|
||||
parse_attr(parser, node);
|
||||
if (parser.path_.display())
|
||||
{
|
||||
parse_element(parser, name, node);
|
||||
parse_element(parser, node->name(), node);
|
||||
}
|
||||
parser.path_.pop_attr();
|
||||
}
|
||||
|
@ -441,32 +548,41 @@ void traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
|
|||
parse_id(parser, node);
|
||||
}
|
||||
|
||||
for (auto const* child = node->first_node();
|
||||
child; child = child->next_sibling())
|
||||
if ("style"_case == name)
|
||||
{
|
||||
traverse_tree(parser, child);
|
||||
// <style> element is not expected to have nested elements
|
||||
// we're only interested in DATA or CDATA
|
||||
for (auto const* child = node->first_node();
|
||||
child; child = child->next_sibling())
|
||||
{
|
||||
if (child->type() == rapidxml::node_data ||
|
||||
child->type() == rapidxml::node_cdata)
|
||||
{
|
||||
auto const grammar = mapnik::grammar();
|
||||
auto const skipper = mapnik::skipper();
|
||||
char const* first = child->value();
|
||||
char const* last = first + child->value_size();
|
||||
bool result = boost::spirit::x3::phrase_parse(first, last, grammar, skipper, parser.css_data_);
|
||||
if (result && first == last && !parser.css_data_.empty())
|
||||
{
|
||||
parser.css_style_ = true;
|
||||
//print_css(parser.css_data_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto const* child = node->first_node();
|
||||
child; child = child->next_sibling())
|
||||
{
|
||||
traverse_tree(parser, child);
|
||||
}
|
||||
}
|
||||
|
||||
end_element(parser, node);
|
||||
}
|
||||
break;
|
||||
#if 0 //
|
||||
// Data nodes
|
||||
case rapidxml::node_data:
|
||||
case rapidxml::node_cdata:
|
||||
{
|
||||
|
||||
if (node->value_size() > 0) // Don't add empty text nodes
|
||||
{
|
||||
// parsed text values should have leading and trailing
|
||||
// whitespace trimmed.
|
||||
//std::string trimmed = node->value();
|
||||
//mapnik::util::trim(trimmed);
|
||||
//std::cerr << "CDATA:" << node->value() << std::endl;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1426,6 +1542,7 @@ svg_parser::svg_parser(svg_converter_type & path, bool strict)
|
|||
: path_(path),
|
||||
is_defs_(false),
|
||||
ignore_(false),
|
||||
css_style_(false),
|
||||
err_handler_(strict) {}
|
||||
|
||||
svg_parser::~svg_parser() {}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit c3982b76602bcbfea6403793ac2f3fab44552270
|
||||
Subproject commit e83b7f7c6d2d646a638bba404a0d637609722a09
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#include <mapnik/safe_cast.hpp>
|
||||
#include <mapnik/color.hpp>
|
||||
#include <mapnik/css_color_grammar_x3.hpp>
|
||||
#include <mapnik/css_color_grammar_x3_def.hpp>
|
||||
#include <mapnik/css/css_color_grammar_x3.hpp>
|
||||
#include <mapnik/css/css_color_grammar_x3_def.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
|
|
109
test/unit/svg/svg_renderer_test.cpp
Normal file
109
test/unit/svg/svg_renderer_test.cpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2020 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
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <mapnik/debug.hpp>
|
||||
#include <mapnik/marker.hpp>
|
||||
#include <mapnik/marker_cache.hpp>
|
||||
#include <mapnik/image_util.hpp>
|
||||
#include <mapnik/svg/svg_parser.hpp>
|
||||
#include <mapnik/svg/svg_converter.hpp>
|
||||
#include <mapnik/svg/svg_path_adapter.hpp>
|
||||
#include <mapnik/svg/svg_renderer_agg.hpp>
|
||||
#include <mapnik/svg/svg_path_attributes.hpp>
|
||||
#include <boost/range/combine.hpp>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore_agg.hpp>
|
||||
#include "agg_rasterizer_scanline_aa.h"
|
||||
#include "agg_basics.h"
|
||||
#include "agg_rendering_buffer.h"
|
||||
#include "agg_renderer_base.h"
|
||||
#include "agg_pixfmt_rgba.h"
|
||||
#include "agg_scanline_u.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
mapnik::image_rgba8 render_svg(std::string const& filename, double scale_factor)
|
||||
{
|
||||
using pixfmt = agg::pixfmt_rgba32_pre;
|
||||
using renderer_base = agg::renderer_base<pixfmt>;
|
||||
using renderer_solid = agg::renderer_scanline_aa_solid<renderer_base>;
|
||||
|
||||
agg::rasterizer_scanline_aa<> ras_ptr;
|
||||
agg::scanline_u8 sl;
|
||||
std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(filename, false);
|
||||
mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
|
||||
double svg_width, svg_height ;
|
||||
std::tie(svg_width, svg_height) = svg.dimensions();
|
||||
int image_width = static_cast<int>(std::round(svg_width));
|
||||
int image_height = static_cast<int>(std::round(svg_height));
|
||||
|
||||
mapnik::image_rgba8 im(image_width, image_height, true, true);
|
||||
agg::rendering_buffer buf(im.bytes(), im.width(), im.height(), im.row_size());
|
||||
pixfmt pixf(buf);
|
||||
renderer_base renb(pixf);
|
||||
|
||||
agg::trans_affine mtx = agg::trans_affine_translation(-0.5 * svg_width, -0.5 * svg_height);
|
||||
mtx.scale(scale_factor);
|
||||
mtx.translate(0.5 * svg_width, 0.5 * svg_height);
|
||||
|
||||
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(svg.get_data()->source());
|
||||
mapnik::svg::svg_path_adapter svg_path(stl_storage);
|
||||
mapnik::svg::renderer_agg<mapnik::svg_path_adapter,
|
||||
mapnik::svg_attribute_type,
|
||||
renderer_solid,
|
||||
agg::pixfmt_rgba32_pre > renderer(svg_path,
|
||||
svg.get_data()->attributes());
|
||||
double opacity = 1.0;
|
||||
renderer.render(ras_ptr, sl, renb, mtx, opacity, {0, 0, svg_width, svg_height});
|
||||
return im;
|
||||
}
|
||||
|
||||
bool equal(mapnik::image_rgba8 const& im1, mapnik::image_rgba8 const& im2)
|
||||
{
|
||||
if (im1.width() != im2.width() || im1.height() != im2.height())
|
||||
return false;
|
||||
|
||||
for(auto tup : boost::combine(im1, im2))
|
||||
{
|
||||
if (boost::get<0>(tup) != boost::get<1>(tup)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("SVG renderer") {
|
||||
|
||||
SECTION("SVG octocat inline/css")
|
||||
{
|
||||
double scale_factor = 1.0;
|
||||
std::string octocat_inline("./test/data/svg/octocat.svg");
|
||||
std::string octocat_css("./test/data/svg/octocat-css.svg");
|
||||
auto image1 = render_svg(octocat_inline, 1.0);
|
||||
auto image2 = render_svg(octocat_css, 1.0);
|
||||
REQUIRE(equal(image1, image2));
|
||||
}
|
||||
}
|
|
@ -74,7 +74,16 @@ struct main_marker_visitor
|
|||
double opacity = 1;
|
||||
double w, h;
|
||||
std::tie(w, h) = marker.dimensions();
|
||||
|
||||
if (w == 0 || h == 0)
|
||||
{
|
||||
if (verbose_)
|
||||
{
|
||||
std::clog << "Invalid SVG dimensions: " << w << "," << h << " using svgBBOX()" << std::endl;
|
||||
}
|
||||
auto b = marker.bounding_box();
|
||||
w = b.width();
|
||||
h = b.height();
|
||||
}
|
||||
|
||||
double svg_width = w * scale_factor_;
|
||||
double svg_height = h * scale_factor_;
|
||||
|
@ -83,9 +92,7 @@ struct main_marker_visitor
|
|||
int output_height = static_cast<int>(std::round(svg_height));
|
||||
if (verbose_)
|
||||
{
|
||||
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 << "SVG width of '" << w << "' and height of '" << h << "'\n";
|
||||
std::clog << "Output image dimensions:[" << output_width << "," << output_height << "]" << std::endl;
|
||||
}
|
||||
mapnik::image_rgba8 im(output_width, output_height, true, true);
|
||||
|
|
Loading…
Reference in a new issue