[mapnik] 01/06: New upstream version 3.0.16~rc1+ds
Bas Couwenberg
sebastic at debian.org
Wed Nov 15 21:39:00 UTC 2017
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository mapnik.
commit a2cb23fc926faa284162453be0ee5b4440bd286b
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Wed Nov 15 21:08:21 2017 +0100
New upstream version 3.0.16~rc1+ds
---
.travis.yml | 4 +-
CHANGELOG.md | 19 +
SConstruct | 153 +++-
configure | 15 +-
include/mapnik/geom_util.hpp | 126 +--
.../json/extract_bounding_box_grammar_impl.hpp | 11 +-
include/mapnik/marker_cache.hpp | 2 +-
include/mapnik/svg/svg_parser.hpp | 60 +-
include/mapnik/text/harfbuzz_shaper.hpp | 4 +-
include/mapnik/unicode.hpp | 7 +
.../mapnik/{version.hpp => util/name_to_int.hpp} | 22 +-
include/mapnik/version.hpp | 2 +-
plugins/input/gdal/gdal_featureset.cpp | 2 +-
scripts/publish_release.sh | 2 +-
src/load_map.cpp | 37 +-
src/marker_cache.cpp | 21 +-
src/svg/svg_parser.cpp | 954 ++++++++++++++-------
src/text/renderer.cpp | 17 +-
src/wkb.cpp | 2 +
test/unit/svg/svg_parser_test.cpp | 203 +++--
utils/mapnik-config/build.py | 18 +-
utils/mapnik-config/mapnik-config.template.sh | 12 +-
utils/svg2png/svg2png.cpp | 41 +-
23 files changed, 1208 insertions(+), 526 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 3d19711..d2065e4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,6 +18,8 @@ cache:
directories:
- $HOME/.ccache
+dist: precise
+
matrix:
include:
- os: linux
@@ -61,7 +63,7 @@ before_install:
- export PATH=${PREFIX}/bin:$(pwd)/mason_packages/.link/bin:${PATH}
- export COVERAGE=${COVERAGE:-false}
- export BENCH=${BENCH:-false}
- - git_submodule_update --init --depth=10
+ - git_submodule_update --init
install:
- on 'osx' export DATA_PATH=$(brew --prefix)/var/postgres
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0e1ad8c..4930fd3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,24 @@ Developers: Please commit along with changes.
For a complete change history, see the git log.
+## 3.0.16
+
+Released: November xx, 2017
+
+(Packaged from xxxxxxx)
+
+ - Added "strict" SVG parsing mode with consistent error handling and disabled processing of unsupported attributes.
+ - Added support for `<use>` element.
+ - Implemented compile time string literal to integer conversion, to be able to convert large `if/else if/else` statements to `switch`.
+ - WKB reader - pre-allocate optimisations in `multi_polygon` and `geometry_collection`.
+ - Set alpha values in RGBA TIFFs even when `NODATA` value is pesent.
+ - Support building with ICU >= 59.
+ - SCons - added ICU_DATA, PROJ_LIB and GDAL_DATA settings, available via `mapnik-config`
+ - Fixed centroid and interior text placement algorithms (#3771)
+ - Fixed memory leak (#3775)
+ - SVG parser - fixed default gradient vector in linear gradient.
+ - Fixed bounding box collection logic (#3709)
+
## 3.0.15
Released: June 16, 2017
@@ -36,6 +54,7 @@ Released: June 5, 2017
- shapeindex - return error code when no features can read from shapefile (#3198)
- Upgrade Scons to `2.5.1`
- Fixed bug (typo) in `raster_featureset.cpp` (#3696)
+- Made `freetype_engine` singleton again. This allows for better control of its life-time. Original interface is preserved via adding static methods (#3688)
## 3.0.13
diff --git a/SConstruct b/SConstruct
index 0523e18..a3f725c 100644
--- a/SConstruct
+++ b/SConstruct
@@ -99,7 +99,10 @@ pretty_dep_names = {
'osm':'more info: https://github.com/mapnik/mapnik/wiki/OsmPlugin',
'boost_regex_icu':'libboost_regex built with optional ICU unicode support is needed for unicode regex support in mapnik.',
'sqlite_rtree':'The SQLite plugin requires libsqlite3 built with RTREE support (-DSQLITE_ENABLE_RTREE=1)',
- 'pgsql2sqlite_rtree':'The pgsql2sqlite program requires libsqlite3 built with RTREE support (-DSQLITE_ENABLE_RTREE=1)'
+ 'pgsql2sqlite_rtree':'The pgsql2sqlite program requires libsqlite3 built with RTREE support (-DSQLITE_ENABLE_RTREE=1)',
+ 'PROJ_LIB':'The directory where proj4 stores its data files. Must exist for proj4 to work correctly',
+ 'GDAL_DATA':'The directory where GDAL stores its data files. Must exist for GDAL to work correctly',
+ 'ICU_DATA':'The directory where icu stores its data files. If ICU reports a path, it must exist. ICU can also be built without .dat files and in that case this path is empty'
}
# Core plugin build configuration
@@ -472,7 +475,10 @@ pickle_store = [# Scons internal variables
'SQLITE_LINKFLAGS',
'BOOST_LIB_VERSION_FROM_HEADER',
'BIGINT',
- 'HOST'
+ 'HOST',
+ 'QUERIED_GDAL_DATA',
+ 'QUERIED_ICU_DATA',
+ 'QUERIED_PROJ_LIB'
]
# Add all other user configurable options to pickle pickle_store
@@ -799,6 +805,117 @@ int main()
context.Result(ret)
return ret
+def CheckIcuData(context, silent=False):
+
+ if not silent:
+ context.Message('Checking for ICU data directory...')
+ ret = context.TryRun("""
+
+#include <unicode/putil.h>
+#include <iostream>
+
+int main() {
+ std::string result = u_getDataDirectory();
+ std::cout << result;
+ if (result.empty()) {
+ return -1;
+ }
+ return 0;
+}
+
+""", '.cpp')
+ if silent:
+ context.did_show_result=1
+ if ret[0]:
+ context.Result('u_getDataDirectory returned %s' % ret[1])
+ else:
+ context.Result('Failed to detect (mapnik-config will have null value)')
+ return ret[1].strip()
+
+def CheckGdalData(context, silent=False):
+
+ if not silent:
+ context.Message('Checking for GDAL data directory...')
+ ret = context.TryRun("""
+
+#include "cpl_config.h"
+#include <iostream>
+
+int main() {
+ std::cout << GDAL_PREFIX << "/share/gdal" << std::endl;
+ return 0;
+}
+
+""", '.cpp')
+ if silent:
+ context.did_show_result=1
+ if ret[0]:
+ context.Result('GDAL_PREFIX returned %s' % ret[1])
+ else:
+ context.Result('Failed to detect (mapnik-config will have null value)')
+ return ret[1].strip()
+
+def CheckProjData(context, silent=False):
+
+ if not silent:
+ context.Message('Checking for PROJ_LIB directory...')
+ ret = context.TryRun("""
+
+// This is narly, could eventually be replaced using https://github.com/OSGeo/proj.4/pull/551]
+#include <proj_api.h>
+#include <iostream>
+
+static void my_proj4_logger(void * user_data, int /*level*/, const char * msg)
+{
+ std::string* posMsg = static_cast<std::string*>(user_data);
+ *posMsg += msg;
+}
+
+// https://github.com/OSGeo/gdal/blob/ddbf6d39aa4b005a77ca4f27c2d61a3214f336f8/gdal/alg/gdalapplyverticalshiftgrid.cpp#L616-L633
+
+std::string find_proj_path(const char * pszFilename) {
+ std::string osMsg;
+ std::string osFilename;
+ projCtx ctx = pj_ctx_alloc();
+ pj_ctx_set_app_data(ctx, &osMsg);
+ pj_ctx_set_debug(ctx, PJ_LOG_DEBUG_MAJOR);
+ pj_ctx_set_logger(ctx, my_proj4_logger);
+ PAFile f = pj_open_lib(ctx, pszFilename, "rb");
+ if( f )
+ {
+ pj_ctx_fclose(ctx, f);
+ }
+ size_t nPos = osMsg.find("fopen(");
+ if( nPos != std::string::npos )
+ {
+ osFilename = osMsg.substr(nPos + strlen("fopen("));
+ nPos = osFilename.find(")");
+ if( nPos != std::string::npos )
+ osFilename = osFilename.substr(0, nPos);
+ }
+ pj_ctx_free(ctx);
+ return osFilename;
+}
+
+
+int main() {
+ std::string result = find_proj_path(" ");
+ std::cout << result;
+ if (result.empty()) {
+ return -1;
+ }
+ return 0;
+}
+
+""", '.cpp')
+ if silent:
+ context.did_show_result=1
+ if ret[0]:
+ context.Result('pj_open_lib returned %s' % ret[1])
+ else:
+ context.Result('Failed to detect (mapnik-config will have null value)')
+ return ret[1].strip()
+
def CheckCairoHasFreetype(context, silent=False):
if not silent:
context.Message('Checking for cairo freetype font support ... ')
@@ -1067,6 +1184,9 @@ conf_tests = { 'prioritize_paths' : prioritize_paths,
'CheckPKGVersion' : CheckPKGVersion,
'FindBoost' : FindBoost,
'CheckBoost' : CheckBoost,
+ 'CheckIcuData' : CheckIcuData,
+ 'CheckProjData' : CheckProjData,
+ 'CheckGdalData' : CheckGdalData,
'CheckCairoHasFreetype' : CheckCairoHasFreetype,
'CheckHasDlfcn' : CheckHasDlfcn,
'GetBoostLibVersion' : GetBoostLibVersion,
@@ -1159,6 +1279,10 @@ if not preconfigured:
env['PLUGINS'] = PLUGINS
env['EXTRA_FREETYPE_LIBS'] = []
env['SQLITE_LINKFLAGS'] = []
+ env['QUERIED_PROJ_LIB'] = None
+ env['QUERIED_ICU_DATA'] = None
+ env['QUERIED_GDAL_DATA'] = None
+
# previously a leading / was expected for LIB_DIR_NAME
# now strip it to ensure expected behavior
if env['LIB_DIR_NAME'].startswith(os.path.sep):
@@ -1476,6 +1600,31 @@ if not preconfigured:
if env['HOST']:
SQLITE_HAS_RTREE = True
+ if not env['HOST']:
+ env['QUERIED_PROJ_LIB'] = conf.CheckProjData()
+ if os.environ.get('PROJ_LIB'):
+ env['QUERIED_PROJ_LIB'] = os.environ['PROJ_LIB']
+ color_print(4,'Detected PROJ_LIB in environ, using env value instead: %s' % os.environ['PROJ_LIB'] )
+ env['QUERIED_ICU_DATA'] = conf.CheckIcuData()
+ if os.environ.get('ICU_DATA'):
+ env['QUERIED_ICU_DATA'] = os.environ['ICU_DATA']
+ color_print(4,'Detected ICU_DATA in environ, using env value instead: %s' % os.environ['ICU_DATA'] )
+ env['QUERIED_GDAL_DATA'] = conf.CheckGdalData()
+ if os.environ.get('GDAL_DATA'):
+ env['QUERIED_GDAL_DATA'] = os.environ['GDAL_DATA']
+ color_print(4,'Detected GDAL_DATA in environ, using env value instead: %s' % os.environ['GDAL_DATA'] )
+ # now validate the paths actually exist
+ if env['QUERIED_PROJ_LIB'] and not os.path.exists(env['QUERIED_PROJ_LIB']):
+ color_print(1,'%s not detected on your system' % env['QUERIED_PROJ_LIB'] )
+ env['MISSING_DEPS'].append('PROJ_LIB')
+ if env['QUERIED_GDAL_DATA'] and not os.path.exists(env['QUERIED_GDAL_DATA']):
+ color_print(1,'%s not detected on your system' % env['QUERIED_GDAL_DATA'] )
+ env['MISSING_DEPS'].append('GDAL_DATA')
+ if env['QUERIED_ICU_DATA'] and not os.path.exists(env['QUERIED_ICU_DATA']):
+ color_print(1,'%s not detected on your system' % env['QUERIED_ICU_DATA'] )
+ env['MISSING_DEPS'].append('ICU_DATA')
+
+
CHECK_PKG_CONFIG = conf.CheckPKGConfig('0.15.0')
if len(env['REQUESTED_PLUGINS']):
diff --git a/configure b/configure
index b468f85..2a44205 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,18 @@
-#!/bin/sh
+#!/bin/bash
+
+set -eu
PYTHON=${PYTHON:-python}
+# mapnik-settings.env is an optional file to store
+# environment variables that should be used before
+# running tests like PROJ_LIB, GDAL_DATA, and ICU_DATA
+# These do not normally need to be set except when
+# building against binary versions of dependencies like
+# done via bootstrap.sh
+if [[ -f mapnik-settings.env ]]; then
+ echo "Inheriting from mapnik-settings.env"
+ source mapnik-settings.env
+fi
+
$PYTHON scons/scons.py --implicit-deps-changed configure "$@"
diff --git a/include/mapnik/geom_util.hpp b/include/mapnik/geom_util.hpp
index 7dbe51f..d16c42e 100644
--- a/include/mapnik/geom_util.hpp
+++ b/include/mapnik/geom_util.hpp
@@ -28,6 +28,7 @@
#include <mapnik/coord.hpp>
#include <mapnik/vertex.hpp>
#include <mapnik/geometry_types.hpp>
+#include <mapnik/geometry.hpp>
// stl
#include <cmath>
#include <vector>
@@ -321,56 +322,60 @@ bool middle_point(PathType & path, double & x, double & y)
template <typename PathType>
bool centroid(PathType & path, double & x, double & y)
{
- double x0 = 0.0;
- double y0 = 0.0;
- double x1 = 0.0;
- double y1 = 0.0;
- double start_x;
- double start_y;
+ geometry::point<double> p0, p1, move_to, start;
path.rewind(0);
- unsigned command = path.vertex(&x0, &y0);
+ unsigned command = path.vertex(&p0.x, &p0.y);
if (command == SEG_END) return false;
- start_x = x0;
- start_y = y0;
+ start = move_to = p0;
double atmp = 0.0;
double xtmp = 0.0;
double ytmp = 0.0;
unsigned count = 1;
- while (SEG_END != (command = path.vertex(&x1, &y1)))
+ while (SEG_END != (command = path.vertex(&p1.x, &p1.y)))
{
- if (command == SEG_CLOSE) continue;
- double dx0 = x0 - start_x;
- double dy0 = y0 - start_y;
- double dx1 = x1 - start_x;
- double dy1 = y1 - start_y;
+ switch (command)
+ {
+ case SEG_MOVETO:
+ move_to = p1;
+ break;
+ case SEG_CLOSE:
+ p1 = move_to;
+ case SEG_LINETO:
+ double dx0 = p0.x - start.x;
+ double dy0 = p0.y - start.y;
+ double dx1 = p1.x - start.x;
+ double dy1 = p1.y - start.y;
+
+ double ai = dx0 * dy1 - dx1 * dy0;
+ atmp += ai;
+ xtmp += (dx1 + dx0) * ai;
+ ytmp += (dy1 + dy0) * ai;
+ break;
- double ai = dx0 * dy1 - dx1 * dy0;
- atmp += ai;
- xtmp += (dx1 + dx0) * ai;
- ytmp += (dy1 + dy0) * ai;
- x0 = x1;
- y0 = y1;
+ }
+ p0 = p1;
++count;
}
- if (count <= 2) {
- x = (start_x + x0) * 0.5;
- y = (start_y + y0) * 0.5;
+ if (count <= 2)
+ {
+ x = (start.x + p0.x) * 0.5;
+ y = (start.y + p0.y) * 0.5;
return true;
}
if (atmp != 0)
{
- x = (xtmp/(3*atmp)) + start_x;
- y = (ytmp/(3*atmp)) + start_y;
+ x = (xtmp / (3 * atmp)) + start.x;
+ y = (ytmp / (3 * atmp)) + start.y;
}
else
{
- x = x0;
- y = y0;
+ x = p0.x;
+ y = p0.y;
}
return true;
}
@@ -524,47 +529,50 @@ bool interior_position(PathType & path, double & x, double & y)
// center of the widest intersection between the polygon and the line.
std::vector<double> intersections; // only need to store the X as we know the y
+ geometry::point<double> p0, p1, move_to;
+ unsigned command = SEG_END;
- double x0 = 0;
- double y0 = 0;
path.rewind(0);
- unsigned command = path.vertex(&x0, &y0);
- double x1 = 0;
- double y1 = 0;
- while (SEG_END != (command = path.vertex(&x1, &y1)))
+
+ while (SEG_END != (command = path.vertex(&p0.x, &p0.y)))
{
- if (command == SEG_CLOSE)
- continue;
- if (command != SEG_MOVETO)
+ switch (command)
{
- // if the segments overlap
- if (y0==y1)
- {
- if (y0==y)
+ case SEG_MOVETO:
+ move_to = p0;
+ break;
+ case SEG_CLOSE:
+ p0 = move_to;
+ case SEG_LINETO:
+ // if the segments overlap
+ if (p0.y == p1.y)
{
- double xi = (x0+x1)/2.0;
- intersections.push_back(xi);
+ if (p0.y == y)
+ {
+ double xi = (p0.x + p1.x) / 2.0;
+ intersections.push_back(xi);
+ }
}
- }
- // if the path segment crosses the bisector
- else if ((y0 <= y && y1 >= y) ||
- (y0 >= y && y1 <= y))
- {
- // then calculate the intersection
- double xi = x0;
- if (x0 != x1)
+ // if the path segment crosses the bisector
+ else if ((p0.y <= y && p1.y >= y) ||
+ (p0.y >= y && p1.y <= y))
{
- double m = (y1-y0)/(x1-x0);
- double c = y0 - m*x0;
- xi = (y-c)/m;
- }
+ // then calculate the intersection
+ double xi = p0.x;
+ if (p0.x != p1.x)
+ {
+ double m = (p1.y - p0.y) / (p1.x - p0.x);
+ double c = p0.y - m * p0.x;
+ xi = (y - c) / m;
+ }
- intersections.push_back(xi);
- }
+ intersections.push_back(xi);
+ }
+ break;
}
- x0 = x1;
- y0 = y1;
+ p1 = p0;
}
+
// no intersections we just return the default
if (intersections.empty())
return true;
diff --git a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp
index 4d81de7..b3ac07e 100644
--- a/include/mapnik/json/extract_bounding_box_grammar_impl.hpp
+++ b/include/mapnik/json/extract_bounding_box_grammar_impl.hpp
@@ -61,10 +61,13 @@ struct push_box_impl
template <typename T0, typename T1, typename T2, typename T3>
void operator() (T0 & boxes, T1 const& begin, T2 const& box, T3 const& range) const
{
- boxes.emplace_back(box,
- std::make_pair(std::distance(begin,
- range.begin()),
- std::distance(range.begin(), range.end())));
+ if (box.valid())
+ {
+ boxes.emplace_back(box,
+ std::make_pair(std::distance(begin,
+ range.begin()),
+ std::distance(range.begin(), range.end())));
+ }
}
};
diff --git a/include/mapnik/marker_cache.hpp b/include/mapnik/marker_cache.hpp
index 47aa555..250441f 100644
--- a/include/mapnik/marker_cache.hpp
+++ b/include/mapnik/marker_cache.hpp
@@ -55,7 +55,7 @@ public:
inline bool is_uri(std::string const& path) { return is_svg_uri(path) || is_image_uri(path); }
bool is_svg_uri(std::string const& path);
bool is_image_uri(std::string const& path);
- std::shared_ptr<marker const> find(std::string const& key, bool update_cache = false);
+ std::shared_ptr<marker const> find(std::string const& key, bool update_cache = false, bool strict = false);
void clear();
};
diff --git a/include/mapnik/svg/svg_parser.hpp b/include/mapnik/svg/svg_parser.hpp
index 63caacd..fab0b8e 100644
--- a/include/mapnik/svg/svg_parser.hpp
+++ b/include/mapnik/svg/svg_parser.hpp
@@ -2,7 +2,7 @@
*
* This file is part of Mapnik (c++ mapping toolkit)
*
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2017 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -30,27 +30,55 @@
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/gradient.hpp>
#include <mapnik/util/noncopyable.hpp>
-
// stl
#include <map>
+namespace boost { namespace property_tree { namespace detail { namespace rapidxml {
+template <typename T> class xml_node;
+}}}}
+
namespace mapnik { namespace svg {
- class MAPNIK_DECL svg_parser : private util::noncopyable
+class svg_parser_error_handler
+{
+ using error_message_container = std::vector<std::string> ;
+public:
+ explicit svg_parser_error_handler(bool strict = false)
+ : strict_(strict) {}
+
+ void on_error(std::string const& msg)
{
- using error_message_container = std::vector<std::string> ;
- public:
- explicit svg_parser(svg_converter_type & path);
- ~svg_parser();
- 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_;
- };
+ if (strict_) throw std::runtime_error(msg);
+ else error_messages_.push_back(msg);
+ }
+ error_message_container const& error_messages() const
+ {
+ return error_messages_;
+ }
+ bool strict() const { return strict_; }
+private:
+
+ error_message_container error_messages_;
+ bool strict_;
+};
+
+class MAPNIK_DECL svg_parser : private util::noncopyable
+{
+ using error_handler = svg_parser_error_handler;
+public:
+ explicit svg_parser(svg_converter_type & path, bool strict = false);
+ ~svg_parser();
+ error_handler & err_handler();
+ void parse(std::string const& filename);
+ void parse_from_string(std::string const& svg);
+ svg_converter_type & path_;
+ bool is_defs_;
+ bool strict_;
+ std::map<std::string, gradient> gradient_map_;
+ std::map<std::string, boost::property_tree::detail::rapidxml::xml_node<char> const*> node_cache_;
+ agg::trans_affine viewbox_tr_{};
+ error_handler err_handler_;
+};
}}
diff --git a/include/mapnik/text/harfbuzz_shaper.hpp b/include/mapnik/text/harfbuzz_shaper.hpp
index 8b574b0..14151cf 100644
--- a/include/mapnik/text/harfbuzz_shaper.hpp
+++ b/include/mapnik/text/harfbuzz_shaper.hpp
@@ -40,6 +40,7 @@
#include <mapnik/warning_ignore.hpp>
#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ft.h>
+#include <unicode/uvernum.h>
#include <unicode/uscript.h>
#pragma GCC diagnostic pop
@@ -55,7 +56,8 @@ static inline hb_script_t _icu_script_to_script(UScriptCode script)
static inline const uint16_t * uchar_to_utf16(const UChar* src)
{
static_assert(sizeof(UChar) == sizeof(uint16_t),"UChar is eq size to uint16_t");
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) || (U_ICU_VERSION_MAJOR_NUM >= 59)
+ // ^^ http://site.icu-project.org/download/59#TOC-ICU4C-char16_t1
return reinterpret_cast<const uint16_t *>(src);
#else
return src;
diff --git a/include/mapnik/unicode.hpp b/include/mapnik/unicode.hpp
index f3b270c..94909b3 100644
--- a/include/mapnik/unicode.hpp
+++ b/include/mapnik/unicode.hpp
@@ -31,6 +31,13 @@
// std
#include <cstdint>
#include <string>
+// icu
+#if (U_ICU_VERSION_MAJOR_NUM >= 59)
+#pragma GCC diagnostic push
+#include <mapnik/warning_ignore.hpp>
+#include <unicode/unistr.h>
+#pragma GCC diagnostic pop
+#endif
struct UConverter;
diff --git a/include/mapnik/version.hpp b/include/mapnik/util/name_to_int.hpp
similarity index 62%
copy from include/mapnik/version.hpp
copy to include/mapnik/util/name_to_int.hpp
index 9798d98..096a1f0 100644
--- a/include/mapnik/version.hpp
+++ b/include/mapnik/util/name_to_int.hpp
@@ -2,7 +2,7 @@
*
* This file is part of Mapnik (c++ mapping toolkit)
*
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2017 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -20,19 +20,11 @@
*
*****************************************************************************/
-#ifndef MAPNIK_VERSION_HPP
-#define MAPNIK_VERSION_HPP
+namespace mapnik { namespace util {
-#include <mapnik/stringify_macro.hpp>
+constexpr unsigned name_to_int(const char *str, int off = 0)
+{
+ return !str[off] ? 5381 : (name_to_int(str, off + 1) * 33) ^ static_cast<unsigned>(str[off]);
+}
-#define MAPNIK_MAJOR_VERSION 3
-#define MAPNIK_MINOR_VERSION 0
-#define MAPNIK_PATCH_VERSION 15
-
-#define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION)
-
-#define MAPNIK_VERSION_STRING MAPNIK_STRINGIFY(MAPNIK_MAJOR_VERSION) "." \
- MAPNIK_STRINGIFY(MAPNIK_MINOR_VERSION) "." \
- MAPNIK_STRINGIFY(MAPNIK_PATCH_VERSION)
-
-#endif // MAPNIK_VERSION_HPP
+}}
diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp
index 9798d98..d891d46 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 15
+#define MAPNIK_PATCH_VERSION 16
#define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION)
diff --git a/plugins/input/gdal/gdal_featureset.cpp b/plugins/input/gdal/gdal_featureset.cpp
index 8f755fd..661f8e1 100644
--- a/plugins/input/gdal/gdal_featureset.cpp
+++ b/plugins/input/gdal/gdal_featureset.cpp
@@ -547,7 +547,7 @@ feature_ptr gdal_featureset::get_feature(mapnik::query const& q)
if (alpha)
{
MAPNIK_LOG_DEBUG(gdal) << "gdal_featureset: processing alpha band...";
- if (!raster_has_nodata)
+ if (!raster_has_nodata || (red && green && blue))
{
raster_io_error = alpha->RasterIO(GF_Read, x_off, y_off, width, height, image.bytes() + 3,
image.width(), image.height(), GDT_Byte, 4, 4 * image.width());
diff --git a/scripts/publish_release.sh b/scripts/publish_release.sh
index 7ff8749..3bc2de1 100755
--- a/scripts/publish_release.sh
+++ b/scripts/publish_release.sh
@@ -71,7 +71,7 @@ function check_and_tag() {
step "test data already tagged, no need to initialize submodule"
else
step "tagging test data"
- git submodule update --depth 100 --init ${REPO_DIR}
+ git submodule update --init ${REPO_DIR}
cd ${REPO_DIR}/
git remote set-url origin git at github.com:mapnik/${REPO_NAME}
git tag ${MAPNIK_VERSION} -a -m "tagging for ${MAPNIK_VERSION}"
diff --git a/src/load_map.cpp b/src/load_map.cpp
index 5a8e122..b51f811 100644
--- a/src/load_map.cpp
+++ b/src/load_map.cpp
@@ -49,6 +49,7 @@
#include <mapnik/util/dasharray_parser.hpp>
#include <mapnik/util/conversions.hpp>
#include <mapnik/util/trim.hpp>
+#include <mapnik/util/name_to_int.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/util/noncopyable.hpp>
#include <mapnik/util/fs.hpp>
@@ -80,12 +81,9 @@ using boost::tokenizer;
namespace mapnik
{
-using boost::optional;
-constexpr unsigned name2int(const char *str, int off = 0)
-{
- return !str[off] ? 5381 : (name2int(str, off + 1) * 33) ^ static_cast<unsigned>(str[off]);
-}
+using boost::optional;
+using util::name_to_int;
class map_parser : util::noncopyable
{
@@ -826,57 +824,57 @@ void map_parser::parse_symbolizers(rule & rule, xml_node const & node)
rule.reserve(node.size());
for (auto const& sym_node : node)
{
- switch (name2int(sym_node.name().c_str()))
+ switch (name_to_int(sym_node.name().c_str()))
{
- case name2int("PointSymbolizer"):
+ case name_to_int("PointSymbolizer"):
parse_point_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("LinePatternSymbolizer"):
+ case name_to_int("LinePatternSymbolizer"):
parse_line_pattern_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("PolygonPatternSymbolizer"):
+ case name_to_int("PolygonPatternSymbolizer"):
parse_polygon_pattern_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("TextSymbolizer"):
+ case name_to_int("TextSymbolizer"):
parse_text_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("ShieldSymbolizer"):
+ case name_to_int("ShieldSymbolizer"):
parse_shield_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("LineSymbolizer"):
+ case name_to_int("LineSymbolizer"):
parse_line_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("PolygonSymbolizer"):
+ case name_to_int("PolygonSymbolizer"):
parse_polygon_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("BuildingSymbolizer"):
+ case name_to_int("BuildingSymbolizer"):
parse_building_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("RasterSymbolizer"):
+ case name_to_int("RasterSymbolizer"):
parse_raster_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("MarkersSymbolizer"):
+ case name_to_int("MarkersSymbolizer"):
parse_markers_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("GroupSymbolizer"):
+ case name_to_int("GroupSymbolizer"):
parse_group_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("DebugSymbolizer"):
+ case name_to_int("DebugSymbolizer"):
parse_debug_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
- case name2int("DotSymbolizer"):
+ case name_to_int("DotSymbolizer"):
parse_dot_symbolizer(rule, sym_node);
sym_node.set_processed(true);
break;
@@ -1356,7 +1354,6 @@ void map_parser::parse_raster_symbolizer(rule & rule, xml_node const & node)
{
found_colorizer = true;
raster_colorizer_ptr colorizer = std::make_shared<raster_colorizer>();
- put(raster_sym, keys::colorizer, colorizer);
if (parse_raster_colorizer(colorizer, css))
put(raster_sym, keys::colorizer, colorizer);
}
diff --git a/src/marker_cache.cpp b/src/marker_cache.cpp
index 059280c..d7ea7fa 100644
--- a/src/marker_cache.cpp
+++ b/src/marker_cache.cpp
@@ -2,7 +2,7 @@
*
* This file is part of Mapnik (c++ mapping toolkit)
*
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2017 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -141,7 +141,7 @@ struct visitor_create_marker
} // end detail ns
std::shared_ptr<mapnik::marker const> marker_cache::find(std::string const& uri,
- bool update_cache)
+ bool update_cache, bool strict)
{
if (uri.empty())
{
@@ -174,15 +174,15 @@ std::shared_ptr<mapnik::marker const> marker_cache::find(std::string const& uri,
vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source());
svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes());
- svg_parser p(svg);
+ svg_parser p(svg, strict);
+ p.parse_from_string(known_svg_string);
- if (!p.parse_from_string(known_svg_string))
+ if (!strict)
{
- for (auto const& msg : p.error_messages())
+ for (auto const& msg : p.err_handler().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;
@@ -214,16 +214,15 @@ std::shared_ptr<mapnik::marker const> marker_cache::find(std::string const& uri,
vertex_stl_adapter<svg_path_storage> stl_storage(marker_path->source());
svg_path_adapter svg_path(stl_storage);
svg_converter_type svg(svg_path, marker_path->attributes());
- svg_parser p(svg);
-
+ svg_parser p(svg, strict);
+ p.parse(uri);
- if (!p.parse(uri))
+ if (!strict)
{
- for (auto const& msg : p.error_messages())
+ for (auto const& msg : p.err_handler().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;
diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp
index 827f557..4dd3fbc 100644
--- a/src/svg/svg_parser.cpp
+++ b/src/svg/svg_parser.cpp
@@ -2,7 +2,7 @@
*
* This file is part of Mapnik (c++ mapping toolkit)
*
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2017 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -30,7 +30,7 @@
#include <mapnik/util/file_io.hpp>
#include <mapnik/util/utf_conv_win.hpp>
#include <mapnik/util/dasharray_parser.hpp>
-
+#include <mapnik/util/name_to_int.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore_agg.hpp>
#include "agg_ellipse.h"
@@ -54,28 +54,125 @@
#include <vector>
#include <cstring>
#include <fstream>
+#include <array>
+
+namespace mapnik { namespace svg {
+
+using util::name_to_int;
+
+struct viewbox
+{
+ double x0;
+ double y0;
+ double width;
+ double height;
+};
+}}
+
+BOOST_FUSION_ADAPT_STRUCT (
+ mapnik::svg::viewbox,
+ (double, x0)
+ (double, y0)
+ (double, width)
+ (double, height)
+ )
namespace mapnik { namespace svg {
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);
+void 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_element(svg_parser& parser, char const* name, rapidxml::xml_node<char> const* node);
+void parse_use(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, std::string const& id,
+ mapnik::gradient& gr, rapidxml::xml_node<char> const* node);
+void parse_gradient_stop(svg_parser& parser, mapnik::gradient& gr, 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);
+
+namespace {
+
+static std::array<unsigned, 7> const unsupported_elements
+{ {name_to_int("symbol"),
+ name_to_int("marker"),
+ name_to_int("view"),
+ name_to_int("text"),
+ name_to_int("switch"),
+ name_to_int("image"),
+ name_to_int("a")}
+};
+#if 0 // disable to reduce verbosity
+static std::array<unsigned, 43> const unsupported_attributes
+{ {name_to_int("alignment-baseline"),
+ name_to_int("baseline-shift"),
+ name_to_int("clip"),
+ name_to_int("clip-path"),
+ name_to_int("clip-rule"),
+ name_to_int("color-interpolation"),
+ name_to_int("color-interpolation-filters"),
+ name_to_int("color-profile"),
+ name_to_int("color-rendering"),
+ name_to_int("cursor"),
+ name_to_int("direction"),
+ name_to_int("dominant-baseline"),
+ name_to_int("enable-background"),
+ name_to_int("filter"),
+ name_to_int("flood-color"),
+ name_to_int("flood-opacity"),
+ name_to_int("font-family"),
+ name_to_int("font-size"),
+ name_to_int("font-size-adjust"),
+ name_to_int("font-stretch"),
+ name_to_int("font-style"),
+ name_to_int("font-variant"),
+ name_to_int("font-weight"),
+ name_to_int("glyph-orientation-horizontal"),
+ name_to_int("glyph-orientation-vertical"),
+ name_to_int("image-rendering"),
+ name_to_int("kerning"),
+ name_to_int("letter-spacing"),
+ name_to_int("lighting-color"),
+ name_to_int("marker-end"),
+ name_to_int("marker-mid"),
+ name_to_int("marker-start"),
+ name_to_int("mask"),
+ name_to_int("overflow"),
+ name_to_int("pointer-events"),
+ name_to_int("shape-rendering"),
+ name_to_int("text-anchor"),
+ name_to_int("text-decoration"),
+ name_to_int("text-rendering"),
+ name_to_int("unicode-bidi"),
+ name_to_int("word-spacing"),
+ name_to_int("writing-mode")}
+};
+
+#endif
+
+template <typename T>
+void handle_unsupported(svg_parser& parser, T const& ar, char const* name)
+{
+ unsigned element = name_to_int(name);
+ for (auto const& e : ar)
+ {
+ if (e == element)
+ {
+ parser.err_handler().on_error(std::string("SVG support error: <" + std::string(name) + "> element is not supported"));
+ }
+ }
+}
using color_lookup_type = std::vector<std::pair<double, agg::rgba8> >;
namespace qi = boost::spirit::qi;
@@ -102,7 +199,7 @@ struct key_value_sequence_ordered
};
template <typename T>
-mapnik::color parse_color(T & error_messages, const char* str)
+mapnik::color parse_color(T & err_handler, const char* str)
{
mapnik::color c(100,100,100);
try
@@ -111,34 +208,34 @@ mapnik::color parse_color(T & error_messages, const char* str)
}
catch (mapnik::config_error const& ex)
{
- error_messages.emplace_back(ex.what());
+ err_handler.on_error("SVG parse error: failed to parse <color> with value \"" + std::string(str) + "\"");
}
return c;
}
template <typename T>
-agg::rgba8 parse_color_agg(T & error_messages, const char* str)
+agg::rgba8 parse_color_agg(T & err_handler, const char* str)
{
- auto c = parse_color(error_messages, str);
+ auto c = parse_color(err_handler, str);
return agg::rgba8(c.red(), c.green(), c.blue(), c.alpha());
}
template <typename T>
-double parse_double(T & error_messages, const char* str)
+double parse_double(T & err_handler, const char* str)
{
using namespace boost::spirit::qi;
double_type double_;
double val = 0.0;
if (!parse(str, str + std::strlen(str),double_,val))
{
- error_messages.emplace_back("Failed to parse double: \"" + std::string(str) + "\"");
+ err_handler.on_error("SVG parse error: failed to parse <number> with value \"" + std::string(str) + "\"");
}
return val;
}
// https://www.w3.org/TR/SVG/coords.html#Units
template <typename T, int DPI = 90>
-double parse_svg_value(T & error_messages, const char* str, bool & percent)
+double parse_svg_value(T & err_handler, const char* str, bool & percent)
{
using skip_type = boost::spirit::ascii::space_type;
using boost::phoenix::ref;
@@ -162,35 +259,29 @@ double parse_svg_value(T & error_messages, const char* str, bool & percent)
> - (units[ ref(val) *= _1]
|
lit('%')[ref(val) *= 0.01][ref(percent) = true]),
- skip_type()))
+ skip_type()) || cur != end)
{
- error_messages.emplace_back("Failed to parse SVG value: '" + std::string(str) + "'");
- }
- else if (cur != end)
- {
- error_messages.emplace_back("Failed to parse SVG value: '" + std::string(str) +
- "', trailing garbage: '" + cur + "'");
+ err_handler.on_error("SVG parse error: failed to parse <number> with value \"" + std::string(str) + "\"");
}
return val;
}
-template <typename T>
-bool parse_double_list(T & error_messages, const char* str, double* list)
+template <typename T, typename V>
+bool parse_viewbox(T & err_handler, const char* str, V & viewbox)
{
using namespace boost::spirit::qi;
using boost::phoenix::ref;
- _1_type _1;
double_type double_;
lit_type lit;
using skip_type = boost::spirit::ascii::space_type;
if (!phrase_parse(str, str + std::strlen(str),
- double_[ref(list[0])=_1] >> -lit(',') >>
- double_[ref(list[1])=_1] >> -lit(',') >>
- double_[ref(list[2])=_1] >> -lit(',') >>
- double_[ref(list[3])=_1], skip_type()))
+ double_ >> -lit(',') >>
+ double_ >> -lit(',') >>
+ double_ >> -lit(',') >>
+ double_, skip_type(), viewbox))
{
- error_messages.emplace_back("failed to parse list of doubles from " + std::string(str));
+ err_handler.on_error("SVG parse error: failed to parse <viewbox> with value \"" + std::string(str) + "\"");
return false;
}
return true;
@@ -216,91 +307,146 @@ bool parse_id_from_url (char const* str, std::string & id)
skip_type());
}
-bool traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
+}
+
+boost::property_tree::detail::rapidxml::xml_attribute<char> const * parse_id(svg_parser & parser, rapidxml::xml_node<char> const* node)
+{
+ auto const* id_attr = node->first_attribute("xml:id");
+ if (id_attr == nullptr) id_attr = node->first_attribute("id");
+
+ if (id_attr && parser.node_cache_.count(id_attr->value()) == 0)
+ {
+ parser.node_cache_.emplace(id_attr->value(), node);
+ }
+ return id_attr;
+}
+
+boost::property_tree::detail::rapidxml::xml_attribute<char> const * parse_href(rapidxml::xml_node<char> const* node)
+{
+ auto const* attr = node->first_attribute("xlink:href");
+ if (attr == nullptr) attr = node->first_attribute("href");
+ return attr;
+}
+
+enum aspect_ratio_alignment
+{
+ none = 0,
+ xMinYMin,
+ xMidYMin,
+ xMaxYMin,
+ xMinYMid,
+ xMidYMid,
+ xMaxYMid,
+ xMinYMax,
+ xMidYMax,
+ xMaxYMax
+};
+
+template <typename T>
+std::pair<unsigned,bool> parse_preserve_aspect_ratio(T & err_handler, char const* str)
+{
+ std::pair<unsigned,bool> preserve_aspect_ratio {xMidYMid, true };
+ using skip_type = boost::spirit::ascii::space_type;
+ using boost::phoenix::ref;
+ qi::lit_type lit;
+ qi::_1_type _1;
+ qi::symbols<char, unsigned> align;
+ align.add
+ ("none", none)
+ ("xMinYMin", xMinYMin)
+ ("xMidYMin", xMidYMin)
+ ("xMaxYMin", xMaxYMin)
+ ("xMinYMid", xMinYMid)
+ ("xMidYMid", xMidYMid)
+ ("xMaxYMid", xMaxYMid)
+ ("xMinYMax", xMinYMax)
+ ("xMidYMax", xMidYMax)
+ ("xMaxYMax", xMaxYMax);
+
+
+ char const* cur = str; // phrase_parse mutates the first iterator
+ char const* end = str + std::strlen(str);
+ try
+ {
+ qi::phrase_parse(cur, end, -lit("defer") // only applicable to <image> which we don't support currently
+ > align[ref(preserve_aspect_ratio.first) = _1]
+ > -(lit("meet") | lit("slice")[ref(preserve_aspect_ratio.second) = false]), skip_type());
+ }
+ catch (qi::expectation_failure<char const*> const& ex)
+ {
+ err_handler.on_error("SVG parse error: failed to parse <preserveAspectRatio> with value \"" + std::string(str) + "\"");
+ return {xMidYMid, true} ; // default
+ }
+ return preserve_aspect_ratio;
+}
+
+
+void traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
auto const* name = node->name();
switch (node->type())
{
case rapidxml::node_element:
{
- if (std::strcmp(name, "defs") == 0)
+ switch(name_to_int(name))
+ {
+ case name_to_int("defs"):
{
if (node->first_node() != nullptr)
{
parser.is_defs_ = true;
}
+ break;
}
// 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)
- {
+ case name_to_int("linearGradient"):
parse_linear_gradient(parser, node);
- }
- else if (std::strcmp(name, "radialGradient") == 0)
- {
+ break;
+ case name_to_int("radialGradient"):
parse_radial_gradient(parser, node);
- }
- else if (std::strcmp(name, "stop") == 0)
- {
- parse_gradient_stop(parser, node);
+ break;
+ case name_to_int("symbol"):
+ parse_id(parser, node);
+ //parse_dimensions(parser, node);
+ break;
}
if (!parser.is_defs_) // FIXME
{
- if (std::strcmp(name, "g") == 0)
+ switch (name_to_int(name))
{
+ case name_to_int("g"):
if (node->first_node() != nullptr)
{
parser.path_.push_attr();
+ parse_id(parser, node);
parse_attr(parser, node);
}
- }
- else
- {
+ break;
+ case name_to_int("use"):
parser.path_.push_attr();
+ parse_id(parser, node);
+ parse_attr(parser, node);
+ parse_use(parser, node);
+ parser.path_.pop_attr();
+ break;
+ default:
+ parser.path_.push_attr();
+ parse_id(parser, node);
parse_attr(parser, node);
if (parser.path_.display())
{
- 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";
- }
+ parse_element(parser, name, node);
}
parser.path_.pop_attr();
}
}
+ else
+ {
+ // save node for later
+ parse_id(parser, node);
+ }
for (auto const* child = node->first_node();
child; child = child->next_sibling())
@@ -323,7 +469,7 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
// whitespace trimmed.
//std::string trimmed = node->value();
//mapnik::util::trim(trimmed);
- std::cerr << "CDATA:" << node->value() << std::endl;
+ //std::cerr << "CDATA:" << node->value() << std::endl;
}
}
break;
@@ -331,7 +477,6 @@ bool traverse_tree(svg_parser & parser, rapidxml::xml_node<char> const* node)
default:
break;
}
- return true;
}
@@ -352,67 +497,111 @@ void end_element(svg_parser & parser, rapidxml::xml_node<char> const* node)
parser.is_defs_ = false;
}
}
- else if (std::strcmp(name, "linearGradient") == 0 || std::strcmp(name, "radialGradient") == 0)
+}
+
+void parse_element(svg_parser & parser, char const* name, rapidxml::xml_node<char> const* node)
+{
+ switch (name_to_int(name))
{
- parser.gradient_map_[parser.temporary_gradient_.first] = parser.temporary_gradient_.second;
+ case name_to_int("path"):
+ parser.path_.transform().multiply(parser.viewbox_tr_);
+ parse_path(parser, node);
+ break;
+ case name_to_int("polygon"):
+ parser.path_.transform().multiply(parser.viewbox_tr_);
+ parse_polygon(parser, node);
+ break;
+ case name_to_int("polyline"):
+ parser.path_.transform().multiply(parser.viewbox_tr_);
+ parse_polyline(parser, node);
+ break;
+ case name_to_int("line"):
+ parser.path_.transform().multiply(parser.viewbox_tr_);
+ parse_line(parser, node);
+ break;
+ case name_to_int("rect"):
+ parser.path_.transform().multiply(parser.viewbox_tr_);
+ parse_rect(parser, node);
+ break;
+ case name_to_int("circle"):
+ parser.path_.transform().multiply(parser.viewbox_tr_);
+ parse_circle(parser, node);
+ break;
+ case name_to_int("ellipse"):
+ parser.path_.transform().multiply(parser.viewbox_tr_);
+ parse_ellipse(parser, node);
+ break;
+ case name_to_int("svg"):
+ parse_dimensions(parser, node);
+ break;
+ default:
+ handle_unsupported(parser, unsupported_elements, name);
+ break;
}
}
-void parse_attr(svg_parser & parser, char const* name, char const* value )
+void parse_stroke(svg_parser& parser, char const* value)
{
- if (std::strcmp(name, "transform") == 0)
+ std::string id;
+ if (std::strcmp(value, "none") == 0)
{
- agg::trans_affine tr;
- mapnik::svg::parse_svg_transform(value,tr);
- parser.path_.transform().premultiply(tr);
+ parser.path_.stroke_none();
}
- else if (std::strcmp(name, "fill") == 0)
+ else if (parse_id_from_url(value, id))
{
- std::string id;
- if (std::strcmp(value, "none") == 0)
+ // see if we have a known gradient stroke
+ if (parser.gradient_map_.count(id) > 0)
{
- parser.path_.fill_none();
+ parser.path_.add_stroke_gradient(parser.gradient_map_[id]);
}
- else if (parse_id_from_url(value, id))
+ else if (parser.node_cache_.count(id) > 0)
{
- // see if we have a known gradient fill
+ // try parsing again
+ auto const* gradient_node = parser.node_cache_[id];
+ traverse_tree(parser, gradient_node);
if (parser.gradient_map_.count(id) > 0)
{
- parser.path_.add_fill_gradient(parser.gradient_map_[id]);
+ parser.path_.add_stroke_gradient(parser.gradient_map_[id]);
}
else
{
std::stringstream ss;
- ss << "Failed to find gradient fill: " << id;
- parser.error_messages_.push_back(ss.str());
+ ss << "SVG parse error: failed to locate <gradient> stroke with <id> \"" << id << "\"";
+ parser.err_handler().on_error(ss.str());
}
}
else
{
- parser.path_.fill(parse_color_agg(parser.error_messages_, value));
+ std::stringstream ss;
+ ss << "SVG parse error: failed to locate <gradient> stroke with <id> \"" << id << "\"";
+ parser.err_handler().on_error(ss.str());
}
}
- else if (std::strcmp(name,"fill-opacity") == 0)
+ else
{
- parser.path_.fill_opacity(parse_double(parser.error_messages_, value));
+ parser.path_.stroke(parse_color_agg(parser.err_handler(), value));
}
- else if (std::strcmp(name, "fill-rule") == 0)
+}
+
+void parse_fill(svg_parser& parser, char const* value)
+{
+ std::string id;
+ if (std::strcmp(value, "none") == 0)
{
- if (std::strcmp(value, "evenodd") == 0)
- {
- parser.path_.even_odd(true);
- }
+ parser.path_.fill_none();
}
- else if (std::strcmp(name, "stroke") == 0)
+ else if (parse_id_from_url(value, id))
{
- std::string id;
- if (std::strcmp(value, "none") == 0)
+ // see if we have a known gradient fill
+ if (parser.gradient_map_.count(id) > 0)
{
- parser.path_.stroke_none();
+ parser.path_.add_fill_gradient(parser.gradient_map_[id]);
}
- else if (parse_id_from_url(value, id))
+ else if (parser.node_cache_.count(id) > 0)
{
- // see if we have a known gradient fill
+ // try parsing again
+ auto const* gradient_node = parser.node_cache_[id];
+ traverse_tree(parser, gradient_node);
if (parser.gradient_map_.count(id) > 0)
{
parser.path_.add_stroke_gradient(parser.gradient_map_[id]);
@@ -420,71 +609,109 @@ void parse_attr(svg_parser & parser, char const* name, char const* value )
else
{
std::stringstream ss;
- ss << "Failed to find gradient stroke: " << id;
- parser.error_messages_.push_back(ss.str());
+ ss << "SVG parse error: failed to locate <gradient> fill with <id> \"" << id << "\"";
+ parser.err_handler().on_error(ss.str());
}
}
else
{
- parser.path_.stroke(parse_color_agg(parser.error_messages_, value));
+ std::stringstream ss;
+ ss << "SVG parse error: failed to locate <gradient> fill with <id> \"" << id << "\"";
+ parser.err_handler().on_error(ss.str());
}
}
- else if (std::strcmp(name, "stroke-width") == 0)
+ else
{
- bool percent;
- parser.path_.stroke_width(parse_svg_value(parser.error_messages_, value, percent));
+ parser.path_.fill(parse_color_agg(parser.err_handler(), value));
}
- else if (std::strcmp(name, "stroke-opacity") == 0)
+}
+
+void parse_transform(svg_parser & parser, char const* value)
+{
+ agg::trans_affine tr;
+ mapnik::svg::parse_svg_transform(value,tr);
+ parser.path_.transform().premultiply(tr);
+}
+
+void parse_stroke_dash(svg_parser & parser, char const* value)
+{
+ dash_array dash;
+ if (util::parse_dasharray(value, dash))
{
- parser.path_.stroke_opacity(parse_double(parser.error_messages_, value));
+ parser.path_.dash_array(std::move(dash));
}
- else if(std::strcmp(name, "stroke-linecap") == 0)
+}
+
+void parse_attr(svg_parser & parser, char const* name, char const* value )
+{
+ switch (name_to_int(name))
{
+ case name_to_int("transform"):
+ parse_transform(parser, value);
+ break;
+ case name_to_int("fill"):
+ parse_fill(parser, value);
+ break;
+ case name_to_int("fill-opacity"):
+ parser.path_.fill_opacity(parse_double(parser.err_handler(), value));
+ break;
+ case name_to_int("fill-rule"):
+ if (std::strcmp(value, "evenodd") == 0)
+ {
+ parser.path_.even_odd(true);
+ }
+ break;
+ case name_to_int("stroke"):
+ parse_stroke(parser, value);
+ break;
+ case name_to_int("stroke-width"):
+ bool percent;
+ parser.path_.stroke_width(parse_svg_value(parser.err_handler(), value, percent));
+ break;
+ case name_to_int("stroke-opacity"):
+ parser.path_.stroke_opacity(parse_double(parser.err_handler(), value));
+ break;
+ case name_to_int("stroke-linecap"):
if(std::strcmp(value, "butt") == 0)
parser.path_.line_cap(agg::butt_cap);
else if(std::strcmp(value, "round") == 0)
parser.path_.line_cap(agg::round_cap);
else if(std::strcmp(value, "square") == 0)
parser.path_.line_cap(agg::square_cap);
- }
- else if(std::strcmp(name, "stroke-linejoin") == 0)
- {
- if(std::strcmp(value, "miter") == 0)
+ break;
+ case name_to_int("stroke-linejoin"):
+ if (std::strcmp(value, "miter") == 0)
parser.path_.line_join(agg::miter_join);
- else if(std::strcmp(value, "round") == 0)
+ else if (std::strcmp(value, "round") == 0)
parser.path_.line_join(agg::round_join);
- else if(std::strcmp(value, "bevel") == 0)
+ else if (std::strcmp(value, "bevel") == 0)
parser.path_.line_join(agg::bevel_join);
- }
- else if(std::strcmp(name, "stroke-miterlimit") == 0)
- {
- parser.path_.miter_limit(parse_double(parser.error_messages_,value));
- }
- else if (std::strcmp(name,"stroke-dasharray") == 0)
- {
- dash_array dash;
- if (util::parse_dasharray(value, dash))
+ break;
+ case name_to_int("stroke-miterlimit"):
+ parser.path_.miter_limit(parse_double(parser.err_handler(),value));
+ break;
+ case name_to_int("stroke-dasharray"):
+ parse_stroke_dash(parser, value);
+ break;
+ case name_to_int("stroke-dashoffset"):
+ parser.path_.dash_offset(parse_double(parser.err_handler(), value));
+ break;
+ case name_to_int("opacity"):
+ parser.path_.opacity(parse_double(parser.err_handler(), value));
+ break;
+ case name_to_int("visibility"):
+ parser.path_.visibility(std::strcmp(value, "hidden") != 0);
+ break;
+ case name_to_int("display"):
+ if (std::strcmp(value, "none") == 0)
{
- parser.path_.dash_array(std::move(dash));
+ parser.path_.display(false);
}
- }
- else if (std::strcmp(name,"stroke-dashoffset") == 0)
- {
- double offset = parse_double(parser.error_messages_, value);
- parser.path_.dash_offset(offset);
- }
- else if(std::strcmp(name, "opacity") == 0)
- {
- double opacity = parse_double(parser.error_messages_, value);
- parser.path_.opacity(opacity);
- }
- else if (std::strcmp(name, "visibility") == 0)
- {
- parser.path_.visibility(std::strcmp(value, "hidden") != 0);
- }
- else if (std::strcmp(name, "display") == 0 && std::strcmp(value, "none") == 0)
- {
- parser.path_.display(false);
+ break;
+ default:
+ //handle_unsupported(parser, unsupported_attributes, name);
+ // disable for now to reduce verbosity
+ break;
}
}
@@ -517,7 +744,7 @@ void parse_dimensions(svg_parser & parser, rapidxml::xml_node<char> const* node)
double width = 0;
double height = 0;
double aspect_ratio = 1;
- double viewbox[4] = {0,0,0,0};
+ viewbox vbox = {0, 0, 0, 0};
bool has_viewbox = false;
bool has_percent_height = true;
bool has_percent_width = true;
@@ -525,35 +752,98 @@ void parse_dimensions(svg_parser & parser, rapidxml::xml_node<char> const* node)
auto const* width_attr = node->first_attribute("width");
if (width_attr)
{
- width = parse_svg_value(parser.error_messages_, width_attr->value(), has_percent_width);
+ width = parse_svg_value(parser.err_handler(), width_attr->value(), has_percent_width);
}
auto const* height_attr = node->first_attribute("height");
if (height_attr)
{
- height = parse_svg_value(parser.error_messages_, height_attr->value(), has_percent_height);
+ height = parse_svg_value(parser.err_handler(), height_attr->value(), has_percent_height);
}
+
auto const* viewbox_attr = node->first_attribute("viewBox");
if (viewbox_attr)
{
- has_viewbox = parse_double_list(parser.error_messages_, viewbox_attr->value(), viewbox);
- }
+ has_viewbox = parse_viewbox(parser.err_handler(), viewbox_attr->value(), vbox);
+ if (width > 0 && height > 0 && vbox.width > 0 && vbox.height > 0)
+ {
+ agg::trans_affine t{};
+ std::pair<unsigned,bool> preserve_aspect_ratio {xMidYMid, true};
+ auto const* aspect_ratio_attr = node->first_attribute("preserveAspectRatio");
+ if (aspect_ratio_attr)
+ {
+ preserve_aspect_ratio = parse_preserve_aspect_ratio(parser.err_handler(), aspect_ratio_attr->value());
+ }
+
+ double sx = width / vbox.width;
+ double sy = height / vbox.height;
+ double scale = preserve_aspect_ratio.second ? std::min(sx, sy) : std::max(sx, sy);
+ switch (preserve_aspect_ratio.first)
+ {
+ case none:
+ t = agg::trans_affine_scaling(sx, sy) * t;
+ break;
+ case xMinYMin:
+ t = agg::trans_affine_scaling(scale, scale) * t;
+ break;
+ case xMinYMid:
+ t = agg::trans_affine_scaling(scale, scale) * t;
+ t = agg::trans_affine_translation(0, -0.5 * (vbox.height - height / scale)) * t;
+ break;
+ case xMinYMax:
+ t = agg::trans_affine_scaling(scale, scale) * t;
+ t = agg::trans_affine_translation(0, -1.0 * (vbox.height - height / scale)) * t;
+ break;
+ case xMidYMin:
+ t = agg::trans_affine_scaling(scale, scale) * t;
+ t = agg::trans_affine_translation(-0.5 * (vbox.width - width / scale), 0.0) * t;
+ break;
+ case xMidYMid: // (the default)
+ t = agg::trans_affine_scaling(scale, scale) * t;
+ t = agg::trans_affine_translation(-0.5 * (vbox.width - width / scale),
+ -0.5 * (vbox.height - height / scale)) * t;
+ break;
+ case xMidYMax:
+ t = agg::trans_affine_scaling(scale, scale) * t;
+ t = agg::trans_affine_translation(-0.5 * (vbox.width - width / scale),
+ -1.0 * (vbox.height - height / scale)) * t;
+ break;
+ case xMaxYMin:
+ t = agg::trans_affine_scaling(scale, scale) * t;
+ t = agg::trans_affine_translation(-1.0 * (vbox.width - width / scale), 0.0) * t;
+ break;
+ case xMaxYMid:
+ t = agg::trans_affine_scaling(scale, scale) * t;
+ t = agg::trans_affine_translation(-1.0 * (vbox.width - width / scale),
+ -0.5 * (vbox.height - height / scale)) * t;
+ break;
+ case xMaxYMax:
+ t = agg::trans_affine_scaling(scale, scale) * t;
+ t = agg::trans_affine_translation(-1.0 * (vbox.width - width / scale),
+ -1.0 * (vbox.height - height / scale)) * t;
+ break;
+ };
+
+ t = agg::trans_affine_translation(-vbox.x0, -vbox.y0) * t;
+ parser.viewbox_tr_ = t;
+ }
+
+ }
if (has_percent_width && !has_percent_height && has_viewbox)
{
- aspect_ratio = viewbox[2] / viewbox[3];
+ aspect_ratio = vbox.width / vbox.height;
width = aspect_ratio * height;
}
else if (!has_percent_width && has_percent_height && has_viewbox)
{
- aspect_ratio = viewbox[2] / viewbox[3];
+ aspect_ratio = vbox.width/vbox.height;
height = height / aspect_ratio;
}
else if (has_percent_width && has_percent_height && has_viewbox)
{
- width = viewbox[2];
- height = viewbox[3];
+ width = vbox.width;
+ height = vbox.height;
}
-
parser.path_.set_dimensions(width, height);
}
@@ -569,16 +859,15 @@ void parse_path(svg_parser & parser, rapidxml::xml_node<char> const* node)
if (!mapnik::svg::parse_path(value, parser.path_))
{
- auto const* id_attr = node->first_attribute("xml:id");
- if (id_attr == nullptr) id_attr = node->first_attribute("id");
+ auto const* id_attr = parse_id(parser, node);
if (id_attr)
{
- parser.error_messages_.push_back(std::string("unable to parse invalid svg <path> with id '")
- + id_attr->value() + "'");
+ parser.err_handler().on_error(std::string("SVG parse error: failed to parse <path> with <id> \"")
+ + id_attr->value() + "\"");
}
else
{
- parser.error_messages_.push_back(std::string("unable to parse invalid svg <path>"));
+ parser.err_handler().on_error(std::string("SVG parse error: failed to parse <path>"));
}
}
parser.path_.end_path();
@@ -586,6 +875,74 @@ void parse_path(svg_parser & parser, rapidxml::xml_node<char> const* node)
}
}
+void parse_use(svg_parser & parser, rapidxml::xml_node<char> const* node)
+{
+ auto * attr = parse_href(node);
+ if (attr)
+ {
+ auto const* value = attr->value();
+ if (std::strlen(value) > 1 && value[0] == '#')
+ {
+ std::string id(&value[1]);
+ if (parser.node_cache_.count(id) > 0)
+ {
+ auto const* base_node = parser.node_cache_[id];
+ double x = 0.0;
+ double y = 0.0;
+ double w = 0.0;
+ double h = 0.0;
+ bool percent = false;
+ attr = node->first_attribute("x");
+ if (attr != nullptr)
+ {
+ x = parse_svg_value(parser.err_handler(), attr->value(), percent);
+ }
+
+ attr = node->first_attribute("y");
+ if (attr != nullptr)
+ {
+ y = parse_svg_value(parser.err_handler(), attr->value(), percent);
+ }
+
+ attr = node->first_attribute("width");
+ if (attr != nullptr)
+ {
+ w = parse_svg_value(parser.err_handler(), attr->value(), percent);
+ if (percent) w *= parser.path_.width();
+ }
+ attr = node->first_attribute("height");
+ if (attr)
+ {
+ h = parse_svg_value(parser.err_handler(), attr->value(), percent);
+ if (percent) h *= parser.path_.height();
+ }
+ if (w < 0.0)
+ {
+ std::stringstream ss;
+ ss << "SVG validation error: invalid <use> width \"" << w << "\"";
+ parser.err_handler().on_error(ss.str());
+ }
+ else if (h < 0.0)
+ {
+ std::stringstream ss;
+ ss << "SVG validation error: invalid <use> height \"" << w << "\"";
+ parser.err_handler().on_error(ss.str());
+ }
+ agg::trans_affine t{};
+ if (!node->first_attribute("transform") && w != 0.0 && h != 0.0)
+ {
+ // FIXME
+ double scale = std::min(double(w / parser.path_.width()), double(h / parser.path_.height()));
+ t *= agg::trans_affine_scaling(scale);
+ }
+ t *= agg::trans_affine_translation(x, y);
+ parser.path_.transform().premultiply(t);
+ traverse_tree(parser, base_node);
+ }
+ }
+ }
+}
+
void parse_polygon(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
auto const* attr = node->first_attribute("points");
@@ -594,7 +951,7 @@ void parse_polygon(svg_parser & parser, rapidxml::xml_node<char> const* node)
parser.path_.begin_path();
if (!mapnik::svg::parse_points(attr->value(), parser.path_))
{
- parser.error_messages_.push_back(std::string("Failed to parse <polygon> 'points'"));
+ parser.err_handler().on_error(std::string("SVG parse error: failed to parse <polygon> points"));
}
parser.path_.close_subpath();
parser.path_.end_path();
@@ -609,7 +966,7 @@ void parse_polyline(svg_parser & parser, rapidxml::xml_node<char> const* node)
parser.path_.begin_path();
if (!mapnik::svg::parse_points(attr->value(), parser.path_))
{
- parser.error_messages_.push_back(std::string("Failed to parse <polyline> 'points'"));
+ parser.err_handler().on_error(std::string("SVG parse error: failed to parse <polyline> points"));
}
parser.path_.end_path();
}
@@ -623,16 +980,16 @@ void parse_line(svg_parser & parser, rapidxml::xml_node<char> const* node)
double y2 = 0.0;
bool percent;
auto const* x1_attr = node->first_attribute("x1");
- if (x1_attr) x1 = parse_svg_value(parser.error_messages_, x1_attr->value(), percent);
+ if (x1_attr) x1 = parse_svg_value(parser.err_handler(), x1_attr->value(), percent);
auto const* y1_attr = node->first_attribute("y1");
- if (y1_attr) y1 = parse_svg_value(parser.error_messages_, y1_attr->value(), percent);
+ if (y1_attr) y1 = parse_svg_value(parser.err_handler(), y1_attr->value(), percent);
auto const* x2_attr = node->first_attribute("x2");
- if (x2_attr) x2 = parse_svg_value(parser.error_messages_, x2_attr->value(), percent);
+ if (x2_attr) x2 = parse_svg_value(parser.err_handler(), x2_attr->value(), percent);
auto const* y2_attr = node->first_attribute("y2");
- if (y2_attr) y2 = parse_svg_value(parser.error_messages_, y2_attr->value(), percent);
+ if (y2_attr) y2 = parse_svg_value(parser.err_handler(), y2_attr->value(), percent);
parser.path_.begin_path();
parser.path_.move_to(x1, y1);
@@ -649,19 +1006,19 @@ void parse_circle(svg_parser & parser, rapidxml::xml_node<char> const* node)
auto * attr = node->first_attribute("cx");
if (attr != nullptr)
{
- cx = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ cx = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
attr = node->first_attribute("cy");
if (attr != nullptr)
{
- cy = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ cy = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
attr = node->first_attribute("r");
if (attr != nullptr)
{
- r = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ r = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
parser.path_.begin_path();
@@ -669,7 +1026,9 @@ void parse_circle(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
if (r < 0.0)
{
- parser.error_messages_.emplace_back("parse_circle: Invalid radius");
+ std::stringstream ss;
+ ss << "SVG validation error: invalid <circle> radius \"" << r << "\"";
+ parser.err_handler().on_error(ss.str());
}
else
{
@@ -690,37 +1049,40 @@ void parse_ellipse(svg_parser & parser, rapidxml::xml_node<char> const * node)
auto * attr = node->first_attribute("cx");
if (attr != nullptr)
{
- cx = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ cx = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
attr = node->first_attribute("cy");
if (attr)
{
- cy = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ cy = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
attr = node->first_attribute("rx");
if (attr != nullptr)
{
- rx = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ rx = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
attr = node->first_attribute("ry");
if (attr != nullptr)
{
- ry = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ ry = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
if (rx != 0.0 && ry != 0.0)
{
-
if (rx < 0.0)
{
- parser.error_messages_.emplace_back("parse_ellipse: Invalid rx");
+ std::stringstream ss;
+ ss << "SVG validation error: invalid <ellipse> rx \"" << rx << "\"";
+ parser.err_handler().on_error(ss.str());
}
else if (ry < 0.0)
{
- parser.error_messages_.emplace_back("parse_ellipse: Invalid ry");
+ std::stringstream ss;
+ ss << "SVG validation error: invalid <ellipse> ry \"" << ry << "\"";
+ parser.err_handler().on_error(ss.str());
}
else
{
@@ -745,31 +1107,31 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node<char> const* node)
auto * attr = node->first_attribute("x");
if (attr != nullptr)
{
- x = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ x = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
attr = node->first_attribute("y");
if (attr != nullptr)
{
- y = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ y = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
attr = node->first_attribute("width");
if (attr != nullptr)
{
- w = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ w = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
attr = node->first_attribute("height");
if (attr)
{
- h = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ h = parse_svg_value(parser.err_handler(), attr->value(), percent);
}
bool rounded = true;
attr = node->first_attribute("rx");
if (attr != nullptr)
{
- rx = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ rx = parse_svg_value(parser.err_handler(), attr->value(), percent);
if ( rx > 0.5 * w ) rx = 0.5 * w;
}
else rounded = false;
@@ -777,7 +1139,7 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node<char> const* node)
attr = node->first_attribute("ry");
if (attr != nullptr)
{
- ry = parse_svg_value(parser.error_messages_, attr->value(), percent);
+ ry = parse_svg_value(parser.err_handler(), attr->value(), percent);
if ( ry > 0.5 * h ) ry = 0.5 * h;
if (!rounded)
{
@@ -792,21 +1154,29 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node<char> const* node)
if (w != 0.0 && h != 0.0)
{
- if(w < 0.0)
+ if (w < 0.0)
{
- parser.error_messages_.emplace_back("parse_rect: Invalid width");
+ std::stringstream ss;
+ ss << "SVG validation error: invalid <rect> width \"" << w << "\"";
+ parser.err_handler().on_error(ss.str());
}
- else if(h < 0.0)
+ else if (h < 0.0)
{
- parser.error_messages_.emplace_back("parse_rect: Invalid height");
+ std::stringstream ss;
+ ss << "SVG validation error: invalid <rect> height \"" << h << "\"";
+ parser.err_handler().on_error(ss.str());
}
- else if(rx < 0.0)
+ else if (rx < 0.0)
{
- parser.error_messages_.emplace_back("parse_rect: Invalid rx");
+ std::stringstream ss;
+ ss << "SVG validation error: invalid <rect> rx \"" << rx << "\"";
+ parser.err_handler().on_error(ss.str());
}
- else if(ry < 0.0)
+ else if (ry < 0.0)
{
- parser.error_messages_.emplace_back("parse_rect: Invalid ry");
+ std::stringstream ss;
+ ss << "SVG validation error: invalid <rect> ry \"" << ry << "\"";
+ parser.err_handler().on_error(ss.str());
}
else
{
@@ -832,7 +1202,7 @@ void parse_rect(svg_parser & parser, rapidxml::xml_node<char> const* node)
}
}
-void parse_gradient_stop(svg_parser & parser, rapidxml::xml_node<char> const* node)
+void parse_gradient_stop(svg_parser & parser, mapnik::gradient& gr, rapidxml::xml_node<char> const* node)
{
double offset = 0.0;
mapnik::color stop_color;
@@ -841,7 +1211,8 @@ void parse_gradient_stop(svg_parser & parser, rapidxml::xml_node<char> const* no
auto * attr = node->first_attribute("offset");
if (attr != nullptr)
{
- offset = parse_double(parser.error_messages_,attr->value());
+ bool percent = false;
+ offset = parse_svg_value(parser.err_handler(),attr->value(), percent);
}
attr = node->first_attribute("style");
@@ -856,11 +1227,11 @@ void parse_gradient_stop(svg_parser & parser, rapidxml::xml_node<char> const* no
{
if (kv.first == "stop-color")
{
- stop_color = parse_color(parser.error_messages_, kv.second.c_str());
+ stop_color = parse_color(parser.err_handler(), kv.second.c_str());
}
else if (kv.first == "stop-opacity")
{
- opacity = parse_double(parser.error_messages_,kv.second.c_str());
+ opacity = parse_double(parser.err_handler(),kv.second.c_str());
}
}
}
@@ -868,38 +1239,23 @@ void parse_gradient_stop(svg_parser & parser, rapidxml::xml_node<char> const* no
attr = node->first_attribute("stop-color");
if (attr != nullptr)
{
- stop_color = parse_color(parser.error_messages_, attr->value());
+ stop_color = parse_color(parser.err_handler(), attr->value());
}
attr = node->first_attribute("stop-opacity");
if (attr != nullptr)
{
- opacity = parse_double(parser.error_messages_, attr->value());
+ opacity = parse_double(parser.err_handler(), attr->value());
}
stop_color.set_alpha(static_cast<uint8_t>(opacity * 255));
- parser.temporary_gradient_.second.add_stop(offset, stop_color);
+ gr.add_stop(offset, stop_color);
}
-bool parse_common_gradient(svg_parser & parser, rapidxml::xml_node<char> const* node)
+bool parse_common_gradient(svg_parser & parser, std::string const& id, mapnik::gradient& gr, rapidxml::xml_node<char> const* node)
{
- std::string id;
- auto * attr = node->first_attribute("xml:id");
- if (attr == nullptr) attr = node->first_attribute("id");
-
- if (attr != nullptr)
- {
- // start a new gradient
- parser.temporary_gradient_ = std::make_pair(std::string(attr->value()), gradient());
- }
- else
- {
- // no point without an ID
- return false;
- }
-
// check if we should inherit from another tag
- attr = node->first_attribute("xlink:href");
+ auto * attr = parse_href(node);
if (attr != nullptr)
{
auto const* value = attr->value();
@@ -908,13 +1264,12 @@ bool parse_common_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
std::string linkid(&value[1]); // FIXME !!!
if (parser.gradient_map_.count(linkid))
{
- parser.temporary_gradient_.second = parser.gradient_map_[linkid];
+ gr = parser.gradient_map_[linkid];
}
else
{
- std::stringstream ss;
- ss << "Failed to find linked gradient " << linkid;
- parser.error_messages_.push_back(ss.str());
+ // save node for later
+ parser.node_cache_.emplace(id, node);
return false;
}
}
@@ -925,11 +1280,11 @@ bool parse_common_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
{
if (std::strcmp(attr->value(), "userSpaceOnUse") == 0)
{
- parser.temporary_gradient_.second.set_units(USER_SPACE_ON_USE);
+ gr.set_units(USER_SPACE_ON_USE);
}
else
{
- parser.temporary_gradient_.second.set_units(OBJECT_BOUNDING_BOX);
+ gr.set_units(OBJECT_BOUNDING_BOX);
}
}
@@ -938,14 +1293,19 @@ bool parse_common_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
{
agg::trans_affine tr;
mapnik::svg::parse_svg_transform(attr->value(),tr);
- parser.temporary_gradient_.second.set_transform(tr);
+ gr.set_transform(tr);
}
return true;
}
void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- parse_common_gradient(parser, node);
+ auto * attr = parse_id(parser, node);
+ if (attr == nullptr) return;
+ std::string id = attr->value();
+
+ mapnik::gradient gr;
+ if (!parse_common_gradient(parser, id, gr, node)) return;
double cx = 0.5;
double cy = 0.5;
double fx = 0.0;
@@ -953,22 +1313,22 @@ void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
double r = 0.5;
bool has_percent=true;
- auto * attr = node->first_attribute("cx");
+ attr = node->first_attribute("cx");
if (attr != nullptr)
{
- cx = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
+ cx = parse_svg_value(parser.err_handler(), attr->value(), has_percent);
}
attr = node->first_attribute("cy");
if (attr != nullptr)
{
- cy = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
+ cy = parse_svg_value(parser.err_handler(), attr->value(), has_percent);
}
attr = node->first_attribute("fx");
if (attr != nullptr)
{
- fx = parse_svg_value(parser.error_messages_,attr->value(), has_percent);
+ fx = parse_svg_value(parser.err_handler(),attr->value(), has_percent);
}
else
fx = cx;
@@ -976,83 +1336,105 @@ void parse_radial_gradient(svg_parser & parser, rapidxml::xml_node<char> const*
attr = node->first_attribute("fy");
if (attr != nullptr)
{
- fy = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
+ fy = parse_svg_value(parser.err_handler(), attr->value(), has_percent);
}
- else
- fy = cy;
+ else fy = cy;
attr = node->first_attribute("r");
if (attr != nullptr)
{
- r = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
+ r = parse_svg_value(parser.err_handler(), 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)
+ if (has_percent && gr.get_units() == USER_SPACE_ON_USE)
{
- parser.temporary_gradient_.second.set_units(USER_SPACE_ON_USE_BOUNDING_BOX);
+ gr.set_units(USER_SPACE_ON_USE_BOUNDING_BOX);
}
- parser.temporary_gradient_.second.set_gradient_type(RADIAL);
- parser.temporary_gradient_.second.set_control_points(fx,fy,cx,cy,r);
- // 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;
+ gr.set_gradient_type(RADIAL);
+ gr.set_control_points(fx, fy, cx, cy, r);
+ // parse stops
+ for (auto const* child = node->first_node();
+ child; child = child->next_sibling())
+ {
+ if (std::strcmp(child->name(), "stop") == 0)
+ {
+ parse_gradient_stop(parser, gr, child);
+ }
+ }
+ parser.gradient_map_[id] = gr;
//MAPNIK_LOG_DEBUG(svg_parser) << "Found Radial Gradient: " << " " << cx << " " << cy << " " << fx << " " << fy << " " << r;
}
void parse_linear_gradient(svg_parser & parser, rapidxml::xml_node<char> const* node)
{
- parse_common_gradient(parser, node);
+ auto const* attr = parse_id(parser, node);
+ if (attr == nullptr) return;
+
+ std::string id = attr->value();
+ mapnik::gradient gr;
+ if (!parse_common_gradient(parser, id, gr, node)) return;
double x1 = 0.0;
double x2 = 1.0;
double y1 = 0.0;
- double y2 = 1.0;
+ double y2 = 0.0;
bool has_percent=true;
- auto * attr = node->first_attribute("x1");
+ attr = node->first_attribute("x1");
if (attr != nullptr)
{
- x1 = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
+ x1 = parse_svg_value(parser.err_handler(), attr->value(), has_percent);
}
attr = node->first_attribute("x2");
if (attr != nullptr)
{
- x2 = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
+ x2 = parse_svg_value(parser.err_handler(), attr->value(), has_percent);
}
attr = node->first_attribute("y1");
if (attr != nullptr)
{
- y1 = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
+ y1 = parse_svg_value(parser.err_handler(), attr->value(), has_percent);
}
attr = node->first_attribute("y2");
if (attr != nullptr)
{
- y2 = parse_svg_value(parser.error_messages_, attr->value(), has_percent);
+ y2 = parse_svg_value(parser.err_handler(), 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)
+ if (has_percent && gr.get_units() == USER_SPACE_ON_USE)
{
- parser.temporary_gradient_.second.set_units(USER_SPACE_ON_USE_BOUNDING_BOX);
+ gr.set_units(USER_SPACE_ON_USE_BOUNDING_BOX);
}
- parser.temporary_gradient_.second.set_gradient_type(LINEAR);
- 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;
+ gr.set_gradient_type(LINEAR);
+ gr.set_control_points(x1, y1, x2, y2);
+
+ // parse stops
+ for (auto const* child = node->first_node();
+ child; child = child->next_sibling())
+ {
+ if (std::strcmp(child->name(), "stop") == 0)
+ {
+ parse_gradient_stop(parser, gr, child);
+ }
+ }
+ parser.gradient_map_[id] = gr;
}
svg_parser::svg_parser(svg_converter<svg_path_adapter,
- agg::pod_bvector<mapnik::svg::path_attributes> > & path)
+ agg::pod_bvector<mapnik::svg::path_attributes> > & path, bool strict)
: path_(path),
- is_defs_(false) {}
+ is_defs_(false),
+ err_handler_(strict) {}
svg_parser::~svg_parser() {}
-bool svg_parser::parse(std::string const& filename)
+void svg_parser::parse(std::string const& filename)
{
#ifdef _WINDOWS
std::basic_ifstream<char> stream(mapnik::utf8_to_utf16(filename));
@@ -1062,9 +1444,8 @@ bool svg_parser::parse(std::string const& filename)
if (!stream)
{
std::stringstream ss;
- ss << "Unable to open '" << filename << "'";
- error_messages_.push_back(ss.str());
- return false;
+ ss << "SVG error: unable to open \"" << filename << "\"";
+ throw std::runtime_error(ss.str());
}
stream.unsetf(std::ios::skipws);
@@ -1081,9 +1462,8 @@ bool svg_parser::parse(std::string const& filename)
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;
+ ss << "SVG error: unable to parse \"" << filename << "\"";
+ throw std::runtime_error(ss.str());
}
for (rapidxml::xml_node<char> const* child = doc.first_node();
@@ -1091,10 +1471,9 @@ bool svg_parser::parse(std::string const& filename)
{
traverse_tree(*this, child);
}
- return error_messages_.empty() ? true : false;
}
-bool svg_parser::parse_from_string(std::string const& svg)
+void svg_parser::parse_from_string(std::string const& svg)
{
const int flags = rapidxml::parse_trim_whitespace | rapidxml::parse_validate_closing_tags;
rapidxml::xml_document<> doc;
@@ -1107,21 +1486,20 @@ bool svg_parser::parse_from_string(std::string const& svg)
catch (rapidxml::parse_error const& ex)
{
std::stringstream ss;
- ss << "Unable to parse '" << svg << "'";
- error_messages_.push_back(ss.str());
- return false;
+ std::string str = (svg.length() > 1024) ? svg.substr(0, 1024) + "..." : svg;
+ ss << "SVG error: unable to parse \"" << str << "\"";
+ throw std::runtime_error(ss.str());
}
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;
}
-svg_parser::error_message_container const& svg_parser::error_messages() const
+svg_parser::error_handler & svg_parser::err_handler()
{
- return error_messages_;
+ return err_handler_;
}
}}
diff --git a/src/text/renderer.cpp b/src/text/renderer.cpp
index 163d11c..a549d6c 100644
--- a/src/text/renderer.cpp
+++ b/src/text/renderer.cpp
@@ -178,13 +178,16 @@ void agg_text_renderer<T>::render(glyph_positions const& pos)
if (!error)
{
FT_BitmapGlyph bit = reinterpret_cast<FT_BitmapGlyph>(g);
- composite_bitmap(pixmap_,
- &bit->bitmap,
- halo_fill,
- bit->left,
- height - bit->top,
- halo_opacity,
- halo_comp_op_);
+ if (bit->bitmap.pixel_mode != FT_PIXEL_MODE_BGRA)
+ {
+ composite_bitmap(pixmap_,
+ &bit->bitmap,
+ halo_fill,
+ bit->left,
+ height - bit->top,
+ halo_opacity,
+ halo_comp_op_);
+ }
}
}
else
diff --git a/src/wkb.cpp b/src/wkb.cpp
index 94c1fcc..0882c73 100644
--- a/src/wkb.cpp
+++ b/src/wkb.cpp
@@ -354,6 +354,7 @@ private:
{
int num_polys = read_integer();
mapnik::geometry::multi_polygon<double> multi_poly;
+ multi_poly.reserve(num_polys);
for (int i = 0; i < num_polys; ++i)
{
pos_ += 5;
@@ -366,6 +367,7 @@ private:
{
int num_geometries = read_integer();
mapnik::geometry::geometry_collection<double> collection;
+ collection.reserve(num_geometries);
for (int i = 0; i < num_geometries; ++i)
{
pos_ += 1; // skip byte order
diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp
index 0e38304..2144b57 100644
--- a/test/unit/svg/svg_parser_test.cpp
+++ b/test/unit/svg/svg_parser_test.cpp
@@ -2,7 +2,7 @@
*
* This file is part of Mapnik (c++ mapping toolkit)
*
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2017 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -44,11 +44,11 @@ namespace // internal
mapnik::svg::svg_converter_type svg;
mapnik::svg::svg_parser p;
- test_parser()
+ explicit test_parser(bool strict = false)
: stl_storage(path.source())
, svg_path(stl_storage)
, svg(svg_path, path.attributes())
- , p(svg)
+ , p(svg, strict)
{}
mapnik::svg::svg_parser* operator->()
@@ -87,12 +87,18 @@ TEST_CASE("SVG parser") {
std::string svg_name("FAIL");
char const* expected_errors[] =
{
- "Unable to open 'FAIL'"
+ "SVG error: unable to open \"FAIL\""
};
test_parser p;
- REQUIRE(!p->parse(svg_name));
- REQUIRE(join(p->error_messages()) == join(expected_errors));
+ try
+ {
+ p->parse(svg_name);
+ }
+ catch (std::exception const& ex)
+ {
+ REQUIRE(ex.what() == join(expected_errors));
+ }
}
SECTION("SVG::parse_from_string syntax error")
@@ -100,7 +106,7 @@ TEST_CASE("SVG parser") {
std::string svg_name("./test/data/svg/invalid.svg");
char const* expected_errors[] =
{
- "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'"
+ "SVG error: 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\""
};
std::ifstream in(svg_name.c_str());
@@ -108,8 +114,14 @@ TEST_CASE("SVG parser") {
std::istreambuf_iterator<char>());
test_parser p;
- REQUIRE(!p->parse_from_string(svg_str));
- REQUIRE(join(p->error_messages()) == join(expected_errors));
+ try
+ {
+ p->parse_from_string(svg_str);
+ }
+ catch (std::exception const& ex)
+ {
+ REQUIRE(ex.what() == join(expected_errors));
+ }
}
SECTION("SVG::parse_from_string syntax error")
@@ -117,12 +129,18 @@ TEST_CASE("SVG parser") {
std::string svg_name("./test/data/svg/invalid.svg");
char const* expected_errors[] =
{
- "svg_parser::parse - Unable to parse './test/data/svg/invalid.svg'"
+ "SVG error: unable to parse \"./test/data/svg/invalid.svg\""
};
test_parser p;
- REQUIRE(!p->parse(svg_name));
- REQUIRE(join(p->error_messages()) == join(expected_errors));
+ try
+ {
+ p->parse(svg_name);
+ }
+ catch (std::exception const& ex)
+ {
+ REQUIRE(ex.what() == join(expected_errors));
+ }
}
SECTION("SVG parser color <fail>")
@@ -131,18 +149,31 @@ TEST_CASE("SVG parser") {
std::string svg_name("./test/data/svg/color_fail.svg");
char const* expected_errors[] =
{
- "Failed to parse color: \"fail\"",
- "Failed to parse SVG value: 'fail'",
- "Failed to parse color: \"fail\"",
+ "SVG parse error: failed to parse <color> with value \"fail\"",
+ "SVG parse error: failed to parse <number> with value \"fail\"",
+ "SVG parse error: failed to parse <color> with value \"fail\""
};
std::ifstream in(svg_name.c_str());
std::string svg_str((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
- test_parser p;
- REQUIRE(!p->parse_from_string(svg_str));
- REQUIRE(join(p->error_messages()) == join(expected_errors));
+ {
+ test_parser p;
+ p->parse_from_string(svg_str);
+ REQUIRE(join(p->err_handler().error_messages()) == join(expected_errors));
+ }
+ {
+ test_parser p(true);
+ try
+ {
+ p->parse_from_string(svg_str);
+ }
+ catch (std::exception const& ex)
+ {
+ REQUIRE(ex.what() == std::string(expected_errors[0]));
+ }
+ }
}
SECTION("SVG - cope with erroneous geometries")
@@ -150,29 +181,44 @@ TEST_CASE("SVG parser") {
std::string svg_name("./test/data/svg/errors.svg");
char const* expected_errors[] =
{
- "parse_rect: Invalid width",
- "Failed to parse SVG value: 'FAIL'",
- "parse_rect: Invalid height",
- "parse_rect: Invalid rx",
- "parse_rect: Invalid ry",
- "Failed to parse SVG value: '100invalidunit', trailing garbage: 'validunit'",
- "unable to parse invalid svg <path>",
- "unable to parse invalid svg <path> with id 'fail-path'",
- "unable to parse invalid svg <path> with id 'fail-path'",
- "parse_circle: Invalid radius",
- "Failed to parse <polygon> 'points'",
- "Failed to parse <polyline> 'points'",
- "parse_ellipse: Invalid rx",
- "parse_ellipse: Invalid ry",
+ "SVG validation error: invalid <rect> width \"-100\"",
+ "SVG parse error: failed to parse <number> with value \"FAIL\"",
+ "SVG validation error: invalid <rect> height \"-100\"",
+ "SVG validation error: invalid <rect> rx \"-1000\"",
+ "SVG validation error: invalid <rect> ry \"-1000\"",
+ "SVG parse error: failed to parse <number> with value \"100invalidunit\"",
+ "SVG parse error: failed to parse <path>",
+ "SVG parse error: failed to parse <path> with <id> \"fail-path\"",
+ "SVG parse error: failed to parse <path> with <id> \"fail-path\"",
+ "SVG validation error: invalid <circle> radius \"-50\"",
+ "SVG parse error: failed to parse <polygon> points",
+ "SVG parse error: failed to parse <polyline> points",
+ "SVG validation error: invalid <ellipse> rx \"-10\"",
+ "SVG validation error: invalid <ellipse> ry \"-10\""
};
std::ifstream in(svg_name.c_str());
std::string svg_str((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
- test_parser p;
- REQUIRE(!p->parse_from_string(svg_str));
- REQUIRE(join(p->error_messages()) == join(expected_errors));
+ {
+ test_parser p;
+ p->parse_from_string(svg_str);
+ REQUIRE(join(p->err_handler().error_messages()) == join(expected_errors));
+ }
+
+ {
+ // strict
+ test_parser p(true);
+ try
+ {
+ p->parse_from_string(svg_str);
+ }
+ catch (std::exception const& ex)
+ {
+ REQUIRE(ex.what() == std::string(expected_errors[0]));
+ }
+ }
}
SECTION("SVG parser double % <fail>")
@@ -181,16 +227,29 @@ TEST_CASE("SVG parser") {
std::string svg_name("./test/data/svg/gradient-radial-error.svg");
char const* expected_errors[] =
{
- "Failed to parse SVG value: 'FAIL'"
+ "SVG parse error: failed to parse <number> with value \"FAIL\""
};
std::ifstream in(svg_name.c_str());
std::string svg_str((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
- test_parser p;
- REQUIRE(!p->parse_from_string(svg_str));
- REQUIRE(join(p->error_messages()) == join(expected_errors));
+ {
+ test_parser p;
+ p->parse_from_string(svg_str);
+ REQUIRE(join(p->err_handler().error_messages()) == join(expected_errors));
+ }
+ {
+ test_parser p(true);
+ try
+ {
+ p->parse_from_string(svg_str);
+ }
+ catch (std::exception const& ex)
+ {
+ REQUIRE(ex.what() == std::string(expected_errors[0]));
+ }
+ }
}
SECTION("SVG parser display=none")
@@ -327,7 +386,7 @@ TEST_CASE("SVG parser") {
std::string svg_str((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
test_parser p;
- REQUIRE(p->parse_from_string(svg_str));
+ p->parse_from_string(svg_str);
auto width = p.svg.width();
auto height = p.svg.height();
REQUIRE(width == 100);
@@ -408,7 +467,8 @@ TEST_CASE("SVG parser") {
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,1199.0,399.0));
+ //REQUIRE(bbox == mapnik::box2d<double>(0.3543307086614174,0.3543307086614174,
+ // 424.8425196850394059,141.3779527559055396));
auto storage = svg.get_data();
REQUIRE(storage);
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
@@ -455,7 +515,7 @@ TEST_CASE("SVG parser") {
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,1199.0,399.0));
+ //REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,1199.0,399.0));
auto storage = svg.get_data();
REQUIRE(storage);
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
@@ -511,7 +571,7 @@ TEST_CASE("SVG parser") {
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,1199.0,399.0));
+ //REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,1199.0,399.0));
auto storage = svg.get_data();
REQUIRE(storage);
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(storage->source());
@@ -564,7 +624,7 @@ TEST_CASE("SVG parser") {
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));
+ //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());
@@ -606,13 +666,25 @@ TEST_CASE("SVG parser") {
std::string svg_name("./test/data/svg/gradient-nodef.svg");
char const* expected_errors[] =
{
- "Failed to find gradient fill: MyGradient",
- "Failed to find gradient stroke: MyGradient",
+ "SVG parse error: failed to locate <gradient> fill with <id> \"MyGradient\"",
+ "SVG parse error: failed to locate <gradient> stroke with <id> \"MyGradient\""
};
-
- test_parser p;
- REQUIRE(!p->parse(svg_name));
- REQUIRE(join(p->error_messages()) == join(expected_errors));
+ {
+ test_parser p;
+ p->parse(svg_name);
+ REQUIRE(join(p->err_handler().error_messages()) == join(expected_errors));
+ }
+ {
+ test_parser p(true);
+ try
+ {
+ p->parse(svg_name);
+ }
+ catch (std::exception const& ex)
+ {
+ REQUIRE(ex.what() == std::string(expected_errors[0]));
+ }
+ }
}
SECTION("SVG missing <gradient> id")
@@ -620,17 +692,30 @@ TEST_CASE("SVG parser") {
std::string svg_name("./test/data/svg/gradient-no-id.svg");
char const* expected_errors[] =
{
- "Failed to find gradient fill: MyGradient",
- "Failed to find gradient stroke: MyGradient",
+ "SVG parse error: failed to locate <gradient> fill with <id> \"MyGradient\"",
+ "SVG parse error: failed to locate <gradient> stroke with <id> \"MyGradient\""
};
std::ifstream in(svg_name.c_str());
std::string svg_str((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
- test_parser p;
- REQUIRE(!p->parse_from_string(svg_str));
- REQUIRE(join(p->error_messages()) == join(expected_errors));
+ {
+ test_parser p;
+ p->parse_from_string(svg_str);
+ REQUIRE(join(p->err_handler().error_messages()) == join(expected_errors));
+ }
+ {
+ test_parser p(true);
+ try
+ {
+ p->parse_from_string(svg_str);
+ }
+ catch (std::exception const& ex)
+ {
+ REQUIRE(ex.what() == std::string(expected_errors[0]));
+ }
+ }
}
SECTION("SVG missing <gradient> inheritance")
@@ -642,7 +727,7 @@ TEST_CASE("SVG parser") {
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));
+ //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());
@@ -692,7 +777,7 @@ TEST_CASE("SVG parser") {
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));
+ //REQUIRE(bbox == mapnik::box2d<double>(1.0,1.0,799.0,599.0));
auto storage = svg.get_data();
REQUIRE(storage);
@@ -713,7 +798,7 @@ TEST_CASE("SVG parser") {
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));
+ //REQUIRE(bbox == mapnik::box2d<double>(20,20,460,230));
auto storage = svg.get_data();
REQUIRE(storage);
@@ -732,7 +817,7 @@ TEST_CASE("SVG parser") {
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));
+ //REQUIRE(bbox == mapnik::box2d<double>(0,0,200,200));
auto storage = svg.get_data();
REQUIRE(storage);
diff --git a/utils/mapnik-config/build.py b/utils/mapnik-config/build.py
index 9ffd731..cc34908 100644
--- a/utils/mapnik-config/build.py
+++ b/utils/mapnik-config/build.py
@@ -67,9 +67,9 @@ CONFIG_MAPNIK_INCLUDE="${CONFIG_PREFIX}/include -I${CONFIG_PREFIX}/include/mapni
CONFIG_DEP_INCLUDES="%(dep_includes)s"
CONFIG_CXXFLAGS="%(cxxflags)s"
CONFIG_CXX='%(cxx)s'
-CONFIG_MAPNIK_GDAL_DATA='%(mapnik_bundled_gdal_data)s'
-CONFIG_MAPNIK_PROJ_LIB='%(mapnik_bundled_proj_data)s'
-CONFIG_MAPNIK_ICU_DATA='%(mapnik_bundled_icu_data)s'
+CONFIG_MAPNIK_GDAL_DATA='%(found_gdal_data)s'
+CONFIG_MAPNIK_PROJ_LIB='%(found_proj_data)s'
+CONFIG_MAPNIK_ICU_DATA='%(found_icu_data)s'
'''
@@ -135,9 +135,9 @@ if lib_root in inputpluginspath:
lib_path = "${CONFIG_PREFIX}/" + config_env['LIBDIR_SCHEMA']
-mapnik_bundled_gdal_data = ''
-mapnik_bundled_proj_data = ''
-mapnik_bundled_icu_data = ''
+found_gdal_data = config_env['QUERIED_GDAL_DATA']
+found_proj_data = config_env['QUERIED_PROJ_LIB']
+found_icu_data = config_env['QUERIED_ICU_DATA']
configuration = {
"git_revision": git_revision,
@@ -154,9 +154,9 @@ configuration = {
"defines":defines,
"cxxflags":cxxflags,
"cxx":env['CXX'],
- "mapnik_bundled_gdal_data":mapnik_bundled_gdal_data,
- "mapnik_bundled_proj_data":mapnik_bundled_proj_data,
- "mapnik_bundled_icu_data":mapnik_bundled_icu_data,
+ "found_gdal_data":found_gdal_data,
+ "found_proj_data":found_proj_data,
+ "found_icu_data":found_icu_data,
}
## if we are statically linking dependencies
diff --git a/utils/mapnik-config/mapnik-config.template.sh b/utils/mapnik-config/mapnik-config.template.sh
index 27dc32c..a1ef44b 100755
--- a/utils/mapnik-config/mapnik-config.template.sh
+++ b/utils/mapnik-config/mapnik-config.template.sh
@@ -27,9 +27,9 @@ Known values for OPTION are:
--cflags all include paths, compiler flags, and pre-processor defines (for back-compatibility)
--cxx c++ compiler used to build mapnik (new in 2.2.0)
--all-flags all compile and link flags (new in 2.2.0)
- --gdal-data path to GDAL_DATA directory, if known (relevant only for packaged builds of Mapnik) (new in 3.0.0)
- --proj-lib path to PROJ_LIB directory, if known (relevant only for packaged builds of Mapnik) (new in 3.0.0)
- --icu-data path to ICU_DATA directory, if known (relevant only for packaged builds of Mapnik) (new in 3.0.0)
+ --gdal-data path to GDAL_DATA directory, if detected at build time (new in 3.0.16)
+ --proj-lib path to PROJ_LIB directory, if detected at build time (new in 3.0.16)
+ --icu-data path to ICU_DATA directory, if detected at build time (new in 3.0.16)
EOF
exit $1
@@ -132,15 +132,15 @@ while test $# -gt 0; do
;;
--gdal-data)
- if [[ ${CONFIG_MAPNIK_GDAL_DATA:-unset} != "unset" ]]; then echo ${CONFIG_PREFIX}/${CONFIG_MAPNIK_GDAL_DATA}; fi;
+ if [[ ${CONFIG_MAPNIK_GDAL_DATA:-unset} != "unset" ]]; then echo ${CONFIG_MAPNIK_GDAL_DATA}; fi;
;;
--proj-lib)
- if [[ ${CONFIG_MAPNIK_PROJ_LIB:-unset} != "unset" ]]; then echo ${CONFIG_PREFIX}/${CONFIG_MAPNIK_PROJ_LIB}; fi;
+ if [[ ${CONFIG_MAPNIK_PROJ_LIB:-unset} != "unset" ]]; then echo ${CONFIG_MAPNIK_PROJ_LIB}; fi;
;;
--icu-data)
- if [[ ${CONFIG_MAPNIK_ICU_DATA:-unset} != "unset" ]]; then echo ${CONFIG_PREFIX}/${CONFIG_MAPNIK_ICU_DATA}; fi;
+ if [[ ${CONFIG_MAPNIK_ICU_DATA:-unset} != "unset" ]]; then echo ${CONFIG_MAPNIK_ICU_DATA}; fi;
;;
*)
diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp
index b78f53b..11cc01e 100644
--- a/utils/svg2png/svg2png.cpp
+++ b/utils/svg2png/svg2png.cpp
@@ -2,7 +2,7 @@
*
* This file is part of Mapnik (c++ mapping toolkit)
*
- * Copyright (C) 2015 Artem Pavlenko
+ * Copyright (C) 2017 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -69,30 +69,19 @@ struct main_marker_visitor
agg::scanline_u8 sl;
double opacity = 1;
- int w = marker.width();
- int h = marker.height();
- if (w == 0 || h == 0)
- {
- // fallback to svg width/height or viewBox
- std::tie(w, h) = marker.dimensions();
- }
+ double w, h;
+ std::tie(w, h) = marker.dimensions();
if (verbose_)
{
std::clog << "found width of '" << w << "' and height of '" << h << "'\n";
}
- // 10 pixel buffer to avoid edge clipping of 100% svg's
- mapnik::image_rgba8 im(w+0,h+0);
+ mapnik::image_rgba8 im(static_cast<int>(w + 0.5), static_cast<int>(h + 0.5));
agg::rendering_buffer buf(im.bytes(), im.width(), im.height(), im.row_size());
pixfmt pixf(buf);
renderer_base renb(pixf);
- mapnik::box2d<double> const& bbox = marker.get_data()->bounding_box();
- mapnik::coord<double,2> c = bbox.center();
- // center the svg marker on '0,0'
- agg::trans_affine mtx = agg::trans_affine_translation(-c.x,-c.y);
- // render the marker at the center of the marker box
- mtx.translate(0.5 * im.width(), 0.5 * im.height());
-
+ mapnik::box2d<double> const& bbox = {0, 0, w, h};
+ agg::trans_affine mtx = {};
mapnik::svg::vertex_stl_adapter<mapnik::svg::svg_path_storage> stl_storage(marker.get_data()->source());
mapnik::svg::svg_path_adapter svg_path(stl_storage);
mapnik::svg::svg_renderer_agg<mapnik::svg::svg_path_adapter,
@@ -128,7 +117,7 @@ struct main_marker_visitor
template <typename T>
int operator() (T const&) const
{
- std::clog << "svg2png error: '" << svg_name_ << "' is not a valid vector!\n";
+ std::clog << "svg2png error: failed to process '" << svg_name_ << "'\n";
return -1;
}
@@ -144,6 +133,7 @@ int main (int argc,char** argv)
bool verbose = false;
bool auto_open = false;
+ bool strict = false;
int status = 0;
std::vector<std::string> svg_files;
mapnik::logger::instance().set_severity(mapnik::logger::error);
@@ -155,19 +145,20 @@ int main (int argc,char** argv)
("help,h", "produce usage message")
("version,V","print version string")
("verbose,v","verbose output")
- ("open","automatically open the file after rendering (os x only)")
+ ("open,o","automatically open the file after rendering (os x only)")
+ ("strict,s","enables strict SVG parsing")
("svg",po::value<std::vector<std::string> >(),"svg file to read")
;
po::positional_options_description p;
- p.add("svg",-1);
+ p.add("svg", -1);
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
po::notify(vm);
if (vm.count("version"))
{
- std::clog <<"version " << MAPNIK_VERSION_STRING << std::endl;
+ std::clog << "version " << MAPNIK_VERSION_STRING << std::endl;
return 1;
}
@@ -187,6 +178,11 @@ int main (int argc,char** argv)
auto_open = true;
}
+ if (vm.count("strict"))
+ {
+ strict = true;
+ }
+
if (vm.count("svg"))
{
svg_files=vm["svg"].as< std::vector<std::string> >();
@@ -211,8 +207,7 @@ int main (int argc,char** argv)
{
std::clog << "found: " << svg_name << "\n";
}
-
- std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false);
+ std::shared_ptr<mapnik::marker const> marker = mapnik::marker_cache::instance().find(svg_name, false, strict);
main_marker_visitor visitor(svg_name, verbose, auto_open);
status = mapnik::util::apply_visitor(visitor, *marker);
}
--
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