[mapnik] 03/13: Imported Upstream version 3.0.2+ds
Sebastiaan Couwenberg
sebastic at moszumanska.debian.org
Tue Aug 25 19:37:34 UTC 2015
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository mapnik.
commit d5ace33444b55aaf563770424b0f83e78fb02a95
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Tue Aug 25 20:14:24 2015 +0200
Imported Upstream version 3.0.2+ds
---
.gitmodules | 2 +-
CHANGELOG.md | 23 +-
Makefile | 19 +
SConstruct | 27 +-
benchmark/run | 2 +-
bootstrap.sh | 3 -
include/mapnik/gradient.hpp | 7 +-
include/mapnik/image_filter.hpp | 64 +-
include/mapnik/jpeg_io.hpp | 4 +-
include/mapnik/svg/svg_parser.hpp | 7 +-
.../{svg_parser.hpp => svg_parser_exception.hpp} | 37 +-
include/mapnik/version.hpp | 2 +-
include/mapnik/webp_io.hpp | 22 +-
src/build.py | 3 +-
src/gradient.cpp | 47 +-
src/marker_cache.cpp | 21 +-
src/svg/svg_parser.cpp | 1037 ++++++++++----------
test/cleanup.hpp | 8 +-
test/unit/svg/svg_parser_test.cpp | 485 ++++++++-
utils/svg2png/svg2png.cpp | 35 +-
20 files changed, 1177 insertions(+), 678 deletions(-)
diff --git a/.gitmodules b/.gitmodules
index d2185cd..4cca9a4 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -5,4 +5,4 @@
[submodule "test/data-visual"]
path = test/data-visual
url = https://github.com/mapnik/test-data-visual.git
- branch = master
+ branch = master
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8d10ff..8facec3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,26 @@ Developers: Please commit along with changes.
For a complete change history, see the git log.
+## 3.0.2
+
+Released: July 31, 2015
+
+(Packaged from 8305e74)
+
+#### Summary
+
+This release is centered around improvements to the SVG parsing within mapnik. Most work was done in pull request #3003.
+
+- Added container to log SVG parsing errors
+- Reimplemented to use rapidxml for parsing XML (DOM)
+- Support both xml:id and id attributes ( xml:id takes precedence )
+- Added parse_id_from_url using boost::spirit
+- Added error tracking when parsing doubles
+- Unit tests for svg_parser to improve coverage
+- Fixed rx/ry validation for rounded_rect
+- Fixed dimensions parsing
+- Remove libxml2 dependency
+
## 3.0.1
Released: July 27th, 2015
@@ -14,7 +34,7 @@ Released: July 27th, 2015
#### Summary
-The 3.0.1 fixes a few bugs in geojson parsing, svg parsing, and rendering. It also avoids a potential hang when using `line-geometry-transform` and includes a speedup for text rendering compared to v3.0.0. It is fully back compatibility with v3.0.0 and everyone is encouraged to upgrade.
+The 3.0.1 fixes a few bugs in geojson parsing, svg parsing, and rendering. It also avoids a potential hang when using `line-geometry-transform` and includes a speedup for text rendering compared to v3.0.0. It is fully back compatible with v3.0.0 and everyone is encouraged to upgrade.
- Fixed text placement performance after #2949 (#2963)
- Fixed rendering behavior for `text-minimum-path-length` which regressed in 3.0.0 (#2990)
@@ -24,6 +44,7 @@ The 3.0.1 fixes a few bugs in geojson parsing, svg parsing, and rendering. It al
- Fixed parsing of GeoJSON when unknown properties encountered at `FeatureCollection` level (#2983)
- Fixed parsing of GeoJSON when properties contained `{}` (#2964)
- Fixed potential hang due to invalid use of `line-geometry-transform` (6d6cb15)
+- Moved unmaintained plugins out of core: `osm`, `occi`, and `rasterlite` (#2980)
## 3.0.0
diff --git a/Makefile b/Makefile
index ef02f71..3ff06a5 100755
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,25 @@ all: mapnik
install:
$(PYTHON) scons/scons.py -j$(JOBS) --config=cache --implicit-cache --max-drift=1 install
+release:
+ export MAPNIK_VERSION=$(shell ./utils/mapnik-config/mapnik-config --version) && \
+ export TARBALL_NAME="mapnik-v$${MAPNIK_VERSION}" && \
+ cd /tmp/ && \
+ rm -rf $${TARBALL_NAME} && \
+ git clone --depth 1 --branch v$${MAPNIK_VERSION} git at github.com:mapnik/mapnik.git $${TARBALL_NAME} && \
+ cd $${TARBALL_NAME} && \
+ git checkout "tags/v$${MAPNIK_VERSION}" && \
+ git submodule update --depth 1 --init && \
+ rm -rf test/data/.git && \
+ rm -rf test/data/.gitignore && \
+ rm -rf test/data-visual/.git && \
+ rm -rf test/data-visual/.gitignore && \
+ rm -rf .git && \
+ rm -rf .gitignore && \
+ cd ../ && \
+ tar cjf $${TARBALL_NAME}.tar.bz2 $${TARBALL_NAME}/ && \
+ aws s3 cp --acl public-read $${TARBALL_NAME}.tar.bz2 s3://mapnik/dist/v$${MAPNIK_VERSION}/
+
python:
if [ ! -d ./bindings/python ]; then git clone git at github.com:mapnik/python-mapnik.git --recursive ./bindings/python; else (cd bindings/python && git pull && git submodule update --init); fi;
make
diff --git a/SConstruct b/SConstruct
index 9f3c0f4..359801d 100644
--- a/SConstruct
+++ b/SConstruct
@@ -395,7 +395,7 @@ opts.AddVariables(
BoolVariable('FULL_LIB_PATH', 'Embed the full and absolute path to libmapnik when linking ("install_name" on OS X/rpath on Linux)', 'True'),
BoolVariable('ENABLE_SONAME', 'Embed a soname in libmapnik on Linux', 'True'),
EnumVariable('THREADING','Set threading support','multi', ['multi','single']),
- EnumVariable('XMLPARSER','Set xml parser','libxml2', ['libxml2','ptree']),
+ EnumVariable('XMLPARSER','Set xml parser','ptree', ['libxml2','ptree']),
BoolVariable('DEMO', 'Compile demo c++ application', 'True'),
BoolVariable('PGSQL2SQLITE', 'Compile and install a utility to convert postgres tables to sqlite', 'False'),
BoolVariable('SHAPEINDEX', 'Compile and install a utility to generate shapefile indexes in the custom format (.index) Mapnik supports', 'True'),
@@ -1266,18 +1266,19 @@ if not preconfigured:
# libxml2 should be optional but is currently not
# https://github.com/mapnik/mapnik/issues/913
- if env.get('XML2_LIBS') or env.get('XML2_INCLUDES'):
- REQUIRED_LIBSHEADERS.insert(0,['libxml2','libxml/parser.h',True,'C'])
- if env.get('XML2_INCLUDES'):
- inc_path = env['XML2_INCLUDES']
- env.AppendUnique(CPPPATH = fix_path(inc_path))
- if env.get('XML2_LIBS'):
- lib_path = env['XML2_LIBS']
- env.AppendUnique(LIBPATH = fix_path(lib_path))
- elif conf.parse_config('XML2_CONFIG',checks='--cflags'):
- env['HAS_LIBXML2'] = True
- else:
- env['MISSING_DEPS'].append('libxml2')
+ if env.get('XMLPARSER') and env['XMLPARSER'] == 'libxml2':
+ if env.get('XML2_LIBS') or env.get('XML2_INCLUDES'):
+ OPTIONAL_LIBSHEADERS.insert(0,['libxml2','libxml/parser.h',True,'C'])
+ if env.get('XML2_INCLUDES'):
+ inc_path = env['XML2_INCLUDES']
+ env.AppendUnique(CPPPATH = fix_path(inc_path))
+ if env.get('XML2_LIBS'):
+ lib_path = env['XML2_LIBS']
+ env.AppendUnique(LIBPATH = fix_path(lib_path))
+ elif conf.parse_config('XML2_CONFIG',checks='--cflags'):
+ env['HAS_LIBXML2'] = True
+ else:
+ env['MISSING_DEPS'].append('libxml2')
if not env['HOST']:
if conf.CheckHasDlfcn():
diff --git a/benchmark/run b/benchmark/run
index b17f82a..f918539 100755
--- a/benchmark/run
+++ b/benchmark/run
@@ -55,5 +55,5 @@ run test_offset_converter 10 1000
--threads 1
./benchmark/out/test_quad_tree \
- --iterations 10000 \
+ --iterations 1000 \
--threads 10
diff --git a/bootstrap.sh b/bootstrap.sh
index d68daa8..5d38b16 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -60,7 +60,6 @@ function install_mason_deps() {
install freetype 2.5.5 libfreetype &
install harfbuzz 0.9.40 libharfbuzz &
install jpeg_turbo 1.4.0 libjpeg &
- install libxml2 2.9.2 libxml2 &
install libpng 1.6.17 libpng &
install webp 0.4.2 libwebp &
install icu 54.1 &
@@ -117,8 +116,6 @@ PG_INCLUDES = '${MASON_LINKED_REL}/include'
PG_LIBS = '${MASON_LINKED_REL}/lib'
FREETYPE_INCLUDES = '${MASON_LINKED_REL}/include/freetype2'
FREETYPE_LIBS = '${MASON_LINKED_REL}/lib'
-XML2_INCLUDES = '${MASON_LINKED_REL}/include/libxml2'
-XML2_LIBS = '${MASON_LINKED_REL}/lib'
SVG_RENDERER = True
CAIRO_INCLUDES = '${MASON_LINKED_REL}/include'
CAIRO_LIBS = '${MASON_LINKED_REL}/lib'
diff --git a/include/mapnik/gradient.hpp b/include/mapnik/gradient.hpp
index eb70be4..f8eff6e 100644
--- a/include/mapnik/gradient.hpp
+++ b/include/mapnik/gradient.hpp
@@ -79,8 +79,9 @@ class MAPNIK_DECL gradient
public:
gradient();
gradient(gradient const& other);
- gradient& operator=(const gradient& rhs);
-
+ gradient(gradient && other);
+ gradient& operator=(gradient rhs);
+ bool operator==(gradient const& other) const;
void set_gradient_type(gradient_e grad);
gradient_e get_gradient_type() const;
@@ -100,7 +101,7 @@ public:
void get_control_points(double &x1, double &y1, double &x2, double &y2) const;
private:
- void swap(const gradient& other) throw();
+ void swap(gradient& other) throw();
};
}
diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp
index 37c3327..8301ade 100644
--- a/include/mapnik/image_filter.hpp
+++ b/include/mapnik/image_filter.hpp
@@ -254,10 +254,10 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi
typename Src::x_iterator dst_it = dst_view.row_begin(0);
// top row
- for (std::size_t x = 0 ; x < static_cast<std::size_t>(src_view.width()); ++x)
+ for (std::ptrdiff_t x = 0 ; x < src_view.width(); ++x)
{
(*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a
- for (std::size_t i = 0; i < 3; ++i)
+ for (std::ptrdiff_t i = 0; i < 3; ++i)
{
bits32f p[9];
@@ -275,7 +275,7 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi
p[6] = src_loc[loc02][i];
}
- if ( x == static_cast<std::size_t>(src_view.width())-1)
+ if ( x == (src_view.width())-1)
{
p[5] = p[4];
p[8] = p[7];
@@ -296,15 +296,15 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi
++dst_it;
}
// carrige-return
- src_loc += point2<std::ptrdiff_t>(-static_cast<std::size_t>(src_view.width()),1);
+ src_loc += point2<std::ptrdiff_t>(-src_view.width(),1);
// 1... height-1 rows
- for (std::size_t y = 1; y<static_cast<std::size_t>(src_view.height())-1; ++y)
+ for (std::ptrdiff_t y = 1; y < src_view.height()-1; ++y)
{
- for (std::size_t x = 0; x < static_cast<std::size_t>(src_view.width()); ++x)
+ for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
{
(*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a
- for (std::size_t i = 0; i < 3; ++i)
+ for (std::ptrdiff_t i = 0; i < 3; ++i)
{
bits32f p[9];
@@ -325,7 +325,7 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi
p[6] = src_loc[loc02][i];
}
- if ( x == static_cast<std::size_t>(src_view.width()) - 1)
+ if ( x == (src_view.width()) - 1)
{
p[2] = p[1];
p[5] = p[4];
@@ -343,15 +343,15 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi
++src_loc.x();
}
// carrige-return
- src_loc += point2<std::ptrdiff_t>(-static_cast<std::size_t>(src_view.width()),1);
+ src_loc += point2<std::ptrdiff_t>(-src_view.width(),1);
}
// bottom row
- //src_loc = src_view.xy_at(0,static_cast<std::size_t>(src_view.height())-1);
- for (std::size_t x = 0 ; x < static_cast<std::size_t>(src_view.width()); ++x)
+ //src_loc = src_view.xy_at(0,src_view.height()-1);
+ for (std::ptrdiff_t x = 0 ; x < src_view.width(); ++x)
{
(*dst_it)[3] = src_loc[loc11][3]; // Dst.a = Src.a
- for (std::size_t i = 0; i < 3; ++i)
+ for (std::ptrdiff_t i = 0; i < 3; ++i)
{
bits32f p[9];
@@ -369,7 +369,7 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi
p[3] = src_loc[loc01][i];
}
- if ( x == static_cast<std::size_t>(src_view.width())-1)
+ if ( x == (src_view.width())-1)
{
p[2] = p[1];
p[5] = p[4];
@@ -431,10 +431,10 @@ void apply_filter(Src & src, color_to_alpha const& op)
double cr = static_cast<double>(op.color.red())/255.0;
double cg = static_cast<double>(op.color.green())/255.0;
double cb = static_cast<double>(op.color.blue())/255.0;
- for (std::size_t y=0; y<static_cast<std::size_t>(src_view.height()); ++y)
+ for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
{
rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
- for (std::size_t x=0; x<static_cast<std::size_t>(src_view.width()); ++x)
+ for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
{
uint8_t & r = get_color(src_it[x], red_t());
uint8_t & g = get_color(src_it[x], green_t());
@@ -485,17 +485,17 @@ template <typename Src>
void apply_filter(Src & src, colorize_alpha const& op)
{
using namespace boost::gil;
- std::size_t size = op.size();
+ std::ptrdiff_t size = op.size();
if (op.size() == 1)
{
// no interpolation if only one stop
mapnik::filter::color_stop const& stop = op[0];
mapnik::color const& c = stop.color;
rgba8_view_t src_view = rgba8_view(src);
- for (std::size_t y=0; y<static_cast<std::size_t>(src_view.height()); ++y)
+ for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
{
rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
- for (std::size_t x=0; x<static_cast<std::size_t>(src_view.width()); ++x)
+ for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
{
uint8_t & r = get_color(src_it[x], red_t());
uint8_t & g = get_color(src_it[x], green_t());
@@ -533,10 +533,10 @@ void apply_filter(Src & src, colorize_alpha const& op)
if (grad_lut.build_lut())
{
rgba8_view_t src_view = rgba8_view(src);
- for (std::size_t y=0; y<static_cast<std::size_t>(src_view.height()); ++y)
+ for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
{
rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
- for (std::size_t x=0; x<static_cast<std::size_t>(src_view.width()); ++x)
+ for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
{
uint8_t & r = get_color(src_it[x], red_t());
uint8_t & g = get_color(src_it[x], green_t());
@@ -598,10 +598,10 @@ void apply_filter(Src & src, scale_hsla const& transform)
if (tinting || set_alpha)
{
rgba8_view_t src_view = rgba8_view(src);
- for (std::size_t y=0; y<static_cast<std::size_t>(src_view.height()); ++y)
+ for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
{
rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
- for (std::size_t x=0; x<static_cast<std::size_t>(src_view.width()); ++x)
+ for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
{
uint8_t & r = get_color(src_it[x], red_t());
uint8_t & g = get_color(src_it[x], green_t());
@@ -681,10 +681,10 @@ void apply_filter(Src & src, gray const& /*op*/)
rgba8_view_t src_view = rgba8_view(src);
- for (std::size_t y=0; y<static_cast<std::size_t>(src_view.height()); ++y)
+ for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
{
rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
- for (std::size_t x=0; x<static_cast<std::size_t>(src_view.width()); ++x)
+ for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
{
// formula taken from boost/gil/color_convert.hpp:rgb_to_luminance
uint8_t & r = get_color(src_it[x], red_t());
@@ -699,7 +699,7 @@ void apply_filter(Src & src, gray const& /*op*/)
template <typename Src, typename Dst>
void x_gradient_impl(Src const& src_view, Dst const& dst_view)
{
- for (std::size_t y=0; y<static_cast<std::size_t>(src_view.height()); ++y)
+ for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
{
typename Src::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
typename Dst::x_iterator dst_it = dst_view.row_begin(static_cast<long>(y));
@@ -708,13 +708,13 @@ void x_gradient_impl(Src const& src_view, Dst const& dst_view)
dst_it[0][1] = 128 + (src_it[0][1] - src_it[1][1]) / 2;
dst_it[0][2] = 128 + (src_it[0][2] - src_it[1][2]) / 2;
- dst_it[dst_view.width()-1][0] = 128 + (src_it[static_cast<std::size_t>(src_view.width())-2][0] - src_it[static_cast<std::size_t>(src_view.width())-1][0]) / 2;
- dst_it[dst_view.width()-1][1] = 128 + (src_it[static_cast<std::size_t>(src_view.width())-2][1] - src_it[static_cast<std::size_t>(src_view.width())-1][1]) / 2;
- dst_it[dst_view.width()-1][2] = 128 + (src_it[static_cast<std::size_t>(src_view.width())-2][2] - src_it[static_cast<std::size_t>(src_view.width())-1][2]) / 2;
+ dst_it[dst_view.width()-1][0] = 128 + (src_it[(src_view.width())-2][0] - src_it[(src_view.width())-1][0]) / 2;
+ dst_it[dst_view.width()-1][1] = 128 + (src_it[(src_view.width())-2][1] - src_it[(src_view.width())-1][1]) / 2;
+ dst_it[dst_view.width()-1][2] = 128 + (src_it[(src_view.width())-2][2] - src_it[(src_view.width())-1][2]) / 2;
- dst_it[0][3] = dst_it[static_cast<std::size_t>(src_view.width())-1][3] = 255;
+ dst_it[0][3] = dst_it[(src_view.width())-1][3] = 255;
- for (std::size_t x=1; x<static_cast<std::size_t>(src_view.width())-1; ++x)
+ for (std::ptrdiff_t x = 1; x < src_view.width()-1; ++x)
{
dst_it[x][0] = 128 + (src_it[x-1][0] - src_it[x+1][0]) / 2;
dst_it[x][1] = 128 + (src_it[x-1][1] - src_it[x+1][1]) / 2;
@@ -746,10 +746,10 @@ void apply_filter(Src & src, invert const& /*op*/)
rgba8_view_t src_view = rgba8_view(src);
- for (std::size_t y=0; y<static_cast<std::size_t>(src_view.height()); ++y)
+ for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
{
rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
- for (std::size_t x=0; x<static_cast<std::size_t>(src_view.width()); ++x)
+ for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
{
// we only work with premultiplied source,
// thus all color values must be <= alpha
diff --git a/include/mapnik/jpeg_io.hpp b/include/mapnik/jpeg_io.hpp
index 06a0bc3..dcfd446 100644
--- a/include/mapnik/jpeg_io.hpp
+++ b/include/mapnik/jpeg_io.hpp
@@ -85,8 +85,8 @@ void save_as_jpeg(T1 & file,int quality, T2 const& image)
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
- int width=image.width();
- int height=image.height();
+ int width = static_cast<int>(image.width());
+ int height = static_cast<int>(image.height());
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
diff --git a/include/mapnik/svg/svg_parser.hpp b/include/mapnik/svg/svg_parser.hpp
index ef9eafc..63caacd 100644
--- a/include/mapnik/svg/svg_parser.hpp
+++ b/include/mapnik/svg/svg_parser.hpp
@@ -38,15 +38,18 @@ namespace mapnik { namespace svg {
class MAPNIK_DECL svg_parser : private util::noncopyable
{
+ using error_message_container = std::vector<std::string> ;
public:
explicit svg_parser(svg_converter_type & path);
~svg_parser();
- void parse(std::string const& filename);
- void parse_from_string(std::string const& svg);
+ error_message_container const& error_messages() const;
+ bool parse(std::string const& filename);
+ bool parse_from_string(std::string const& svg);
svg_converter_type & path_;
bool is_defs_;
std::map<std::string, gradient> gradient_map_;
std::pair<std::string, gradient> temporary_gradient_;
+ error_message_container error_messages_;
};
}}
diff --git a/include/mapnik/svg/svg_parser.hpp b/include/mapnik/svg/svg_parser_exception.hpp
similarity index 60%
copy from include/mapnik/svg/svg_parser.hpp
copy to include/mapnik/svg/svg_parser_exception.hpp
index ef9eafc..89139da 100644
--- a/include/mapnik/svg/svg_parser.hpp
+++ b/include/mapnik/svg/svg_parser_exception.hpp
@@ -20,36 +20,35 @@
*
*****************************************************************************/
-#ifndef MAPNIK_SVG_PARSER_HPP
-#define MAPNIK_SVG_PARSER_HPP
+#ifndef MAPNIK_SVG_PARSER_EXCEPTION_HPP
+#define MAPNIK_SVG_PARSER_EXCEPTION_HPP
// mapnik
#include <mapnik/config.hpp>
-#include <mapnik/svg/svg_path_attributes.hpp>
-#include <mapnik/svg/svg_converter.hpp>
-#include <mapnik/svg/svg_path_adapter.hpp>
-#include <mapnik/gradient.hpp>
-#include <mapnik/util/noncopyable.hpp>
+#include <exception>
// stl
#include <map>
namespace mapnik { namespace svg {
- class MAPNIK_DECL svg_parser : private util::noncopyable
+class MAPNIK_DECL svg_parser_exception : public std::exception
+{
+public:
+ svg_parser_exception(std::string const& message)
+ : message_(message) {}
+
+ ~svg_parser_exception() throw() {}
+
+ virtual const char* what() const throw()
{
- public:
- explicit svg_parser(svg_converter_type & path);
- ~svg_parser();
- void parse(std::string const& filename);
- void parse_from_string(std::string const& svg);
- svg_converter_type & path_;
- bool is_defs_;
- std::map<std::string, gradient> gradient_map_;
- std::pair<std::string, gradient> temporary_gradient_;
- };
+ return message_.c_str();
+ }
+private:
+ std::string message_;
+};
}}
-#endif // MAPNIK_SVG_PARSER_HPP
+#endif // MAPNIK_SVG_PARSER_EXCEPTION_HPP
diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp
index cbba22f..9e511c1 100644
--- a/include/mapnik/version.hpp
+++ b/include/mapnik/version.hpp
@@ -27,7 +27,7 @@
#define MAPNIK_MAJOR_VERSION 3
#define MAPNIK_MINOR_VERSION 0
-#define MAPNIK_PATCH_VERSION 1
+#define MAPNIK_PATCH_VERSION 2
// translates to 300001
#define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION)
diff --git a/include/mapnik/webp_io.hpp b/include/mapnik/webp_io.hpp
index 4b5c541..978e6cf 100644
--- a/include/mapnik/webp_io.hpp
+++ b/include/mapnik/webp_io.hpp
@@ -80,20 +80,20 @@ inline int import_image(T2 const& im_in,
image<typename T2::pixel> const& data = im_in.data();
std::size_t width = im_in.width();
std::size_t height = im_in.height();
- int stride = sizeof(typename T2::pixel_type) * width;
+ std::size_t stride = sizeof(typename T2::pixel_type) * width;
if (data.width() == width &&
data.height() == height)
{
if (alpha)
{
- return WebPPictureImportRGBA(&pic, data.bytes(), stride);
+ return WebPPictureImportRGBA(&pic, data.bytes(), static_cast<int>(stride));
}
else
{
#if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1
- return WebPPictureImportRGBX(&pic, data.bytes(), stride);
+ return WebPPictureImportRGBX(&pic, data.bytes(), static_cast<int>(stride));
#else
- return WebPPictureImportRGBA(&pic, data.bytes(), stride);
+ return WebPPictureImportRGBA(&pic, data.bytes(), static_cast<int>(stride));
#endif
}
}
@@ -109,14 +109,14 @@ inline int import_image(T2 const& im_in,
}
if (alpha)
{
- return WebPPictureImportRGBA(&pic, im.bytes(), stride);
+ return WebPPictureImportRGBA(&pic, im.bytes(), static_cast<int>(stride));
}
else
{
#if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1
- return WebPPictureImportRGBX(&pic, im.bytes(), stride);
+ return WebPPictureImportRGBX(&pic, im.bytes(), static_cast<int>(stride));
#else
- return WebPPictureImportRGBA(&pic, im.bytes(), stride);
+ return WebPPictureImportRGBA(&pic, im.bytes(), static_cast<int>(stride));
#endif
}
}
@@ -127,17 +127,17 @@ inline int import_image(image_rgba8 const& im,
WebPPicture & pic,
bool alpha)
{
- int stride = sizeof(image_rgba8::pixel_type) * im.width();
+ std::size_t stride = sizeof(image_rgba8::pixel_type) * im.width();
if (alpha)
{
- return WebPPictureImportRGBA(&pic, im.bytes(), stride);
+ return WebPPictureImportRGBA(&pic, im.bytes(), static_cast<int>(stride));
}
else
{
#if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1
- return WebPPictureImportRGBX(&pic, im.bytes(), stride);
+ return WebPPictureImportRGBX(&pic, im.bytes(), static_cast<int>(stride));
#else
- return WebPPictureImportRGBA(&pic, im.bytes(), stride);
+ return WebPPictureImportRGBA(&pic, im.bytes(), static_cast<int>(stride));
#endif
}
}
diff --git a/src/build.py b/src/build.py
index 520a347..bdc3438 100644
--- a/src/build.py
+++ b/src/build.py
@@ -84,7 +84,8 @@ if '-DHAVE_WEBP' in env['CPPDEFINES']:
lib_env['LIBS'].append('webp')
enabled_imaging_libraries.append('webp_reader.cpp')
-lib_env['LIBS'].append('xml2')
+if env['XMLPARSER'] == 'libxml2' and env['HAS_LIBXML2']:
+ lib_env['LIBS'].append('xml2')
if '-DBOOST_REGEX_HAS_ICU' in env['CPPDEFINES']:
lib_env['LIBS'].append('icui18n')
diff --git a/src/gradient.cpp b/src/gradient.cpp
index ddf6658..037cf8f 100644
--- a/src/gradient.cpp
+++ b/src/gradient.cpp
@@ -66,13 +66,36 @@ gradient::gradient(gradient const& other)
units_(other.units_),
gradient_type_(other.gradient_type_) {}
-gradient & gradient::operator=(const gradient& rhs)
+gradient::gradient(gradient && other)
+ : transform_(std::move(other.transform_)),
+ x1_(std::move(other.x1_)),
+ y1_(std::move(other.y1_)),
+ x2_(std::move(other.x2_)),
+ y2_(std::move(other.y2_)),
+ r_(std::move(other.r_)),
+ stops_(std::move(other.stops_)),
+ units_(std::move(other.units_)),
+ gradient_type_(std::move(other.gradient_type_)) {}
+
+gradient & gradient::operator=(gradient rhs)
{
- gradient tmp(rhs);
- swap(tmp);
+ swap(rhs);
return *this;
}
+bool gradient::operator==(gradient const& other) const
+{
+ return transform_ == other.transform_ &&
+ x1_ == other.x1_ &&
+ y1_ == other.y1_ &&
+ x2_ == other.x2_ &&
+ y2_ == other.y2_ &&
+ r_ == other.r_ &&
+ std::equal(stops_.begin(),stops_.end(), other.stops_.begin()),
+ units_ == other.units_ &&
+ gradient_type_ == other.gradient_type_;
+}
+
void gradient::set_gradient_type(gradient_e grad)
{
gradient_type_=grad;
@@ -108,7 +131,7 @@ void gradient::add_stop(double offset,mapnik::color const& c)
bool gradient::has_stop() const
{
- return ! stops_.empty();
+ return !stops_.empty();
}
stop_array const& gradient::get_stop_array() const
@@ -116,13 +139,17 @@ stop_array const& gradient::get_stop_array() const
return stops_;
}
-void gradient::swap(const gradient& other) throw()
+void gradient::swap(gradient& other) throw()
{
- gradient_type_=other.gradient_type_;
- stops_=other.stops_;
- units_=other.units_;
- transform_=other.transform_;
- other.get_control_points(x1_,y1_,x2_,y2_,r_);
+ std::swap(gradient_type_, other.gradient_type_);
+ std::swap(stops_, other.stops_);
+ std::swap(units_, other.units_);
+ std::swap(transform_, other.transform_);
+ std::swap(x1_, other.x1_);
+ std::swap(y1_, other.y1_);
+ std::swap(x2_, other.x2_);
+ std::swap(y2_, other.y2_);
+ std::swap(r_, other.r_);
}
void gradient::set_control_points(double x1, double y1, double x2, double y2, double r)
diff --git a/src/marker_cache.cpp b/src/marker_cache.cpp
index 3f8f939..330f4d0 100644
--- a/src/marker_cache.cpp
+++ b/src/marker_cache.cpp
@@ -175,7 +175,15 @@ std::shared_ptr<mapnik::marker const> marker_cache::find(std::string const& uri,
svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes());
svg_parser p(svg);
- p.parse_from_string(known_svg_string);
+
+ if (!p.parse_from_string(known_svg_string))
+ {
+ for (auto const& msg : p.error_messages())
+ {
+ MAPNIK_LOG_ERROR(marker_cache) << "SVG PARSING ERROR:\"" << msg << "\"";
+ }
+ return std::make_shared<mapnik::marker const>(mapnik::marker_null());
+ }
//svg.arrange_orientations();
double lox,loy,hix,hiy;
svg.bounding_rect(&lox, &loy, &hix, &hiy);
@@ -207,7 +215,16 @@ std::shared_ptr<mapnik::marker const> marker_cache::find(std::string const& uri,
svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes());
svg_parser p(svg);
- p.parse(uri);
+
+
+ if (!p.parse(uri))
+ {
+ for (auto const& msg : p.error_messages())
+ {
+ MAPNIK_LOG_ERROR(marker_cache) << "SVG PARSING ERROR:\"" << msg << "\"";
+ }
+ return std::make_shared<mapnik::marker const>(mapnik::marker_null());
+ }
//svg.arrange_orientations();
double lox,loy,hix,hiy;
svg.bounding_rect(&lox, &loy, &hix, &hiy);
diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp
index 548ccd2..06c7202 100644
--- a/src/svg/svg_parser.cpp
+++ b/src/svg/svg_parser.cpp
@@ -26,7 +26,8 @@
#include <mapnik/svg/svg_path_parser.hpp>
#include <mapnik/config_error.hpp>
#include <mapnik/safe_cast.hpp>
-
+#include <mapnik/svg/svg_parser_exception.hpp>
+#include <mapnik/util/file_io.hpp>
#include "agg_ellipse.h"
#include "agg_rounded_rect.h"
#include "agg_span_gradient.h"
@@ -41,42 +42,40 @@
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/algorithm/string/predicate.hpp>
+// rapidxml
+#include <boost/property_tree/detail/xml_parser_read_rapidxml.hpp>
#pragma GCC diagnostic pop
#include <string>
#include <stdexcept>
#include <vector>
#include <cstring>
-
-// xml2
-#include <libxml/xmlreader.h>
-
+#include <fstream>
namespace mapnik { namespace svg {
-bool parse_reader(svg_parser & parser,xmlTextReaderPtr reader);
-void process_node(svg_parser & parser,xmlTextReaderPtr reader);
-void start_element(svg_parser & parser,xmlTextReaderPtr reader);
-void end_element(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_path(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_dimensions(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_polygon(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_polyline(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_line(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_rect(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_circle(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_ellipse(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_linear_gradient(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_radial_gradient(svg_parser & parser,xmlTextReaderPtr reader);
-bool parse_common_gradient(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_gradient_stop(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_attr(svg_parser & parser,xmlTextReaderPtr reader);
-void parse_attr(svg_parser & parser,const xmlChar * name, const xmlChar * value );
+namespace rapidxml = boost::property_tree::detail::rapidxml;
+
+bool traverse_tree(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void end_element(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_path(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_dimensions(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_polygon(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_polyline(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_line(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_rect(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_circle(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_ellipse(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_linear_gradient(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_radial_gradient(svg_parser & parser,rapidxml::xml_node<char> const* node);
+bool parse_common_gradient(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_gradient_stop(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_attr(svg_parser & parser,rapidxml::xml_node<char> const* node);
+void parse_attr(svg_parser & parser,char const * name, char const* value);
-using color_lookup_type = std::vector<std::pair<double, agg::rgba8> >;
+using color_lookup_type = std::vector<std::pair<double, agg::rgba8> >;
namespace qi = boost::spirit::qi;
-
using pairs_type = std::vector<std::pair<std::string, std::string> >;
template <typename Iterator,typename SkipType>
@@ -99,7 +98,8 @@ struct key_value_sequence_ordered
qi::rule<Iterator, std::string(), SkipType> key, value;
};
-agg::rgba8 parse_color(const char* str)
+template <typename T>
+mapnik::color parse_color(T & error_messages, const char* str)
{
mapnik::color c(100,100,100);
try
@@ -108,25 +108,38 @@ agg::rgba8 parse_color(const char* str)
}
catch (mapnik::config_error const& ex)
{
- MAPNIK_LOG_ERROR(svg_parser) << ex.what();
+
+ error_messages.emplace_back(ex.what());
}
+ return c;
+}
+
+template <typename T>
+agg::rgba8 parse_color_agg(T & error_messages, const char* str)
+{
+ auto c = parse_color(error_messages, str);
return agg::rgba8(c.red(), c.green(), c.blue(), c.alpha());
}
-double parse_double(const char* str)
+template <typename T>
+double parse_double(T & error_messages, const char* str)
{
using namespace boost::spirit::qi;
qi::double_type double_;
double val = 0.0;
- parse(str, str + std::strlen(str),double_,val);
+ if (!parse(str, str + std::strlen(str),double_,val))
+ {
+ error_messages.emplace_back("Failed to parse double: \"" + std::string(str) + "\"");
+ }
return val;
}
-/*
- * parse a double that might end with a %
- * if it does then set the ref bool true and divide the result by 100
- */
-double parse_double_optional_percent(const char* str, bool &percent)
+
+// parse a double that might end with a %
+// if it does then set the ref bool true and divide the result by 100
+
+template <typename T>
+double parse_double_optional_percent(T & error_messages, const char* str, bool &percent)
{
using namespace boost::spirit::qi;
using boost::phoenix::ref;
@@ -135,12 +148,15 @@ double parse_double_optional_percent(const char* str, bool &percent)
qi::char_type char_;
double val = 0.0;
- parse(str, str + std::strlen(str),double_[ref(val)=_1, ref(percent) = false]
- >> -char_('%')[ref(val)/100.0, ref(percent) = true]);
+ if (!parse(str, str + std::strlen(str),double_[ref(val)=_1, ref(percent) = false]
+ >> -char_('%')[ref(val) /= 100.0, ref(percent) = true]))
+ {
+ error_messages.emplace_back("Failed to parse double (optional %) from " + std::string(str));
+ }
return val;
}
-bool parse_style (const char* str, pairs_type & v)
+bool parse_style (char const* str, pairs_type & v)
{
using namespace boost::spirit::qi;
using skip_type = boost::spirit::ascii::space_type;
@@ -148,540 +164,497 @@ bool parse_style (const char* str, pairs_type & v)
return phrase_parse(str, str + std::strlen(str), kv_parser, skip_type(), v);
}
-bool parse_reader(svg_parser & parser, xmlTextReaderPtr reader)
+bool parse_id_from_url (char const* str, std::string & id)
{
- int ret = xmlTextReaderRead(reader);
- try {
- while (ret == 1)
- {
- process_node(parser,reader);
- ret = xmlTextReaderRead(reader);
- }
- }
- catch (std::exception const& ex)
- {
- xmlFreeTextReader(reader);
- throw ex;
- }
- xmlFreeTextReader(reader);
- if (ret != 0)
- {
- // parsing failure
- return false;
- }
- return true;
+ using namespace boost::spirit::qi;
+ using skip_type = boost::spirit::ascii::space_type;
+ qi::_1_type _1;
+ qi::char_type char_;
+ qi::lit_type lit;
+ return phrase_parse(str, str + std::strlen(str),
+ lit("url") > "(" > "#" > *(char_ - lit(')'))[boost::phoenix::ref(id) += _1] > ")",
+ skip_type());
}
-
-void start_element(svg_parser & parser, xmlTextReaderPtr reader)
+bool traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- const xmlChar *name;
- name = xmlTextReaderConstName(reader);
-
- if (xmlStrEqual(name, BAD_CAST "defs"))
+ auto const* name = node->name();
+ switch (node->type())
{
- if (xmlTextReaderIsEmptyElement(reader) == 0)
- parser.is_defs_ = true;
- }
- // the gradient tags *should* be in defs, but illustrator seems not to put them in there so
- // accept them anywhere
- else if (xmlStrEqual(name, BAD_CAST "linearGradient"))
+ case rapidxml::node_element:
{
- parse_linear_gradient(parser,reader);
- }
- else if (xmlStrEqual(name, BAD_CAST "radialGradient"))
- {
- parse_radial_gradient(parser,reader);
- }
- else if (xmlStrEqual(name, BAD_CAST "stop"))
- {
- parse_gradient_stop(parser,reader);
- }
- if ( !parser.is_defs_ )
- {
-
- if (xmlStrEqual(name, BAD_CAST "g"))
+ if (std::strcmp(name, "defs") == 0)
{
- if (xmlTextReaderIsEmptyElement(reader) == 0)
+ if (node->first_node() != nullptr)
{
- parser.path_.push_attr();
- parse_attr(parser,reader);
+ parser.is_defs_ = true;
}
}
- else
+ // the gradient tags *should* be in defs, but illustrator seems not to put them in there so
+ // accept them anywhere
+ else if (std::strcmp(name, "linearGradient") == 0)
+ {
+ parse_linear_gradient(parser, node);
+ }
+ else if (std::strcmp(name, "radialGradient") == 0)
+ {
+ parse_radial_gradient(parser, node);
+ }
+ else if (std::strcmp(name, "stop") == 0)
{
- parser.path_.push_attr();
- parse_attr(parser,reader);
- if (parser.path_.display())
+ parse_gradient_stop(parser, node);
+ }
+
+ if (!parser.is_defs_) // FIXME
+ {
+ if (std::strcmp(name, "g") == 0)
{
- if (xmlStrEqual(name, BAD_CAST "path"))
- {
- parse_path(parser,reader);
- }
- else if (xmlStrEqual(name, BAD_CAST "polygon") )
- {
- parse_polygon(parser,reader);
- }
- else if (xmlStrEqual(name, BAD_CAST "polyline"))
- {
- parse_polyline(parser,reader);
- }
- else if (xmlStrEqual(name, BAD_CAST "line"))
- {
- parse_line(parser,reader);
- }
- else if (xmlStrEqual(name, BAD_CAST "rect"))
- {
- parse_rect(parser,reader);
- }
- else if (xmlStrEqual(name, BAD_CAST "circle"))
+ if (node->first_node() != nullptr)
{
- parse_circle(parser,reader);
+ parser.path_.push_attr();
+ parse_attr(parser, node);
}
- else if (xmlStrEqual(name, BAD_CAST "ellipse"))
- {
- parse_ellipse(parser,reader);
- }
- else if (xmlStrEqual(name, BAD_CAST "svg"))
- {
- parse_dimensions(parser,reader);
- }
-#ifdef MAPNIK_LOG
- else if (!xmlStrEqual(name, BAD_CAST "svg"))
+ }
+ else
+ {
+ parser.path_.push_attr();
+ parse_attr(parser, node);
+ if (parser.path_.display())
{
- MAPNIK_LOG_WARN(svg_parser) << "svg_parser: Unhandled svg element=" << name;
+ if (std::strcmp(name, "path") == 0)
+ {
+ parse_path(parser, node);
+ }
+ else if (std::strcmp("polygon", name) == 0)
+ {
+ parse_polygon(parser, node);
+ }
+ else if (std::strcmp("polyline", name) == 0)
+ {
+ parse_polyline(parser, node);
+ }
+ else if (std::strcmp(name, "line") == 0)
+ {
+ parse_line(parser, node);
+ }
+ else if (std::strcmp(name, "rect") == 0)
+ {
+ parse_rect(parser, node);
+ }
+ else if (std::strcmp(name, "circle") == 0)
+ {
+ parse_circle(parser, node);
+ }
+ else if (std::strcmp(name, "ellipse") == 0)
+ {
+ parse_ellipse(parser, node);
+ }
+ else if (std::strcmp(name, "svg") == 0)
+ {
+ parse_dimensions(parser, node);
+ }
+ else
+ {
+ //std::cerr << "unprocessed node <--[" << node->name() << "]\n";
+ }
}
-#endif
+ parser.path_.pop_attr();
}
- parser.path_.pop_attr();
}
- }
-}
-
-void end_element(svg_parser & parser, xmlTextReaderPtr reader)
-{
- const xmlChar *name;
- name = xmlTextReaderConstName(reader);
+ for (auto const* child = node->first_node();
+ child; child = child->next_sibling())
+ {
+ traverse_tree(parser, child);
+ }
- if (!parser.is_defs_ && xmlStrEqual(name, BAD_CAST "g"))
- {
- parser.path_.pop_attr();
+ end_element(parser, node);
}
- else if (xmlStrEqual(name, BAD_CAST "defs"))
+ break;
+#if 0 //
+ // Data nodes
+ case rapidxml::node_data:
+ case rapidxml::node_cdata:
{
- parser.is_defs_ = false;
+
+ 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;
+ }
}
- else if ((xmlStrEqual(name, BAD_CAST "linearGradient")) || (xmlStrEqual(name, BAD_CAST "radialGradient")))
- {
- parser.gradient_map_[parser.temporary_gradient_.first] = parser.temporary_gradient_.second;
+ break;
+#endif
+ default:
+ break;
}
-
+ return true;
}
-void process_node(svg_parser & parser, xmlTextReaderPtr reader)
+
+void end_element(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- int node_type = xmlTextReaderNodeType(reader);
- switch (node_type)
+ auto const* name = node->name();
+ if (!parser.is_defs_ && std::strcmp(name, "g") == 0)
{
- case 1: //start element
- start_element(parser,reader);
- break;
- case 15:// end element
- end_element(parser,reader);
- break;
- default:
- break;
+ if (node->first_node() != nullptr)
+ {
+ parser.path_.pop_attr();
+ }
+ }
+ else if (std::strcmp(name, "defs") == 0)
+ {
+ if (node->first_node() != nullptr)
+ {
+ parser.is_defs_ = false;
+ }
+ }
+ else if (std::strcmp(name, "linearGradient") == 0 || std::strcmp(name, "radialGradient") == 0)
+ {
+ parser.gradient_map_[parser.temporary_gradient_.first] = parser.temporary_gradient_.second;
}
}
-void parse_attr(svg_parser & parser, const xmlChar * name, const xmlChar * value )
+void parse_attr(svg_parser & parser, char const* name, char const* value )
{
- if (xmlStrEqual(name, BAD_CAST "transform"))
+ if (std::strcmp(name, "transform") == 0)
{
agg::trans_affine tr;
- mapnik::svg::parse_svg_transform((const char*) value,tr);
+ mapnik::svg::parse_svg_transform(value,tr);
parser.path_.transform().premultiply(tr);
}
- else if (xmlStrEqual(name, BAD_CAST "fill"))
+ else if (std::strcmp(name, "fill") == 0)
{
- if (xmlStrEqual(value, BAD_CAST "none"))
+ std::string id;
+ if (std::strcmp(value, "none") == 0)
{
parser.path_.fill_none();
}
- else if (boost::starts_with((const char*)value, "url(#"))
+ else if (parse_id_from_url(value, id))
{
// see if we have a known gradient fill
- std::string id = std::string((const char*)&value[5]);
- // get rid of the trailing )
- id.erase(id.end()-1);
if (parser.gradient_map_.count(id) > 0)
{
parser.path_.add_fill_gradient(parser.gradient_map_[id]);
}
else
{
- MAPNIK_LOG_ERROR(svg_parser) << "Failed to find gradient fill: " << id;
+ std::stringstream ss;
+ ss << "Failed to find gradient fill: " << id;
+ parser.error_messages_.push_back(ss.str());
}
}
else
{
- parser.path_.fill(parse_color((const char*) value));
+ parser.path_.fill(parse_color_agg(parser.error_messages_, value));
}
}
- else if (xmlStrEqual(name, BAD_CAST "fill-opacity"))
+ else if (std::strcmp(name,"fill-opacity") == 0)
{
- parser.path_.fill_opacity(parse_double((const char*) value));
+ parser.path_.fill_opacity(parse_double(parser.error_messages_, value));
}
- else if (xmlStrEqual(name, BAD_CAST "fill-rule"))
+ else if (std::strcmp(name, "fill-rule") == 0)
{
- if (xmlStrEqual(value, BAD_CAST "evenodd"))
+ if (std::strcmp(value, "evenodd") == 0)
{
parser.path_.even_odd(true);
}
}
- else if (xmlStrEqual(name, BAD_CAST "stroke"))
+ else if (std::strcmp(name, "stroke") == 0)
{
- if (xmlStrEqual(value, BAD_CAST "none"))
+ std::string id;
+ if (std::strcmp(value, "none") == 0)
{
parser.path_.stroke_none();
}
- else if (boost::starts_with((const char*)value, "url(#"))
+ else if (parse_id_from_url(value, id))
{
// see if we have a known gradient fill
- std::string id = std::string((const char*)&value[5]);
- // get rid of the trailing )
- id.erase(id.end()-1);
if (parser.gradient_map_.count(id) > 0)
{
parser.path_.add_stroke_gradient(parser.gradient_map_[id]);
}
else
{
- MAPNIK_LOG_ERROR(svg_parser) << "Failed to find gradient fill: " << id;
+ std::stringstream ss;
+ ss << "Failed to find gradient stroke: " << id;
+ parser.error_messages_.push_back(ss.str());
}
}
else
{
- parser.path_.stroke(parse_color((const char*) value));
+ parser.path_.stroke(parse_color_agg(parser.error_messages_, value));
}
}
- else if (xmlStrEqual(name, BAD_CAST "stroke-width"))
- {
- parser.path_.stroke_width(parse_double((const char*)value));
- }
- else if (xmlStrEqual(name, BAD_CAST "stroke-opacity"))
+ else if (std::strcmp(name, "stroke-width") == 0)
{
- parser.path_.stroke_opacity(parse_double((const char*)value));
+ parser.path_.stroke_width(parse_double(parser.error_messages_, value));
}
- else if(xmlStrEqual(name,BAD_CAST "stroke-width"))
+ else if (std::strcmp(name, "stroke-opacity") == 0)
{
- parser.path_.stroke_width(parse_double((const char*) value));
+ parser.path_.stroke_opacity(parse_double(parser.error_messages_, value));
}
- else if(xmlStrEqual(name,BAD_CAST "stroke-linecap"))
+ else if(std::strcmp(name, "stroke-linecap") == 0)
{
- if(xmlStrEqual(value,BAD_CAST "butt"))
+ if(std::strcmp(value, "butt") == 0)
parser.path_.line_cap(agg::butt_cap);
- else if(xmlStrEqual(value,BAD_CAST "round"))
+ else if(std::strcmp(value, "round") == 0)
parser.path_.line_cap(agg::round_cap);
- else if(xmlStrEqual(value,BAD_CAST "square"))
+ else if(std::strcmp(value, "square") == 0)
parser.path_.line_cap(agg::square_cap);
}
- else if(xmlStrEqual(name,BAD_CAST "stroke-linejoin"))
+ else if(std::strcmp(name, "stroke-linejoin") == 0)
{
- if(xmlStrEqual(value,BAD_CAST "miter"))
+ if(std::strcmp(value, "miter") == 0)
parser.path_.line_join(agg::miter_join);
- else if(xmlStrEqual(value,BAD_CAST "round"))
+ else if(std::strcmp(value, "round") == 0)
parser.path_.line_join(agg::round_join);
- else if(xmlStrEqual(value,BAD_CAST "bevel"))
+ else if(std::strcmp(value, "bevel") == 0)
parser.path_.line_join(agg::bevel_join);
}
- else if(xmlStrEqual(name,BAD_CAST "stroke-miterlimit"))
+ else if(std::strcmp(name, "stroke-miterlimit") == 0)
{
- parser.path_.miter_limit(parse_double((const char*)value));
+ parser.path_.miter_limit(parse_double(parser.error_messages_,value));
}
- else if(xmlStrEqual(name, BAD_CAST "opacity"))
+ else if(std::strcmp(name, "opacity") == 0)
{
- double opacity = parse_double((const char*)value);
+ double opacity = parse_double(parser.error_messages_, value);
parser.path_.opacity(opacity);
}
- else if (xmlStrEqual(name, BAD_CAST "visibility"))
+ else if (std::strcmp(name, "visibility") == 0)
{
- parser.path_.visibility(!xmlStrEqual(value, BAD_CAST "hidden"));
+ parser.path_.visibility(std::strcmp(value, "hidden") != 0);
}
- else if (xmlStrEqual(name, BAD_CAST "display") && xmlStrEqual(value, BAD_CAST "none"))
+ else if (std::strcmp(name, "display") == 0 && std::strcmp(value, "none") == 0)
{
parser.path_.display(false);
}
}
-void parse_attr(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_attr(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- const xmlChar *name, *value;
-
- if (xmlTextReaderMoveToFirstAttribute(reader) == 1)
+ for (rapidxml::xml_attribute<char> const* attr = node->first_attribute();
+ attr; attr = attr->next_attribute())
{
- do
+ auto const* name = attr->name();
+ if (std::strcmp(name, "style") == 0)
{
- name = xmlTextReaderConstName(reader);
- value = xmlTextReaderConstValue(reader);
-
- if (xmlStrEqual(name, BAD_CAST "style"))
- {
- using cont_type = std::vector<std::pair<std::string,std::string> >;
- using value_type = cont_type::value_type;
- cont_type vec;
- parse_style((const char*)value, vec);
- for (value_type kv : vec )
- {
- parse_attr(parser,BAD_CAST kv.first.c_str(),BAD_CAST kv.second.c_str());
- }
- }
- else
+ using cont_type = std::vector<std::pair<std::string,std::string> >;
+ using value_type = cont_type::value_type;
+ cont_type vec;
+ parse_style(attr->value(), vec);
+ for (value_type kv : vec )
{
- parse_attr(parser,name,value);
+ parse_attr(parser, kv.first.c_str(), kv.second.c_str());
}
- } while(xmlTextReaderMoveToNextAttribute(reader) == 1);
+ }
+ else
+ {
+ parse_attr(parser,name, attr->value());
+ }
}
- xmlTextReaderMoveToElement(reader);
}
-void parse_dimensions(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_dimensions(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- xmlChar *value;
double width = 0;
double height = 0;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "width");
- if (value)
+ auto const* width_attr = node->first_attribute("width");
+ if (width_attr)
{
- width = parse_double((const char*)value);
- xmlFree(value);
+ width = parse_double(parser.error_messages_, width_attr->value());
}
- xmlChar *value2;
- value2 = xmlTextReaderGetAttribute(reader, BAD_CAST "width");
- if (value2)
+ auto const* height_attr = node->first_attribute("height");
+ if (height_attr)
{
- height = parse_double((const char*)value2);
- xmlFree(value2);
+ height = parse_double(parser.error_messages_, height_attr->value());
}
- parser.path_.set_dimensions(width,height);
-
+ parser.path_.set_dimensions(width, height);
}
-void parse_path(svg_parser & parser, xmlTextReaderPtr reader)
-{
- xmlChar *value;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "d");
- if (value)
+void parse_path(svg_parser & parser, rapidxml::xml_node<char> const* node)
+{
+ auto const* attr = node->first_attribute("d");
+ if (attr != nullptr)
{
- // d="" (empty paths) are valid
- if (std::strlen((const char*)value) < 1)
- {
- xmlFree(value);
- }
- else
+ auto const* value = attr->value();
+ if (std::strlen(value) > 0)
{
parser.path_.begin_path();
- if (!mapnik::svg::parse_path((const char*) value, parser.path_))
+ if (!mapnik::svg::parse_path(value, parser.path_))
{
- xmlFree(value);
- xmlChar *id_value;
- id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "xml:id");
- if (!id_value) id_value = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
- if (id_value)
+ auto const* id_attr = node->first_attribute("xml:id");
+ if (id_attr == nullptr) id_attr = node->first_attribute("id");
+ if (id_attr)
{
- std::string id_string((const char *) id_value);
- xmlFree(id_value);
- throw std::runtime_error(std::string("unable to parse invalid svg <path> with id '") + id_string + "'");
+ parser.error_messages_.push_back(std::string("unable to parse invalid svg <path> with id '")
+ + id_attr->value() + "'");
}
else
{
- throw std::runtime_error("unable to parse invalid svg <path>");
+ parser.error_messages_.push_back(std::string("unable to parse invalid svg <path>"));
}
}
parser.path_.end_path();
- xmlFree(value);
}
}
}
-void parse_polygon(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_polygon(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- xmlChar *value;
-
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "points");
- if (value)
+ auto const* attr = node->first_attribute("points");
+ if (attr != nullptr)
{
parser.path_.begin_path();
- if (!mapnik::svg::parse_points((const char*) value, parser.path_))
+ if (!mapnik::svg::parse_points(attr->value(), parser.path_))
{
- xmlFree(value);
- throw std::runtime_error("Failed to parse <polygon>");
+ parser.error_messages_.push_back(std::string("Failed to parse <polygon> 'points'"));
}
parser.path_.close_subpath();
parser.path_.end_path();
- xmlFree(value);
}
}
-void parse_polyline(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_polyline(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- xmlChar *value;
-
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "points");
- if (value)
+ auto const* attr = node->first_attribute("points");
+ if (attr != nullptr)
{
parser.path_.begin_path();
- if (!mapnik::svg::parse_points((const char*) value, parser.path_))
+ if (!mapnik::svg::parse_points(attr->value(), parser.path_))
{
- xmlFree(value);
- throw std::runtime_error("Failed to parse <polygon>");
+ parser.error_messages_.push_back(std::string("Failed to parse <polyline> 'points'"));
}
-
parser.path_.end_path();
- xmlFree(value);
}
}
-void parse_line(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_line(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- xmlChar *value;
double x1 = 0.0;
double y1 = 0.0;
double x2 = 0.0;
double y2 = 0.0;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1");
- if (value)
- {
- x1 = parse_double((const char*)value);
- xmlFree(value);
- }
+ auto const* x1_attr = node->first_attribute("x1");
+ if (x1_attr) x1 = parse_double(parser.error_messages_, x1_attr->value());
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1");
- if (value)
- {
- y1 = parse_double((const char*)value);
- xmlFree(value);
- }
+ auto const* y1_attr = node->first_attribute("y1");
+ if (y1_attr) y1 = parse_double(parser.error_messages_, y1_attr->value());
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2");
- if (value)
- {
- x2 = parse_double((const char*)value);
- xmlFree(value);
- }
+ auto const* x2_attr = node->first_attribute("x2");
+ if (x2_attr) x2 = parse_double(parser.error_messages_, x2_attr->value());
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2");
- if (value)
- {
- y2 = parse_double((const char*)value);
- xmlFree(value);
- }
+ auto const* y2_attr = node->first_attribute("y2");
+ if (y2_attr) y2 = parse_double(parser.error_messages_, y2_attr->value());
parser.path_.begin_path();
parser.path_.move_to(x1, y1);
parser.path_.line_to(x2, y2);
parser.path_.end_path();
-
}
-void parse_circle(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_circle(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- xmlChar *value;
double cx = 0.0;
double cy = 0.0;
double r = 0.0;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx");
- if (value)
+
+ auto * attr = node->first_attribute("cx");
+ if (attr != nullptr)
{
- cx = parse_double((const char*)value);
- xmlFree(value);
+ cx = parse_double(parser.error_messages_, attr->value());
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy");
- if (value)
+ attr = node->first_attribute("cy");
+ if (attr != nullptr)
{
- cy = parse_double((const char*)value);
- xmlFree(value);
+ cy = parse_double(parser.error_messages_, attr->value());
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "r");
- if (value)
+ attr = node->first_attribute("r");
+ if (attr != nullptr)
{
- r = parse_double((const char*)value);
- xmlFree(value);
+ r = parse_double(parser.error_messages_, attr->value());
}
parser.path_.begin_path();
-
if(r != 0.0)
{
- if(r < 0.0) throw std::runtime_error("parse_circle: Invalid radius");
- agg::ellipse c(cx, cy, r, r);
- parser.path_.storage().concat_path(c);
+ if (r < 0.0)
+ {
+ parser.error_messages_.emplace_back("parse_circle: Invalid radius");
+ }
+ else
+ {
+ agg::ellipse c(cx, cy, r, r);
+ parser.path_.storage().concat_path(c);
+ }
}
-
parser.path_.end_path();
}
-void parse_ellipse(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_ellipse(svg_parser & parser, rapidxml::xml_node<char> const * node)
{
- xmlChar *value;
double cx = 0.0;
double cy = 0.0;
double rx = 0.0;
double ry = 0.0;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx");
- if (value)
+ auto * attr = node->first_attribute("cx");
+ if (attr != nullptr)
{
- cx = parse_double((const char*)value);
- xmlFree(value);
+ cx = parse_double(parser.error_messages_, attr->value());
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy");
- if (value)
+ attr = node->first_attribute("cy");
+ if (attr)
{
- cy = parse_double((const char*)value);
- xmlFree(value);
+ cy = parse_double(parser.error_messages_, attr->value());
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "rx");
- if (value)
+ attr = node->first_attribute("rx");
+ if (attr != nullptr)
{
- rx = parse_double((const char*)value);
- xmlFree(value);
+ rx = parse_double(parser.error_messages_, attr->value());
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "ry");
- if (value)
+ attr = node->first_attribute("ry");
+ if (attr != nullptr)
{
- ry = parse_double((const char*)value);
- xmlFree(value);
+ ry = parse_double(parser.error_messages_, attr->value());
}
- parser.path_.begin_path();
-
- if(rx != 0.0 && ry != 0.0)
+ if (rx != 0.0 && ry != 0.0)
{
- if(rx < 0.0) throw std::runtime_error("parse_ellipse: Invalid rx");
- if(ry < 0.0) throw std::runtime_error("parse_ellipse: Invalid ry");
- agg::ellipse c(cx, cy, rx, ry);
- parser.path_.storage().concat_path(c);
- }
-
- parser.path_.end_path();
+ if (rx < 0.0)
+ {
+ parser.error_messages_.emplace_back("parse_ellipse: Invalid rx");
+ }
+ else if (ry < 0.0)
+ {
+ parser.error_messages_.emplace_back("parse_ellipse: Invalid ry");
+ }
+ else
+ {
+ parser.path_.begin_path();
+ agg::ellipse c(cx, cy, rx, ry);
+ parser.path_.storage().concat_path(c);
+ parser.path_.end_path();
+ }
+ }
}
-void parse_rect(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_rect(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
// http://www.w3.org/TR/SVGTiny12/shapes.html#RectElement
- xmlChar *value;
double x = 0.0;
double y = 0.0;
double w = 0.0;
@@ -689,187 +662,155 @@ void parse_rect(svg_parser & parser, xmlTextReaderPtr reader)
double rx = 0.0;
double ry = 0.0;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "x");
- if (value)
+ auto * attr = node->first_attribute("x");
+ if (attr != nullptr)
{
- x = parse_double((const char*)value);
- xmlFree(value);
+ x = parse_double(parser.error_messages_, attr->value());
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "y");
- if (value)
+ attr = node->first_attribute("y");
+ if (attr != nullptr)
{
- y = parse_double((const char*)value);
- xmlFree(value);
+ y = parse_double(parser.error_messages_, attr->value());
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "width");
- if (value)
+ attr = node->first_attribute("width");
+ if (attr != nullptr)
{
- w = parse_double((const char*)value);
- xmlFree(value);
+ w = parse_double(parser.error_messages_, attr->value());
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "height");
- if (value)
+ attr = node->first_attribute("height");
+ if (attr)
{
- h = parse_double((const char*)value);
- xmlFree(value);
+ h = parse_double(parser.error_messages_, attr->value());
}
bool rounded = true;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "rx");
- if (value)
+ attr = node->first_attribute("rx");
+ if (attr != nullptr)
{
- rx = parse_double((const char*)value);
+ rx = parse_double(parser.error_messages_, attr->value());
if ( rx > 0.5 * w ) rx = 0.5 * w;
- xmlFree(value);
}
else rounded = false;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "ry");
- if (value)
+ attr = node->first_attribute("ry");
+ if (attr != nullptr)
{
- ry = parse_double((const char*)value);
+ ry = parse_double(parser.error_messages_, attr->value());
if ( ry > 0.5 * h ) ry = 0.5 * h;
if (!rounded)
{
rx = ry;
rounded = true;
}
- xmlFree(value);
}
else if (rounded)
{
ry = rx;
}
- if(w != 0.0 && h != 0.0)
+ if (w != 0.0 && h != 0.0)
{
- if(w < 0.0) throw std::runtime_error("parse_rect: Invalid width");
- if(h < 0.0) throw std::runtime_error("parse_rect: Invalid height");
- if(rx < 0.0) throw std::runtime_error("parse_rect: Invalid rx");
- if(ry < 0.0) throw std::runtime_error("parse_rect: Invalid ry");
- parser.path_.begin_path();
-
- if(rounded)
+ if(w < 0.0)
+ {
+ parser.error_messages_.emplace_back("parse_rect: Invalid width");
+ }
+ else if(h < 0.0)
+ {
+ parser.error_messages_.emplace_back("parse_rect: Invalid height");
+ }
+ else if(rx < 0.0)
{
- agg::rounded_rect r;
- r.rect(x,y,x+w,y+h);
- r.radius(rx,ry);
- parser.path_.storage().concat_path(r);
+ parser.error_messages_.emplace_back("parse_rect: Invalid rx");
+ }
+ else if(ry < 0.0)
+ {
+ parser.error_messages_.emplace_back("parse_rect: Invalid ry");
}
else
{
- parser.path_.move_to(x, y);
- parser.path_.line_to(x + w, y);
- parser.path_.line_to(x + w, y + h);
- parser.path_.line_to(x, y + h);
- parser.path_.close_subpath();
+ parser.path_.begin_path();
+
+ if(rounded)
+ {
+ agg::rounded_rect r;
+ r.rect(x,y,x+w,y+h);
+ r.radius(rx,ry);
+ parser.path_.storage().concat_path(r);
+ }
+ else
+ {
+ parser.path_.move_to(x, y);
+ parser.path_.line_to(x + w, y);
+ parser.path_.line_to(x + w, y + h);
+ parser.path_.line_to(x, y + h);
+ parser.path_.close_subpath();
+ }
+ parser.path_.end_path();
}
- parser.path_.end_path();
}
}
-
-/*
- * <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="1"
- id="stop3763" />
-*/
-void parse_gradient_stop(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_gradient_stop(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- xmlChar *value;
-
double offset = 0.0;
mapnik::color stop_color;
double opacity = 1.0;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "offset");
- if (value)
+ auto * attr = node->first_attribute("offset");
+ if (attr != nullptr)
{
- offset = parse_double((const char*)value);
- xmlFree(value);
+ offset = parse_double(parser.error_messages_,attr->value());
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "style");
- if (value)
+ attr = node->first_attribute("style");
+ if (attr != nullptr)
{
using cont_type = std::vector<std::pair<std::string,std::string> >;
using value_type = cont_type::value_type;
cont_type vec;
- parse_style((const char*)value, vec);
+ parse_style(attr->value(), vec);
for (value_type kv : vec )
{
if (kv.first == "stop-color")
{
- try
- {
- stop_color = mapnik::parse_color(kv.second.c_str());
- }
- catch (mapnik::config_error const& ex)
- {
- MAPNIK_LOG_ERROR(svg_parser) << ex.what();
- }
+ stop_color = parse_color(parser.error_messages_, kv.second.c_str());
}
else if (kv.first == "stop-opacity")
{
- opacity = parse_double(kv.second.c_str());
+ opacity = parse_double(parser.error_messages_,kv.second.c_str());
}
}
- xmlFree(value);
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-color");
- if (value)
+ attr = node->first_attribute("stop-color");
+ if (attr != nullptr)
{
- try
- {
- stop_color = mapnik::parse_color((const char *) value);
- }
- catch (mapnik::config_error const& ex)
- {
- MAPNIK_LOG_ERROR(svg_parser) << ex.what();
- }
- xmlFree(value);
+ stop_color = parse_color(parser.error_messages_, attr->value());
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "stop-opacity");
- if (value)
+ attr = node->first_attribute("stop-opacity");
+ if (attr != nullptr)
{
- opacity = parse_double((const char *) value);
- xmlFree(value);
+ opacity = parse_double(parser.error_messages_, attr->value());
}
-
stop_color.set_alpha(static_cast<uint8_t>(opacity * 255));
parser.temporary_gradient_.second.add_stop(offset, stop_color);
-
- /*
- MAPNIK_LOG_DEBUG(svg_parser) << "\tFound Stop: " << offset << " "
- << (unsigned)stop_color.red() << " "
- << (unsigned)stop_color.green() << " "
- << (unsigned)stop_color.blue() << " "
- << (unsigned)stop_color.alpha();
- */
}
-bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader)
+bool parse_common_gradient(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- xmlChar *value;
-
std::string id;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "xml:id");
- if (!value) value = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
+ auto * attr = node->first_attribute("xml:id");
+ if (attr == nullptr) attr = node->first_attribute("id");
- if (value)
+ if (attr != nullptr)
{
// start a new gradient
- gradient new_grad;
- id = std::string((const char *) value);
- parser.temporary_gradient_ = std::make_pair(id, new_grad);
- xmlFree(value);
+ parser.temporary_gradient_ = std::make_pair(std::string(attr->value()), gradient());
}
else
{
@@ -878,28 +819,31 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader)
}
// check if we should inherit from another tag
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "xlink:href");
- if (value)
+ attr = node->first_attribute("xlink:href");
+ if (attr != nullptr)
{
- if (value[0] == '#')
+ auto const* value = attr->value();
+ if (std::strlen(value) > 1 && value[0] == '#')
{
- std::string linkid = (const char *) &value[1];
+ std::string linkid(&value[1]); // FIXME !!!
if (parser.gradient_map_.count(linkid))
{
parser.temporary_gradient_.second = parser.gradient_map_[linkid];
}
else
{
- MAPNIK_LOG_ERROR(svg_parser) << "Failed to find linked gradient " << linkid;
+ std::stringstream ss;
+ ss << "Failed to find linked gradient " << linkid;
+ parser.error_messages_.push_back(ss.str());
+ return false;
}
}
- xmlFree(value);
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "gradientUnits");
- if (value)
+ attr = node->first_attribute("gradientUnits");
+ if (attr != nullptr)
{
- if (xmlStrEqual(value, BAD_CAST "userSpaceOnUse"))
+ if (std::strcmp(attr->value(), "userSpaceOnUse") == 0)
{
parser.temporary_gradient_.second.set_units(USER_SPACE_ON_USE);
}
@@ -907,39 +851,21 @@ bool parse_common_gradient(svg_parser & parser, xmlTextReaderPtr reader)
{
parser.temporary_gradient_.second.set_units(OBJECT_BOUNDING_BOX);
}
- xmlFree(value);
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "gradientTransform");
- if (value)
+ attr = node->first_attribute("gradientTransform");
+ if (attr != nullptr)
{
agg::trans_affine tr;
- mapnik::svg::parse_svg_transform((const char*) value,tr);
+ mapnik::svg::parse_svg_transform(attr->value(),tr);
parser.temporary_gradient_.second.set_transform(tr);
- xmlFree(value);
}
-
return true;
}
-/**
- * <radialGradient
- collect="always"
- xlink:href="#linearGradient3759"
- id="radialGradient3765"
- cx="-1.2957155"
- cy="-21.425594"
- fx="-1.2957155"
- fy="-21.425594"
- r="5.1999998"
- gradientUnits="userSpaceOnUse" />
-*/
-void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- if (!parse_common_gradient(parser,reader))
- return;
-
- xmlChar *value;
+ parse_common_gradient(parser, node);
double cx = 0.5;
double cy = 0.5;
double fx = 0.0;
@@ -947,43 +873,38 @@ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader)
double r = 0.5;
bool has_percent=true;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "cx");
- if (value)
+ auto * attr = node->first_attribute("cx");
+ if (attr != nullptr)
{
- cx = parse_double_optional_percent((const char*)value, has_percent);
- xmlFree(value);
+ cx = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "cy");
- if (value)
+ attr = node->first_attribute("cy");
+ if (attr != nullptr)
{
- cy = parse_double_optional_percent((const char*)value, has_percent);
- xmlFree(value);
+ cy = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "fx");
- if (value)
+ attr = node->first_attribute("fx");
+ if (attr != nullptr)
{
- fx = parse_double_optional_percent((const char*)value, has_percent);
- xmlFree(value);
+ fx = parse_double_optional_percent(parser.error_messages_,attr->value(), has_percent);
}
else
fx = cx;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "fy");
- if (value)
+ attr = node->first_attribute("fy");
+ if (attr != nullptr)
{
- fy = parse_double_optional_percent((const char*)value, has_percent);
- xmlFree(value);
+ fy = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
}
else
fy = cy;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "r");
- if (value)
+ attr = node->first_attribute("r");
+ if (attr != nullptr)
{
- r = parse_double_optional_percent((const char*)value, has_percent);
- xmlFree(value);
+ r = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
}
// this logic for detecting %'s will not support mixed coordinates.
if (has_percent && parser.temporary_gradient_.second.get_units() == USER_SPACE_ON_USE)
@@ -999,44 +920,38 @@ void parse_radial_gradient(svg_parser & parser, xmlTextReaderPtr reader)
//MAPNIK_LOG_DEBUG(svg_parser) << "Found Radial Gradient: " << " " << cx << " " << cy << " " << fx << " " << fy << " " << r;
}
-void parse_linear_gradient(svg_parser & parser, xmlTextReaderPtr reader)
+void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- if (!parse_common_gradient(parser,reader))
- return;
+ parse_common_gradient(parser, node);
- xmlChar *value;
double x1 = 0.0;
double x2 = 1.0;
double y1 = 0.0;
double y2 = 1.0;
bool has_percent=true;
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "x1");
- if (value)
+ auto * attr = node->first_attribute("x1");
+ if (attr != nullptr)
{
- x1 = parse_double_optional_percent((const char*)value, has_percent);
- xmlFree(value);
+ x1 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "x2");
- if (value)
+ attr = node->first_attribute("x2");
+ if (attr != nullptr)
{
- x2 = parse_double_optional_percent((const char*)value, has_percent);
- xmlFree(value);
+ x2 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "y1");
- if (value)
+ attr = node->first_attribute("y1");
+ if (attr != nullptr)
{
- y1 = parse_double_optional_percent((const char*)value, has_percent);
- xmlFree(value);
+ y1 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
}
- value = xmlTextReaderGetAttribute(reader, BAD_CAST "y2");
- if (value)
+ attr = node->first_attribute("y2");
+ if (attr != nullptr)
{
- y2 = parse_double_optional_percent((const char*)value, has_percent);
- xmlFree(value);
+ y2 = parse_double_optional_percent(parser.error_messages_, attr->value(), has_percent);
}
// this logic for detecting %'s will not support mixed coordinates.
if (has_percent && parser.temporary_gradient_.second.get_units() == USER_SPACE_ON_USE)
@@ -1048,8 +963,6 @@ void parse_linear_gradient(svg_parser & parser, xmlTextReaderPtr reader)
parser.temporary_gradient_.second.set_control_points(x1,y1,x2,y2);
// add this here in case we have no end tag, will be replaced if we do
parser.gradient_map_[parser.temporary_gradient_.first] = parser.temporary_gradient_.second;
-
- //MAPNIK_LOG_DEBUG(svg_parser) << "Found Linear Gradient: " << "(" << x1 << " " << y1 << "),(" << x2 << " " << y2 << ")";
}
svg_parser::svg_parser(svg_converter<svg_path_adapter,
@@ -1059,32 +972,72 @@ svg_parser::svg_parser(svg_converter<svg_path_adapter,
svg_parser::~svg_parser() {}
-void svg_parser::parse(std::string const& filename)
+bool svg_parser::parse(std::string const& filename)
{
- xmlTextReaderPtr reader = xmlNewTextReaderFilename(filename.c_str());
- if (reader == nullptr)
+ std::basic_ifstream<char> stream(filename.c_str());
+ if (!stream)
{
- MAPNIK_LOG_ERROR(svg_parser) << "Unable to open '" << filename << "'";
+ std::stringstream ss;
+ ss << "Unable to open '" << filename << "'";
+ error_messages_.push_back(ss.str());
+ return false;
}
- else if (!parse_reader(*this,reader))
+
+ stream.unsetf(std::ios::skipws);
+ std::vector<char> buffer(std::istreambuf_iterator<char>(stream.rdbuf()),
+ std::istreambuf_iterator<char>());
+ buffer.push_back(0);
+
+ const int flags = rapidxml::parse_trim_whitespace | rapidxml::parse_validate_closing_tags;
+ rapidxml::xml_document<> doc;
+ try
{
- MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << filename << "'";
+ doc.parse<flags>(buffer.data());
}
+ catch (rapidxml::parse_error const& ex)
+ {
+ std::stringstream ss;
+ ss << "svg_parser::parse - Unable to parse '" << filename << "'";
+ error_messages_.push_back(ss.str());
+ return false;
+ }
+
+ for (rapidxml::xml_node<char> const* child = doc.first_node();
+ child; child = child->next_sibling())
+ {
+ traverse_tree(*this, child);
+ }
+ return error_messages_.empty() ? true : false;
}
-void svg_parser::parse_from_string(std::string const& svg)
+bool svg_parser::parse_from_string(std::string const& svg)
{
- xmlTextReaderPtr reader = xmlReaderForMemory(svg.c_str(),safe_cast<int>(svg.size()),nullptr,nullptr,
- (XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | XML_PARSE_NOERROR | XML_PARSE_NOWARNING));
- if (reader == nullptr)
+ const int flags = rapidxml::parse_trim_whitespace | rapidxml::parse_validate_closing_tags;
+ rapidxml::xml_document<> doc;
+ std::vector<char> buffer(svg.begin(), svg.end());
+ buffer.push_back(0);
+ try
+ {
+ doc.parse<flags>(buffer.data());
+ }
+ catch (rapidxml::parse_error const& ex)
{
- MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << svg << "'";
+ std::stringstream ss;
+ ss << "Unable to parse '" << svg << "'";
+ error_messages_.push_back(ss.str());
+ return false;
}
- else if (!parse_reader(*this,reader))
+ for (rapidxml::xml_node<char> const* child = doc.first_node();
+ child; child = child->next_sibling())
{
- MAPNIK_LOG_ERROR(svg_parser) << "Unable to parse '" << svg << "'";
+ traverse_tree(*this, child);
}
+ return error_messages_.empty() ? true : false;
}
+svg_parser::error_message_container const& svg_parser::error_messages() const
+{
+ return error_messages_;
+}
}}
diff --git a/test/cleanup.hpp b/test/cleanup.hpp
index 264a6b5..f175e72 100644
--- a/test/cleanup.hpp
+++ b/test/cleanup.hpp
@@ -1,9 +1,11 @@
#ifndef TEST_MEMORY_CLEANUP
#define TEST_MEMORY_CLEANUP
+#if defined(HAVE_LIBXML2)
#include <libxml/parser.h>
#include <libxml/entities.h>
#include <libxml/globals.h>
+#endif
#if defined(HAVE_CAIRO)
#include <cairo.h>
@@ -21,6 +23,7 @@ inline void run_cleanup()
// only call this once, on exit
// to make sure valgrind output is clean
// http://xmlsoft.org/xmlmem.html
+#if defined(HAVE_LIBXML2)
xmlCleanupCharEncodingHandlers();
xmlCleanupEncodingAliases();
xmlCleanupGlobals();
@@ -29,6 +32,7 @@ inline void run_cleanup()
xmlCleanupInputCallbacks();
xmlCleanupOutputCallbacks();
xmlCleanupMemory();
+#endif
#if defined(HAVE_CAIRO)
// http://cairographics.org/manual/cairo-Error-handling.html#cairo-debug-reset-static-data
@@ -45,9 +49,9 @@ inline void run_cleanup()
#endif
// https://trac.osgeo.org/proj/wiki/ProjAPI#EnvironmentFunctions
pj_deallocate_grids();
-#endif
+#endif
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp
index 0c2752a..f33962d 100644
--- a/test/unit/svg/svg_parser_test.cpp
+++ b/test/unit/svg/svg_parser_test.cpp
@@ -27,11 +27,15 @@
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/vertex.hpp>
+#include <mapnik/svg/svg_parser.hpp>
+#include <mapnik/svg/svg_storage.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 <libxml/parser.h> // for xmlInitParser(), xmlCleanupParser()
+
#include <cmath>
+#include <fstream>
+#include <streambuf>
namespace detail {
@@ -51,7 +55,209 @@ struct vertex_equal
TEST_CASE("SVG parser") {
- xmlInitParser();
+ SECTION("SVG i/o")
+ {
+ mapnik::logger::instance().set_severity(mapnik::logger::none);
+ std::string svg_name("FAIL");
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ REQUIRE(marker);
+ REQUIRE(marker->is<mapnik::marker_null>());
+ mapnik::logger::instance().set_severity(mapnik::logger::error);
+ }
+
+ SECTION("SVG::parse i/o")
+ {
+ std::string svg_name("FAIL");
+
+ using namespace mapnik::svg;
+ mapnik::svg_storage_type path;
+ vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
+ svg_path_adapter svg_path(stl_storage);
+ svg_converter_type svg(svg_path, path.attributes());
+ svg_parser p(svg);
+
+ if (!p.parse(svg_name))
+ {
+ auto const& errors = p.error_messages();
+ REQUIRE(errors.size() == 1);
+ REQUIRE(errors[0] == "Unable to open 'FAIL'");
+ }
+ }
+
+ SECTION("SVG::parse_from_string syntax error")
+ {
+ std::string svg_name("./test/data/svg/invalid.svg");
+ std::ifstream in(svg_name.c_str());
+ std::string svg_str((std::istreambuf_iterator<char>(in)),
+ std::istreambuf_iterator<char>());
+
+ using namespace mapnik::svg;
+ mapnik::svg_storage_type path;
+ vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
+ svg_path_adapter svg_path(stl_storage);
+ svg_converter_type svg(svg_path, path.attributes());
+ svg_parser p(svg);
+
+ if (!p.parse_from_string(svg_str))
+ {
+ auto const& errors = p.error_messages();
+ REQUIRE(errors.size() == 1);
+ REQUIRE(errors[0] == "Unable to parse '<?xml version=\"1.0\"?>\n<svg width=\"12cm\" height=\"4cm\" viewBox=\"0 0 1200 400\"\nxmlns=\"http://www.w3.org/2000/svg\" version=\"1.2\" baseProfile=\"tiny\">\n'");
+ }
+ }
+
+ SECTION("SVG::parse_from_string syntax error")
+ {
+ std::string svg_name("./test/data/svg/invalid.svg");
+
+ using namespace mapnik::svg;
+ mapnik::svg_storage_type path;
+ vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
+ svg_path_adapter svg_path(stl_storage);
+ svg_converter_type svg(svg_path, path.attributes());
+ svg_parser p(svg);
+
+ if (!p.parse(svg_name))
+ {
+ auto const& errors = p.error_messages();
+ REQUIRE(errors.size() == 1);
+ REQUIRE(errors[0] == "svg_parser::parse - Unable to parse './test/data/svg/invalid.svg'");
+ }
+ }
+
+ SECTION("SVG parser color <fail>")
+ {
+
+ std::string svg_name("./test/data/svg/color_fail.svg");
+ std::ifstream in(svg_name.c_str());
+ std::string svg_str((std::istreambuf_iterator<char>(in)),
+ std::istreambuf_iterator<char>());
+
+ using namespace mapnik::svg;
+ mapnik::svg_storage_type path;
+ vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
+ svg_path_adapter svg_path(stl_storage);
+ svg_converter_type svg(svg_path, path.attributes());
+ svg_parser p(svg);
+
+ if (!p.parse_from_string(svg_str))
+ {
+ auto const& errors = p.error_messages();
+ REQUIRE(errors.size() == 3);
+ REQUIRE(errors[0] == "Failed to parse color: \"fail\"");
+ REQUIRE(errors[1] == "Failed to parse double: \"fail\"");
+ REQUIRE(errors[2] == "Failed to parse color: \"fail\"");
+ }
+ }
+
+ SECTION("SVG - cope with erroneous geometries")
+ {
+ std::string svg_name("./test/data/svg/errors.svg");
+ std::ifstream in(svg_name.c_str());
+ std::string svg_str((std::istreambuf_iterator<char>(in)),
+ std::istreambuf_iterator<char>());
+
+ using namespace mapnik::svg;
+ mapnik::svg_storage_type path;
+ vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
+ svg_path_adapter svg_path(stl_storage);
+ svg_converter_type svg(svg_path, path.attributes());
+ svg_parser p(svg);
+
+ if (!p.parse_from_string(svg_str))
+ {
+ auto const& errors = p.error_messages();
+ REQUIRE(errors.size() == 14);
+ REQUIRE(errors[0] == "parse_rect: Invalid width");
+ REQUIRE(errors[1] == "Failed to parse double: \"FAIL\"");
+ REQUIRE(errors[2] == "parse_rect: Invalid height");
+ REQUIRE(errors[3] == "parse_rect: Invalid rx");
+ REQUIRE(errors[4] == "parse_rect: Invalid ry");
+ REQUIRE(errors[5] == "unable to parse invalid svg <path>");
+ REQUIRE(errors[6] == "unable to parse invalid svg <path> with id 'fail-path'");
+ REQUIRE(errors[7] == "unable to parse invalid svg <path> with id 'fail-path'");
+ REQUIRE(errors[8] == "parse_circle: Invalid radius");
+ REQUIRE(errors[9] == "Failed to parse <polygon> 'points'");
+ REQUIRE(errors[10] == "Failed to parse <polyline> 'points'");
+ REQUIRE(errors[11] == "parse_ellipse: Invalid rx");
+ REQUIRE(errors[12] == "parse_ellipse: Invalid ry");
+ REQUIRE(errors[13] == "parse_rect: Invalid height");
+ }
+ }
+
+ SECTION("SVG parser double % <fail>")
+ {
+
+ std::string svg_name("./test/data/svg/gradient-radial-error.svg");
+ std::ifstream in(svg_name.c_str());
+ std::string svg_str((std::istreambuf_iterator<char>(in)),
+ std::istreambuf_iterator<char>());
+
+ using namespace mapnik::svg;
+ mapnik::svg_storage_type path;
+ vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
+ svg_path_adapter svg_path(stl_storage);
+ svg_converter_type svg(svg_path, path.attributes());
+ svg_parser p(svg);
+
+ if (!p.parse_from_string(svg_str))
+ {
+ auto const& errors = p.error_messages();
+ REQUIRE(errors.size() == 1);
+ REQUIRE(errors[0] == "Failed to parse double (optional %) from FAIL");
+ }
+ }
+
+ SECTION("SVG parser display=none")
+ {
+
+ std::string svg_name("./test/data/svg/invisible.svg");
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ REQUIRE(marker);
+ REQUIRE(marker->is<mapnik::marker_svg>());
+ mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
+ auto bbox = svg.bounding_box();
+ REQUIRE(bbox == mapnik::box2d<double>(0, 0, 1, 1));
+ auto storage = svg.get_data();
+ REQUIRE(storage);
+ mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
+ mapnik::svg::svg_path_adapter path(stl_storage);
+ double x,y;
+ REQUIRE(path.vertex(&x,&y) == mapnik::SEG_END);
+ }
+
+ SECTION("SVG parser stroke-linecap=square")
+ {
+
+ std::string svg_name("./test/data/svg/stroke-linecap-square.svg");
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ REQUIRE(marker);
+ REQUIRE(marker->is<mapnik::marker_svg>());
+ mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
+ auto bbox = svg.bounding_box();
+ REQUIRE(bbox == mapnik::box2d<double>(5, 60, 220, 60));
+ auto storage = svg.get_data();
+ REQUIRE(storage);
+ mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
+ mapnik::svg::svg_path_adapter path(stl_storage);
+
+ auto const& attrs = storage->attributes();
+ agg::line_cap_e expected_cap(agg::square_cap);
+ REQUIRE(attrs.size() == 1 );
+ REQUIRE(attrs[0].line_cap == expected_cap);
+
+ double x,y;
+ unsigned cmd;
+ std::vector<std::tuple<double,double,unsigned>> vec;
+ while ((cmd = path.vertex(&x,&y)) != mapnik::SEG_END)
+ {
+ vec.emplace_back(x, y, cmd);
+ }
+ std::vector<std::tuple<double,double,unsigned>> expected = { std::make_tuple(5, 60, 1),
+ std::make_tuple(220, 60, 2) };
+ REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin()));
+ }
+
SECTION("SVG <rect>")
{
//<rect width="20" height="15" style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)" />
@@ -129,6 +335,71 @@ TEST_CASE("SVG parser") {
REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(),detail::vertex_equal<3>()));
}
+ SECTION("SVG rounded <rect>s missing rx or ry")
+ {
+ std::string svg_name("./test/data/svg/rounded_rect-missing-one-radius.svg");
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ REQUIRE(marker);
+ REQUIRE(marker->is<mapnik::marker_svg>());
+ mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
+ auto bbox = svg.bounding_box();
+ REQUIRE(bbox == mapnik::box2d<double>(0, 0, 20, 15));
+ auto storage = svg.get_data();
+ REQUIRE(storage);
+ mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
+ mapnik::svg::svg_path_adapter path(stl_storage);
+ double x,y;
+ unsigned cmd;
+ std::vector<std::tuple<double,double,unsigned>> vec;
+
+ while ((cmd = path.vertex(&x,&y)) != mapnik::SEG_END)
+ {
+ vec.emplace_back(x, y, cmd);
+ }
+ std::vector<std::tuple<double,double,unsigned>> expected = {std::make_tuple(0, 5,1),
+ std::make_tuple(0.481856, 2.85842,2),
+ std::make_tuple(1.83455, 1.12961,2),
+ std::make_tuple(3.79736, 0.146789,2),
+ std::make_tuple(5, 0,2),
+ std::make_tuple(15, 0,2),
+ std::make_tuple(17.1416, 0.481856,2),
+ std::make_tuple(18.8704, 1.83455,2),
+ std::make_tuple(19.8532, 3.79736,2),
+ std::make_tuple(20, 5,2),
+ std::make_tuple(20, 10,2),
+ std::make_tuple(19.5181, 12.1416,2),
+ std::make_tuple(18.1654, 13.8704,2),
+ std::make_tuple(16.2026, 14.8532,2),
+ std::make_tuple(15, 15,2),
+ std::make_tuple(5, 15,2),
+ std::make_tuple(2.85842, 14.5181,2),
+ std::make_tuple(1.12961, 13.1654,2),
+ std::make_tuple(0.146789, 11.2026,2),
+ std::make_tuple(0, 10,2),
+ std::make_tuple(0, 10,95)};
+
+ REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin(),detail::vertex_equal<3>()));
+ }
+
+ SECTION("SVG beveled <rect>")
+ {
+ std::string svg_name("./test/data/svg/stroke-linejoin-bevel.svg");
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ REQUIRE(marker);
+ REQUIRE(marker->is<mapnik::marker_svg>());
+ mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
+ auto bbox = svg.bounding_box();
+ REQUIRE(bbox == mapnik::box2d<double>(10, 10, 30, 25));
+ auto storage = svg.get_data();
+ REQUIRE(storage);
+ mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
+
+ auto const& attrs = storage->attributes();
+ agg::line_join_e expected_join(agg::bevel_join);
+ REQUIRE(attrs.size() == 1 );
+ REQUIRE(attrs[0].line_join == expected_join);
+ }
+
SECTION("SVG <line>")
{
//
@@ -147,17 +418,11 @@ TEST_CASE("SVG parser") {
unsigned cmd;
std::vector<std::tuple<double,double,unsigned>> vec;
std::size_t num_vertices = path.total_vertices();
- //std::cerr << "Num vertices = " << num_vertices << std::endl;
- //std::cerr << "{";
for (std::size_t i = 0; i < num_vertices; ++i)
{
cmd = path.vertex(&x,&y);
vec.emplace_back(x, y, cmd);
- //if (vec.size() > 1) std::cerr << ",";
- //std::cerr << std::setprecision(6) << "std::make_tuple(" << x << ", " << y << ", " << cmd << ")";
}
- //std::cerr << "}" << std::endl;
-
std::vector<std::tuple<double,double,unsigned>> expected = {std::make_tuple(1, 1, 1),
std::make_tuple(1199, 1, 2),
std::make_tuple(1199, 399, 2),
@@ -291,5 +556,205 @@ TEST_CASE("SVG parser") {
REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin()));
}
- xmlCleanupParser();
+ SECTION("SVG <gradient>")
+ {
+ //
+ std::string svg_name("./test/data/svg/gradient.svg");
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ REQUIRE(marker);
+ REQUIRE(marker->is<mapnik::marker_svg>());
+ mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
+ auto bbox = svg.bounding_box();
+ REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,799.0,599.0));
+ auto storage = svg.get_data();
+ REQUIRE(storage);
+ mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
+ mapnik::svg::svg_path_adapter path(stl_storage);
+ double x,y;
+ unsigned cmd;
+ std::vector<std::tuple<double,double,unsigned>> vec;
+ std::size_t num_vertices = path.total_vertices();
+
+ for (std::size_t i = 0; i < num_vertices; ++i)
+ {
+ cmd = path.vertex(&x,&y);
+ vec.emplace_back(x, y, cmd);
+ }
+
+ std::vector<std::tuple<double,double,unsigned>> expected = {std::make_tuple(1, 1, 1),
+ std::make_tuple(799, 1, 2),
+ std::make_tuple(799, 599, 2),
+ std::make_tuple(1, 599, 2),
+ std::make_tuple(1, 1, 79),
+ std::make_tuple(0, 0, 0),
+ std::make_tuple(100, 100, 1),
+ std::make_tuple(700, 100, 2),
+ std::make_tuple(700, 300, 2),
+ std::make_tuple(100, 300, 2),
+ std::make_tuple(100, 100, 79),
+ std::make_tuple(0, 0, 0),
+ std::make_tuple(100, 320, 1),
+ std::make_tuple(700, 320, 2),
+ std::make_tuple(700, 520, 2),
+ std::make_tuple(100, 520, 2),
+ std::make_tuple(100, 320, 79)};
+
+ REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin()));
+ }
+
+ SECTION("SVG missing <gradient> def")
+ {
+ std::string svg_name("./test/data/svg/gradient-nodef.svg");
+ using namespace mapnik::svg;
+ mapnik::svg_storage_type path;
+ vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
+ svg_path_adapter svg_path(stl_storage);
+ svg_converter_type svg(svg_path, path.attributes());
+ svg_parser p(svg);
+ REQUIRE(!p.parse(svg_name));
+ auto const& errors = p.error_messages();
+ REQUIRE(errors.size() == 2);
+ REQUIRE(errors[0] == "Failed to find gradient fill: MyGradient");
+ REQUIRE(errors[1] == "Failed to find gradient stroke: MyGradient");
+ }
+
+ SECTION("SVG missing <gradient> id")
+ {
+
+ std::string svg_name("./test/data/svg/gradient-no-id.svg");
+ std::ifstream in(svg_name.c_str());
+ std::string svg_str((std::istreambuf_iterator<char>(in)),
+ std::istreambuf_iterator<char>());
+
+ using namespace mapnik::svg;
+ mapnik::svg_storage_type path;
+ vertex_stl_adapter<svg_path_storage> stl_storage(path.source());
+ svg_path_adapter svg_path(stl_storage);
+ svg_converter_type svg(svg_path, path.attributes());
+ svg_parser p(svg);
+
+ if (!p.parse_from_string(svg_str))
+ {
+ auto const& errors = p.error_messages();
+ REQUIRE(errors.size() == 2);
+ REQUIRE(errors[0] == "Failed to find gradient fill: MyGradient");
+ REQUIRE(errors[1] == "Failed to find gradient stroke: MyGradient");
+ }
+ }
+
+ SECTION("SVG missing <gradient> inheritance")
+ {
+ //
+ std::string svg_name("./test/data/svg/gradient-inherit.svg");
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ REQUIRE(marker);
+ REQUIRE(marker->is<mapnik::marker_svg>());
+ mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
+ auto bbox = svg.bounding_box();
+ REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,699.0,199.0));
+ auto storage = svg.get_data();
+ REQUIRE(storage);
+ mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
+
+ auto const& attrs = storage->attributes();
+ REQUIRE(attrs.size() == 3 );
+ REQUIRE(attrs[1].fill_gradient == attrs[2].fill_gradient);
+
+ mapnik::svg::svg_path_adapter path(stl_storage);
+ double x,y;
+ unsigned cmd;
+ std::vector<std::tuple<double,double,unsigned>> vec;
+ std::size_t num_vertices = path.total_vertices();
+ for (std::size_t i = 0; i < num_vertices; ++i)
+ {
+ cmd = path.vertex(&x,&y);
+ vec.emplace_back(x, y, cmd);
+ }
+
+ std::vector<std::tuple<double,double,unsigned>> expected = {std::make_tuple(1, 1, 1),
+ std::make_tuple(699, 1, 2),
+ std::make_tuple(699, 199, 2),
+ std::make_tuple(1, 199, 2),
+ std::make_tuple(1, 1, 79),
+ std::make_tuple(0, 0, 0),
+ std::make_tuple(100, 50, 1),
+ std::make_tuple(300, 50, 2),
+ std::make_tuple(300, 150, 2),
+ std::make_tuple(100, 150, 2),
+ std::make_tuple(100, 50, 79),
+ std::make_tuple(0, 0, 0),
+ std::make_tuple(400, 50, 1),
+ std::make_tuple(600, 50, 2),
+ std::make_tuple(600, 150, 2),
+ std::make_tuple(400, 150, 2),
+ std::make_tuple(400, 50, 79)};
+
+ REQUIRE(std::equal(expected.begin(),expected.end(), vec.begin()));
+ }
+
+ SECTION("SVG <gradient> with transformations")
+ {
+ //
+ std::string svg_name("./test/data/svg/gradient-transform.svg");
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ REQUIRE(marker);
+ REQUIRE(marker->is<mapnik::marker_svg>());
+ mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
+ auto bbox = svg.bounding_box();
+ REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,799.0,599.0));
+ auto storage = svg.get_data();
+ REQUIRE(storage);
+
+ auto const& attrs = storage->attributes();
+ REQUIRE(attrs.size() == 3 );
+ REQUIRE(attrs[1].fill_gradient == attrs[2].fill_gradient);
+ REQUIRE(attrs[1].fill_gradient.get_gradient_type() == mapnik::RADIAL);
+ agg::trans_affine transform;
+ transform *= agg::trans_affine_translation(240,155);
+ REQUIRE(attrs[1].fill_gradient.get_transform() == transform);
+ }
+
+ SECTION("SVG <gradient> with xlink:href")
+ {
+ std::string svg_name("./test/data/svg/gradient-xhref.svg");
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ REQUIRE(marker);
+ REQUIRE(marker->is<mapnik::marker_svg>());
+ mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
+ auto bbox = svg.bounding_box();
+ REQUIRE(bbox == mapnik::box2d<double>(20,20,460,230));
+ auto storage = svg.get_data();
+ REQUIRE(storage);
+
+ auto const& attrs = storage->attributes();
+ REQUIRE(attrs.size() == 2 );
+ REQUIRE(attrs[0].fill_gradient.get_gradient_type() == mapnik::LINEAR);
+ REQUIRE(attrs[1].fill_gradient.get_gradient_type() == mapnik::LINEAR);
+ REQUIRE(attrs[1].fill_gradient.has_stop());
+ }
+
+ SECTION("SVG <gradient> with radial percents")
+ {
+ std::string svg_name("./test/data/svg/gradient-radial-percents.svg");
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ REQUIRE(marker);
+ REQUIRE(marker->is<mapnik::marker_svg>());
+ mapnik::marker_svg const& svg = mapnik::util::get<mapnik::marker_svg>(*marker);
+ auto bbox = svg.bounding_box();
+ REQUIRE(bbox == mapnik::box2d<double>(0,0,200,200));
+ auto storage = svg.get_data();
+ REQUIRE(storage);
+
+ double x1, x2, y1, y2, r;
+ auto const& attrs = storage->attributes();
+ REQUIRE(attrs.size() == 1 );
+ REQUIRE(attrs[0].fill_gradient.get_gradient_type() == mapnik::RADIAL);
+ REQUIRE(attrs[0].fill_gradient.has_stop());
+ attrs[0].fill_gradient.get_control_points(x1, y1, x2, y2, r);
+ REQUIRE(x1 == 0);
+ REQUIRE(y1 == 0.25);
+ REQUIRE(x2 == 0.10);
+ REQUIRE(y2 == 0.10);
+ REQUIRE(r == 0.75);
+ }
}
diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp
index 21041b2..3e00663 100644
--- a/utils/svg2png/svg2png.cpp
+++ b/utils/svg2png/svg2png.cpp
@@ -48,8 +48,6 @@
#include "agg_pixfmt_rgba.h"
#include "agg_scanline_u.h"
-#include <libxml/parser.h> // for xmlInitParser(), xmlCleanupParser()
-
struct main_marker_visitor
{
main_marker_visitor(std::string const& svg_name,
@@ -59,18 +57,6 @@ struct main_marker_visitor
verbose_(verbose),
auto_open_(auto_open) {}
- int operator() (mapnik::marker_null const&)
- {
- std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n";
- return -1;
- }
-
- int operator() (mapnik::marker_rgba8 const&)
- {
- std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n";
- return -1;
- }
-
int operator() (mapnik::marker_svg const& marker)
{
using pixfmt = agg::pixfmt_rgba32_pre;
@@ -130,6 +116,14 @@ struct main_marker_visitor
return status;
}
+ // default
+ template <typename T>
+ int operator() (T const&)
+ {
+ std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n";
+ return -1;
+ }
+
private:
std::string const& svg_name_;
bool verbose_;
@@ -202,8 +196,6 @@ int main (int argc,char** argv)
return 0;
}
- xmlInitParser();
-
while (itr != svg_files.end())
{
std::string svg_name (*itr++);
@@ -217,16 +209,15 @@ int main (int argc,char** argv)
status = mapnik::util::apply_visitor(visitor, *marker);
}
}
+ catch (std::exception const& ex)
+ {
+ std::clog << "Exception caught:" << ex.what() << std::endl;
+ return -1;
+ }
catch (...)
{
std::clog << "Exception of unknown type!" << std::endl;
- xmlCleanupParser();
return -1;
}
-
- // only call this once, on exit
- // to make sure valgrind output is clean
- // http://xmlsoft.org/xmlmem.html
- xmlCleanupParser();
return status;
}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/mapnik.git
More information about the Pkg-grass-devel
mailing list