diff --git a/.travis.yml b/.travis.yml index e1b77a6d9..327bcb547 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,21 +17,22 @@ before_install: - sudo apt-get -qq purge postgis* postgresql* - sudo apt-add-repository -y ppa:cartodb/postgresql-9.3 - sudo apt-add-repository -y ppa:cartodb/gis + # grab harfbuzz from ppa + - sudo apt-add-repository -y ppa:fontforge/fontforge # we need at least g++-4.7 for c++11 features - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - # upgrade boost - - sudo add-apt-repository -y ppa:boost-latest/ppa + # enable to test against latest boost rather that v1.48 + #- sudo add-apt-repository -y ppa:boost-latest/ppa - sudo rm -Rf /var/lib/postgresql /etc/postgresql - sudo apt-get update -qq - - sudo apt-get install -q postgresql-9.3-postgis-2.1 - - sudo apt-get install -q postgresql-contrib-9.3 - - sudo apt-get install -q gdal-bin libgdal-dev + - sudo apt-get install -q libharfbuzz-dev postgresql-9.3-postgis-2.1 postgresql-contrib-9.3 gdal-bin libgdal-dev - echo -e "local\tall\tall\ttrust\nhost\tall\tall\t127.0.0.1/32\ttrust\nhost\tall\tall\t::1/128\ttrust" |sudo tee /etc/postgresql/9.3/main/pg_hba.conf - sudo service postgresql restart install: - #- sudo apt-get install -y libboost-python1.48-dev libboost-thread1.48-dev libboost-filesystem1.48-dev libboost-regex1.48-dev libboost-program-options1.48-dev - - sudo apt-get install -y boost1.55 + # enable to test against boost ppa + #- sudo apt-get install -y boost1.55 + - sudo apt-get install -y libboost-python1.48-dev libboost-thread1.48-dev libboost-filesystem1.48-dev libboost-regex1.48-dev libboost-program-options1.48-dev - sudo apt-get install -y ttf-wqy-microhei make libstdc++6 libstdc++-4.8-dev valgrind python-nose libicu-dev libproj-dev libcairo-dev python-cairo-dev libcairo-dev python-cairo-dev libpng-dev libjpeg-dev libtiff-dev libwebp-dev libz-dev libfreetype6-dev libxml2-dev libsqlite3-dev before_script: @@ -39,11 +40,6 @@ before_script: - psql -U postgres -c 'create extension postgis' -d template_postgis - if [[ "${CXX}" == 'g++' ]]; then export JOBS=2; sudo apt-get install gcc-4.8 g++-4.8; export CXX="$(which g++-4.8)"; export CC="$(which gcc-4.8)"; fi; - if [[ "${CXX}" == 'clang++' ]]; then export JOBS=4; export CXX="$(which clang++)"; export CC="$(which clang)"; fi; - - wget http://www.freedesktop.org/software/harfbuzz/release/harfbuzz-0.9.35.tar.bz2 - - tar xf harfbuzz-0.9.35.tar.bz2 - - cd harfbuzz-0.9.35 - - ./configure --prefix=/usr --with-icu --with-cairo=no --with-glib=no --with-gobject=no --with-graphite2=no --with-freetype --with-uniscribe=no --with-coretext=no && make -j2 && sudo make -j2 install - - cd ../ script: - ./configure CXX="${CXX}" CC="${CC}" CUSTOM_CXXFLAGS="${CUSTOM_CXXFLAGS}" CUSTOM_LDFLAGS="${CUSTOM_LDFLAGS}" XML_PARSER="${XML_PARSER}" ENABLE_LOG="${ENABLE_LOG}" DEBUG="${DEBUG}" DEMO="${DEMO}" BENCHMARK="${BENCHMARK}" CPP_TESTS=True CAIRO=True FAST=True || cat config.log diff --git a/INSTALL.md b/INSTALL.md index c75eccbe0..95fe2531a 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -50,7 +50,7 @@ For troubleshooting help see https://github.com/mapnik/mapnik/wiki/InstallationT Build system dependencies are: - * C++ compiler (like g++ or clang++) + * C++ compiler supporting `-std=c++11` (like >= g++ 4.8 or >= clang++ 3.4) * >= 2 GB RAM (> 5 GB for g++) * Python 2.4-2.7 * Scons (a copy is bundled) diff --git a/benchmark/test_face_ptr_creation.cpp b/benchmark/test_face_ptr_creation.cpp index b546c32c8..9e3aa9567 100644 --- a/benchmark/test_face_ptr_creation.cpp +++ b/benchmark/test_face_ptr_creation.cpp @@ -11,24 +11,38 @@ public: { std::size_t count = 0; std::size_t expected_count = mapnik::freetype_engine::face_names().size(); - mapnik::freetype_engine engine; + mapnik::freetype_engine::font_file_mapping_type font_file_mapping; + mapnik::freetype_engine::font_memory_cache_type font_cache; + mapnik::font_library library; for (std::string const& name : mapnik::freetype_engine::face_names()) { - mapnik::face_ptr f = engine.create_face(name); + mapnik::face_ptr f = mapnik::freetype_engine::create_face(name, + library, + font_file_mapping, + font_cache, + mapnik::freetype_engine::get_mapping(), + mapnik::freetype_engine::get_cache()); if (f) ++count; } return count == expected_count; } void operator()() const { - mapnik::freetype_engine engine; std::size_t expected_count = mapnik::freetype_engine::face_names().size(); for (unsigned i=0;i const& engine, face_ptr const& face); + cairo_face(std::shared_ptr const& library, face_ptr const& face); ~cairo_face(); cairo_font_face_t * face() const; private: class handle { public: - handle(std::shared_ptr const& engine, face_ptr const& face) - : engine_(engine), face_(face) {} + handle(std::shared_ptr const& library, face_ptr const& face) + : library_(library), face_(face) {} private: - std::shared_ptr engine_; + std::shared_ptr library_; face_ptr face_; }; @@ -109,12 +109,12 @@ using cairo_face_ptr = std::shared_ptr; class cairo_face_manager : private mapnik::noncopyable { public: - cairo_face_manager(std::shared_ptr engine); + cairo_face_manager(std::shared_ptr library); cairo_face_ptr get_face(face_ptr face); private: using cairo_face_cache = std::map; - std::shared_ptr font_engine_; + std::shared_ptr font_library_; cairo_face_cache cache_; }; @@ -321,7 +321,7 @@ public: void glyph_path(unsigned long index, pixel_position const& pos); void add_text(glyph_positions_ptr pos, cairo_face_manager & manager, - face_manager & font_manager, + face_manager_freetype & font_manager, composite_mode_e comp_op = src_over, composite_mode_e halo_comp_op = src_over, double scale_factor = 1.0); diff --git a/include/mapnik/feature_type_style.hpp b/include/mapnik/feature_type_style.hpp index 760e8b6e8..783595401 100644 --- a/include/mapnik/feature_type_style.hpp +++ b/include/mapnik/feature_type_style.hpp @@ -68,7 +68,7 @@ public: // ctor feature_type_style(); feature_type_style(feature_type_style const& rhs); - feature_type_style(feature_type_style &&) = default; + feature_type_style(feature_type_style && rhs); feature_type_style& operator=(feature_type_style rhs); // comparison diff --git a/include/mapnik/font_engine_freetype.hpp b/include/mapnik/font_engine_freetype.hpp index 1bae33087..e141131d8 100644 --- a/include/mapnik/font_engine_freetype.hpp +++ b/include/mapnik/font_engine_freetype.hpp @@ -26,6 +26,7 @@ // mapnik #include #include +#include #include // stl @@ -38,8 +39,6 @@ #include #endif -struct FT_LibraryRec_; -struct FT_MemoryRec_; namespace boost { template class optional; } namespace mapnik @@ -48,14 +47,15 @@ namespace mapnik class stroker; using stroker_ptr = std::shared_ptr; class font_face_set; -using face_set_ptr = std::shared_ptr; +using face_set_ptr = std::unique_ptr; class font_face; using face_ptr = std::shared_ptr; - class MAPNIK_DECL freetype_engine { public: + using font_file_mapping_type = std::map>; + using font_memory_cache_type = std::map, std::size_t>>; static bool is_font_file(std::string const& file_name); /*! \brief register a font file * @param file_name path to a font file. @@ -69,50 +69,59 @@ public: */ static bool register_fonts(std::string const& dir, bool recurse = false); static std::vector face_names(); - static std::map > const& get_mapping(); - face_ptr create_face(std::string const& family_name); - stroker_ptr create_stroker(); + static font_file_mapping_type const& get_mapping(); + static font_memory_cache_type & get_cache(); + static bool can_open(std::string const& face_name, + font_library & library, + font_file_mapping_type const& font_file_mapping, + font_file_mapping_type const& global_font_file_mapping); + static face_ptr create_face(std::string const& face_name, + font_library & library, + font_file_mapping_type const& font_file_mapping, + freetype_engine::font_memory_cache_type const& font_cache, + font_file_mapping_type const& global_font_file_mapping, + freetype_engine::font_memory_cache_type & global_memory_fonts); + static bool register_font_impl(std::string const& file_name, + font_library & libary, + font_file_mapping_type & font_file_mapping); + static bool register_fonts_impl(std::string const& dir, + font_library & libary, + font_file_mapping_type & font_file_mapping, + bool recurse = false); virtual ~freetype_engine(); freetype_engine(); private: static bool register_font_impl(std::string const& file_name, FT_LibraryRec_ * library); static bool register_fonts_impl(std::string const& dir, FT_LibraryRec_ * library, bool recurse = false); - FT_LibraryRec_ * library_; - std::unique_ptr memory_; #ifdef MAPNIK_THREADSAFE static std::mutex mutex_; #endif - static std::map > name2file_; - static std::map, std::size_t> > memory_fonts_; + static font_file_mapping_type global_font_file_mapping_; + static font_memory_cache_type global_memory_fonts_; }; -template class MAPNIK_DECL face_manager : private mapnik::noncopyable { - using font_engine_type = T; using face_ptr_cache_type = std::map; public: - face_manager(T & engine) - : engine_(engine), - stroker_(engine_.create_stroker()), - face_ptr_cache_() {} - + face_manager(font_library & library, + freetype_engine::font_file_mapping_type const& font_file_mapping, + freetype_engine::font_memory_cache_type const& font_cache); face_ptr get_face(std::string const& name); face_set_ptr get_face_set(std::string const& name); face_set_ptr get_face_set(font_set const& fset); face_set_ptr get_face_set(std::string const& name, boost::optional fset); - - inline stroker_ptr get_stroker() { return stroker_; } - private: - font_engine_type & engine_; - stroker_ptr stroker_; face_ptr_cache_type face_ptr_cache_; + font_library & library_; + freetype_engine::font_file_mapping_type const& font_file_mapping_; + freetype_engine::font_memory_cache_type const& font_memory_cache_; + stroker_ptr stroker_; }; -using face_manager_freetype = face_manager; +using face_manager_freetype = face_manager; } diff --git a/include/mapnik/grid/grid_renderer.hpp b/include/mapnik/grid/grid_renderer.hpp index c5ced18f5..a07ff76c4 100644 --- a/include/mapnik/grid/grid_renderer.hpp +++ b/include/mapnik/grid/grid_renderer.hpp @@ -26,7 +26,6 @@ // mapnik #include #include -#include #include #include #include // for rule, symbolizers diff --git a/include/mapnik/group/group_layout_manager.hpp b/include/mapnik/group/group_layout_manager.hpp index 3db329df8..b44921b93 100644 --- a/include/mapnik/group/group_layout_manager.hpp +++ b/include/mapnik/group/group_layout_manager.hpp @@ -40,7 +40,7 @@ using bound_box = box2d; struct group_layout_manager { - group_layout_manager(const group_layout &layout) + group_layout_manager(group_layout const& layout) : layout_(layout), input_origin_(0, 0), member_boxes_(vector()), @@ -49,7 +49,7 @@ struct group_layout_manager { } - group_layout_manager(const group_layout &layout, const pixel_position &input_origin) + group_layout_manager(group_layout const& layout, pixel_position const& input_origin) : layout_(layout), input_origin_(input_origin), member_boxes_(vector()), @@ -58,8 +58,8 @@ struct group_layout_manager { } - group_layout_manager(const group_layout &layout, const pixel_position &input_origin, - const vector &item_boxes) + group_layout_manager(group_layout const& layout, pixel_position const& input_origin, + vector const& item_boxes) : layout_(layout), input_origin_(input_origin), member_boxes_(item_boxes), @@ -68,19 +68,19 @@ struct group_layout_manager { } - inline void set_layout(const group_layout &layout) + inline void set_layout(group_layout const& layout) { layout_ = layout; update_layout_ = true; } - inline void add_member_bound_box(const bound_box &member_box) + inline void add_member_bound_box(bound_box const& member_box) { member_boxes_.push_back(member_box); update_layout_ = true; } - inline const pixel_position &offset_at(size_t i) + inline pixel_position const& offset_at(size_t i) { handle_update(); return member_offsets_.at(i); diff --git a/include/mapnik/group/group_symbolizer_helper.hpp b/include/mapnik/group/group_symbolizer_helper.hpp index d5cb95b69..ae44feaec 100644 --- a/include/mapnik/group/group_symbolizer_helper.hpp +++ b/include/mapnik/group/group_symbolizer_helper.hpp @@ -60,7 +60,7 @@ public: unsigned width, unsigned height, double scale_factor, - view_transform const &t, + view_transform const& t, DetectorType &detector, box2d const& query_extent); @@ -89,10 +89,10 @@ private: /** Check if a point placement fits at given position */ bool check_point_placement(pixel_position const& pos); /** Checks for collision. */ - bool collision(box2d const& box, const value_unicode_string &repeat_key = "") const; + bool collision(box2d const& box, value_unicode_string const& repeat_key = "") const; double get_spacing(double path_length) const; - DetectorType &detector_; + DetectorType & detector_; /** Boxes and repeat keys to take into account when finding placement. * Boxes are relative to starting point of current placement. diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index da2ac10ce..815d6a8ee 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -32,6 +32,7 @@ #include #include #include +#include // boost #include @@ -97,6 +98,9 @@ private: boost::optional > maximum_extent_; std::string base_path_; parameters extra_params_; + boost::optional font_directory_; + freetype_engine::font_file_mapping_type font_file_mapping_; + freetype_engine::font_memory_cache_type font_memory_cache_; public: @@ -166,13 +170,21 @@ public: */ style_iterator end_styles(); - /*! \brief Insert a style in the map. + /*! \brief Insert a style in the map by copying. * @param name The name of the style. * @param style The style to insert. * @return true If success. * false If no success. */ - bool insert_style(std::string const& name,feature_type_style style); + bool insert_style(std::string const& name,feature_type_style const& style); + + /*! \brief Insert a style in the map by moving.. + * @param name The name of the style. + * @param style The style to insert. + * @return true If success. + * false If no success. + */ + bool insert_style(std::string const& name,feature_type_style && style); /*! \brief Remove a style from the map. * @param name The name of the style. @@ -185,13 +197,21 @@ public: */ boost::optional find_style(std::string const& name) const; - /*! \brief Insert a fontset into the map. + /*! \brief Insert a fontset into the map by copying. * @param name The name of the fontset. * @param fontset The fontset to insert. * @return true If success. * false If failure. */ - bool insert_fontset(std::string const& name, font_set fontset); + bool insert_fontset(std::string const& name, font_set const& fontset); + + /*! \brief Insert a fontset into the map by moving. + * @param name The name of the fontset. + * @param fontset The fontset to insert. + * @return true If success. + * false If failure. + */ + bool insert_fontset(std::string const& name, font_set && fontset); /*! \brief Find a fontset. * @param name The name of the fontset. @@ -209,14 +229,27 @@ public: */ std::map & fontsets(); + /*! \brief register fonts. + */ + bool register_fonts(std::string const& dir, bool recurse); + + /*! \brief cache registered fonts. + */ + bool load_fonts(); + /*! \brief Get number of all layers. */ size_t layer_count() const; - /*! \brief Add a layer to the map. + /*! \brief Add a layer to the map by copying it. * @param l The layer to add. */ - void add_layer(layer l); + void add_layer(layer const& l); + + /*! \brief Add a layer to the map by moving it. + * @param l The layer to add. + */ + void add_layer(layer && l); /*! \brief Get a layer. * @param index layer number. @@ -437,6 +470,36 @@ public: */ void set_extra_parameters(parameters& params); + boost::optional const& font_directory() const + { + return font_directory_; + } + + void set_font_directory(std::string const& dir) + { + font_directory_ = dir; + } + + freetype_engine::font_file_mapping_type const& get_font_file_mapping() const + { + return font_file_mapping_; + } + + freetype_engine::font_file_mapping_type & get_font_file_mapping() + { + return font_file_mapping_; + } + + freetype_engine::font_memory_cache_type const& get_font_memory_cache() const + { + return font_memory_cache_; + } + + freetype_engine::font_memory_cache_type & get_font_memory_cache() + { + return font_memory_cache_; + } + private: friend void swap(Map & rhs, Map & lhs); void fixAspectRatio(); diff --git a/include/mapnik/miniz_png.hpp b/include/mapnik/miniz_png.hpp index 1956e5183..cc59931c8 100644 --- a/include/mapnik/miniz_png.hpp +++ b/include/mapnik/miniz_png.hpp @@ -32,9 +32,8 @@ #include #include -#ifdef _MSC_VER -#include -#endif +#include +#include /* miniz.c porting issues: - duplicate symbols in python bindings require moving miniz.c include to just cpp file @@ -84,14 +83,12 @@ private: static const unsigned char IEND_tpl[]; }; -#ifdef _MSC_VER -template MAPNIK_DECL void PNGWriter::writeIDAT(image_data_8 const& image); -template MAPNIK_DECL void PNGWriter::writeIDAT >(image_view const& image); -template MAPNIK_DECL void PNGWriter::writeIDAT(image_data_32 const& image); -template MAPNIK_DECL void PNGWriter::writeIDAT >(image_view const& image); -template MAPNIK_DECL void PNGWriter::writeIDATStripAlpha(image_data_32 const& image); -template MAPNIK_DECL void PNGWriter::writeIDATStripAlpha >(image_view const& image); -#endif +extern template MAPNIK_DECL void PNGWriter::writeIDAT(image_data_8 const& image); +extern template MAPNIK_DECL void PNGWriter::writeIDAT >(image_view const& image); +extern template MAPNIK_DECL void PNGWriter::writeIDAT(image_data_32 const& image); +extern template MAPNIK_DECL void PNGWriter::writeIDAT >(image_view const& image); +extern template MAPNIK_DECL void PNGWriter::writeIDATStripAlpha(image_data_32 const& image); +extern template MAPNIK_DECL void PNGWriter::writeIDATStripAlpha >(image_view const& image); }} diff --git a/include/mapnik/renderer_common.hpp b/include/mapnik/renderer_common.hpp index fc10cdcfc..45b70f8a7 100644 --- a/include/mapnik/renderer_common.hpp +++ b/include/mapnik/renderer_common.hpp @@ -55,9 +55,9 @@ struct renderer_common : private mapnik::noncopyable double scale_factor_; attributes vars_; // TODO: dirty hack for cairo renderer, figure out how to remove this - std::shared_ptr shared_font_engine_; - freetype_engine &font_engine_; - face_manager font_manager_; + std::shared_ptr shared_font_library_; + font_library & font_library_; + face_manager_freetype font_manager_; box2d query_extent_; view_transform t_; std::shared_ptr detector_; diff --git a/include/mapnik/renderer_common/process_group_symbolizer.hpp b/include/mapnik/renderer_common/process_group_symbolizer.hpp index 4dee1b33d..774296f13 100644 --- a/include/mapnik/renderer_common/process_group_symbolizer.hpp +++ b/include/mapnik/renderer_common/process_group_symbolizer.hpp @@ -57,8 +57,8 @@ struct virtual_renderer_common : private mapnik::noncopyable height_(common.height_), scale_factor_(common.scale_factor_), vars_(common.vars_), - shared_font_engine_(common.shared_font_engine_), - font_engine_(*shared_font_engine_), + shared_font_library_(common.shared_font_library_), + font_library_(*shared_font_library_), font_manager_(common.font_manager_), query_extent_(common.query_extent_), t_(common.t_), @@ -69,9 +69,9 @@ struct virtual_renderer_common : private mapnik::noncopyable double & scale_factor_; attributes & vars_; // TODO: dirty hack for cairo renderer, figure out how to remove this - std::shared_ptr & shared_font_engine_; - freetype_engine & font_engine_; - face_manager & font_manager_; + std::shared_ptr & shared_font_library_; + font_library & font_library_; + face_manager_freetype & font_manager_; box2d & query_extent_; view_transform & t_; std::shared_ptr detector_; diff --git a/include/mapnik/svg/output/svg_renderer.hpp b/include/mapnik/svg/output/svg_renderer.hpp index 9c1db3438..285cbad6a 100644 --- a/include/mapnik/svg/output/svg_renderer.hpp +++ b/include/mapnik/svg/output/svg_renderer.hpp @@ -26,7 +26,6 @@ // mapnik #include #include -#include #include #include #include diff --git a/include/mapnik/text/face.hpp b/include/mapnik/text/face.hpp index 0a4908ff0..78b2b74c6 100644 --- a/include/mapnik/text/face.hpp +++ b/include/mapnik/text/face.hpp @@ -98,7 +98,7 @@ public: private: std::vector faces_; }; -using face_set_ptr = std::shared_ptr; +using face_set_ptr = std::unique_ptr; // FT_Stroker wrapper diff --git a/include/mapnik/text/font_library.hpp b/include/mapnik/text/font_library.hpp new file mode 100644 index 000000000..b31efe68c --- /dev/null +++ b/include/mapnik/text/font_library.hpp @@ -0,0 +1,51 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2014 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_FONT_LIBRARY_HPP +#define MAPNIK_FONT_LIBRARY_HPP + +// mapnik +#include +#include + +// stl +#include + +struct FT_LibraryRec_; +struct FT_MemoryRec_; + +namespace mapnik { + +class MAPNIK_DECL font_library : public noncopyable +{ +public: + explicit font_library(); + ~font_library(); + FT_LibraryRec_ * get(); +private: + FT_LibraryRec_ * library_; + std::unique_ptr memory_; +}; + +} + +#endif // MAPNIK_FONT_LIBRARY_HPP diff --git a/include/mapnik/text/formatting/layout.hpp b/include/mapnik/text/formatting/layout.hpp index 9f14a2d94..f84e4c48d 100644 --- a/include/mapnik/text/formatting/layout.hpp +++ b/include/mapnik/text/formatting/layout.hpp @@ -47,6 +47,7 @@ public: boost::optional jalign; boost::optional text_ratio; boost::optional wrap_width; + boost::optional wrap_char; boost::optional wrap_before; boost::optional repeat_wrap_char; boost::optional rotate_displacement; diff --git a/include/mapnik/text/text_layout.hpp b/include/mapnik/text/text_layout.hpp index eb59838ff..511d62c5e 100644 --- a/include/mapnik/text/text_layout.hpp +++ b/include/mapnik/text/text_layout.hpp @@ -97,7 +97,7 @@ public: void add_child(text_layout_ptr const& child_layout); inline text_layout_vector const& get_child_layouts() const { return child_layout_list_; } - inline face_manager & get_font_manager() const { return font_manager_; } + inline face_manager_freetype & get_font_manager() const { return font_manager_; } inline double get_scale_factor() const { return scale_factor_; } inline text_layout_properties const& get_layout_properties() const { return properties_; } diff --git a/include/mapnik/text/vertex_cache.hpp b/include/mapnik/text/vertex_cache.hpp index bd4920f7f..1214d2577 100644 --- a/include/mapnik/text/vertex_cache.hpp +++ b/include/mapnik/text/vertex_cache.hpp @@ -26,6 +26,7 @@ #include #include #include +#include // agg #include "agg_basics.h" @@ -39,10 +40,10 @@ namespace mapnik { class vertex_cache; -using vertex_cache_ptr = std::shared_ptr; +using vertex_cache_ptr = std::unique_ptr; // Caches all path points and their lengths. Allows easy moving in both directions. -class MAPNIK_DECL vertex_cache +class MAPNIK_DECL vertex_cache : noncopyable { struct segment { diff --git a/include/mapnik/util/file_io.hpp b/include/mapnik/util/file_io.hpp new file mode 100644 index 000000000..8664009bf --- /dev/null +++ b/include/mapnik/util/file_io.hpp @@ -0,0 +1,92 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2014 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifndef MAPNIK_FILE_IO_HPP +#define MAPNIK_FILE_IO_HPP + +// mapnik +#include +#include +#include +//#include + +// stl +#include +#include +#include + +namespace mapnik { namespace util { + +class file : public noncopyable +{ +public: + using file_ptr = std::unique_ptr; + using data_type = std::unique_ptr; + + explicit file(std::string const& filename) +#ifdef _WINDOWS + : file_(_wfopen(mapnik::utf8_to_utf16(filename).c_str(), L"rb"), std::fclose), +#else + : file_(std::fopen(filename.c_str(),"rb"), std::fclose), +#endif + size_(0) + + { + if (file_) + { + std::fseek(file_.get(), 0, SEEK_END); + size_ = std::ftell(file_.get()); + } + } + + inline bool open() const + { + return file_ ? true : false; + } + + inline std::FILE * get() const + { + return file_.get();; + } + + inline std::size_t size() const + { + return size_; + } + + inline data_type data() const + { + if (!size_) return nullptr; + std::fseek(file_.get(), 0, SEEK_SET); + data_type buffer(new char[size_]); + std::fread(buffer.get(), size_, 1, file_.get()); + return std::move(buffer); + } +private: + file_ptr file_; + std::size_t size_; +}; + +}} + + +#endif // FILE_IO diff --git a/src/build.py b/src/build.py index 40e25f5fb..adfae461f 100644 --- a/src/build.py +++ b/src/build.py @@ -203,6 +203,7 @@ source = Split( svg/svg_transform_parser.cpp warp.cpp css_color_grammar.cpp + text/font_library.cpp text/vertex_cache.cpp text/text_layout.cpp text/text_line.cpp diff --git a/src/cairo/cairo_context.cpp b/src/cairo/cairo_context.cpp index 1c59a8303..9e570785c 100644 --- a/src/cairo/cairo_context.cpp +++ b/src/cairo/cairo_context.cpp @@ -32,12 +32,12 @@ #include namespace mapnik { -cairo_face::cairo_face(std::shared_ptr const& engine, face_ptr const& face) +cairo_face::cairo_face(std::shared_ptr const& library, face_ptr const& face) : face_(face) { static cairo_user_data_key_t key; c_face_ = cairo_ft_font_face_create_for_ft_face(face->get_face(), FT_LOAD_NO_HINTING); - cairo_font_face_set_user_data(c_face_, &key, new handle(engine, face), destroy); + cairo_font_face_set_user_data(c_face_, &key, new handle(library, face), destroy); } cairo_face::~cairo_face() @@ -432,7 +432,7 @@ void cairo_context::glyph_path(unsigned long index, pixel_position const &pos) void cairo_context::add_text(glyph_positions_ptr path, cairo_face_manager & manager, - face_manager & font_manager, + face_manager_freetype & font_manager, composite_mode_e comp_op, composite_mode_e halo_comp_op, double scale_factor) @@ -508,8 +508,8 @@ void cairo_context::add_text(glyph_positions_ptr path, } -cairo_face_manager::cairo_face_manager(std::shared_ptr font_engine) - : font_engine_(font_engine) +cairo_face_manager::cairo_face_manager(std::shared_ptr font_library) + : font_library_(font_library) { } @@ -524,7 +524,7 @@ cairo_face_ptr cairo_face_manager::get_face(face_ptr face) } else { - entry = std::make_shared(font_engine_, face); + entry = std::make_shared(font_library_, face); cache_.emplace(face, entry); } return entry; diff --git a/src/cairo/cairo_renderer.cpp b/src/cairo/cairo_renderer.cpp index 57b80d252..088c314c8 100644 --- a/src/cairo/cairo_renderer.cpp +++ b/src/cairo/cairo_renderer.cpp @@ -58,7 +58,7 @@ cairo_renderer::cairo_renderer(Map const& m, m_(m), context_(cairo), common_(m, attributes(), offset_x, offset_y, m.width(), m.height(), scale_factor), - face_manager_(common_.shared_font_engine_) + face_manager_(common_.shared_font_library_) { setup(m); } @@ -75,7 +75,7 @@ cairo_renderer::cairo_renderer(Map const& m, m_(m), context_(cairo), common_(m, req, vars, offset_x, offset_y, req.width(), req.height(), scale_factor), - face_manager_(common_.shared_font_engine_) + face_manager_(common_.shared_font_library_) { setup(m); } @@ -91,7 +91,7 @@ cairo_renderer::cairo_renderer(Map const& m, m_(m), context_(cairo), common_(m, attributes(), offset_x, offset_y, m.width(), m.height(), scale_factor, detector), - face_manager_(common_.shared_font_engine_) + face_manager_(common_.shared_font_library_) { setup(m); } diff --git a/src/feature_type_style.cpp b/src/feature_type_style.cpp index d98be3ba8..87752261b 100644 --- a/src/feature_type_style.cpp +++ b/src/feature_type_style.cpp @@ -56,9 +56,16 @@ feature_type_style::feature_type_style(feature_type_style const& rhs) direct_filters_(rhs.direct_filters_), comp_op_(rhs.comp_op_), opacity_(rhs.opacity_), - image_filters_inflate_(rhs.image_filters_inflate_) -{ -} + image_filters_inflate_(rhs.image_filters_inflate_) {} + +feature_type_style::feature_type_style(feature_type_style && rhs) + : rules_(std::move(rhs.rules_)), + filter_mode_(std::move(rhs.filter_mode_)), + filters_(std::move(rhs.filters_)), + direct_filters_(std::move(rhs.direct_filters_)), + comp_op_(std::move(rhs.comp_op_)), + opacity_(std::move(rhs.opacity_)), + image_filters_inflate_(std::move(rhs.image_filters_inflate_)) {} feature_type_style& feature_type_style::operator=(feature_type_style rhs) { diff --git a/src/font_engine_freetype.cpp b/src/font_engine_freetype.cpp index 4fc64c6b4..da568f3bb 100644 --- a/src/font_engine_freetype.cpp +++ b/src/font_engine_freetype.cpp @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include // boost #include @@ -36,7 +38,6 @@ // stl #include #include -#include // freetype2 extern "C" @@ -47,45 +48,12 @@ extern "C" #include FT_MODULE_H } -void* _Alloc_Func(FT_Memory memory, long size) -{ - return std::malloc(size); -} - -void _Free_Func(FT_Memory memory, void *block) -{ - std::free(block); -} - -void* _Realloc_Func(FT_Memory memory, long cur_size, long new_size, void* block) -{ - return std::realloc(block, new_size); -} namespace mapnik { -void init_freetype(FT_Memory memory, FT_Library & library) -{ - memory->alloc = _Alloc_Func; - memory->free = _Free_Func; - memory->realloc = _Realloc_Func; - FT_Error error = FT_New_Library(memory, &library ); - if (error) throw std::runtime_error("can not initalise FreeType2 library"); - FT_Add_Default_Modules(library); -} - -freetype_engine::freetype_engine() - : library_(nullptr), - memory_(new FT_MemoryRec_) -{ - init_freetype(&*memory_, library_); -} - -freetype_engine::~freetype_engine() -{ - FT_Done_Library(library_); -} +freetype_engine::freetype_engine() {} +freetype_engine::~freetype_engine() {} bool freetype_engine::is_font_file(std::string const& file_name) { @@ -116,37 +84,27 @@ bool freetype_engine::register_font(std::string const& file_name) #ifdef MAPNIK_THREADSAFE mapnik::scoped_lock lock(mutex_); #endif - - FT_Library library = 0; - std::unique_ptr memory(new FT_MemoryRec_); - init_freetype(&*memory, library); - bool result = register_font_impl(file_name, library); - FT_Done_Library(library); - return result; + font_library library; + return register_font_impl(file_name, library, global_font_file_mapping_); } -bool freetype_engine::register_font_impl(std::string const& file_name, FT_LibraryRec_ * library) +bool freetype_engine::register_font_impl(std::string const& file_name, + font_library & library, + freetype_engine::font_file_mapping_type & font_file_mapping) { MAPNIK_LOG_DEBUG(font_engine_freetype) << "registering: " << file_name; -#ifdef _WINDOWS - FILE * file = _wfopen(mapnik::utf8_to_utf16(file_name).c_str(), L"rb"); -#else - FILE * file = std::fopen(file_name.c_str(),"rb"); -#endif - if (file == nullptr) return false; + mapnik::util::file file(file_name); + if (!file.open()) return false; FT_Face face = 0; FT_Open_Args args; FT_StreamRec streamRec; memset(&args, 0, sizeof(args)); memset(&streamRec, 0, sizeof(streamRec)); - fseek(file, 0, SEEK_END); - std::size_t file_size = std::ftell(file); - fseek(file, 0, SEEK_SET); streamRec.base = 0; streamRec.pos = 0; - streamRec.size = file_size; - streamRec.descriptor.pointer = file; + streamRec.size = file.size(); + streamRec.descriptor.pointer = file.get(); streamRec.read = ft_read_cb; streamRec.close = NULL; args.flags = FT_OPEN_STREAM; @@ -159,7 +117,7 @@ bool freetype_engine::register_font_impl(std::string const& file_name, FT_Librar for ( int i = 0; face == 0 || i < num_faces; ++i ) { // if face is null then this is the first face - FT_Error error = FT_Open_Face(library, &args, i, &face); + FT_Error error = FT_Open_Face(library.get(), &args, i, &face); if (error) break; // store num_faces locally, after FT_Done_Face it can not be accessed any more if (num_faces == 0) @@ -173,10 +131,10 @@ bool freetype_engine::register_font_impl(std::string const& file_name, FT_Librar if (!boost::algorithm::starts_with(name,".")) { // http://stackoverflow.com/a/24795559/2333354 - auto range = name2file_.equal_range(name); + auto range = font_file_mapping.equal_range(name); if (range.first == range.second) // the key was previously absent; insert a pair { - name2file_.emplace_hint(range.first,name,std::move(std::make_pair(i,file_name))); + font_file_mapping.emplace_hint(range.first,name,std::move(std::make_pair(i,file_name))); } else // the key was present, replace the associated value { /* some action with value range.first->second about to be overwritten here */ @@ -200,7 +158,6 @@ bool freetype_engine::register_font_impl(std::string const& file_name, FT_Librar } if (face) FT_Done_Face(face); } - std::fclose(file); return success; } @@ -209,15 +166,14 @@ bool freetype_engine::register_fonts(std::string const& dir, bool recurse) #ifdef MAPNIK_THREADSAFE mapnik::scoped_lock lock(mutex_); #endif - std::unique_ptr memory(new FT_MemoryRec_); - FT_Library library = 0; - init_freetype(&*memory, library); - bool result = register_fonts_impl(dir, library, recurse); - FT_Done_Library(library); - return result; + font_library library; + return register_fonts_impl(dir, library, global_font_file_mapping_, recurse); } -bool freetype_engine::register_fonts_impl(std::string const& dir, FT_LibraryRec_ * library, bool recurse) +bool freetype_engine::register_fonts_impl(std::string const& dir, + font_library & library, + freetype_engine::font_file_mapping_type & font_file_mapping, + bool recurse) { if (!mapnik::util::exists(dir)) { @@ -225,7 +181,7 @@ bool freetype_engine::register_fonts_impl(std::string const& dir, FT_LibraryRec_ } if (!mapnik::util::is_directory(dir)) { - return mapnik::freetype_engine::register_font_impl(dir, library); + return register_font_impl(dir, library, font_file_mapping); } bool success = false; try @@ -251,7 +207,7 @@ bool freetype_engine::register_fonts_impl(std::string const& dir, FT_LibraryRec_ #endif if (boost::filesystem::is_directory(*itr) && recurse) { - if (register_fonts_impl(file_name, library, true)) + if (register_fonts_impl(file_name, library, font_file_mapping, true)) { success = true; } @@ -267,7 +223,7 @@ bool freetype_engine::register_fonts_impl(std::string const& dir, FT_LibraryRec_ mapnik::util::is_regular_file(file_name) && is_font_file(file_name)) { - if (mapnik::freetype_engine::register_font_impl(file_name, library)) + if (register_font_impl(file_name, library, font_file_mapping)) { success = true; } @@ -286,91 +242,160 @@ bool freetype_engine::register_fonts_impl(std::string const& dir, FT_LibraryRec_ std::vector freetype_engine::face_names () { std::vector names; - for (auto const& kv : name2file_) + for (auto const& kv : global_font_file_mapping_) { names.push_back(kv.first); } return names; } -std::map > const& freetype_engine::get_mapping() +freetype_engine::font_file_mapping_type const& freetype_engine::get_mapping() { - return name2file_; + return global_font_file_mapping_; } - -face_ptr freetype_engine::create_face(std::string const& family_name) +freetype_engine::font_memory_cache_type & freetype_engine::get_cache() { - auto itr = name2file_.find(family_name); - if (itr != name2file_.end()) + return global_memory_fonts_; +} + +bool freetype_engine::can_open(std::string const& face_name, + font_library & library, + font_file_mapping_type const& font_file_mapping, + font_file_mapping_type const& global_font_file_mapping) +{ + bool found_font_file = false; + font_file_mapping_type::const_iterator itr = font_file_mapping.find(face_name); + if (itr != font_file_mapping.end()) { - FT_Face face; - - auto mem_font_itr = memory_fonts_.find(itr->second.second); - - if (mem_font_itr != memory_fonts_.end()) // memory font + found_font_file = true; + } + else + { + itr = global_font_file_mapping.find(face_name); + if (itr != global_font_file_mapping.end()) { - FT_Error error = FT_New_Memory_Face(library_, + found_font_file = true; + } + } + if (!found_font_file) return false; + mapnik::util::file file(itr->second.second); + if (!file.open()) return false; + FT_Face face = 0; + FT_Open_Args args; + FT_StreamRec streamRec; + memset(&args, 0, sizeof(args)); + memset(&streamRec, 0, sizeof(streamRec)); + streamRec.base = 0; + streamRec.pos = 0; + streamRec.size = file.size(); + streamRec.descriptor.pointer = file.get(); + streamRec.read = ft_read_cb; + streamRec.close = NULL; + args.flags = FT_OPEN_STREAM; + args.stream = &streamRec; + // -1 is used to quickly check if the font file appears valid without iterating each face + FT_Error error = FT_Open_Face(library.get(), &args, -1, &face); + if (face) FT_Done_Face(face); + if (error) return false; + return true; +} + +face_ptr freetype_engine::create_face(std::string const& family_name, + font_library & library, + freetype_engine::font_file_mapping_type const& font_file_mapping, + freetype_engine::font_memory_cache_type const& font_cache, + freetype_engine::font_file_mapping_type const& global_font_file_mapping, + freetype_engine::font_memory_cache_type & global_memory_fonts) +{ + bool found_font_file = false; + font_file_mapping_type::const_iterator itr = font_file_mapping.find(family_name); + // look for font registered on specific map + if (itr != font_file_mapping.end()) + { + auto mem_font_itr = font_cache.find(itr->second.second); + // if map has font already in memory, use it + if (mem_font_itr != font_cache.end()) + { + FT_Face face; + FT_Error error = FT_New_Memory_Face(library.get(), reinterpret_cast(mem_font_itr->second.first.get()), // data static_cast(mem_font_itr->second.second), // size itr->second.first, // face index &face); - if (!error) return std::make_shared(face); } - else + // we don't add to cache here because the map and its font_cache + // must be immutable during rendering for predictable thread safety + found_font_file = true; + } + else + { + // otherwise search global registry + itr = global_font_file_mapping.find(family_name); + if (itr != global_font_file_mapping.end()) + { + auto mem_font_itr = global_memory_fonts_.find(itr->second.second); + // if font already in memory, use it + if (mem_font_itr != global_memory_fonts_.end()) + { + FT_Face face; + FT_Error error = FT_New_Memory_Face(library.get(), + reinterpret_cast(mem_font_itr->second.first.get()), // data + static_cast(mem_font_itr->second.second), // size + itr->second.first, // face index + &face); + if (!error) return std::make_shared(face); + } + found_font_file = true; + } + } + // if we found file file but it is not yet in memory + if (found_font_file) + { + mapnik::util::file file(itr->second.second); + if (file.open()) { - // load font into memory #ifdef MAPNIK_THREADSAFE mapnik::scoped_lock lock(mutex_); #endif - -#ifdef _WINDOWS - std::unique_ptr file(_wfopen(mapnik::utf8_to_utf16(itr->second.second).c_str(), L"rb"), fclose); -#else - std::unique_ptr file(std::fopen(itr->second.second.c_str(),"rb"), std::fclose); -#endif - if (file != nullptr) + auto result = global_memory_fonts_.emplace(itr->second.second, std::make_pair(std::move(file.data()),file.size())); + FT_Face face; + FT_Error error = FT_New_Memory_Face(library.get(), + reinterpret_cast(result.first->second.first.get()), // data + static_cast(result.first->second.second), // size + itr->second.first, // face index + &face); + if (error) { - std::fseek(file.get(), 0, SEEK_END); - std::size_t file_size = std::ftell(file.get()); - std::fseek(file.get(), 0, SEEK_SET); - std::unique_ptr buffer(new char[file_size]); - std::fread(buffer.get(), file_size, 1, file.get()); - auto result = memory_fonts_.emplace(itr->second.second, std::make_pair(std::move(buffer),file_size)); - FT_Error error = FT_New_Memory_Face (library_, - reinterpret_cast(result.first->second.first.get()), - static_cast(result.first->second.second), - itr->second.first, - &face); - if (!error) return std::make_shared(face); - else - { - // we can't load font, erase it. - memory_fonts_.erase(result.first); - } + // we can't load font, erase it. + global_memory_fonts_.erase(result.first); + return face_ptr(); } + return std::make_shared(face); } } return face_ptr(); } -stroker_ptr freetype_engine::create_stroker() -{ - FT_Stroker s; - FT_Error error = FT_Stroker_New(library_, &s); - if (!error) - { - return std::make_shared(s); - } - return stroker_ptr(); -} +face_manager::face_manager(font_library & library, + freetype_engine::font_file_mapping_type const& font_file_mapping, + freetype_engine::font_memory_cache_type const& font_cache) + : face_ptr_cache_(), + library_(library), + font_file_mapping_(font_file_mapping), + font_memory_cache_(font_cache) + { + FT_Stroker s; + FT_Error error = FT_Stroker_New(library_.get(), &s); + if (!error) + { + stroker_ = std::make_shared(s); + } + } - - -template -face_ptr face_manager::get_face(std::string const& name) +face_ptr face_manager::get_face(std::string const& name) { auto itr = face_ptr_cache_.find(name); if (itr != face_ptr_cache_.end()) @@ -379,7 +404,12 @@ face_ptr face_manager::get_face(std::string const& name) } else { - face_ptr face = engine_.create_face(name); + face_ptr face = freetype_engine::create_face(name, + library_, + font_file_mapping_, + font_memory_cache_, + freetype_engine::get_mapping(), + freetype_engine::get_cache()); if (face) { face_ptr_cache_.emplace(name,face); @@ -388,10 +418,9 @@ face_ptr face_manager::get_face(std::string const& name) } } -template -face_set_ptr face_manager::get_face_set(std::string const& name) +face_set_ptr face_manager::get_face_set(std::string const& name) { - face_set_ptr face_set = std::make_shared(); + face_set_ptr face_set = std::make_unique(); if (face_ptr face = get_face(name)) { face_set->add(face); @@ -399,12 +428,11 @@ face_set_ptr face_manager::get_face_set(std::string const& name) return face_set; } -template -face_set_ptr face_manager::get_face_set(font_set const& fset) +face_set_ptr face_manager::get_face_set(font_set const& fset) { std::vector const& names = fset.get_face_names(); - face_set_ptr face_set = std::make_shared(); - for (auto const& name : names) + face_set_ptr face_set = std::make_unique(); + for (auto const& name : names) { face_ptr face = get_face(name); if (face) @@ -423,8 +451,7 @@ face_set_ptr face_manager::get_face_set(font_set const& fset) return face_set; } -template -face_set_ptr face_manager::get_face_set(const std::string &name, boost::optional fset) +face_set_ptr face_manager::get_face_set(std::string const& name, boost::optional fset) { if (fset && fset->size() > 0) { @@ -439,8 +466,7 @@ face_set_ptr face_manager::get_face_set(const std::string &name, boost::optio #ifdef MAPNIK_THREADSAFE std::mutex freetype_engine::mutex_; #endif -std::map > freetype_engine::name2file_; -std::map,std::size_t> > freetype_engine::memory_fonts_; -template class face_manager; +freetype_engine::font_file_mapping_type freetype_engine::global_font_file_mapping_; +freetype_engine::font_memory_cache_type freetype_engine::global_memory_fonts_; } diff --git a/src/group/group_layout_manager.cpp b/src/group/group_layout_manager.cpp index 6090d734f..7799d5fda 100644 --- a/src/group/group_layout_manager.cpp +++ b/src/group/group_layout_manager.cpp @@ -44,9 +44,9 @@ struct process_layout : public util::static_visitor<> // and the offset values should position them around (0,0) pixel_position const& input_origin_; - process_layout(const vector &member_bboxes, + process_layout(vector const& member_bboxes, vector &member_offsets, - const pixel_position &input_origin) + pixel_position const& input_origin) : member_boxes_(member_bboxes), member_offsets_(member_offsets), input_origin_(input_origin) @@ -140,7 +140,7 @@ private: // stores corresponding offset, and returns modified bounding box bound_box box_offset_align(size_t i, double x, double y, int x_dir, int y_dir) const { - const bound_box &box = member_boxes_[i]; + bound_box const& box = member_boxes_[i]; pixel_position offset((x_dir == 0 ? x - input_origin_.x : x - (x_dir < 0 ? box.maxx() : box.minx())), (y_dir == 0 ? y - input_origin_.y : y - (y_dir < 0 ? box.maxy() : box.miny()))); @@ -152,8 +152,8 @@ private: bound_box group_layout_manager::offset_box_at(size_t i) { handle_update(); - const pixel_position &offset = member_offsets_.at(i); - const bound_box &box = member_boxes_.at(i); + pixel_position const& offset = member_offsets_.at(i); + bound_box const& box = member_boxes_.at(i); return box2d(box.minx() + offset.x, box.miny() + offset.y, box.maxx() + offset.x, box.maxy() + offset.y); } diff --git a/src/group/group_symbolizer_helper.cpp b/src/group/group_symbolizer_helper.cpp index e779f433d..8af250436 100644 --- a/src/group/group_symbolizer_helper.cpp +++ b/src/group/group_symbolizer_helper.cpp @@ -38,12 +38,12 @@ namespace mapnik { group_symbolizer_helper::group_symbolizer_helper( - const group_symbolizer &sym, const feature_impl &feature, + group_symbolizer const& sym, feature_impl const& feature, attributes const& vars, - const proj_transform &prj_trans, + proj_transform const& prj_trans, unsigned width, unsigned height, double scale_factor, - const view_transform &t, DetectorType &detector, - const box2d &query_extent) + view_transform const& t, DetectorType & detector, + box2d const& query_extent) : base_symbolizer_helper(sym, feature, vars, prj_trans, width, height, scale_factor, t, query_extent), detector_(detector) {} @@ -154,7 +154,7 @@ bool group_symbolizer_helper::check_point_placement(pixel_position const& pos) return true; } -bool group_symbolizer_helper::collision(const box2d &box, const value_unicode_string &repeat_key) const +bool group_symbolizer_helper::collision(box2d const& box, value_unicode_string const& repeat_key) const { if (!detector_.extent().intersects(box) || diff --git a/src/layer.cpp b/src/layer.cpp index 11cdb78b6..8b6541e57 100644 --- a/src/layer.cpp +++ b/src/layer.cpp @@ -30,6 +30,7 @@ namespace mapnik { + layer::layer(std::string const& name, std::string const& srs) : name_(name), srs_(srs), @@ -39,8 +40,11 @@ layer::layer(std::string const& name, std::string const& srs) queryable_(false), clear_label_cache_(false), cache_features_(false), - group_by_(""), + group_by_(), + styles_(), ds_(), + buffer_size_(), + maximum_extent_() opacity_(1.0f) {} layer::layer(layer const& rhs) @@ -235,7 +239,6 @@ void layer::reset_buffer_size() buffer_size_.reset(); } - box2d layer::envelope() const { if (ds_) return ds_->envelope(); diff --git a/src/load_map.cpp b/src/load_map.cpp index ebe7ae690..bfd5bcfb7 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -84,12 +84,15 @@ constexpr unsigned name2int(const char *str, int off = 0) class map_parser : mapnik::noncopyable { public: - map_parser(bool strict, std::string const& filename = "") : + map_parser(Map & map, bool strict, std::string const& filename = "") : strict_(strict), filename_(filename), - font_manager_(font_engine_), - xml_base_path_() - {} + font_library_(), + font_file_mapping_(map.get_font_file_mapping()), + font_name_cache_(), + file_sources_(), + fontsets_(), + xml_base_path_() {} void parse_map(Map & map, xml_node const& node, std::string const& base_path); private: @@ -129,8 +132,9 @@ private: bool strict_; std::string filename_; std::map datasource_templates_; - freetype_engine font_engine_; - face_manager font_manager_; + font_library font_library_; + freetype_engine::font_file_mapping_type & font_file_mapping_; + std::map font_name_cache_; std::map file_sources_; std::map fontsets_; std::string xml_base_path_; @@ -143,7 +147,7 @@ void load_map(Map & map, std::string const& filename, bool strict, std::string b xml_tree tree("utf8"); tree.set_filename(filename); read_xml(filename, tree.root()); - map_parser parser(strict, filename); + map_parser parser(map, strict, filename); parser.parse_map(map, tree.root(), base_path); //dump_xml(tree.root()); } @@ -160,7 +164,7 @@ void load_map_string(Map & map, std::string const& str, bool strict, std::string { read_xml_string(str, tree.root(), map.base_path()); // FIXME - this value is not fully known yet } - map_parser parser(strict, base_path); + map_parser parser(map, strict, base_path); parser.parse_map(map, tree.root(), base_path); } @@ -262,7 +266,8 @@ void map_parser::parse_map(Map & map, xml_node const& node, std::string const& b optional font_directory = map_node.get_opt_attr("font-directory"); if (font_directory) { - if (!freetype_engine::register_fonts(ensure_relative_to_xml(font_directory), false)) + map.set_font_directory(*font_directory); + if (!map.register_fonts(ensure_relative_to_xml(font_directory), false)) { if (strict_) { @@ -511,21 +516,34 @@ void map_parser::parse_fontset(Map & map, xml_node const& node) } } -bool map_parser::parse_font(font_set &fset, xml_node const& f) +bool map_parser::parse_font(font_set & fset, xml_node const& f) { - optional face_name = f.get_opt_attr("face-name"); - if (face_name) + optional has_face_name = f.get_opt_attr("face-name"); + if (has_face_name) { - face_ptr face = font_manager_.get_face(*face_name); - if (face) + std::string face_name = *has_face_name; + bool found = false; + auto itr = font_name_cache_.find(face_name); + if (itr != font_name_cache_.end()) { - fset.add_face_name(*face_name); + found = itr->second; + } + else + { + found = freetype_engine::can_open(face_name, + font_library_, + font_file_mapping_, + freetype_engine::get_mapping()); + font_name_cache_.emplace(face_name,found); + } + if (found) + { + fset.add_face_name(face_name); return true; } else if (strict_) { - throw config_error("Failed to find font face '" + - *face_name + "'"); + throw config_error("Failed to find font face '" + face_name + "'"); } } else @@ -1525,7 +1543,21 @@ void map_parser::parse_pair_layout(group_symbolizer_properties & prop, xml_node void map_parser::ensure_font_face(std::string const& face_name) { - if (! font_manager_.get_face(face_name)) + bool found = false; + auto itr = font_name_cache_.find(face_name); + if (itr != font_name_cache_.end()) + { + found = itr->second; + } + else + { + found = freetype_engine::can_open(face_name, + font_library_, + font_file_mapping_, + freetype_engine::get_mapping()); + font_name_cache_.emplace(face_name,found); + } + if (!found) { throw config_error("Failed to find font face '" + face_name + "'"); diff --git a/src/map.cpp b/src/map.cpp index a58bb51f6..0bd9b66b5 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -38,8 +38,9 @@ #include #include #include // for PROJ_ENVELOPE_POINTS - -// boost +#include +#include +#include // stl #include @@ -70,7 +71,11 @@ Map::Map() background_image_comp_op_(src_over), background_image_opacity_(1.0), aspectFixMode_(GROW_BBOX), - base_path_("") {} + base_path_(""), + extra_params_(), + font_directory_(), + font_file_mapping_(), + font_memory_cache_() {} Map::Map(int width,int height, std::string const& srs) : width_(width), @@ -80,7 +85,11 @@ Map::Map(int width,int height, std::string const& srs) background_image_comp_op_(src_over), background_image_opacity_(1.0), aspectFixMode_(GROW_BBOX), - base_path_("") {} + base_path_(""), + extra_params_(), + font_directory_(), + font_file_mapping_(), + font_memory_cache_() {} Map::Map(Map const& rhs) : width_(rhs.width_), @@ -98,7 +107,11 @@ Map::Map(Map const& rhs) current_extent_(rhs.current_extent_), maximum_extent_(rhs.maximum_extent_), base_path_(rhs.base_path_), - extra_params_(rhs.extra_params_) {} + extra_params_(rhs.extra_params_), + font_directory_(rhs.font_directory_), + font_file_mapping_(rhs.font_file_mapping_), + // on copy discard memory cache + font_memory_cache_() {} Map::Map(Map && rhs) @@ -117,11 +130,13 @@ Map::Map(Map && rhs) current_extent_(std::move(rhs.current_extent_)), maximum_extent_(std::move(rhs.maximum_extent_)), base_path_(std::move(rhs.base_path_)), - extra_params_(std::move(rhs.extra_params_)) {} + extra_params_(std::move(rhs.extra_params_)), + font_directory_(std::move(rhs.font_directory_)), + font_file_mapping_(std::move(rhs.font_file_mapping_)), + font_memory_cache_(std::move(rhs.font_memory_cache_)) {} Map::~Map() {} - Map& Map::operator=(Map rhs) { swap(*this, rhs); @@ -147,9 +162,12 @@ void swap (Map & lhs, Map & rhs) std::swap(lhs.maximum_extent_, rhs.maximum_extent_); std::swap(lhs.base_path_, rhs.base_path_); std::swap(lhs.extra_params_, rhs.extra_params_); + std::swap(lhs.font_directory_,rhs.font_directory_); + std::swap(lhs.font_file_mapping_,rhs.font_file_mapping_); + // on assignment discard memory cache + //std::swap(lhs.font_memory_cache_,rhs.font_memory_cache_); } - bool Map::operator==(Map const& rhs) const { return (width_ == rhs.width_) && @@ -167,7 +185,10 @@ bool Map::operator==(Map const& rhs) const (current_extent_ == rhs.current_extent_) && (maximum_extent_ == rhs.maximum_extent_) && (base_path_ == rhs.base_path_) && - (extra_params_ == rhs.extra_params_); + (extra_params_ == rhs.extra_params_) && + (font_directory_ == rhs.font_directory_) && + (font_file_mapping_ == rhs.font_file_mapping_); + // Note: we don't care about font_memory_cache in comparison } std::map const& Map::styles() const @@ -200,7 +221,12 @@ Map::const_style_iterator Map::end_styles() const return styles_.end(); } -bool Map::insert_style(std::string const& name,feature_type_style style) +bool Map::insert_style(std::string const& name, feature_type_style const& style) +{ + return styles_.emplace(name, style).second; +} + +bool Map::insert_style(std::string const& name, feature_type_style && style) { return styles_.emplace(name, std::move(style)).second; } @@ -219,7 +245,16 @@ boost::optional Map::find_style(std::string const& na return boost::optional() ; } -bool Map::insert_fontset(std::string const& name, font_set fontset) +bool Map::insert_fontset(std::string const& name, font_set const& fontset) +{ + if (fontset.get_name() != name) + { + throw mapnik::config_error("Fontset name must match the name used to reference it on the map"); + } + return fontsets_.emplace(name, fontset).second; +} + +bool Map::insert_fontset(std::string const& name, font_set && fontset) { if (fontset.get_name() != name) { @@ -247,12 +282,43 @@ std::map & Map::fontsets() return fontsets_; } +bool Map::register_fonts(std::string const& dir, bool recurse) +{ + font_library library; + return freetype_engine::register_fonts_impl(dir, library, font_file_mapping_, recurse); +} + +bool Map::load_fonts() +{ + bool result = false; + for (auto const& kv : font_file_mapping_) + { + auto const& global_mapping = freetype_engine::get_mapping(); + if ((global_mapping.find(kv.first) == global_mapping.end()) && + (font_memory_cache_.find(kv.second.second) == font_memory_cache_.end())) + { + mapnik::util::file file(kv.second.second); + if (file.open()) + { + auto item = font_memory_cache_.emplace(kv.second.second, std::make_pair(std::move(file.data()),file.size())); + if (item.second) result = true; + } + } + } + return result; +} + size_t Map::layer_count() const { return layers_.size(); } -void Map::add_layer(layer l) +void Map::add_layer(layer const& l) +{ + layers_.emplace_back(l); +} + +void Map::add_layer(layer && l) { layers_.push_back(std::move(l)); } diff --git a/src/renderer_common.cpp b/src/renderer_common.cpp index 15ba84e89..17111bb73 100644 --- a/src/renderer_common.cpp +++ b/src/renderer_common.cpp @@ -36,9 +36,9 @@ renderer_common::renderer_common(Map const& map, unsigned width, unsigned height height_(height), scale_factor_(scale_factor), vars_(vars), - shared_font_engine_(std::make_shared()), - font_engine_(*shared_font_engine_), - font_manager_(font_engine_), + shared_font_library_(std::make_shared()), + font_library_(*shared_font_library_), + font_manager_(font_library_,map.get_font_file_mapping(),map.get_font_memory_cache()), query_extent_(), t_(t), detector_(detector) diff --git a/src/save_map.cpp b/src/save_map.cpp index 076f05e82..78c3a7888 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -625,6 +625,12 @@ void serialize_map(ptree & pt, Map const& map, bool explicit_defaults) set_attr( map_node, "background-color", * c ); } + optional const& font_directory = map.font_directory(); + if ( font_directory ) + { + set_attr( map_node, "font-directory", *font_directory ); + } + optional const& image_filename = map.background_image(); if ( image_filename ) { diff --git a/src/text/font_feature_settings.cpp b/src/text/font_feature_settings.cpp index 0a985c20e..897e2c891 100644 --- a/src/text/font_feature_settings.cpp +++ b/src/text/font_feature_settings.cpp @@ -26,11 +26,13 @@ // boost #include +#include // stl #include #include #include +#include namespace mapnik { @@ -54,9 +56,16 @@ void font_feature_settings::from_string(std::string const& features) qi::char_type char_; qi::as_string_type as_string; +#if BOOST_VERSION <= 104800 + // Call correct overload. + using std::placeholders::_1; + void (font_feature_settings::*append)(std::string const&) = &font_feature_settings::append; + if (!qi::parse(features.begin(), features.end(), as_string[+(char_ - ',')][std::bind(append, this, _1)] % ',')) +#else auto app = [&](std::string const& s) { append(s); }; - if (!qi::parse(features.begin(), features.end(), as_string[+(char_ - ',')][app] % ',')) +#endif + { throw config_error("failed to parse font-feature-settings: '" + features + "'"); } @@ -96,4 +105,3 @@ void font_feature_settings::append(std::string const& feature) } } - diff --git a/src/text/font_library.cpp b/src/text/font_library.cpp new file mode 100644 index 000000000..0e19b8c85 --- /dev/null +++ b/src/text/font_library.cpp @@ -0,0 +1,81 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2014 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +// mapnik +#include + +// stl +#include +#include + +// freetype2 +extern "C" +{ +#include +#include FT_FREETYPE_H +#include FT_MODULE_H +} + +namespace { + +void* _Alloc_Func(FT_Memory memory, long size) +{ + return std::malloc(size); +} + +void _Free_Func(FT_Memory memory, void *block) +{ + std::free(block); +} + +void* _Realloc_Func(FT_Memory memory, long cur_size, long new_size, void* block) +{ + return std::realloc(block, new_size); +} + +} + +namespace mapnik { + +font_library::font_library() + : library_(nullptr), + memory_(new FT_MemoryRec_) +{ + memory_->alloc = _Alloc_Func; + memory_->free = _Free_Func; + memory_->realloc = _Realloc_Func; + FT_Error error = FT_New_Library(&*memory_, &library_); + if (error) throw std::runtime_error("can not initalize FreeType2 library"); + FT_Add_Default_Modules(library_); +} + +FT_Library font_library::get() +{ + return library_; +} + +font_library::~font_library() +{ + FT_Done_Library(library_); +} + +} // end namespace mapnik diff --git a/src/text/formatting/layout.cpp b/src/text/formatting/layout.cpp index c0becc0b1..91c3cc1a6 100644 --- a/src/text/formatting/layout.cpp +++ b/src/text/formatting/layout.cpp @@ -50,6 +50,7 @@ void layout_node::to_xml(ptree &xml) const if (dy) serialize_property("dy", *dy, new_node); if (text_ratio) serialize_property("text-ratio", *text_ratio, new_node); if (wrap_width) serialize_property("wrap-width", *wrap_width, new_node); + if (wrap_char) serialize_property("wrap-character", *wrap_char, new_node); if (wrap_before) serialize_property("wrap-before", *wrap_before, new_node); if (repeat_wrap_char) serialize_property("repeat-wrap-character", *repeat_wrap_char, new_node); if (rotate_displacement) serialize_property("rotate-displacement", *rotate_displacement, new_node); @@ -71,6 +72,7 @@ node_ptr layout_node::from_xml(xml_node const& xml, fontset_map const& fontsets) if (xml.has_attribute("dy")) set_property_from_xml(n->dy, "dy", xml); if (xml.has_attribute("text-ratio")) set_property_from_xml(n->text_ratio, "text-ratio", xml); if (xml.has_attribute("wrap-width")) set_property_from_xml(n->wrap_width, "wrap-width", xml); + if (xml.has_attribute("wrap-character")) set_property_from_xml(n->wrap_char, "wrap-character", xml); if (xml.has_attribute("wrap-before")) set_property_from_xml(n->wrap_before, "wrap-before", xml); if (xml.has_attribute("repeat-wrap-character")) set_property_from_xml(n->repeat_wrap_char, "repeat-wrap-character", xml); if (xml.has_attribute("rotate-displacement")) set_property_from_xml(n->rotate_displacement, "rotate-displacement", xml); @@ -88,6 +90,7 @@ void layout_node::apply(evaluated_format_properties_ptr p, feature_impl const& f if (dy) new_properties.dy = *dy; if (text_ratio) new_properties.text_ratio = *text_ratio; if (wrap_width) new_properties.wrap_width = *wrap_width; + if (wrap_char) new_properties.wrap_char = *wrap_char; if (wrap_before) new_properties.wrap_before = *wrap_before; if (repeat_wrap_char) new_properties.repeat_wrap_char = *repeat_wrap_char; if (rotate_displacement) new_properties.rotate_displacement = *rotate_displacement; @@ -128,6 +131,7 @@ void layout_node::add_expressions(expression_set & output) const if (dy && is_expression(*dy)) output.insert(util::get(*dy)); if (orientation && is_expression(*orientation)) output.insert(util::get(*orientation)); if (wrap_width && is_expression(*wrap_width)) output.insert(util::get(*wrap_width)); + if (wrap_char && is_expression(*wrap_char)) output.insert(util::get(*wrap_char)); if (wrap_before && is_expression(*wrap_before)) output.insert(util::get(*wrap_before)); if (repeat_wrap_char && is_expression(*repeat_wrap_char)) output.insert(util::get(*repeat_wrap_char)); if (rotate_displacement && is_expression(*rotate_displacement)) output.insert(util::get(*rotate_displacement)); diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp index 9d4cf450c..7b3851628 100644 --- a/src/text/symbolizer_helpers.cpp +++ b/src/text/symbolizer_helpers.cpp @@ -344,7 +344,7 @@ template text_symbolizer_helper::text_symbolizer_helper( unsigned height, double scale_factor, view_transform const& t, - face_manager &font_manager, + face_manager_freetype & font_manager, label_collision_detector4 &detector, box2d const& query_extent, agg::trans_affine const&); @@ -358,7 +358,7 @@ template text_symbolizer_helper::text_symbolizer_helper( unsigned height, double scale_factor, view_transform const& t, - face_manager &font_manager, + face_manager_freetype & font_manager, label_collision_detector4 &detector, box2d const& query_extent, agg::trans_affine const&); diff --git a/src/text/vertex_cache.cpp b/src/text/vertex_cache.cpp index e0ddeb07f..8a2fb2b89 100644 --- a/src/text/vertex_cache.cpp +++ b/src/text/vertex_cache.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace mapnik { @@ -127,18 +128,15 @@ vertex_cache & vertex_cache::get_offseted(double offset, double region_width) return *this; } - vertex_cache_ptr offseted_line; offseted_lines_map::iterator pos = offseted_lines_.find(offset); - if (pos != offseted_lines_.end()) - { - offseted_line = pos->second; - } - else + if (pos == offseted_lines_.end()) { offset_converter converter(*this); converter.set_offset(offset); - offseted_line = vertex_cache_ptr(new vertex_cache(converter)); + pos = offseted_lines_.emplace(offset, std::make_unique(converter)).first; } + vertex_cache_ptr & offseted_line = pos->second; + offseted_line->reset(); offseted_line->next_subpath(); //TODO: Multiple subpath support @@ -146,8 +144,6 @@ vertex_cache & vertex_cache::get_offseted(double offset, double region_width) // which we'll use to make the offset line aligned to this one. double seek = offseted_line->position_closest_to(current_position_); offseted_line->move(seek); - - offseted_lines_[offset] = offseted_line; return *offseted_line; } diff --git a/tests/cpp_tests/font_registration_test.cpp b/tests/cpp_tests/font_registration_test.cpp index 37a3ba68c..b89460a5c 100644 --- a/tests/cpp_tests/font_registration_test.cpp +++ b/tests/cpp_tests/font_registration_test.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include @@ -31,10 +33,34 @@ int main(int argc, char** argv) BOOST_TEST( mapnik::util::exists( fontdir ) ); BOOST_TEST( mapnik::util::is_directory( fontdir ) ); + // test map cached fonts + mapnik::Map m(1,1); + BOOST_TEST( m.register_fonts(fontdir , false ) ); + BOOST_TEST( m.get_font_memory_cache().size() == 0 ); + BOOST_TEST( m.get_font_file_mapping().size() == 1 ); + BOOST_TEST( m.load_fonts() ); + BOOST_TEST( m.get_font_memory_cache().size() == 1 ); + BOOST_TEST( m.register_fonts(fontdir , true ) ); + BOOST_TEST( m.get_font_file_mapping().size() == 22 ); + BOOST_TEST( m.load_fonts() ); + BOOST_TEST( m.get_font_memory_cache().size() == 22 ); + + // copy discards memory cache but not file mapping + mapnik::Map m2(m); + BOOST_TEST( m2.get_font_memory_cache().size() == 0 ); + BOOST_TEST( m2.get_font_file_mapping().size() == 22 ); + BOOST_TEST( m2.load_fonts() ); + BOOST_TEST( m2.get_font_memory_cache().size() == 22 ); + + // test font-directory from XML + mapnik::Map m3(1,1); + mapnik::load_map_string(m3,""); + BOOST_TEST( m3.get_font_memory_cache().size() == 0 ); + BOOST_TEST( m3.load_fonts() ); + BOOST_TEST( m3.get_font_memory_cache().size() == 1 ); + std::vector face_names; - std::string foo("foo"); - // fake directories BOOST_TEST( !mapnik::freetype_engine::register_fonts(foo , true ) ); face_names = mapnik::freetype_engine::face_names(); diff --git a/tests/cpp_tests/line_offset_test.cpp b/tests/cpp_tests/line_offset_test.cpp index aa2fcab93..cbb6e314c 100644 --- a/tests/cpp_tests/line_offset_test.cpp +++ b/tests/cpp_tests/line_offset_test.cpp @@ -4,7 +4,6 @@ // boost #include -#include // stl #include diff --git a/tests/cpp_tests/params_test.cpp b/tests/cpp_tests/params_test.cpp index 92f39d7ad..5d89b8cbe 100644 --- a/tests/cpp_tests/params_test.cpp +++ b/tests/cpp_tests/params_test.cpp @@ -75,7 +75,8 @@ int main(int argc, char** argv) // value_null params["null"] = mapnik::value_null(); - BOOST_TEST( (params.get("null") && *params.get("null") == mapnik::value_null()) ); + // https://github.com/mapnik/mapnik/issues/2471 + //BOOST_TEST( (params.get("null") && *params.get("null") == mapnik::value_null()) ); if (!::boost::detail::test_errors()) { if (quiet) std::clog << "\x1b[1;32m.\x1b[0m"; diff --git a/tests/cpp_tests/symbolizer_test.cpp b/tests/cpp_tests/symbolizer_test.cpp index 78bd55b22..0dc8353d1 100644 --- a/tests/cpp_tests/symbolizer_test.cpp +++ b/tests/cpp_tests/symbolizer_test.cpp @@ -1,4 +1,3 @@ -#include #include #include #include