/home/andreas/src/svn/mapnik/include/mapnik/font_engine_freetype.hpp

Go to the documentation of this file.
00001 /*****************************************************************************
00002  * 
00003  * This file is part of Mapnik (c++ mapping toolkit)
00004  *
00005  * Copyright (C) 2006 Artem Pavlenko
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020  *
00021  *****************************************************************************/
00022 
00023 //$Id$
00024 
00025 #ifndef FONT_ENGINE_FREETYPE_HPP
00026 #define FONT_ENGINE_FREETYPE_HPP
00027 
00028 // freetype2
00029 extern "C"
00030 {
00031 #include <ft2build.h>
00032 #include FT_FREETYPE_H
00033 #include FT_GLYPH_H
00034 }
00035 
00036 // stl
00037 #include <string>
00038 #include <vector>
00039 #include <map>
00040 #include <iostream>
00041 // boost
00042 #include <boost/shared_ptr.hpp>
00043 #include <boost/utility.hpp>
00044 #include <boost/ptr_container/ptr_vector.hpp>
00045 #include <boost/thread/mutex.hpp>
00046 // mapnik
00047 #include <mapnik/color.hpp>
00048 #include <mapnik/utils.hpp>
00049 #include <mapnik/ctrans.hpp>
00050 #include <mapnik/geometry.hpp>
00051 
00052 #include <mapnik/text_path.hpp>
00053 
00054 namespace mapnik
00055 {
00056     class font_face : boost::noncopyable
00057     {
00058     public:
00059         font_face(FT_Face face)
00060             : face_(face) {}
00061         
00062         std::string  family_name() const
00063         {
00064             return std::string(face_->family_name);
00065         }
00066 
00067         std::string  style_name() const
00068         {
00069             return std::string(face_->style_name);
00070         }
00071         
00072         unsigned num_glyphs() const
00073         {
00074             return face_->num_glyphs;
00075         }
00076 
00077         FT_GlyphSlot glyph() const
00078         {
00079             return face_->glyph;
00080         }
00081         
00082         FT_Face get_face() const
00083         {
00084             return face_;
00085         }
00086         
00087         bool set_pixel_sizes(unsigned size)
00088         {
00089             if (! FT_Set_Pixel_Sizes( face_, 0, size ))
00090                 return true;
00091             
00092             return false;
00093         }
00094         
00095         
00096         ~font_face()
00097         {
00098 #ifdef MAPNIK_DEBUG
00099             std::clog << "clean up face:" << family_name()<<":" << style_name() << std::endl;
00100 #endif
00101             FT_Done_Face(face_);
00102         }
00103         
00104     private:
00105         FT_Face face_;
00106     };
00107     
00108     typedef boost::shared_ptr<font_face> face_ptr;
00109     
00110     class MAPNIK_DECL freetype_engine : public mapnik::singleton<freetype_engine,mapnik::CreateStatic>,
00111                                         private boost::noncopyable
00112     {
00113         friend class mapnik::CreateStatic<freetype_engine>;
00114     public:
00115 
00116         static bool register_font(std::string const& file_name);
00117         static std::vector<std::string> face_names ();
00118         static face_ptr create_face(std::string const& family_name);
00119 
00120     private:
00121         freetype_engine();
00122         virtual ~freetype_engine();
00123         static FT_Library library_;
00124         static std::map<std::string,std::string> name2file_;
00125     }; 
00126     
00127     template <typename T>
00128     class MAPNIK_DECL face_manager : private boost::noncopyable
00129     {
00130         typedef T font_engine_type;
00131         typedef std::map<std::string,face_ptr> faces;
00132         
00133     public:
00134         face_ptr get_face(std::string const& name)
00135         {
00136             typename faces::iterator itr;
00137             itr = faces_.find(name);
00138             if (itr != faces_.end())
00139             {
00140                 return itr->second;
00141             }
00142             else
00143             {
00144                 face_ptr face = font_engine_type::instance()->create_face(name);
00145                 if (face)
00146                 {
00147                     faces_.insert(make_pair(name,face));
00148                 }
00149                 return face;    
00150             }
00151         }
00152     private:
00153         faces faces_;
00154     };
00155         
00156     template <typename T>
00157     struct text_renderer : private boost::noncopyable
00158     {
00159         struct glyph_t : boost::noncopyable
00160         {
00161             FT_Glyph image;
00162             glyph_t(FT_Glyph image_) : image(image_) {}
00163             ~glyph_t () { FT_Done_Glyph(image);}
00164         };
00165         
00166         typedef boost::ptr_vector<glyph_t> glyphs_t;
00167         typedef std::pair<unsigned,unsigned> dimension_t;
00168         typedef T pixmap_type;
00169         
00170         text_renderer (pixmap_type & pixmap, face_ptr face)
00171             : pixmap_(pixmap),
00172               face_(face),
00173               fill_(0,0,0), 
00174               halo_fill_(255,255,255),
00175               halo_radius_(0) {}
00176     
00177         void set_pixel_size(unsigned size)
00178         {
00179             face_->set_pixel_sizes(size);
00180         }
00181     
00182         void set_fill(mapnik::Color const& fill)
00183         {
00184             fill_=fill;
00185         }
00186     
00187         void set_halo_fill(mapnik::Color const& halo)
00188         {
00189             halo_fill_=halo;
00190         }
00191     
00192         void set_halo_radius( int radius=1)
00193         {
00194             halo_radius_=radius;
00195         }
00196 
00197         Envelope<double> prepare_glyphs(text_path *path)
00198         {
00199             //clear glyphs
00200             glyphs_.clear();
00201             
00202             FT_Matrix matrix;
00203             FT_Vector pen;
00204             FT_Error  error;
00205             
00206             FT_Face face = face_->get_face();
00207             //            FT_GlyphSlot slot = face->glyph;
00208             
00209             FT_BBox bbox;   
00210             bbox.xMin = bbox.yMin = 32000; 
00211             bbox.xMax = bbox.yMax = -32000; //hmm?? 
00212             
00213             for (int i = 0; i < path->num_nodes(); i++) 
00214             {
00215                 int c;
00216                 double x, y, angle;
00217                 
00218                 path->vertex(&c, &x, &y, &angle);
00219 //                std::clog << "   prepare_glyph: " << (unsigned char)c << "," << x << "," << y << "," << angle << std::endl;
00220 
00221 
00222                 FT_BBox glyph_bbox; 
00223                 FT_Glyph image;
00224                 
00225                 pen.x = int(x * 64);
00226                 pen.y = int(y * 64);
00227                         
00228                 matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); 
00229                 matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); 
00230                 matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); 
00231                 matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
00232                 
00233                 FT_Set_Transform (face,&matrix,&pen);
00234                 
00235                 FT_UInt glyph_index = FT_Get_Char_Index( face, unsigned(c));
00236                 
00237                 error = FT_Load_Glyph (face,glyph_index, FT_LOAD_NO_HINTING); 
00238                 if ( error )
00239                     continue;
00240                 
00241                 error = FT_Get_Glyph( face->glyph, &image);
00242                 if ( error )
00243                     continue;
00244                 
00245                 FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox); 
00246                 if (glyph_bbox.xMin < bbox.xMin) 
00247                     bbox.xMin = glyph_bbox.xMin; 
00248                 if (glyph_bbox.yMin < bbox.yMin) 
00249                     bbox.yMin = glyph_bbox.yMin; 
00250                 if (glyph_bbox.xMax > bbox.xMax) 
00251                     bbox.xMax = glyph_bbox.xMax; 
00252                 if (glyph_bbox.yMax > bbox.yMax) 
00253                     bbox.yMax = glyph_bbox.yMax;
00254                 
00255                 if ( bbox.xMin > bbox.xMax )
00256                 {
00257                     bbox.xMin = 0; 
00258                     bbox.yMin = 0; 
00259                     bbox.xMax = 0; 
00260                     bbox.yMax = 0; 
00261                 }
00262                 
00263                 // take ownership of the glyph
00264                 glyphs_.push_back(new glyph_t(image));
00265             }
00266             
00267             return Envelope<double>(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax);
00268         }
00269       
00270         dimension_t character_dimensions(const unsigned c)
00271         {
00272             FT_Matrix matrix;
00273             FT_Vector pen;
00274             FT_Error  error;
00275             
00276             FT_Face face = face_->get_face();
00277             FT_GlyphSlot slot = face->glyph;
00278             
00279             pen.x = 0;
00280             pen.y = 0;
00281             
00282             FT_BBox glyph_bbox; 
00283             FT_Glyph image;
00284             
00285             matrix.xx = (FT_Fixed)( 1 * 0x10000L ); 
00286             matrix.xy = (FT_Fixed)( 0 * 0x10000L ); 
00287             matrix.yx = (FT_Fixed)( 0 * 0x10000L ); 
00288             matrix.yy = (FT_Fixed)( 1 * 0x10000L );
00289                     
00290             FT_Set_Transform (face,&matrix,&pen);
00291             
00292             FT_UInt glyph_index = FT_Get_Char_Index( face, c);
00293             
00294             error = FT_Load_Glyph (face,glyph_index,FT_LOAD_NO_HINTING); 
00295             if ( error )
00296                 return dimension_t(0, 0);
00297             
00298             error = FT_Get_Glyph( face->glyph, &image);
00299             if ( error )
00300                 return dimension_t(0, 0);
00301             
00302             FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox); 
00303             FT_Done_Glyph(image); 
00304             return dimension_t(slot->advance.x >> 6, glyph_bbox.yMax - glyph_bbox.yMin);
00305         }
00306         
00307         void get_string_info(std::wstring const& text, string_info *info)
00308         {
00309             unsigned width = 0;
00310             unsigned height = 0;
00311           
00312             for (std::wstring::const_iterator i=text.begin();i!=text.end();++i)
00313             {
00314                 dimension_t char_dim = character_dimensions(*i);
00315               
00316                 info->add_info(*i, char_dim.first, char_dim.second);
00317               
00318                 width += char_dim.first;
00319                 height = char_dim.second > height ? char_dim.second : height;
00320                 
00321             }
00322             info->set_dimensions(width, height);
00323         }
00324         
00325         void render(double x0, double y0)
00326         {
00327             FT_Error  error;
00328             FT_Vector start;
00329             unsigned height = pixmap_.height();
00330             
00331             start.x =  static_cast<FT_Pos>(x0 * (1 << 6)); 
00332             start.y =  static_cast<FT_Pos>((height - y0) * (1 << 6));
00333             
00334             // now render transformed glyphs
00335             typename glyphs_t::iterator pos;
00336             
00337             //make sure we've got reasonable values.
00338             if (halo_radius_ > 0 && halo_radius_ < 256)
00339             {
00340                 //render halo 
00341                 for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
00342                 {
00343             
00344                     FT_Glyph_Transform(pos->image,0,&start);
00345             
00346                     error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1);
00347                     if ( ! error )
00348                     {
00349                         
00350                         FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image;
00351                         render_halo(&bit->bitmap, halo_fill_.rgba(), 
00352                                     bit->left,
00353                                     height - bit->top,halo_radius_);
00354                     }
00355                 }  
00356             }
00357             //render actual text
00358             for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
00359             {
00360             
00361                 FT_Glyph_Transform(pos->image,0,&start);
00362             
00363                 error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1);
00364                 if ( ! error )
00365                 {
00366                    
00367                     FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image;
00368                     render_bitmap(&bit->bitmap, fill_.rgba(), 
00369                                   bit->left,
00370                                   height - bit->top);
00371                 }
00372             }  
00373         }
00374         
00375     private:
00376     
00377         void render_halo(FT_Bitmap *bitmap,unsigned rgba,int x,int y,int radius)
00378         {
00379             int x_max=x+bitmap->width;
00380             int y_max=y+bitmap->rows;
00381             int i,p,j,q;
00382         
00383             for (i=x,p=0;i<x_max;++i,++p)
00384             {
00385                 for (j=y,q=0;j<y_max;++j,++q)
00386                 {
00387                     int gray = bitmap->buffer[q*bitmap->width+p];
00388                     if (gray)
00389                     {
00390                         for (int n=-halo_radius_; n <=halo_radius_; ++n)
00391                             for (int m=-halo_radius_;m <= halo_radius_; ++m)
00392                                 pixmap_.blendPixel(i+m,j+n,rgba,gray);                  
00393                     }
00394                 }
00395             }
00396         }
00397     
00398         void render_bitmap(FT_Bitmap *bitmap,unsigned rgba,int x,int y)
00399         {
00400             int x_max=x+bitmap->width;
00401             int y_max=y+bitmap->rows;
00402             int i,p,j,q;
00403         
00404             for (i=x,p=0;i<x_max;++i,++p)
00405             {
00406                 for (j=y,q=0;j<y_max;++j,++q)
00407                 {
00408                     int gray=bitmap->buffer[q*bitmap->width+p];
00409                     if (gray)
00410                     {
00411                         pixmap_.blendPixel(i,j,rgba,gray);
00412                     }
00413                 }
00414             }
00415         }
00416     
00417         pixmap_type & pixmap_;
00418         face_ptr face_;
00419         mapnik::Color fill_;
00420         mapnik::Color halo_fill_;
00421         int halo_radius_;
00422         unsigned text_ratio_;
00423         unsigned wrap_width_;
00424         glyphs_t glyphs_;
00425     }; 
00426 }
00427 
00428 
00429 #endif // FONT_ENGINE_FREETYPE_HPP

Generated on Thu Jul 19 17:59:26 2007 for Mapnik by  doxygen 1.4.7