Index: include/mapnik/font_engine_freetype.hpp =================================================================== --- include/mapnik/font_engine_freetype.hpp (revision 698) +++ include/mapnik/font_engine_freetype.hpp (working copy) @@ -30,6 +30,7 @@ #include #include #include +#include // freetype2 extern "C" @@ -61,9 +62,7 @@ class font_face : boost::noncopyable { public: - typedef std::pair dimension_t; - - font_face(FT_Face face) + font_face(FT_Face face) : face_(face) {} std::string family_name() const @@ -104,40 +103,153 @@ return false; } + ~font_face() + { +#ifdef MAPNIK_DEBUG + std::clog << "~font_face: Clean up face \"" << family_name() + << " " << style_name() << "\"" << std::endl; +#endif + FT_Done_Face(face_); + } + + private: + FT_Face face_; + }; + + typedef boost::shared_ptr face_ptr; + + class MAPNIK_DECL freetype_engine // : public mapnik::singleton, + // private boost::noncopyable + { + // friend class mapnik::CreateStatic; + public: + static bool register_font(std::string const& file_name); + static std::vector face_names (); + face_ptr create_face(std::string const& family_name); + virtual ~freetype_engine(); + freetype_engine(); + private: + FT_Library library_; + static boost::mutex mutex_; + static std::map name2file_; + }; + + template + class MAPNIK_DECL face_manager : private boost::noncopyable + { + typedef T font_engine_type; + typedef std::map faces; + + public: + face_manager(T & engine) + : engine_(engine) {} + + face_ptr get_face(std::string const& name) + { + typename faces::iterator itr; + itr = faces_.find(name); + if (itr != faces_.end()) + { + return itr->second; + } + else + { + face_ptr face = engine_.create_face(name); + if (face) + { + faces_.insert(make_pair(name,face)); + } + return face; + } + } + private: + faces faces_; + font_engine_type & engine_; + }; + + template + struct text_renderer : private boost::noncopyable + { + typedef std::pair dimension_t; + + struct glyph_t : boost::noncopyable + { + FT_Glyph image; + glyph_t(FT_Glyph image_) : image(image_) {} + ~glyph_t () { FT_Done_Glyph(image);} + }; + + typedef boost::ptr_vector glyphs_t; + typedef T pixmap_type; + + text_renderer (pixmap_type & pixmap, std::vector faces) + : pixmap_(pixmap), + faces_(faces), + fill_(0,0,0), + halo_fill_(255,255,255), + halo_radius_(0) {} + 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; + + 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::iterator itr = faces_.begin(); + std::vector::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); - FT_UInt glyph_index = FT_Get_Char_Index( face_, c); - - error = FT_Load_Glyph (face_,glyph_index,FT_LOAD_NO_HINTING); + 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); + 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); + + 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) @@ -187,7 +299,6 @@ 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| @@ -215,94 +326,17 @@ info.set_dimensions(width, height); } - - ~font_face() - { -#ifdef MAPNIK_DEBUG - std::clog << "clean up face:" << family_name()<<":" << style_name() << std::endl; -#endif - FT_Done_Face(face_); - } - - private: - FT_Face face_; - }; - - typedef boost::shared_ptr face_ptr; - class MAPNIK_DECL freetype_engine // : public mapnik::singleton, - // private boost::noncopyable - { - // friend class mapnik::CreateStatic; - public: - static bool register_font(std::string const& file_name); - static std::vector face_names (); - face_ptr create_face(std::string const& family_name); - virtual ~freetype_engine(); - freetype_engine(); - private: - FT_Library library_; - static boost::mutex mutex_; - static std::map name2file_; - }; - - template - class MAPNIK_DECL face_manager : private boost::noncopyable - { - typedef T font_engine_type; - typedef std::map faces; - - public: - face_manager(T & engine) - : engine_(engine) {} - - face_ptr get_face(std::string const& name) + + void set_pixel_size(unsigned size) { - typename faces::iterator itr; - itr = faces_.find(name); - if (itr != faces_.end()) + std::vector::iterator itr = faces_.begin(); + std::vector::iterator end = faces_.end(); + for (; itr != end; ++itr) { - return itr->second; + (*itr)->set_pixel_sizes(size); } - else - { - face_ptr face = engine_.create_face(name); - if (face) - { - faces_.insert(make_pair(name,face)); - } - return face; - } } - private: - faces faces_; - font_engine_type & engine_; - }; - - template - struct text_renderer : private boost::noncopyable - { - struct glyph_t : boost::noncopyable - { - FT_Glyph image; - glyph_t(FT_Glyph image_) : image(image_) {} - ~glyph_t () { FT_Done_Glyph(image);} - }; - - typedef boost::ptr_vector glyphs_t; - typedef T pixmap_type; - - text_renderer (pixmap_type & pixmap, face_ptr face) - : pixmap_(pixmap), - face_(face), - fill_(0,0,0), - halo_fill_(255,255,255), - halo_radius_(0) {} - void set_pixel_size(unsigned size) - { - face_->set_pixel_sizes(size); - } - void set_fill(mapnik::Color const& fill) { fill_=fill; @@ -326,22 +360,25 @@ FT_Matrix matrix; FT_Vector pen; FT_Error error; - - FT_Face face = face_->get_face(); - // FT_GlyphSlot slot = face->glyph; - + FT_BBox bbox; - bbox.xMin = bbox.yMin = 32000; - bbox.xMax = bbox.yMax = -32000; //hmm?? + bbox.xMin = bbox.yMin = 32000; // Initialize these so we can tell if we + bbox.xMax = bbox.yMax = -32000; // properly grew the bbox later + std::vector::iterator end = faces_.end(); + for (int i = 0; i < path->num_nodes(); i++) { int c; double x, y, angle; - + path->vertex(&c, &x, &y, &angle); -// std::clog << " prepare_glyph: " << (unsigned char)c << "," << x << "," << y << "," << angle << std::endl; +#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_Glyph image; @@ -349,23 +386,59 @@ pen.x = int(x * 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::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.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); - - 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); + + FT_Set_Transform(face, &matrix, &pen); + + error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_HINTING); if ( error ) continue; - - error = FT_Get_Glyph( face->glyph, &image); + + error = FT_Get_Glyph(face->glyph, &image); if ( error ) continue; - + FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox); if (glyph_bbox.xMin < bbox.xMin) bbox.xMin = glyph_bbox.xMin; @@ -375,7 +448,8 @@ bbox.xMax = glyph_bbox.xMax; if (glyph_bbox.yMax > bbox.yMax) bbox.yMax = glyph_bbox.yMax; - + + // Check if we properly grew the bbox if ( bbox.xMin > bbox.xMax ) { bbox.xMin = 0; @@ -484,7 +558,7 @@ } pixmap_type & pixmap_; - face_ptr face_; + std::vector faces_; mapnik::Color fill_; mapnik::Color halo_fill_; int halo_radius_; @@ -494,5 +568,4 @@ }; } - #endif // FONT_ENGINE_FREETYPE_HPP Index: include/mapnik/text_symbolizer.hpp =================================================================== --- include/mapnik/text_symbolizer.hpp (revision 698) +++ include/mapnik/text_symbolizer.hpp (working copy) @@ -27,7 +27,8 @@ // mapnik #include #include -#include +#include +#include // boost #include #include @@ -49,6 +50,7 @@ { text_symbolizer(std::string const& name,std::string const& face_name, unsigned size, Color const& fill); + text_symbolizer(std::string const& name, unsigned size, Color const& fill); text_symbolizer(text_symbolizer const& rhs); text_symbolizer& operator=(text_symbolizer const& rhs); std::string const& get_name() const; @@ -66,6 +68,9 @@ void set_max_char_angle_delta(double angle); unsigned get_text_size() 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; void set_halo_fill(Color const& fill); Color const& get_halo_fill() const; @@ -86,6 +91,7 @@ private: std::string name_; std::string face_name_; + FontSet fontset_; unsigned size_; unsigned text_ratio_; unsigned wrap_width_; Index: include/mapnik/map.hpp =================================================================== --- include/mapnik/map.hpp (revision 698) +++ include/mapnik/map.hpp (working copy) @@ -44,6 +44,7 @@ std::string srs_; boost::optional background_; std::map styles_; + std::map fontsets_; std::vector layers_; Envelope currentExtent_; @@ -129,6 +130,20 @@ */ 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. */ size_t layerCount() const; Index: include/mapnik/font_set.hpp =================================================================== --- include/mapnik/font_set.hpp (revision 0) +++ include/mapnik/font_set.hpp (revision 0) @@ -0,0 +1,54 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2006 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 + * + *****************************************************************************/ + +//$Id$ + +#ifndef FONT_SET_HPP +#define FONT_SET_HPP +// mapnik +#include +// boost +#include +// stl +#include +#include + +namespace mapnik +{ + class MAPNIK_DECL FontSet + { + public: + FontSet(); + FontSet(std::string const& name); + FontSet(FontSet const& rhs); + FontSet& operator=(FontSet const& rhs); + std::string const& get_name() const; + void add_face_name(std::string); + std::vector const& get_face_names() const; + ~FontSet(); + private: + std::string name_; + std::vector face_names_; + }; +} + +#endif //FONT_SET_HPP Index: src/load_map.cpp =================================================================== --- src/load_map.cpp (revision 698) +++ src/load_map.cpp (working copy) @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -66,7 +67,10 @@ private: void parse_style( Map & map, ptree const & sty); 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_point_symbolizer( rule_type & rule, ptree const & sym); @@ -79,13 +83,14 @@ void parse_building_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_; std::map datasource_templates_; freetype_engine font_engine_; face_manager font_manager_; std::map file_sources_; + std::map fontsets_; }; void load_map(Map & map, std::string const& filename, bool strict) @@ -142,9 +147,12 @@ } else if (v.first == "Layer") { - parse_layer(map, v.second ); } + else if (v.first == "FontSet") + { + parse_fontset(map, v.second); + } else if (v.first == "FileSource") { std::string name = get_attr( v.second, "name"); @@ -227,6 +235,71 @@ } } + void map_parser::parse_fontset( Map & map, ptree const & fset ) + { + string name(""); + try + { + name = get_attr(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 != "" && + font_tag.first != "" ) + { + 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(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 ) { std::string name; @@ -603,13 +676,40 @@ try { std::string name = get_attr(sym, "name"); - std::string face_name = get_attr(sym, "face_name"); - unsigned size = get_attr(sym, "size", 10U ); + optional face_name = + get_opt_attr(sym, "face_name"); + + optional fontset_name = + get_opt_attr(sym, "fontset_name"); + + unsigned size = get_attr(sym, "size", 10U); + Color c = get_attr(sym, "fill", Color(0,0,0)); + + 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::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")); + } - text_symbolizer text_symbol(name, face_name, size, c); - int dx = get_attr(sym, "dx", 0); int dy = get_attr(sym, "dy", 0); text_symbol.set_displacement(dx,dy); @@ -683,7 +783,7 @@ if ( strict_ ) { - ensure_font_face( text_symbol ); + ensure_font_face( text_symbol.get_face_name() ); } rule.append(text_symbol); @@ -913,12 +1013,12 @@ } } - 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 '" + - text_symbol.get_face_name() + "'"); + face_name + "'"); } } } // end of namespace mapnik Index: src/map.cpp =================================================================== --- src/map.cpp (revision 698) +++ src/map.cpp (working copy) @@ -101,7 +101,21 @@ { 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::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 { std::map::const_iterator itr = styles_.find(name); Index: src/font_set.cpp =================================================================== --- src/font_set.cpp (revision 0) +++ src/font_set.cpp (revision 0) @@ -0,0 +1,69 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2006 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 + * + *****************************************************************************/ + +//$Id$ + +//mapnik +#include +//stl +#include +#include + +namespace mapnik +{ + FontSet::FontSet() + : name_("default") {} + + FontSet::FontSet(std::string const& name) + : name_(name) {} + + FontSet::FontSet(FontSet const& rhs) + : name_(rhs.name_), + face_names_(rhs.face_names_) {} + + FontSet& FontSet::operator=(FontSet const& other) + { + if (this == &other) + return *this; + name_ = other.name_; + face_names_ = other.face_names_; + + return *this; + } + + FontSet::~FontSet() {} + + void FontSet::add_face_name(std::string face_name) + { + face_names_.push_back(face_name); + } + + std::string const& FontSet::get_name() const + { + return name_; + } + + std::vector const& FontSet::get_face_names() const + { + return face_names_; + } +} Index: src/agg_renderer.cpp =================================================================== --- src/agg_renderer.cpp (revision 698) +++ src/agg_renderer.cpp (working copy) @@ -29,6 +29,7 @@ #include #include #include +#include // agg #define AGG_RENDERING_BUFFER row_ptr_cache @@ -280,7 +281,6 @@ frame->move_to(itr->get<0>(),itr->get<1>()); frame->line_to(itr->get<0>(),itr->get<1>()+height); - } geom.rewind(0); @@ -310,7 +310,6 @@ ras_ptr->add_path(roof_path); ren.color(agg::rgba8(r, g, b, int(255 * sym.get_opacity()))); agg::render_scanlines(*ras_ptr, sl, ren); - } } } @@ -460,15 +459,20 @@ boost::shared_ptr const& data = sym.get_image(); if (text.length() > 0 && data) { - face_ptr face = font_manager_.get_face(sym.get_face_name()); - if (face) + std::vector faces; + + faces.push_back(font_manager_.get_face(sym.get_face_name())); + + if (faces.size() > 0) { - text_renderer ren(pixmap_,face); + text_renderer ren(pixmap_, faces); + ren.set_pixel_size(sym.get_text_size()); ren.set_fill(sym.get_fill()); string_info info(text); - face->get_string_info(info); + + ren.get_string_info(info); placement_finder finder(detector_); @@ -669,11 +673,31 @@ UnicodeString text = feature[sym.get_name()].to_unicode(); if ( text.length() > 0 ) { - Color const& fill = sym.get_fill(); - face_ptr face = font_manager_.get_face(sym.get_face_name()); - if (face) + Color const& fill = sym.get_fill(); + + std::vector faces; + + FontSet fontset = sym.get_fontset(); + std::vector face_names = fontset.get_face_names(); + + if (face_names.size() > 0) { - text_renderer ren(pixmap_,face); + std::vector::iterator itr = face_names.begin(); + std::vector::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 ren(pixmap_, faces); ren.set_pixel_size(sym.get_text_size()); ren.set_fill(fill); ren.set_halo_fill(sym.get_halo_fill()); @@ -682,7 +706,8 @@ placement_finder finder(detector_); string_info info(text); - face->get_string_info(info); + + ren.get_string_info(info); unsigned num_geom = feature.num_geometries(); for (unsigned i=0;i