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" @@ -124,7 +125,7 @@ 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); @@ -229,7 +230,8 @@ }; typedef boost::shared_ptr face_ptr; - class MAPNIK_DECL freetype_engine // : public mapnik::singleton, + + class MAPNIK_DECL freetype_engine // : public mapnik::singleton, // private boost::noncopyable { // friend class mapnik::CreateStatic; @@ -291,16 +293,21 @@ typedef boost::ptr_vector glyphs_t; typedef T pixmap_type; - text_renderer (pixmap_type & pixmap, face_ptr face) + text_renderer (pixmap_type & pixmap, std::vector faces) : pixmap_(pixmap), - face_(face), + faces_(faces), fill_(0,0,0), halo_fill_(255,255,255), halo_radius_(0) {} - + void set_pixel_size(unsigned size) { - face_->set_pixel_sizes(size); + std::vector::iterator itr = faces_.begin(); + std::vector::iterator end = faces_.end(); + for (; itr != end; ++itr) + { + (*itr)->set_pixel_sizes(size); + } } void set_fill(mapnik::Color const& fill) @@ -326,23 +333,23 @@ 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?? - for (int i = 0; i < path->num_nodes(); i++) + 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; + std::clog << " prepare_glyph: " << (unsigned char)c << "," << x << "," << y << "," << angle << std::endl; - FT_BBox glyph_bbox; FT_Glyph image; @@ -353,19 +360,50 @@ 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); - if ( error ) + + FT_Face face = (*faces_.begin())->get_face(); + FT_Set_Transform(face, &matrix, &pen); + + FT_UInt glyph_index = FT_Get_Char_Index(face, unsigned(c)); + + std::clog << "glyph_index: " << glyph_index << std::endl; + + if (!glyph_index) { + std::vector::iterator itr = faces_.begin(); + ++itr; + + for (; itr != end; ++itr) + { + std::clog << " Falling back to font named: " << (*itr)->family_name() << ":" << (*itr)->style_name() << std::endl; + + FT_Face f = (*itr)->get_face(); + glyph_index = FT_Get_Char_Index(f, unsigned(c)); + + if (glyph_index) { + face = f; + break; + } + } + + if (!glyph_index) { + std::clog << " Failed to fall back, glyph not found in any font." << std::endl; + } + } + + error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_HINTING); + if ( error ) { + std::clog << "FT_Load_Glyph error: " << error << std::endl; + + continue; + } + + error = FT_Get_Glyph(face->glyph, &image); + if ( error ) { + std::clog << "FT_Get_Glyph error: " << error << std::endl; + continue; - - 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; @@ -484,8 +522,8 @@ } pixmap_type & pixmap_; - face_ptr face_; - mapnik::Color fill_; + std::vector faces_; + mapnik::Color fill_; mapnik::Color halo_fill_; int halo_radius_; unsigned text_ratio_; 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 @@ -66,7 +67,9 @@ void set_max_char_angle_delta(double angle); unsigned get_text_size() const; std::string const& get_face_name() const; - Color const& get_fill() const; + 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; void set_halo_radius(unsigned radius); @@ -85,7 +88,8 @@ bool get_allow_overlap() const; private: std::string name_; - std::string face_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: 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,8 +67,11 @@ 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_rule( feature_type_style & style, ptree const & r); void parse_point_symbolizer( rule_type & rule, ptree const & sym); void parse_line_pattern_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,75 @@ } } + void map_parser::parse_fontset( Map & map, ptree const & fset ) + { + string name(""); + try + { + name = get_attr(fset, "name"); + FontSet fontset(name); + + ptree::const_iterator fontIter = fset.begin(); + ptree::const_iterator endFont = fset.end(); + + std::clog << "In FontSet tag, name: " << name << std::endl; + + for (; fontIter != endFont; ++fontIter) + { + ptree::value_type const& font_tag = *fontIter; + + if (font_tag.first == "Font") + { + std::clog << "About to parse a font tag..." << std::endl; + 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()); + std::clog << "In Font tag, name: " << face_name << std::endl; + + 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; @@ -598,14 +675,16 @@ } } + // TODO (BAG) add fallback face selection support here void map_parser::parse_text_symbolizer( rule_type & rule, ptree const & sym ) { try { std::string name = get_attr(sym, "name"); - std::string face_name = get_attr(sym, "face_name"); - unsigned size = get_attr(sym, "size", 10U ); + std::string face_name = get_attr(sym, "face_name"); + unsigned size = get_attr(sym, "size", 10U); + Color c = get_attr(sym, "fill", Color(0,0,0)); text_symbolizer text_symbol(name, face_name, size, c); @@ -631,6 +710,17 @@ text_symbol.set_halo_radius(*halo_radius); } + optional fontset_name = + get_opt_attr(sym, "fontset_name"); + if (fontset_name) + { + std::map::const_iterator itr = fontsets_.find(*fontset_name); + if (itr!=fontsets_.end()) + { + text_symbol.set_fontset(itr->second); + } + } + // text ratio and wrap width optional text_ratio = get_opt_attr(sym, "text_ratio"); @@ -683,7 +773,7 @@ if ( strict_ ) { - ensure_font_face( text_symbol ); + ensure_font_face( text_symbol.get_face_name() ); } rule.append(text_symbol); @@ -913,12 +1003,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("default"); + 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/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()) { - 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); + + // XXX Hack, only get the info from the first face listed + (*faces.begin())->get_string_info(info); placement_finder finder(detector_); @@ -669,11 +673,32 @@ 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(); + + std::clog << "face_names.size(): " << face_names.size() << std::endl; + if (face_names.size()) + { + 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()) { - text_renderer ren(pixmap_,face); + 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 +707,9 @@ placement_finder finder(detector_); string_info info(text); - face->get_string_info(info); + + // XXX Hack, only get the info from the first face listed + (*faces.begin())->get_string_info(info); unsigned num_geom = feature.num_geometries(); for (unsigned i=0;i