Merge pull request #3418 from mapycz/visual-tests-cairo-vectors

visual tests: support for Cairo PS, PDF and SVG renderers
This commit is contained in:
Artem Pavlenko 2016-05-18 15:05:27 +02:00
commit 536ea734e8
2 changed files with 137 additions and 35 deletions

View file

@ -38,10 +38,21 @@
#if defined(GRID_RENDERER) #if defined(GRID_RENDERER)
#include <mapnik/grid/grid_renderer.hpp> #include <mapnik/grid/grid_renderer.hpp>
#endif #endif
#if defined(HAVE_CAIRO) #if defined(HAVE_CAIRO)
#include <mapnik/cairo/cairo_renderer.hpp> #include <mapnik/cairo/cairo_renderer.hpp>
#include <mapnik/cairo/cairo_image_util.hpp> #include <mapnik/cairo/cairo_image_util.hpp>
#ifdef CAIRO_HAS_SVG_SURFACE
#include <cairo-svg.h>
#endif #endif
#ifdef CAIRO_HAS_PS_SURFACE
#include <cairo-ps.h>
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
#include <cairo-pdf.h>
#endif
#endif
#if defined(SVG_RENDERER) #if defined(SVG_RENDERER)
#include <mapnik/svg/output/svg_renderer.hpp> #include <mapnik/svg/output/svg_renderer.hpp>
#endif #endif
@ -53,7 +64,7 @@ namespace visual_tests
{ {
template <typename ImageType> template <typename ImageType>
struct renderer_base struct raster_renderer_base
{ {
using image_type = ImageType; using image_type = ImageType;
@ -80,7 +91,35 @@ struct renderer_base
} }
}; };
struct agg_renderer : renderer_base<mapnik::image_rgba8> struct vector_renderer_base
{
using image_type = std::string;
static constexpr const bool support_tiles = false;
unsigned compare(image_type const & actual, boost::filesystem::path const& reference) const
{
std::ifstream stream(reference.string().c_str(), std::ios_base::in | std::ios_base::binary);
if (!stream)
{
throw std::runtime_error("Could not open: " + reference.string());
}
std::string expected(std::istreambuf_iterator<char>(stream.rdbuf()), std::istreambuf_iterator<char>());
return std::max(actual.size(), expected.size()) - std::min(actual.size(), expected.size());
}
void save(image_type const & image, boost::filesystem::path const& path) const
{
std::ofstream file(path.string().c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
if (!file)
{
throw std::runtime_error("Cannot open file for writing: " + path.string());
}
file << image;
}
};
struct agg_renderer : raster_renderer_base<mapnik::image_rgba8>
{ {
static constexpr const char * name = "agg"; static constexpr const char * name = "agg";
@ -94,7 +133,7 @@ struct agg_renderer : renderer_base<mapnik::image_rgba8>
}; };
#if defined(HAVE_CAIRO) #if defined(HAVE_CAIRO)
struct cairo_renderer : renderer_base<mapnik::image_rgba8> struct cairo_renderer : raster_renderer_base<mapnik::image_rgba8>
{ {
static constexpr const char * name = "cairo"; static constexpr const char * name = "cairo";
@ -111,14 +150,65 @@ struct cairo_renderer : renderer_base<mapnik::image_rgba8>
return image; return image;
} }
}; };
using surface_create_type = cairo_surface_t *(&)(cairo_write_func_t, void *, double, double);
template <surface_create_type SurfaceCreateFunction>
struct cairo_vector_renderer : vector_renderer_base
{
static cairo_status_t write(void *closure,
const unsigned char *data,
unsigned int length)
{
std::ostringstream & ss = *reinterpret_cast<std::ostringstream*>(closure);
ss.write(reinterpret_cast<char const *>(data), length);
return ss ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
}
image_type render(mapnik::Map const & map, double scale_factor) const
{
std::ostringstream ss(std::stringstream::binary);
mapnik::cairo_surface_ptr image_surface(
SurfaceCreateFunction(write, &ss, map.width(), map.height()),
mapnik::cairo_surface_closer());
mapnik::cairo_ptr image_context(mapnik::create_context(image_surface));
mapnik::cairo_renderer<mapnik::cairo_ptr> ren(map, image_context, scale_factor);
ren.apply();
cairo_surface_finish(&*image_surface);
return ss.str();
}
};
#ifdef CAIRO_HAS_SVG_SURFACE
struct cairo_svg_renderer : cairo_vector_renderer<cairo_svg_surface_create_for_stream>
{
static constexpr const char * name = "cairo-svg";
static constexpr const char * ext = ".svg";
};
#endif
#ifdef CAIRO_HAS_PS_SURFACE
struct cairo_ps_renderer : cairo_vector_renderer<cairo_ps_surface_create_for_stream>
{
static constexpr const char * name = "cairo-ps";
static constexpr const char * ext = ".ps";
};
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
struct cairo_pdf_renderer : cairo_vector_renderer<cairo_pdf_surface_create_for_stream>
{
static constexpr const char * name = "cairo-pdf";
static constexpr const char * ext = ".pdf";
};
#endif
#endif #endif
#if defined(SVG_RENDERER) #if defined(SVG_RENDERER)
struct svg_renderer : renderer_base<std::string> struct svg_renderer : vector_renderer_base
{ {
static constexpr const char * name = "svg"; static constexpr const char * name = "svg";
static constexpr const char * ext = ".svg"; static constexpr const char * ext = ".svg";
static constexpr const bool support_tiles = false;
image_type render(mapnik::Map const & map, double scale_factor) const image_type render(mapnik::Map const & map, double scale_factor) const
{ {
@ -128,35 +218,11 @@ struct svg_renderer : renderer_base<std::string>
ren.apply(); ren.apply();
return ss.str(); return ss.str();
} }
unsigned compare(image_type const & actual, boost::filesystem::path const& reference) const
{
std::ifstream stream(reference.string().c_str(),std::ios_base::in|std::ios_base::binary);
if (!stream.is_open())
{
throw std::runtime_error("could not open: '" + reference.string() + "'");
}
std::string expected(std::istreambuf_iterator<char>(stream.rdbuf()),(std::istreambuf_iterator<char>()));
stream.close();
return std::max(actual.size(), expected.size()) - std::min(actual.size(), expected.size());
}
void save(image_type const & image, boost::filesystem::path const& path) const
{
std::ofstream file(path.string().c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
if (!file) {
throw std::runtime_error((std::string("cannot open file for writing file ") + path.string()).c_str());
} else {
file << image;
file.close();
}
}
}; };
#endif #endif
#if defined(GRID_RENDERER) #if defined(GRID_RENDERER)
struct grid_renderer : renderer_base<mapnik::image_rgba8> struct grid_renderer : raster_renderer_base<mapnik::image_rgba8>
{ {
static constexpr const char * name = "grid"; static constexpr const char * name = "grid";
@ -335,6 +401,15 @@ private:
using renderer_type = mapnik::util::variant<renderer<agg_renderer> using renderer_type = mapnik::util::variant<renderer<agg_renderer>
#if defined(HAVE_CAIRO) #if defined(HAVE_CAIRO)
,renderer<cairo_renderer> ,renderer<cairo_renderer>
#ifdef CAIRO_HAS_SVG_SURFACE
,renderer<cairo_svg_renderer>
#endif
#ifdef CAIRO_HAS_PS_SURFACE
,renderer<cairo_ps_renderer>
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
,renderer<cairo_pdf_renderer>
#endif
#endif #endif
#if defined(SVG_RENDERER) #if defined(SVG_RENDERER)
,renderer<svg_renderer> ,renderer<svg_renderer>

View file

@ -48,30 +48,48 @@ namespace po = boost::program_options;
runner::renderer_container create_renderers(po::variables_map const & args, runner::renderer_container create_renderers(po::variables_map const & args,
boost::filesystem::path const & output_dir, boost::filesystem::path const & output_dir,
bool append_all = false) bool force_append = false)
{ {
boost::filesystem::path reference_dir(args["images-dir"].as<std::string>()); boost::filesystem::path reference_dir(args["images-dir"].as<std::string>());
bool overwrite = args.count("overwrite"); bool overwrite = args.count("overwrite");
runner::renderer_container renderers; runner::renderer_container renderers;
if (append_all || args.count(agg_renderer::name)) if (force_append || args.count(agg_renderer::name))
{ {
renderers.emplace_back(renderer<agg_renderer>(output_dir, reference_dir, overwrite)); renderers.emplace_back(renderer<agg_renderer>(output_dir, reference_dir, overwrite));
} }
#if defined(HAVE_CAIRO) #if defined(HAVE_CAIRO)
if (append_all || args.count(cairo_renderer::name)) if (force_append || args.count(cairo_renderer::name))
{ {
renderers.emplace_back(renderer<cairo_renderer>(output_dir, reference_dir, overwrite)); renderers.emplace_back(renderer<cairo_renderer>(output_dir, reference_dir, overwrite));
} }
#ifdef CAIRO_HAS_SVG_SURFACE
if (args.count(cairo_svg_renderer::name))
{
renderers.emplace_back(renderer<cairo_svg_renderer>(output_dir, reference_dir, overwrite));
}
#endif
#ifdef CAIRO_HAS_PS_SURFACE
if (args.count(cairo_ps_renderer::name))
{
renderers.emplace_back(renderer<cairo_ps_renderer>(output_dir, reference_dir, overwrite));
}
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
if (args.count(cairo_pdf_renderer::name))
{
renderers.emplace_back(renderer<cairo_pdf_renderer>(output_dir, reference_dir, overwrite));
}
#endif
#endif #endif
#if defined(SVG_RENDERER) #if defined(SVG_RENDERER)
if (append_all || args.count(svg_renderer::name)) if (force_append || args.count(svg_renderer::name))
{ {
renderers.emplace_back(renderer<svg_renderer>(output_dir, reference_dir, overwrite)); renderers.emplace_back(renderer<svg_renderer>(output_dir, reference_dir, overwrite));
} }
#endif #endif
#if defined(GRID_RENDERER) #if defined(GRID_RENDERER)
if (append_all || args.count(grid_renderer::name)) if (force_append || args.count(grid_renderer::name))
{ {
renderers.emplace_back(renderer<grid_renderer>(output_dir, reference_dir, overwrite)); renderers.emplace_back(renderer<grid_renderer>(output_dir, reference_dir, overwrite));
} }
@ -112,6 +130,15 @@ int main(int argc, char** argv)
(agg_renderer::name, "render with AGG renderer") (agg_renderer::name, "render with AGG renderer")
#if defined(HAVE_CAIRO) #if defined(HAVE_CAIRO)
(cairo_renderer::name, "render with Cairo renderer") (cairo_renderer::name, "render with Cairo renderer")
#ifdef CAIRO_HAS_SVG_SURFACE
(cairo_svg_renderer::name, "render with Cairo SVG renderer")
#endif
#ifdef CAIRO_HAS_PS_SURFACE
(cairo_ps_renderer::name, "render with Cairo PS renderer")
#endif
#ifdef CAIRO_HAS_PDF_SURFACE
(cairo_pdf_renderer::name, "render with Cairo PDF renderer")
#endif
#endif #endif
#if defined(SVG_RENDERER) #if defined(SVG_RENDERER)
(svg_renderer::name, "render with SVG renderer") (svg_renderer::name, "render with SVG renderer")