fixup font registration code ensuring invalid fonts will warn but not throw and register_fonts will only return success if > one font is registered and none have failed

This commit is contained in:
Dane Springmeyer 2012-03-23 15:07:28 -07:00
parent 5e6632f6eb
commit 3b498efbd9
9 changed files with 65 additions and 41 deletions

View file

@ -27,4 +27,7 @@ pep8:
@pep8 -r --select=W293 -q --filename=*.py `pwd`/tests/ | xargs gsed -i 's/^[ \r\t]*$//'
@pep8 -r --select=W391 -q --filename=*.py `pwd`/tests/ | xargs gsed -i -e :a -e '/^\n*$/{$d;N;ba' -e '}'
grind:
@valgrind --leak-check=full tests/cpp_tests/font_registration_test
.PHONY: clean reset uninstall test install

View file

@ -1716,7 +1716,7 @@ if not HELP_REQUESTED:
# build C++ tests
# not ready for release
#SConscript('tests/cpp_tests/build.py')
SConscript('tests/cpp_tests/build.py')
# not ready for release
#if env['SVG_RENDERER']:

View file

@ -71,11 +71,10 @@ bool freetype_engine::is_font_file(std::string const& file_name)
bool freetype_engine::register_font(std::string const& file_name)
{
if (!boost::filesystem::is_regular_file(file_name) || !is_font_file(file_name)) return false;
#ifdef MAPNIK_THREADSAFE
mutex::scoped_lock lock(mutex_);
#endif
FT_Library library;
FT_Library library = 0;
FT_Error error = FT_Init_FreeType(&library);
if (error)
{
@ -84,6 +83,7 @@ bool freetype_engine::register_font(std::string const& file_name)
FT_Face face = 0;
int num_faces = 0;
bool success = false;
// some font files have multiple fonts in a file
// the count is in the 'root' face library[0]
// see the FT_FaceRec in freetype.h
@ -92,31 +92,37 @@ bool freetype_engine::register_font(std::string const& file_name)
error = FT_New_Face (library,file_name.c_str(),i,&face);
if (error)
{
FT_Done_FreeType(library);
return false;
break;
}
// store num_faces locally, after FT_Done_Face it can not be accessed any more
if (!num_faces)
num_faces = face->num_faces;
// some fonts can lack names, skip them
// http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_FaceRec
if (face->family_name && face->style_name) {
if (face->family_name && face->style_name)
{
success = true;
std::string name = std::string(face->family_name) + " " + std::string(face->style_name);
name2file_.insert(std::make_pair(name, std::make_pair(i,file_name)));
FT_Done_Face(face);
//FT_Done_FreeType(library);
//return true;
} else {
FT_Done_Face(face);
FT_Done_FreeType(library);
}
else
{
std::ostringstream s;
s << "Error: unable to load invalid font file which lacks identifiable family and style name: '"
<< file_name << "'";
throw std::runtime_error(s.str());
s << "Warning: unable to load font file '" << file_name << "' ";
if (!face->family_name && !face->style_name)
s << "which lacks both a family name and style name";
else if (face->family_name)
s << "which reports a family name of '" << std::string(face->family_name) << "' and lacks a style name";
else if (face->style_name)
s << "which reports a style name of '" << std::string(face->style_name) << "' and lacks a family name";
std::clog << s.str() << std::endl;
}
}
FT_Done_FreeType(library);
return true;
if (face)
FT_Done_Face(face);
if (library)
FT_Done_FreeType(library);
return success;
}
bool freetype_engine::register_fonts(std::string const& dir, bool recurse)
@ -130,26 +136,24 @@ bool freetype_engine::register_fonts(std::string const& dir, bool recurse)
return mapnik::freetype_engine::register_font(dir);
boost::filesystem::directory_iterator end_itr;
bool success = false;
for (boost::filesystem::directory_iterator itr(dir); itr != end_itr; ++itr)
{
#if (BOOST_FILESYSTEM_VERSION == 3)
std::string const& file_name = itr->path().string();
#else // v2
std::string const& file_name = itr->string();
#endif
if (boost::filesystem::is_directory(*itr) && recurse)
{
#if (BOOST_FILESYSTEM_VERSION == 3)
if (!register_fonts(itr->path().string(), true)) return false;
#else // v2
if (!register_fonts(itr->string(), true)) return false;
#endif
success = register_fonts(file_name, true);
}
else
else if (boost::filesystem::is_regular_file(file_name) && is_font_file(file_name))
{
#if (BOOST_FILESYSTEM_VERSION == 3)
mapnik::freetype_engine::register_font(itr->path().string());
#else // v2
mapnik::freetype_engine::register_font(itr->string());
#endif
success = mapnik::freetype_engine::register_font(file_name);
}
}
return true;
return success;
}

View file

@ -242,7 +242,13 @@ void map_parser::parse_map(Map & map, xml_node const& pt, std::string const& bas
if (font_directory)
{
extra_attr["font-directory"] = *font_directory;
freetype_engine::register_fonts(ensure_relative_to_xml(font_directory), false);
if (!freetype_engine::register_fonts(ensure_relative_to_xml(font_directory), false))
{
if (strict_)
{
throw config_error(std::string("Failed to load fonts from: ") << *font_directory);
}
}
}
optional<std::string> min_version_string = map_node.get_opt_attr<std::string>("minimum-version");

View file

@ -11,6 +11,8 @@ headers = env['CPPPATH']
libraries = copy(env['LIBMAPNIK_LIBS'])
libraries.append('mapnik')
test_env.Append(CXXFLAGS='-g')
for cpp_test in glob.glob('*_test.cpp'):
test_program = test_env.Program(cpp_test.replace('.cpp',''), [cpp_test], CPPPATH=headers, LIBS=libraries, LINKFLAGS=env['CUSTOM_LDFLAGS'])
Depends(test_program, env.subst('../../src/%s' % env['MAPNIK_LIB_NAME']))

View file

@ -6,8 +6,6 @@ using fs::path;
namespace sys = boost::system;
#include <boost/detail/lightweight_test.hpp>
//#include <boost/bind.hpp>
//#include <fstream>
#include <iostream>
#include <mapnik/font_engine_freetype.hpp>
@ -39,14 +37,23 @@ int main( int, char*[] )
// directories without fonts
std::string src("src");
// a legitimate directory will return true even it is does not
// successfully register a font...
BOOST_TEST( mapnik::freetype_engine::register_fonts(src , true ) );
face_names = mapnik::freetype_engine::face_names();
BOOST_TEST( face_names.size() == 0 );
std::clog << "number of registered fonts: " << face_names.size() << std::endl;
// an empty directory will not return true
// we need to register at least one font and not fail on any
// to return true
BOOST_TEST( mapnik::freetype_engine::register_font(src) == false );
BOOST_TEST( mapnik::freetype_engine::register_fonts(src, true) == false );
BOOST_TEST( mapnik::freetype_engine::face_names().size() == 0 );
// register unifont
// bogus, emtpy file that looks like font
BOOST_TEST( mapnik::freetype_engine::register_font("tests/data/fonts/fake.ttf") == false );
BOOST_TEST( mapnik::freetype_engine::register_fonts("tests/data/fonts/fake.ttf") == false );
BOOST_TEST( mapnik::freetype_engine::face_names().size() == 0 );
BOOST_TEST( mapnik::freetype_engine::register_font("tests/data/fonts/intentionally-broken.ttf") == false );
BOOST_TEST( mapnik::freetype_engine::register_fonts("tests/data/fonts/intentionally-broken.ttf") == false );
BOOST_TEST( mapnik::freetype_engine::face_names().size() == 0 );
// register unifont, since we know it sits in the root fonts/ dir
BOOST_TEST( mapnik::freetype_engine::register_fonts(fontdir) );
face_names = mapnik::freetype_engine::face_names();
std::clog << "number of registered fonts: " << face_names.size() << std::endl;
@ -71,7 +78,7 @@ int main( int, char*[] )
BOOST_TEST( mapnik::freetype_engine::register_fonts(fontdir, true) );
face_names = mapnik::freetype_engine::face_names();
std::clog << "number of registered fonts: " << face_names.size() << std::endl;
BOOST_TEST( face_names.size() == 21 );
BOOST_TEST( face_names.size() == 22 );
return ::boost::report_errors();
}

View file

Binary file not shown.

View file

@ -1,5 +1,7 @@
<!-- nik2img.py -b 60 46 80 28 rtl_text_map.xml text_styles.png --><Map background-color="#eee" srs="+init=epsg:4326" minimum-version="0.7.2" font-directory="../fonts/">
<Map background-color="#eee" srs="+init=epsg:4326" minimum-version="0.7.2" font-directory="../fonts/XB Zar.ttf">
<!-- nik2img.py -b 60 46 80 28 rtl_text_map.xml text_styles.png -->
<Style name="custom_font">
<Rule>
<TextSymbolizer fill="#000" size="20" dy="-2" face-name="XB Zar Regular" halo-fill="#fff" halo-radius="2" wrap-width="50" line-spacing="3" allow-overlap="true">[NAME_FA]</TextSymbolizer>