applied font fallback patch from Beau Gunderson

This commit is contained in:
Artem Pavlenko 2008-06-29 10:58:48 +00:00
parent 3f9127b5d0
commit 1b47afde07
8 changed files with 444 additions and 171 deletions

View file

@ -30,6 +30,7 @@
#include <mapnik/ctrans.hpp> #include <mapnik/ctrans.hpp>
#include <mapnik/geometry.hpp> #include <mapnik/geometry.hpp>
#include <mapnik/text_path.hpp> #include <mapnik/text_path.hpp>
#include <mapnik/font_set.hpp>
// freetype2 // freetype2
extern "C" extern "C"
@ -61,9 +62,7 @@ namespace mapnik
class font_face : boost::noncopyable class font_face : boost::noncopyable
{ {
public: public:
typedef std::pair<unsigned,unsigned> dimension_t; font_face(FT_Face face)
font_face(FT_Face face)
: face_(face) {} : face_(face) {}
std::string family_name() const std::string family_name() const
@ -104,122 +103,11 @@ namespace mapnik
return false; return false;
} }
dimension_t character_dimensions(const unsigned c)
{
FT_Matrix matrix;
FT_Vector pen;
FT_Error error;
FT_GlyphSlot slot = face_->glyph;
pen.x = 0;
pen.y = 0;
FT_BBox glyph_bbox;
FT_Glyph image;
matrix.xx = (FT_Fixed)( 1 * 0x10000L );
matrix.xy = (FT_Fixed)( 0 * 0x10000L );
matrix.yx = (FT_Fixed)( 0 * 0x10000L );
matrix.yy = (FT_Fixed)( 1 * 0x10000L );
FT_Set_Transform (face_,&matrix,&pen);
FT_UInt glyph_index = FT_Get_Char_Index( face_, c);
error = FT_Load_Glyph (face_,glyph_index,FT_LOAD_NO_HINTING);
if ( error )
return dimension_t(0, 0);
error = FT_Get_Glyph( face_->glyph, &image);
if ( error )
return dimension_t(0, 0);
FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox);
FT_Done_Glyph(image);
return dimension_t(slot->advance.x >> 6, glyph_bbox.yMax - glyph_bbox.yMin);
}
void get_string_info(string_info & info)
{
unsigned width = 0;
unsigned height = 0;
UErrorCode err = U_ZERO_ERROR;
UnicodeString const& ustr = info.get_string();
const UChar * text = ustr.getBuffer();
UBiDi * bidi = ubidi_openSized(ustr.length(),0,&err);
if (U_SUCCESS(err))
{
ubidi_setPara(bidi,text,ustr.length(), UBIDI_DEFAULT_LTR,0,&err);
if (U_SUCCESS(err))
{
int32_t count = ubidi_countRuns(bidi,&err);
int32_t logicalStart;
int32_t length;
for (int32_t i=0; i< count;++i)
{
if (UBIDI_LTR == ubidi_getVisualRun(bidi,i,&logicalStart,&length))
{
do {
UChar ch = text[logicalStart++];
dimension_t char_dim = character_dimensions(ch);
info.add_info(ch, char_dim.first, char_dim.second);
width += char_dim.first;
height = char_dim.second > height ? char_dim.second : height;
} while (--length > 0);
}
else
{
logicalStart += length;
int32_t j=0,i=length;
UnicodeString arabic;
UChar * buf = arabic.getBuffer(length);
do {
UChar ch = text[--logicalStart];
buf[j++] = ch;
} while (--i > 0);
arabic.releaseBuffer(length);
if ( *arabic.getBuffer() >= 0x0600 && *arabic.getBuffer() <= 0x06ff)
{
UnicodeString shaped;
u_shapeArabic(arabic.getBuffer(),arabic.length(),shaped.getBuffer(arabic.length()),arabic.length(),
U_SHAPE_LETTERS_SHAPE|U_SHAPE_LENGTH_FIXED_SPACES_NEAR|
U_SHAPE_TEXT_DIRECTION_VISUAL_LTR
,&err);
shaped.releaseBuffer(arabic.length());
if (U_SUCCESS(err))
{
for (int j=0;j<shaped.length();++j)
{
dimension_t char_dim = character_dimensions(shaped[j]);
info.add_info(shaped[j], char_dim.first, char_dim.second);
width += char_dim.first;
height = char_dim.second > height ? char_dim.second : height;
}
}
}
}
}
}
ubidi_close(bidi);
}
info.set_dimensions(width, height);
}
~font_face() ~font_face()
{ {
#ifdef MAPNIK_DEBUG #ifdef MAPNIK_DEBUG
std::clog << "clean up face:" << family_name()<<":" << style_name() << std::endl; std::clog << "~font_face: Clean up face \"" << family_name()
<< " " << style_name() << "\"" << std::endl;
#endif #endif
FT_Done_Face(face_); FT_Done_Face(face_);
} }
@ -228,8 +116,9 @@ namespace mapnik
FT_Face face_; FT_Face face_;
}; };
typedef boost::shared_ptr<font_face> face_ptr; typedef boost::shared_ptr<font_face> face_ptr;
class MAPNIK_DECL freetype_engine // : public mapnik::singleton<freetype_engine,mapnik::CreateStatic>,
class MAPNIK_DECL freetype_engine // : public mapnik::singleton<freetype_engine,mapnik::CreateStatic>,
// private boost::noncopyable // private boost::noncopyable
{ {
// friend class mapnik::CreateStatic<freetype_engine>; // friend class mapnik::CreateStatic<freetype_engine>;
@ -281,6 +170,8 @@ namespace mapnik
template <typename T> template <typename T>
struct text_renderer : private boost::noncopyable struct text_renderer : private boost::noncopyable
{ {
typedef std::pair<unsigned,unsigned> dimension_t;
struct glyph_t : boost::noncopyable struct glyph_t : boost::noncopyable
{ {
FT_Glyph image; FT_Glyph image;
@ -291,16 +182,159 @@ namespace mapnik
typedef boost::ptr_vector<glyph_t> glyphs_t; typedef boost::ptr_vector<glyph_t> glyphs_t;
typedef T pixmap_type; typedef T pixmap_type;
text_renderer (pixmap_type & pixmap, face_ptr face) text_renderer (pixmap_type & pixmap, std::vector<face_ptr> faces)
: pixmap_(pixmap), : pixmap_(pixmap),
face_(face), faces_(faces),
fill_(0,0,0), fill_(0,0,0),
halo_fill_(255,255,255), halo_fill_(255,255,255),
halo_radius_(0) {} halo_radius_(0) {}
dimension_t character_dimensions(const unsigned c)
{
FT_Matrix matrix;
FT_Vector pen;
FT_Error error;
pen.x = 0;
pen.y = 0;
FT_BBox glyph_bbox;
FT_Glyph image;
FT_Face face = (*faces_.begin())->get_face();
FT_UInt glyph_index = FT_Get_Char_Index(face, c);
// If there's no glyph_index we loop through the remaining fonts
// in the fontset looking for one.
if (!glyph_index) {
std::vector<face_ptr>::iterator itr = faces_.begin();
std::vector<face_ptr>::iterator end = faces_.end();
++itr; // Skip the first one, we already tried it.
for (; itr != end; ++itr)
{
FT_Face f = (*itr)->get_face();
glyph_index = FT_Get_Char_Index(f, c);
if (glyph_index) {
face = f;
break;
}
}
}
matrix.xx = (FT_Fixed)( 1 * 0x10000L );
matrix.xy = (FT_Fixed)( 0 * 0x10000L );
matrix.yx = (FT_Fixed)( 0 * 0x10000L );
matrix.yy = (FT_Fixed)( 1 * 0x10000L );
FT_Set_Transform(face, &matrix, &pen);
error = FT_Load_Glyph (face, glyph_index, FT_LOAD_NO_HINTING);
if ( error )
return dimension_t(0, 0);
error = FT_Get_Glyph(face->glyph, &image);
if ( error )
return dimension_t(0, 0);
FT_Glyph_Get_CBox(image, ft_glyph_bbox_pixels, &glyph_bbox);
FT_Done_Glyph(image);
unsigned tempx = face->glyph->advance.x >> 6;
unsigned tempy = glyph_bbox.yMax - glyph_bbox.yMin;
//std::clog << "glyph: " << glyph_index << " x: " << tempx << " y: " << tempy << std::endl;
return dimension_t(tempx, tempy);
}
void get_string_info(string_info & info)
{
unsigned width = 0;
unsigned height = 0;
UErrorCode err = U_ZERO_ERROR;
UnicodeString const& ustr = info.get_string();
const UChar * text = ustr.getBuffer();
UBiDi * bidi = ubidi_openSized(ustr.length(),0,&err);
if (U_SUCCESS(err))
{
ubidi_setPara(bidi,text,ustr.length(), UBIDI_DEFAULT_LTR,0,&err);
if (U_SUCCESS(err))
{
int32_t count = ubidi_countRuns(bidi,&err);
int32_t logicalStart;
int32_t length;
for (int32_t i=0; i< count;++i)
{
if (UBIDI_LTR == ubidi_getVisualRun(bidi,i,&logicalStart,&length))
{
do {
UChar ch = text[logicalStart++];
dimension_t char_dim = character_dimensions(ch);
info.add_info(ch, char_dim.first, char_dim.second);
width += char_dim.first;
height = char_dim.second > height ? char_dim.second : height;
} while (--length > 0);
}
else
{
logicalStart += length;
int32_t j=0,i=length;
UnicodeString arabic;
UChar * buf = arabic.getBuffer(length);
do {
UChar ch = text[--logicalStart];
buf[j++] = ch;
} while (--i > 0);
arabic.releaseBuffer(length);
if ( *arabic.getBuffer() >= 0x0600 && *arabic.getBuffer() <= 0x06ff)
{
UnicodeString shaped;
u_shapeArabic(arabic.getBuffer(),arabic.length(),shaped.getBuffer(arabic.length()),arabic.length(),
U_SHAPE_LETTERS_SHAPE|U_SHAPE_LENGTH_FIXED_SPACES_NEAR|
U_SHAPE_TEXT_DIRECTION_VISUAL_LTR
,&err);
shaped.releaseBuffer(arabic.length());
if (U_SUCCESS(err))
{
for (int j=0;j<shaped.length();++j)
{
dimension_t char_dim = character_dimensions(shaped[j]);
info.add_info(shaped[j], char_dim.first, char_dim.second);
width += char_dim.first;
height = char_dim.second > height ? char_dim.second : height;
}
}
}
}
}
}
ubidi_close(bidi);
}
info.set_dimensions(width, height);
}
void set_pixel_size(unsigned size) void set_pixel_size(unsigned size)
{ {
face_->set_pixel_sizes(size); std::vector<face_ptr>::iterator itr = faces_.begin();
std::vector<face_ptr>::iterator end = faces_.end();
for (; itr != end; ++itr)
{
(*itr)->set_pixel_sizes(size);
}
} }
void set_fill(mapnik::Color const& fill) void set_fill(mapnik::Color const& fill)
@ -326,22 +360,25 @@ namespace mapnik
FT_Matrix matrix; FT_Matrix matrix;
FT_Vector pen; FT_Vector pen;
FT_Error error; FT_Error error;
FT_Face face = face_->get_face();
// FT_GlyphSlot slot = face->glyph;
FT_BBox bbox; FT_BBox bbox;
bbox.xMin = bbox.yMin = 32000; bbox.xMin = bbox.yMin = 32000; // Initialize these so we can tell if we
bbox.xMax = bbox.yMax = -32000; //hmm?? bbox.xMax = bbox.yMax = -32000; // properly grew the bbox later
std::vector<face_ptr>::iterator end = faces_.end();
for (int i = 0; i < path->num_nodes(); i++) for (int i = 0; i < path->num_nodes(); i++)
{ {
int c; int c;
double x, y, angle; double x, y, angle;
path->vertex(&c, &x, &y, &angle);
// std::clog << " prepare_glyph: " << (unsigned char)c << "," << x << "," << y << "," << angle << std::endl;
path->vertex(&c, &x, &y, &angle);
#ifdef MAPNIK_DEBUG
// TODO Enable when we have support for setting verbosity
//std::clog << "prepare_glyphs: " << c << "," << x <<
// "," << y << "," << angle << std::endl;
#endif
FT_BBox glyph_bbox; FT_BBox glyph_bbox;
FT_Glyph image; FT_Glyph image;
@ -349,23 +386,59 @@ namespace mapnik
pen.x = int(x * 64); pen.x = int(x * 64);
pen.y = int(y * 64); pen.y = int(y * 64);
FT_Face face = (*faces_.begin())->get_face();
FT_UInt glyph_index = FT_Get_Char_Index(face, unsigned(c));
// If there's no glyph_index we loop through the remaining fonts
// in the fontset looking for one.
if (!glyph_index) {
std::vector<face_ptr>::iterator itr = faces_.begin();
++itr; // Skip the first one, we already tried it.
for (; itr != end; ++itr)
{
#ifdef MAPNIK_DEBUG
// TODO Enable when we have support for setting verbosity
//std::clog << "prepare_glyphs: Falling back to font named \""
// << (*itr)->family_name() << " " << (*itr)->style_name()
// << "\"" << std::endl;
#endif
FT_Face f = (*itr)->get_face();
glyph_index = FT_Get_Char_Index(f, unsigned(c));
if (glyph_index) {
face = f;
break;
}
}
#ifdef MAPNIK_DEBUG
// TODO Enable when we have support for setting verbosity
//if (!glyph_index) {
// std::clog << "prepare_glyphs: Failed to fall back, glyph "
// << c << " not found in any font." << std::endl;
//}
#endif
}
matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
FT_Set_Transform (face,&matrix,&pen); FT_Set_Transform(face, &matrix, &pen);
FT_UInt glyph_index = FT_Get_Char_Index( face, unsigned(c)); error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_HINTING);
error = FT_Load_Glyph (face,glyph_index, FT_LOAD_NO_HINTING);
if ( error ) if ( error )
continue; continue;
error = FT_Get_Glyph( face->glyph, &image); error = FT_Get_Glyph(face->glyph, &image);
if ( error ) if ( error )
continue; continue;
FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox); FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox);
if (glyph_bbox.xMin < bbox.xMin) if (glyph_bbox.xMin < bbox.xMin)
bbox.xMin = glyph_bbox.xMin; bbox.xMin = glyph_bbox.xMin;
@ -375,7 +448,8 @@ namespace mapnik
bbox.xMax = glyph_bbox.xMax; bbox.xMax = glyph_bbox.xMax;
if (glyph_bbox.yMax > bbox.yMax) if (glyph_bbox.yMax > bbox.yMax)
bbox.yMax = glyph_bbox.yMax; bbox.yMax = glyph_bbox.yMax;
// Check if we properly grew the bbox
if ( bbox.xMin > bbox.xMax ) if ( bbox.xMin > bbox.xMax )
{ {
bbox.xMin = 0; bbox.xMin = 0;
@ -484,7 +558,7 @@ namespace mapnik
} }
pixmap_type & pixmap_; pixmap_type & pixmap_;
face_ptr face_; std::vector<face_ptr> faces_;
mapnik::Color fill_; mapnik::Color fill_;
mapnik::Color halo_fill_; mapnik::Color halo_fill_;
int halo_radius_; int halo_radius_;
@ -494,5 +568,4 @@ namespace mapnik
}; };
} }
#endif // FONT_ENGINE_FREETYPE_HPP #endif // FONT_ENGINE_FREETYPE_HPP

View file

@ -44,6 +44,7 @@ namespace mapnik
std::string srs_; std::string srs_;
boost::optional<Color> background_; boost::optional<Color> background_;
std::map<std::string,feature_type_style> styles_; std::map<std::string,feature_type_style> styles_;
std::map<std::string,FontSet> fontsets_;
std::vector<Layer> layers_; std::vector<Layer> layers_;
Envelope<double> currentExtent_; Envelope<double> currentExtent_;
@ -129,6 +130,20 @@ namespace mapnik
*/ */
feature_type_style const& find_style(std::string const& name) const; feature_type_style const& find_style(std::string const& name) const;
/*! \brief Insert a fontset into the map.
* @param name The name of the fontset.
* @param style The fontset to insert.
* @return true If success.
* @return false If failure.
*/
bool insert_fontset(std::string const& name, FontSet const& fontset);
/*! \brief Find a fontset.
* @param name The name of the fontset.
* @return The fontset if found. If not found return the default map fontset.
*/
FontSet const& find_fontset(std::string const& name) const;
/*! \brief Get number of all layers. /*! \brief Get number of all layers.
*/ */
size_t layerCount() const; size_t layerCount() const;

View file

@ -27,7 +27,8 @@
// mapnik // mapnik
#include <mapnik/enumeration.hpp> #include <mapnik/enumeration.hpp>
#include <mapnik/color.hpp> #include <mapnik/color.hpp>
#include <mapnik/graphics.hpp> #include <mapnik/font_set.hpp>
#include <mapnik/graphics.hpp>
// boost // boost
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
@ -49,6 +50,7 @@ namespace mapnik
{ {
text_symbolizer(std::string const& name,std::string const& face_name, text_symbolizer(std::string const& name,std::string const& face_name,
unsigned size, Color const& fill); unsigned size, Color const& fill);
text_symbolizer(std::string const& name, unsigned size, Color const& fill);
text_symbolizer(text_symbolizer const& rhs); text_symbolizer(text_symbolizer const& rhs);
text_symbolizer& operator=(text_symbolizer const& rhs); text_symbolizer& operator=(text_symbolizer const& rhs);
std::string const& get_name() const; std::string const& get_name() const;
@ -66,6 +68,9 @@ namespace mapnik
void set_max_char_angle_delta(double angle); void set_max_char_angle_delta(double angle);
unsigned get_text_size() const; unsigned get_text_size() const;
std::string const& get_face_name() const; std::string const& get_face_name() const;
void set_face_name(std::string face_name);
FontSet const& get_fontset() const;
void set_fontset(FontSet fontset);
Color const& get_fill() const; Color const& get_fill() const;
void set_halo_fill(Color const& fill); void set_halo_fill(Color const& fill);
Color const& get_halo_fill() const; Color const& get_halo_fill() const;
@ -86,6 +91,7 @@ namespace mapnik
private: private:
std::string name_; std::string name_;
std::string face_name_; std::string face_name_;
FontSet fontset_;
unsigned size_; unsigned size_;
unsigned text_ratio_; unsigned text_ratio_;
unsigned wrap_width_; unsigned wrap_width_;

View file

@ -47,6 +47,7 @@ source = Split(
envelope.cpp envelope.cpp
filter_factory.cpp filter_factory.cpp
font_engine_freetype.cpp font_engine_freetype.cpp
font_set.cpp
graphics.cpp graphics.cpp
image_reader.cpp image_reader.cpp
image_util.cpp image_util.cpp

View file

@ -29,6 +29,7 @@
#include <mapnik/markers_converter.hpp> #include <mapnik/markers_converter.hpp>
#include <mapnik/arrow.hpp> #include <mapnik/arrow.hpp>
#include <mapnik/config_error.hpp> #include <mapnik/config_error.hpp>
#include <mapnik/font_set.hpp>
// agg // agg
#define AGG_RENDERING_BUFFER row_ptr_cache<int8u> #define AGG_RENDERING_BUFFER row_ptr_cache<int8u>
@ -280,7 +281,6 @@ namespace mapnik
frame->move_to(itr->get<0>(),itr->get<1>()); frame->move_to(itr->get<0>(),itr->get<1>());
frame->line_to(itr->get<0>(),itr->get<1>()+height); frame->line_to(itr->get<0>(),itr->get<1>()+height);
} }
geom.rewind(0); geom.rewind(0);
@ -310,7 +310,6 @@ namespace mapnik
ras_ptr->add_path(roof_path); ras_ptr->add_path(roof_path);
ren.color(agg::rgba8(r, g, b, int(255 * sym.get_opacity()))); ren.color(agg::rgba8(r, g, b, int(255 * sym.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren); agg::render_scanlines(*ras_ptr, sl, ren);
} }
} }
} }
@ -460,15 +459,20 @@ namespace mapnik
boost::shared_ptr<ImageData32> const& data = sym.get_image(); boost::shared_ptr<ImageData32> const& data = sym.get_image();
if (text.length() > 0 && data) if (text.length() > 0 && data)
{ {
face_ptr face = font_manager_.get_face(sym.get_face_name()); std::vector<face_ptr> faces;
if (face)
faces.push_back(font_manager_.get_face(sym.get_face_name()));
if (faces.size() > 0)
{ {
text_renderer<mapnik::Image32> ren(pixmap_,face); text_renderer<mapnik::Image32> ren(pixmap_, faces);
ren.set_pixel_size(sym.get_text_size()); ren.set_pixel_size(sym.get_text_size());
ren.set_fill(sym.get_fill()); ren.set_fill(sym.get_fill());
string_info info(text); string_info info(text);
face->get_string_info(info);
ren.get_string_info(info);
placement_finder<label_collision_detector4> finder(detector_); placement_finder<label_collision_detector4> finder(detector_);
@ -669,11 +673,31 @@ namespace mapnik
UnicodeString text = feature[sym.get_name()].to_unicode(); UnicodeString text = feature[sym.get_name()].to_unicode();
if ( text.length() > 0 ) if ( text.length() > 0 )
{ {
Color const& fill = sym.get_fill(); Color const& fill = sym.get_fill();
face_ptr face = font_manager_.get_face(sym.get_face_name());
if (face) std::vector<face_ptr> faces;
FontSet fontset = sym.get_fontset();
std::vector<std::string> face_names = fontset.get_face_names();
if (face_names.size() > 0)
{ {
text_renderer<mapnik::Image32> ren(pixmap_,face); std::vector<std::string>::iterator itr = face_names.begin();
std::vector<std::string>::iterator end = face_names.end();
for (; itr != end; ++itr)
{
faces.push_back(font_manager_.get_face(*itr));
}
}
else
{
faces.push_back(font_manager_.get_face(sym.get_face_name()));
}
if (faces.size() > 0)
{
text_renderer<mapnik::Image32> ren(pixmap_, faces);
ren.set_pixel_size(sym.get_text_size()); ren.set_pixel_size(sym.get_text_size());
ren.set_fill(fill); ren.set_fill(fill);
ren.set_halo_fill(sym.get_halo_fill()); ren.set_halo_fill(sym.get_halo_fill());
@ -682,7 +706,8 @@ namespace mapnik
placement_finder<label_collision_detector4> finder(detector_); placement_finder<label_collision_detector4> finder(detector_);
string_info info(text); string_info info(text);
face->get_string_info(info);
ren.get_string_info(info);
unsigned num_geom = feature.num_geometries(); unsigned num_geom = feature.num_geometries();
for (unsigned i=0;i<num_geom;++i) for (unsigned i=0;i<num_geom;++i)
{ {

View file

@ -29,6 +29,7 @@
#include <mapnik/layer.hpp> #include <mapnik/layer.hpp>
#include <mapnik/datasource_cache.hpp> #include <mapnik/datasource_cache.hpp>
#include <mapnik/font_engine_freetype.hpp> #include <mapnik/font_engine_freetype.hpp>
#include <mapnik/font_set.hpp>
#include <mapnik/ptree_helpers.hpp> #include <mapnik/ptree_helpers.hpp>
#include <mapnik/libxml2_loader.hpp> #include <mapnik/libxml2_loader.hpp>
@ -66,7 +67,10 @@ namespace mapnik
private: private:
void parse_style( Map & map, ptree const & sty); void parse_style( Map & map, ptree const & sty);
void parse_layer( Map & map, ptree const & lay); void parse_layer( Map & map, ptree const & lay);
void parse_fontset(Map & map, ptree const & fset);
void parse_font(FontSet & fset, ptree const & f);
void parse_rule( feature_type_style & style, ptree const & r); void parse_rule( feature_type_style & style, ptree const & r);
void parse_point_symbolizer( rule_type & rule, ptree const & sym); void parse_point_symbolizer( rule_type & rule, ptree const & sym);
@ -79,13 +83,14 @@ namespace mapnik
void parse_building_symbolizer( rule_type & rule, ptree const & sym ); void parse_building_symbolizer( rule_type & rule, ptree const & sym );
void parse_markers_symbolizer( rule_type & rule, ptree const & sym ); void parse_markers_symbolizer( rule_type & rule, ptree const & sym );
void ensure_font_face( const text_symbolizer & text_symbol ); void ensure_font_face( const std::string & face_name );
bool strict_; bool strict_;
std::map<std::string,parameters> datasource_templates_; std::map<std::string,parameters> datasource_templates_;
freetype_engine font_engine_; freetype_engine font_engine_;
face_manager<freetype_engine> font_manager_; face_manager<freetype_engine> font_manager_;
std::map<std::string,std::string> file_sources_; std::map<std::string,std::string> file_sources_;
std::map<std::string,FontSet> fontsets_;
}; };
void load_map(Map & map, std::string const& filename, bool strict) void load_map(Map & map, std::string const& filename, bool strict)
@ -142,9 +147,12 @@ namespace mapnik
} }
else if (v.first == "Layer") else if (v.first == "Layer")
{ {
parse_layer(map, v.second ); parse_layer(map, v.second );
} }
else if (v.first == "FontSet")
{
parse_fontset(map, v.second);
}
else if (v.first == "FileSource") else if (v.first == "FileSource")
{ {
std::string name = get_attr<string>( v.second, "name"); std::string name = get_attr<string>( v.second, "name");
@ -227,6 +235,71 @@ namespace mapnik
} }
} }
void map_parser::parse_fontset( Map & map, ptree const & fset )
{
string name("<missing name>");
try
{
name = get_attr<string>(fset, "name");
FontSet fontset(name);
ptree::const_iterator itr = fset.begin();
ptree::const_iterator end = fset.end();
for (; itr != end; ++itr)
{
ptree::value_type const& font_tag = *itr;
if (font_tag.first == "Font")
{
parse_font(fontset, font_tag.second);
}
else if (font_tag.first != "<xmlcomment>" &&
font_tag.first != "<xmlattr>" )
{
throw config_error(std::string("Unknown child node in 'FontSet'.") +
"Expected 'Font' but got '" + font_tag.first + "'");
}
}
map.insert_fontset(name, fontset);
// XXX Hack because map object isn't accessible by text_symbolizer
// when it's parsed
fontsets_.insert(pair<std::string, FontSet>(name, fontset));
} catch (const config_error & ex) {
if ( ! name.empty() ) {
ex.append_context(string("in FontSet '") + name + "'");
}
throw;
}
}
void map_parser::parse_font(FontSet & fset, ptree const & f)
{
std::string face_name;
try
{
face_name = get_attr(f, "face_name", string());
if ( strict_ )
{
ensure_font_face( face_name );
}
}
catch (const config_error & ex)
{
if (!face_name.empty())
{
ex.append_context(string("in Font '") + face_name + "'");
}
throw;
}
fset.add_face_name(face_name);
}
void map_parser::parse_layer( Map & map, ptree const & lay ) void map_parser::parse_layer( Map & map, ptree const & lay )
{ {
std::string name; std::string name;
@ -603,12 +676,39 @@ namespace mapnik
try try
{ {
std::string name = get_attr<string>(sym, "name"); std::string name = get_attr<string>(sym, "name");
std::string face_name = get_attr<string>(sym, "face_name");
unsigned size = get_attr(sym, "size", 10U ); optional<std::string> face_name =
get_opt_attr<std::string>(sym, "face_name");
optional<std::string> fontset_name =
get_opt_attr<std::string>(sym, "fontset_name");
unsigned size = get_attr(sym, "size", 10U);
Color c = get_attr(sym, "fill", Color(0,0,0)); Color c = get_attr(sym, "fill", Color(0,0,0));
text_symbolizer text_symbol(name, face_name, size, c); text_symbolizer text_symbol = text_symbolizer(name, size, c);
if (fontset_name && face_name)
{
throw config_error(std::string("Can't have both face_name and fontset_name"));
}
else if (fontset_name)
{
std::map<std::string,FontSet>::const_iterator itr = fontsets_.find(*fontset_name);
if (itr != fontsets_.end())
{
text_symbol.set_fontset(itr->second);
}
}
else if (face_name)
{
text_symbol.set_face_name(*face_name);
}
else
{
throw config_error(std::string("Must have face_name or fontset_name"));
}
int dx = get_attr(sym, "dx", 0); int dx = get_attr(sym, "dx", 0);
int dy = get_attr(sym, "dy", 0); int dy = get_attr(sym, "dy", 0);
@ -683,7 +783,7 @@ namespace mapnik
if ( strict_ ) if ( strict_ )
{ {
ensure_font_face( text_symbol ); ensure_font_face( text_symbol.get_face_name() );
} }
rule.append(text_symbol); rule.append(text_symbol);
@ -913,12 +1013,12 @@ namespace mapnik
} }
} }
void map_parser::ensure_font_face( const text_symbolizer & text_symbol ) void map_parser::ensure_font_face( const std::string & face_name )
{ {
if ( ! font_manager_.get_face( text_symbol.get_face_name() ) ) if ( ! font_manager_.get_face( face_name ) )
{ {
throw config_error("Failed to find font face '" + throw config_error("Failed to find font face '" +
text_symbol.get_face_name() + "'"); face_name + "'");
} }
} }
} // end of namespace mapnik } // end of namespace mapnik

View file

@ -101,7 +101,21 @@ namespace mapnik
{ {
styles_.erase(name); styles_.erase(name);
} }
bool Map::insert_fontset(std::string const& name, FontSet const& fontset)
{
return fontsets_.insert(make_pair(name, fontset)).second;
}
FontSet const& Map::find_fontset(std::string const& name) const
{
std::map<std::string,FontSet>::const_iterator itr = fontsets_.find(name);
if (itr!=fontsets_.end())
return itr->second;
static FontSet default_fontset;
return default_fontset;
}
feature_type_style const& Map::find_style(std::string const& name) const feature_type_style const& Map::find_style(std::string const& name) const
{ {
std::map<std::string,feature_type_style>::const_iterator itr = styles_.find(name); std::map<std::string,feature_type_style>::const_iterator itr = styles_.find(name);

View file

@ -35,13 +35,35 @@ static const char * label_placement_strings[] = {
"" ""
}; };
IMPLEMENT_ENUM( mapnik::label_placement_e, label_placement_strings ); IMPLEMENT_ENUM( mapnik::label_placement_e, label_placement_strings );
namespace mapnik namespace mapnik
{ {
text_symbolizer::text_symbolizer(std::string const& name, std::string const& face_name, unsigned size,Color const& fill) text_symbolizer::text_symbolizer(std::string const& name, std::string const& face_name, unsigned size, Color const& fill)
: name_(name), : name_(name),
face_name_(face_name), face_name_(face_name),
//fontset_(default_fontset),
size_(size),
text_ratio_(0),
wrap_width_(0),
label_spacing_(0),
label_position_tolerance_(0),
force_odd_labels_(false),
max_char_angle_delta_(0),
fill_(fill),
halo_fill_(Color(255,255,255)),
halo_radius_(0),
label_p_(POINT_PLACEMENT),
anchor_(0.0,0.5),
displacement_(0.0,0.0),
avoid_edges_(false),
minimum_distance_(0.0),
overlap_(false) {}
text_symbolizer::text_symbolizer(std::string const& name, unsigned size, Color const& fill)
: name_(name),
//face_name_(""),
//fontset_(default_fontset),
size_(size), size_(size),
text_ratio_(0), text_ratio_(0),
wrap_width_(0), wrap_width_(0),
@ -61,6 +83,7 @@ namespace mapnik
text_symbolizer::text_symbolizer(text_symbolizer const& rhs) text_symbolizer::text_symbolizer(text_symbolizer const& rhs)
: name_(rhs.name_), : name_(rhs.name_),
face_name_(rhs.face_name_), face_name_(rhs.face_name_),
fontset_(rhs.fontset_),
size_(rhs.size_), size_(rhs.size_),
text_ratio_(rhs.text_ratio_), text_ratio_(rhs.text_ratio_),
wrap_width_(rhs.wrap_width_), wrap_width_(rhs.wrap_width_),
@ -84,6 +107,7 @@ namespace mapnik
return *this; return *this;
name_ = other.name_; name_ = other.name_;
face_name_ = other.face_name_; face_name_ = other.face_name_;
fontset_ = other.fontset_;
size_ = other.size_; size_ = other.size_;
text_ratio_ = other.text_ratio_; text_ratio_ = other.text_ratio_;
wrap_width_ = other.wrap_width_; wrap_width_ = other.wrap_width_;
@ -113,10 +137,25 @@ namespace mapnik
{ {
return face_name_; return face_name_;
} }
void text_symbolizer::set_face_name(std::string face_name)
{
face_name_ = face_name;
}
void text_symbolizer::set_fontset(FontSet fontset)
{
fontset_ = fontset;
}
FontSet const& text_symbolizer::get_fontset() const
{
return fontset_;
}
unsigned text_symbolizer::get_text_ratio() const unsigned text_symbolizer::get_text_ratio() const
{ {
return text_ratio_; return text_ratio_;
} }
void text_symbolizer::set_text_ratio(unsigned ratio) void text_symbolizer::set_text_ratio(unsigned ratio)
@ -126,7 +165,7 @@ namespace mapnik
unsigned text_symbolizer::get_wrap_width() const unsigned text_symbolizer::get_wrap_width() const
{ {
return wrap_width_; return wrap_width_;
} }
void text_symbolizer::set_wrap_width(unsigned width) void text_symbolizer::set_wrap_width(unsigned width)