[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