make font-engine-singleton to have better control over objects life-time.

This commit is contained in:
artemp 2017-05-26 16:52:50 +02:00
parent e6173385ca
commit 1c101c3243
12 changed files with 83 additions and 80 deletions

View file

@ -10,39 +10,39 @@ public:
bool validate() const bool validate() const
{ {
std::size_t count = 0; std::size_t count = 0;
std::size_t expected_count = mapnik::freetype_engine::face_names().size(); std::size_t expected_count = mapnik::freetype_engine::instance().face_names().size();
mapnik::freetype_engine::font_file_mapping_type font_file_mapping; mapnik::freetype_engine::font_file_mapping_type font_file_mapping;
mapnik::freetype_engine::font_memory_cache_type font_cache; mapnik::freetype_engine::font_memory_cache_type font_cache;
mapnik::font_library library; mapnik::font_library library;
for (std::string const& name : mapnik::freetype_engine::face_names()) for (std::string const& name : mapnik::freetype_engine::instance().face_names())
{ {
mapnik::face_ptr f = mapnik::freetype_engine::create_face(name, mapnik::face_ptr f = mapnik::freetype_engine::instance().create_face(name,
library, library,
font_file_mapping, font_file_mapping,
font_cache, font_cache,
mapnik::freetype_engine::get_mapping(), mapnik::freetype_engine::instance().get_mapping(),
mapnik::freetype_engine::get_cache()); mapnik::freetype_engine::instance().get_cache());
if (f) ++count; if (f) ++count;
} }
return count == expected_count; return count == expected_count;
} }
bool operator()() const bool operator()() const
{ {
std::size_t expected_count = mapnik::freetype_engine::face_names().size(); std::size_t expected_count = mapnik::freetype_engine::instance().face_names().size();
for (unsigned i=0;i<iterations_;++i) for (unsigned i=0;i<iterations_;++i)
{ {
std::size_t count = 0; std::size_t count = 0;
mapnik::freetype_engine::font_file_mapping_type font_file_mapping; mapnik::freetype_engine::font_file_mapping_type font_file_mapping;
mapnik::freetype_engine::font_memory_cache_type font_cache; mapnik::freetype_engine::font_memory_cache_type font_cache;
mapnik::font_library library; mapnik::font_library library;
for (std::string const& name : mapnik::freetype_engine::face_names()) for (std::string const& name : mapnik::freetype_engine::instance().face_names())
{ {
mapnik::face_ptr f = mapnik::freetype_engine::create_face(name, mapnik::face_ptr f = mapnik::freetype_engine::instance().create_face(name,
library, library,
font_file_mapping, font_file_mapping,
font_cache, font_cache,
mapnik::freetype_engine::get_mapping(), mapnik::freetype_engine::instance().get_mapping(),
mapnik::freetype_engine::get_cache()); mapnik::freetype_engine::instance().get_cache());
if (f) ++count; if (f) ++count;
} }
if (count != expected_count) { if (count != expected_count) {
@ -57,13 +57,12 @@ int main(int argc, char** argv)
{ {
mapnik::parameters params; mapnik::parameters params;
benchmark::handle_args(argc,argv,params); benchmark::handle_args(argc,argv,params);
bool success = mapnik::freetype_engine::register_fonts("./fonts", true); bool success = mapnik::freetype_engine::instance().register_fonts("./fonts", true);
if (!success) { if (!success) {
std::clog << "warning, did not register any new fonts!\n"; std::clog << "warning, did not register any new fonts!\n";
return -1; return -1;
} }
std::size_t face_count = mapnik::freetype_engine::face_names().size(); std::size_t face_count = mapnik::freetype_engine::instance().face_names().size();
test test_runner(params); test test_runner(params);
return run(test_runner,(boost::format("font_engine: creating %ld faces") % (face_count)).str()); return run(test_runner,(boost::format("font_engine: creating %ld faces") % (face_count)).str());
} }

View file

@ -10,14 +10,14 @@ public:
: test_case(params) {} : test_case(params) {}
bool validate() const bool validate() const
{ {
return mapnik::freetype_engine::register_fonts("./fonts", true); return mapnik::freetype_engine::instance().register_fonts("./fonts", true);
} }
bool operator()() const bool operator()() const
{ {
unsigned long count = 0; unsigned long count = 0;
for (unsigned i=0;i<iterations_;++i) for (unsigned i=0;i<iterations_;++i)
{ {
mapnik::freetype_engine::register_fonts("./fonts", true); mapnik::freetype_engine::instance().register_fonts("./fonts", true);
count++; count++;
} }
return true; return true;

View file

@ -99,7 +99,7 @@ int main(int argc, char** argv)
std::clog << "please provide a name for this test\n"; std::clog << "please provide a name for this test\n";
return -1; return -1;
} }
mapnik::freetype_engine::register_fonts("./fonts/",true); mapnik::freetype_engine::instance().register_fonts("./fonts/",true);
mapnik::datasource_cache::instance().register_datasources("./plugins/input/"); mapnik::datasource_cache::instance().register_datasources("./plugins/input/");
{ {
test test_runner(params); test test_runner(params);

View file

@ -156,7 +156,7 @@ int main(int argc, char** argv)
std::clog << "please provide a name for this test\n"; std::clog << "please provide a name for this test\n";
return -1; return -1;
} }
mapnik::freetype_engine::register_fonts("./fonts/",true); mapnik::freetype_engine::instance().register_fonts("./fonts/",true);
mapnik::datasource_cache::instance().register_datasources("./plugins/input/"); mapnik::datasource_cache::instance().register_datasources("./plugins/input/");
{ {
test test_runner(params); test test_runner(params);

View file

@ -55,7 +55,7 @@ int main ( int, char** )
try { try {
std::cout << " running demo ... \n"; std::cout << " running demo ... \n";
datasource_cache::instance().register_datasources("plugins/input/"); datasource_cache::instance().register_datasources("plugins/input/");
freetype_engine::register_font("fonts/dejavu-fonts-ttf-2.37/ttf/DejaVuSans.ttf"); freetype_engine::instance().register_font("fonts/dejavu-fonts-ttf-2.37/ttf/DejaVuSans.ttf");
Map m(800,600); Map m(800,600);
m.set_background(parse_color("white")); m.set_background(parse_color("white"));

View file

@ -25,9 +25,10 @@
// mapnik // mapnik
#include <mapnik/config.hpp> #include <mapnik/config.hpp>
#include <mapnik/util/singleton.hpp>
#include <mapnik/util/noncopyable.hpp>
#include <mapnik/font_set.hpp> #include <mapnik/font_set.hpp>
#include <mapnik/text/font_library.hpp> #include <mapnik/text/font_library.hpp>
#include <mapnik/util/noncopyable.hpp>
// stl // stl
#include <memory> #include <memory>
@ -51,53 +52,55 @@ using face_set_ptr = std::unique_ptr<font_face_set>;
class font_face; class font_face;
using face_ptr = std::shared_ptr<font_face>; using face_ptr = std::shared_ptr<font_face>;
class MAPNIK_DECL freetype_engine class MAPNIK_DECL freetype_engine : public singleton<freetype_engine, CreateUsingNew>,
private util::noncopyable
{ {
friend class CreateUsingNew<freetype_engine>;
public: public:
using font_file_mapping_type = std::map<std::string,std::pair<int,std::string>>; using font_file_mapping_type = std::map<std::string,std::pair<int,std::string>>;
using font_memory_cache_type = std::map<std::string, std::pair<std::unique_ptr<char[]>, std::size_t>>; using font_memory_cache_type = std::map<std::string, std::pair<std::unique_ptr<char[]>, std::size_t>>;
static bool is_font_file(std::string const& file_name); bool is_font_file(std::string const& file_name);
/*! \brief register a font file /*! \brief register a font file
* @param file_name path to a font file. * @param file_name path to a font file.
* @return bool - true if at least one face was successfully registered in the file. * @return bool - true if at least one face was successfully registered in the file.
*/ */
static bool register_font(std::string const& file_name); bool register_font(std::string const& file_name);
/*! \brief register a font files /*! \brief register a font files
* @param dir - path to a directory containing fonts or subdirectories. * @param dir - path to a directory containing fonts or subdirectories.
* @param recurse - default false, whether to search for fonts in sub directories. * @param recurse - default false, whether to search for fonts in sub directories.
* @return bool - true if at least one face was successfully registered. * @return bool - true if at least one face was successfully registered.
*/ */
static bool register_fonts(std::string const& dir, bool recurse = false); bool register_fonts(std::string const& dir, bool recurse = false);
static std::vector<std::string> face_names(); std::vector<std::string> face_names();
static font_file_mapping_type const& get_mapping(); font_file_mapping_type const& get_mapping();
static font_memory_cache_type & get_cache(); font_memory_cache_type & get_cache();
static bool can_open(std::string const& face_name, bool can_open(std::string const& face_name,
font_library & library, font_library & library,
font_file_mapping_type const& font_file_mapping, font_file_mapping_type const& font_file_mapping,
font_file_mapping_type const& global_font_file_mapping); font_file_mapping_type const& global_font_file_mapping);
static face_ptr create_face(std::string const& face_name, face_ptr create_face(std::string const& face_name,
font_library & library, font_library & library,
font_file_mapping_type const& font_file_mapping, font_file_mapping_type const& font_file_mapping,
freetype_engine::font_memory_cache_type const& font_cache, freetype_engine::font_memory_cache_type const& font_cache,
font_file_mapping_type const& global_font_file_mapping, font_file_mapping_type const& global_font_file_mapping,
freetype_engine::font_memory_cache_type & global_memory_fonts); freetype_engine::font_memory_cache_type & global_memory_fonts);
static bool register_font_impl(std::string const& file_name, bool register_font_impl(std::string const& file_name,
font_library & libary, font_library & libary,
font_file_mapping_type & font_file_mapping); font_file_mapping_type & font_file_mapping);
static bool register_fonts_impl(std::string const& dir, bool register_fonts_impl(std::string const& dir,
font_library & libary, font_library & libary,
font_file_mapping_type & font_file_mapping, font_file_mapping_type & font_file_mapping,
bool recurse = false); bool recurse = false);
virtual ~freetype_engine(); virtual ~freetype_engine();
freetype_engine(); freetype_engine();
private: private:
static bool register_font_impl(std::string const& file_name, FT_LibraryRec_ * library); 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); bool register_fonts_impl(std::string const& dir, FT_LibraryRec_ * library, bool recurse = false);
#ifdef MAPNIK_THREADSAFE #ifdef MAPNIK_THREADSAFE
static std::mutex mutex_; std::mutex mutex_;
#endif #endif
static font_file_mapping_type global_font_file_mapping_; font_file_mapping_type global_font_file_mapping_;
static font_memory_cache_type global_memory_fonts_; font_memory_cache_type global_memory_fonts_;
}; };
class MAPNIK_DECL face_manager class MAPNIK_DECL face_manager
@ -124,7 +127,7 @@ private:
}; };
using face_manager_freetype = face_manager; using face_manager_freetype = face_manager;
extern template class MAPNIK_DECL singleton<freetype_engine, CreateUsingNew>;
} }
#endif // MAPNIK_FONT_ENGINE_FREETYPE_HPP #endif // MAPNIK_FONT_ENGINE_FREETYPE_HPP

View file

@ -51,6 +51,7 @@ extern "C"
namespace mapnik namespace mapnik
{ {
template class MAPNIK_DECL singleton<freetype_engine, CreateUsingNew>;
freetype_engine::freetype_engine() {} freetype_engine::freetype_engine() {}
freetype_engine::~freetype_engine() {} freetype_engine::~freetype_engine() {}
@ -382,12 +383,12 @@ face_ptr face_manager::get_face(std::string const& name)
} }
else else
{ {
face_ptr face = freetype_engine::create_face(name, face_ptr face = freetype_engine::instance().create_face(name,
library_, library_,
font_file_mapping_, font_file_mapping_,
font_memory_cache_, font_memory_cache_,
freetype_engine::get_mapping(), freetype_engine::instance().get_mapping(),
freetype_engine::get_cache()); freetype_engine::instance().get_cache());
if (face) if (face)
{ {
face_cache_->emplace(name, face); face_cache_->emplace(name, face);
@ -442,9 +443,9 @@ face_set_ptr face_manager::get_face_set(std::string const& name, boost::optional
} }
#ifdef MAPNIK_THREADSAFE #ifdef MAPNIK_THREADSAFE
std::mutex freetype_engine::mutex_; //std::mutex freetype_engine::mutex_;
#endif #endif
freetype_engine::font_file_mapping_type freetype_engine::global_font_file_mapping_; //freetype_engine::font_file_mapping_type freetype_engine::global_font_file_mapping_;
freetype_engine::font_memory_cache_type freetype_engine::global_memory_fonts_; //freetype_engine::font_memory_cache_type freetype_engine::global_memory_fonts_;
} }

View file

@ -536,10 +536,10 @@ bool map_parser::parse_font(font_set & fset, xml_node const& f)
} }
else else
{ {
found = freetype_engine::can_open(face_name, found = freetype_engine::instance().can_open(face_name,
font_library_, font_library_,
font_file_mapping_, font_file_mapping_,
freetype_engine::get_mapping()); freetype_engine::instance().get_mapping());
font_name_cache_.emplace(face_name,found); font_name_cache_.emplace(face_name,found);
} }
if (found) if (found)
@ -1599,10 +1599,10 @@ void map_parser::ensure_font_face(std::string const& face_name)
} }
else else
{ {
found = freetype_engine::can_open(face_name, found = freetype_engine::instance().can_open(face_name,
font_library_, font_library_,
font_file_mapping_, font_file_mapping_,
freetype_engine::get_mapping()); freetype_engine::instance().get_mapping());
font_name_cache_.emplace(face_name,found); font_name_cache_.emplace(face_name,found);
} }
if (!found) if (!found)

View file

@ -285,16 +285,16 @@ std::map<std::string,font_set> & Map::fontsets()
bool Map::register_fonts(std::string const& dir, bool recurse) bool Map::register_fonts(std::string const& dir, bool recurse)
{ {
font_library library; font_library library;
return freetype_engine::register_fonts_impl(dir, library, font_file_mapping_, recurse); return freetype_engine::instance().register_fonts_impl(dir, library, font_file_mapping_, recurse);
} }
bool Map::load_fonts() bool Map::load_fonts()
{ {
bool result = false; bool result = false;
auto const& global_mapping = freetype_engine::get_mapping(); auto const& global_mapping = freetype_engine::instance().get_mapping();
for (auto const& kv : font_file_mapping_) // for every face-name -> idx/filepath for (auto const& kv : font_file_mapping_) // for every face-name -> idx/filepath
{ {
auto const& file_path = kv.second.second; auto const& file_path = kv.second.second;
// do not attemp to re-cache in memory // do not attemp to re-cache in memory
if (font_memory_cache_.find(file_path) != font_memory_cache_.end()) if (font_memory_cache_.find(file_path) != font_memory_cache_.end())
{ {

View file

@ -20,8 +20,8 @@ SECTION("registration") {
mapnik::logger::severity_type original_severity = logger.get_severity(); mapnik::logger::severity_type original_severity = logger.get_severity();
// grab references to global statics of registered/cached fonts // grab references to global statics of registered/cached fonts
auto const& global_mapping = mapnik::freetype_engine::get_mapping(); auto const& global_mapping = mapnik::freetype_engine::instance().get_mapping();
auto const& global_cache = mapnik::freetype_engine::get_cache(); auto const& global_cache = mapnik::freetype_engine::instance().get_cache();
// mapnik.Map object has parallel structure for localized fonts // mapnik.Map object has parallel structure for localized fonts
mapnik::Map m(1,1); mapnik::Map m(1,1);
@ -62,11 +62,11 @@ SECTION("registration") {
std::vector<std::string> face_names; std::vector<std::string> face_names;
std::string foo("foo"); std::string foo("foo");
// fake directories // fake directories
REQUIRE( !mapnik::freetype_engine::register_fonts(foo , true ) ); REQUIRE( !mapnik::freetype_engine::instance().register_fonts(foo , true ) );
face_names = mapnik::freetype_engine::face_names(); face_names = mapnik::freetype_engine::instance().face_names();
REQUIRE( face_names.size() == 0 ); REQUIRE( face_names.size() == 0 );
REQUIRE( !mapnik::freetype_engine::register_fonts(foo) ); REQUIRE( !mapnik::freetype_engine::instance().register_fonts(foo) );
face_names = mapnik::freetype_engine::face_names(); face_names = mapnik::freetype_engine::instance().face_names();
REQUIRE( face_names.size() == 0 ); REQUIRE( face_names.size() == 0 );
// directories without fonts // directories without fonts
@ -76,31 +76,31 @@ SECTION("registration") {
// an empty directory will not return true // an empty directory will not return true
// we need to register at least one font and not fail on any // we need to register at least one font and not fail on any
// to return true // to return true
REQUIRE( mapnik::freetype_engine::register_font(src) == false ); REQUIRE( mapnik::freetype_engine::instance().register_font(src) == false );
REQUIRE( mapnik::freetype_engine::register_fonts(src, true) == false ); REQUIRE( mapnik::freetype_engine::instance().register_fonts(src, true) == false );
REQUIRE( mapnik::freetype_engine::face_names().size() == 0 ); REQUIRE( mapnik::freetype_engine::instance().face_names().size() == 0 );
// bogus, emtpy file that looks like font // bogus, emtpy file that looks like font
REQUIRE( mapnik::freetype_engine::register_font("test/data/fonts/fake.ttf") == false ); REQUIRE( mapnik::freetype_engine::instance().register_font("test/data/fonts/fake.ttf") == false );
REQUIRE( mapnik::freetype_engine::register_fonts("test/data/fonts/fake.ttf") == false ); REQUIRE( mapnik::freetype_engine::instance().register_fonts("test/data/fonts/fake.ttf") == false );
REQUIRE( mapnik::freetype_engine::face_names().size() == 0 ); REQUIRE( mapnik::freetype_engine::instance().face_names().size() == 0 );
REQUIRE( mapnik::freetype_engine::register_font("test/data/fonts/intentionally-broken.ttf") == false ); REQUIRE( mapnik::freetype_engine::instance().register_font("test/data/fonts/intentionally-broken.ttf") == false );
REQUIRE( mapnik::freetype_engine::register_fonts("test/data/fonts/intentionally-broken.ttf") == false ); REQUIRE( mapnik::freetype_engine::instance().register_fonts("test/data/fonts/intentionally-broken.ttf") == false );
REQUIRE( mapnik::freetype_engine::face_names().size() == 0 ); REQUIRE( mapnik::freetype_engine::instance().face_names().size() == 0 );
// now restore the original severity // now restore the original severity
logger.set_severity(original_severity); logger.set_severity(original_severity);
// single dejavu font in separate location // single dejavu font in separate location
std::string dejavu_bold_oblique("test/data/fonts/DejaVuSansMono-BoldOblique.ttf"); std::string dejavu_bold_oblique("test/data/fonts/DejaVuSansMono-BoldOblique.ttf");
REQUIRE( mapnik::freetype_engine::register_font(dejavu_bold_oblique) ); REQUIRE( mapnik::freetype_engine::instance().register_font(dejavu_bold_oblique) );
face_names = mapnik::freetype_engine::face_names(); face_names = mapnik::freetype_engine::instance().face_names();
REQUIRE( face_names.size() == 1 ); REQUIRE( face_names.size() == 1 );
// now, inspect font mapping and confirm the correct 'DejaVu Sans Mono Bold Oblique' is registered // now, inspect font mapping and confirm the correct 'DejaVu Sans Mono Bold Oblique' is registered
using font_file_mapping = std::map<std::string, std::pair<int,std::string> >; using font_file_mapping = std::map<std::string, std::pair<int,std::string> >;
font_file_mapping const& name2file = mapnik::freetype_engine::get_mapping(); font_file_mapping const& name2file = mapnik::freetype_engine::instance().get_mapping();
bool found_dejavu = false; bool found_dejavu = false;
for (auto const& item : name2file) for (auto const& item : name2file)
{ {
@ -114,8 +114,8 @@ SECTION("registration") {
REQUIRE( found_dejavu ); REQUIRE( found_dejavu );
// recurse to find all dejavu fonts // recurse to find all dejavu fonts
REQUIRE( mapnik::freetype_engine::register_fonts(fontdir, true) ); REQUIRE( mapnik::freetype_engine::instance().register_fonts(fontdir, true) );
face_names = mapnik::freetype_engine::face_names(); face_names = mapnik::freetype_engine::instance().face_names();
REQUIRE( face_names.size() == 22 ); REQUIRE( face_names.size() == 22 );
// we should have re-registered 'DejaVu Sans Mono Bold Oblique' again, // we should have re-registered 'DejaVu Sans Mono Bold Oblique' again,
@ -148,21 +148,21 @@ SECTION("registration") {
// check that we can correctly read a .ttc containing // check that we can correctly read a .ttc containing
// multiple valid faces // multiple valid faces
// https://github.com/mapnik/mapnik/issues/2274 // https://github.com/mapnik/mapnik/issues/2274
REQUIRE( mapnik::freetype_engine::register_font("test/data/fonts/NotoSans-Regular.ttc") ); REQUIRE( mapnik::freetype_engine::instance().register_font("test/data/fonts/NotoSans-Regular.ttc") );
face_names = mapnik::freetype_engine::face_names(); face_names = mapnik::freetype_engine::instance().face_names();
REQUIRE( face_names.size() == 24 ); REQUIRE( face_names.size() == 24 );
// now blindly register as many system fonts as possible // now blindly register as many system fonts as possible
// the goal here to make sure we don't crash // the goal here to make sure we don't crash
// linux // linux
mapnik::freetype_engine::register_fonts("/usr/share/fonts/", true); mapnik::freetype_engine::instance().register_fonts("/usr/share/fonts/", true);
mapnik::freetype_engine::register_fonts("/usr/local/share/fonts/", true); mapnik::freetype_engine::instance().register_fonts("/usr/local/share/fonts/", true);
// osx // osx
mapnik::freetype_engine::register_fonts("/Library/Fonts/", true); mapnik::freetype_engine::instance().register_fonts("/Library/Fonts/", true);
mapnik::freetype_engine::register_fonts("/System/Library/Fonts/", true); mapnik::freetype_engine::instance().register_fonts("/System/Library/Fonts/", true);
// windows // windows
mapnik::freetype_engine::register_fonts("C:\\Windows\\Fonts", true); mapnik::freetype_engine::instance().register_fonts("C:\\Windows\\Fonts", true);
face_names = mapnik::freetype_engine::face_names(); face_names = mapnik::freetype_engine::instance().face_names();
REQUIRE( face_names.size() > 22 ); REQUIRE( face_names.size() > 22 );
} }
catch (std::exception const & ex) catch (std::exception const & ex)

View file

@ -174,7 +174,7 @@ int main(int argc, char** argv)
} }
#endif #endif
mapnik::freetype_engine::register_fonts(vm["fonts"].as<std::string>(), true); mapnik::freetype_engine::instance().register_fonts(vm["fonts"].as<std::string>(), true);
mapnik::datasource_cache::instance().register_datasources(vm["plugins"].as<std::string>()); mapnik::datasource_cache::instance().register_datasources(vm["plugins"].as<std::string>());
boost::filesystem::path output_dir(vm["output-dir"].as<std::string>()); boost::filesystem::path output_dir(vm["output-dir"].as<std::string>());

View file

@ -104,7 +104,7 @@ int main (int argc,char** argv)
} }
mapnik::datasource_cache::instance().register_datasources("./plugins/input/"); mapnik::datasource_cache::instance().register_datasources("./plugins/input/");
mapnik::freetype_engine::register_fonts("./fonts",true); mapnik::freetype_engine::instance().register_fonts("./fonts",true);
mapnik::Map map(600,400); mapnik::Map map(600,400);
mapnik::load_map(map,xml_file,true); mapnik::load_map(map,xml_file,true);
map.zoom_all(); map.zoom_all();