root/trunk/include/mapnik/font_engine_freetype.hpp

Revision 1495, 18.1 kB (checked in by artem, 3 months ago)

+ merge mapnik2 to trunk

Line 
1/*****************************************************************************
2 *
3 * This file is part of Mapnik (c++ mapping toolkit)
4 *
5 * Copyright (C) 2006 Artem Pavlenko
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 *
21 *****************************************************************************/
22
23//$Id$
24
25#ifndef FONT_ENGINE_FREETYPE_HPP
26#define FONT_ENGINE_FREETYPE_HPP
27// mapnik
28#include <mapnik/color.hpp>
29#include <mapnik/utils.hpp>
30#include <mapnik/ctrans.hpp>
31#include <mapnik/geometry.hpp>
32#include <mapnik/text_path.hpp>
33#include <mapnik/font_set.hpp>
34
35// freetype2
36extern "C"
37{
38#include <ft2build.h>
39#include FT_FREETYPE_H
40#include FT_GLYPH_H
41}
42
43// boost
44#include <boost/shared_ptr.hpp>
45#include <boost/utility.hpp>
46#include <boost/ptr_container/ptr_vector.hpp>
47#include <boost/thread/mutex.hpp>
48
49// stl
50#include <string>
51#include <vector>
52#include <map>
53#include <iostream>
54
55// icu
56#include <unicode/ubidi.h>
57#include <unicode/ushape.h>
58
59namespace mapnik
60{
61    class font_face;
62
63    typedef boost::shared_ptr<font_face> face_ptr;
64
65    class MAPNIK_DECL font_glyph : private boost::noncopyable
66    {
67    public:
68        font_glyph(face_ptr face, unsigned index)
69           : face_(face), index_(index) {}
70
71        face_ptr get_face() const
72        {
73            return face_;
74        }
75
76        unsigned get_index() const
77        {
78            return index_;
79        }
80    private:
81        face_ptr face_;
82        unsigned index_;
83    };
84
85    typedef boost::shared_ptr<font_glyph> glyph_ptr;
86
87    class font_face : boost::noncopyable
88    {
89    public:
90        font_face(FT_Face face)
91           : face_(face) {}
92
93        std::string  family_name() const
94        {
95            return std::string(face_->family_name);
96        }
97
98        std::string  style_name() const
99        {
100            return std::string(face_->style_name);
101        }
102
103        FT_GlyphSlot glyph() const
104        {
105            return face_->glyph;
106        }
107
108        FT_Face get_face() const
109        {
110            return face_;
111        }
112
113        unsigned get_char(unsigned c) const
114        {
115            return FT_Get_Char_Index(face_, c);
116        }
117
118        bool set_pixel_sizes(unsigned size)
119        {
120            if (! FT_Set_Pixel_Sizes( face_, 0, size ))
121                return true;
122
123            return false;
124        }
125
126        ~font_face()
127        {
128#ifdef MAPNIK_DEBUG
129        std::clog << "~font_face: Clean up face \"" << family_name()
130            << " " << style_name() << "\"" << std::endl;
131#endif
132        FT_Done_Face(face_);
133        }
134
135    private:
136    FT_Face face_;
137    };
138
139    class MAPNIK_DECL font_face_set : private boost::noncopyable
140    {
141    public:
142        typedef std::pair<unsigned,unsigned> dimension_t;
143
144        font_face_set(void)
145           : faces_() {}
146
147        void add(face_ptr face)
148        {
149            faces_.push_back(face);
150        }
151
152        unsigned size() const
153        {
154            return faces_.size();
155        }
156
157        glyph_ptr get_glyph(unsigned c) const
158        {
159            for (std::vector<face_ptr>::const_iterator face = faces_.begin(); face != faces_.end(); ++face)
160            {
161               FT_UInt g = (*face)->get_char(c);
162
163               if (g) return glyph_ptr(new font_glyph(*face, g));
164            }
165
166            // Final fallback to empty square if nothing better in any font
167            return glyph_ptr(new font_glyph(*faces_.begin(), 0));
168        }
169
170        dimension_t character_dimensions(const unsigned c)
171        {
172            FT_Matrix matrix;
173            FT_Vector pen;
174            FT_Error  error;
175
176            pen.x = 0;
177            pen.y = 0;
178
179            FT_BBox glyph_bbox;
180            FT_Glyph image;
181
182            glyph_ptr glyph = get_glyph(c);
183            FT_Face face = glyph->get_face()->get_face();
184
185            matrix.xx = (FT_Fixed)( 1 * 0x10000L );
186            matrix.xy = (FT_Fixed)( 0 * 0x10000L );
187            matrix.yx = (FT_Fixed)( 0 * 0x10000L );
188            matrix.yy = (FT_Fixed)( 1 * 0x10000L );
189
190            FT_Set_Transform(face, &matrix, &pen);
191
192            error = FT_Load_Glyph (face, glyph->get_index(), FT_LOAD_NO_HINTING);
193            if ( error )
194                return dimension_t(0, 0);
195
196            error = FT_Get_Glyph(face->glyph, &image);
197            if ( error )
198                return dimension_t(0, 0);
199
200            FT_Glyph_Get_CBox(image, ft_glyph_bbox_pixels, &glyph_bbox);
201            FT_Done_Glyph(image);
202
203            unsigned tempx = face->glyph->advance.x >> 6;
204            unsigned tempy = glyph_bbox.yMax - glyph_bbox.yMin;
205
206            //std::clog << "glyph: " << glyph_index << " x: " << tempx << " y: " << tempy << std::endl;
207
208            return dimension_t(tempx, tempy);
209        }
210
211        void get_string_info(string_info & info)
212        {
213            unsigned width = 0;
214            unsigned height = 0;
215            UErrorCode err = U_ZERO_ERROR;
216            UnicodeString const& ustr = info.get_string();
217            const UChar * text = ustr.getBuffer();
218            UBiDi * bidi = ubidi_openSized(ustr.length(),0,&err);
219
220            if (U_SUCCESS(err))
221            {
222               ubidi_setPara(bidi,text,ustr.length(), UBIDI_DEFAULT_LTR,0,&err);
223
224               if (U_SUCCESS(err))
225               {
226                  int32_t count = ubidi_countRuns(bidi,&err);
227                  int32_t logicalStart;
228                  int32_t length;
229
230                  for (int32_t i=0; i< count;++i)
231                  {
232                     if (UBIDI_LTR == ubidi_getVisualRun(bidi,i,&logicalStart,&length))
233                     {
234                        do {
235                           UChar ch = text[logicalStart++];
236                           dimension_t char_dim = character_dimensions(ch);
237                           info.add_info(ch, char_dim.first, char_dim.second);
238                           width += char_dim.first;
239                           height = char_dim.second > height ? char_dim.second : height;
240
241                        } while (--length > 0);
242                     }
243                     else
244                     {
245                        logicalStart += length;
246
247                        int32_t j=0,i=length;
248                        UnicodeString arabic;
249                        UChar * buf = arabic.getBuffer(length);
250                        do {
251                           UChar ch = text[--logicalStart];
252                           buf[j++] = ch;
253                        } while (--i > 0);
254
255                        arabic.releaseBuffer(length);
256                        if ( *arabic.getBuffer() >= 0x0600 && *arabic.getBuffer() <= 0x06ff)
257                        {
258                           UnicodeString shaped;
259                           u_shapeArabic(arabic.getBuffer(),arabic.length(),shaped.getBuffer(arabic.length()),arabic.length(),
260                                         U_SHAPE_LETTERS_SHAPE|U_SHAPE_LENGTH_FIXED_SPACES_NEAR|
261                                         U_SHAPE_TEXT_DIRECTION_VISUAL_LTR
262                                         ,&err);
263
264                           shaped.releaseBuffer(arabic.length());
265
266                           if (U_SUCCESS(err))
267                           {
268                              for (int j=0;j<shaped.length();++j)
269                              {
270                                 dimension_t char_dim = character_dimensions(shaped[j]);
271                                 info.add_info(shaped[j], char_dim.first, char_dim.second);
272                                 width += char_dim.first;
273                                 height = char_dim.second > height ? char_dim.second : height;
274                              }
275                           }
276                        } else {
277                           // Non-Arabic RTL
278                           for (int j=0;j<arabic.length();++j)
279                           {
280                              dimension_t char_dim = character_dimensions(arabic[j]);
281                              info.add_info(arabic[j], char_dim.first, char_dim.second);
282                              width += char_dim.first;
283                              height = char_dim.second > height ? char_dim.second : height;
284                           }
285                        }
286                     }
287                  }
288               }
289               ubidi_close(bidi);
290            }
291
292            info.set_dimensions(width, height);
293        }
294
295        void set_pixel_sizes(unsigned size)
296        {
297            for (std::vector<face_ptr>::iterator face = faces_.begin(); face != faces_.end(); ++face)
298            {
299                (*face)->set_pixel_sizes(size);
300            }
301        }
302    private:
303        std::vector<face_ptr> faces_;
304    };
305
306    typedef boost::shared_ptr<font_face_set> face_set_ptr;
307
308    class MAPNIK_DECL freetype_engine
309    {
310      public:
311        static bool register_font(std::string const& file_name);
312        static std::vector<std::string> face_names ();
313        face_ptr create_face(std::string const& family_name);
314        virtual ~freetype_engine();
315        freetype_engine();
316      private:
317        FT_Library library_;
318        static boost::mutex mutex_;
319        static std::map<std::string,std::string> name2file_;
320    };
321
322    template <typename T>
323    class MAPNIK_DECL face_manager : private boost::noncopyable
324    {
325        typedef T font_engine_type;
326        typedef std::map<std::string,face_ptr> faces;
327
328    public:
329        face_manager(T & engine)
330           : engine_(engine) {}
331
332        face_ptr get_face(std::string const& name)
333        {
334            typename faces::iterator itr;
335            itr = faces_.find(name);
336            if (itr != faces_.end())
337            {
338                return itr->second;
339            }
340            else
341            {
342                face_ptr face = engine_.create_face(name);
343                if (face)
344                {
345                    faces_.insert(make_pair(name,face));
346                }
347                return face;
348            }
349        }
350
351        face_set_ptr get_face_set(std::string const& name)
352        {
353            face_set_ptr face_set(new font_face_set);
354            if (face_ptr face = get_face(name))
355            {
356                face_set->add(face);
357            }
358            return face_set;
359        }
360
361        face_set_ptr get_face_set(font_set const& fset)
362        {
363            std::vector<std::string> const& names = fset.get_face_names();
364            face_set_ptr face_set(new font_face_set);
365            for (std::vector<std::string>::const_iterator name = names.begin(); name != names.end(); ++name)
366            {
367                if (face_ptr face = get_face(*name))
368                {
369                    face_set->add(face);
370                }
371            }
372            return face_set;
373        }
374    private:
375        faces faces_;
376        font_engine_type & engine_;
377    };
378
379    template <typename T>
380    struct text_renderer : private boost::noncopyable
381    {
382        struct glyph_t : boost::noncopyable
383        {
384            FT_Glyph image;
385            glyph_t(FT_Glyph image_) : image(image_) {}
386            ~glyph_t () { FT_Done_Glyph(image);}
387        };
388
389        typedef boost::ptr_vector<glyph_t> glyphs_t;
390        typedef T pixmap_type;
391
392        text_renderer (pixmap_type & pixmap, face_set_ptr faces)
393            : pixmap_(pixmap),
394              faces_(faces),
395              fill_(0,0,0),
396              halo_fill_(255,255,255),
397              halo_radius_(0),
398              opacity_(1.0) {}
399
400        void set_pixel_size(unsigned size)
401        {
402            faces_->set_pixel_sizes(size);
403        }
404
405        void set_fill(mapnik::color const& fill)
406        {
407            fill_=fill;
408        }
409
410        void set_halo_fill(mapnik::color const& halo)
411        {
412            halo_fill_=halo;
413        }
414
415        void set_halo_radius( int radius=1)
416        {
417            halo_radius_=radius;
418        }
419
420        void set_opacity( double opacity=1.0)
421        {
422            opacity_=opacity;
423        }
424
425        box2d<double> prepare_glyphs(text_path *path)
426        {
427            //clear glyphs
428            glyphs_.clear();
429
430            FT_Matrix matrix;
431            FT_Vector pen;
432            FT_Error  error;
433
434            FT_BBox bbox;
435            bbox.xMin = bbox.yMin = 32000;  // Initialize these so we can tell if we
436            bbox.xMax = bbox.yMax = -32000; // properly grew the bbox later
437
438            for (int i = 0; i < path->num_nodes(); i++)
439            {
440                int c;
441                double x, y, angle;
442
443                path->vertex(&c, &x, &y, &angle);
444
445#ifdef MAPNIK_DEBUG
446                // TODO Enable when we have support for setting verbosity
447                //std::clog << "prepare_glyphs: " << c << "," << x <<
448                //    "," << y << "," << angle << std::endl;
449#endif
450
451                FT_BBox glyph_bbox;
452                FT_Glyph image;
453
454                pen.x = int(x * 64);
455                pen.y = int(y * 64);
456
457                glyph_ptr glyph = faces_->get_glyph(unsigned(c));
458                FT_Face face = glyph->get_face()->get_face();
459
460                matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
461                matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
462                matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
463                matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
464
465                FT_Set_Transform(face, &matrix, &pen);
466
467                error = FT_Load_Glyph(face, glyph->get_index(), FT_LOAD_NO_HINTING);
468                if ( error )
469                    continue;
470
471                error = FT_Get_Glyph(face->glyph, &image);
472                if ( error )
473                    continue;
474
475                FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox);
476                if (glyph_bbox.xMin < bbox.xMin)
477                    bbox.xMin = glyph_bbox.xMin;
478                if (glyph_bbox.yMin < bbox.yMin)
479                    bbox.yMin = glyph_bbox.yMin;
480                if (glyph_bbox.xMax > bbox.xMax)
481                    bbox.xMax = glyph_bbox.xMax;
482                if (glyph_bbox.yMax > bbox.yMax)
483                    bbox.yMax = glyph_bbox.yMax;
484
485                // Check if we properly grew the bbox
486                if ( bbox.xMin > bbox.xMax )
487                {
488                    bbox.xMin = 0;
489                    bbox.yMin = 0;
490                    bbox.xMax = 0;
491                    bbox.yMax = 0;
492                }
493
494                // take ownership of the glyph
495                glyphs_.push_back(new glyph_t(image));
496            }
497
498            return box2d<double>(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax);
499        }
500
501        void render(double x0, double y0)
502        {
503            FT_Error  error;
504            FT_Vector start;
505            unsigned height = pixmap_.height();
506
507            start.x =  static_cast<FT_Pos>(x0 * (1 << 6));
508            start.y =  static_cast<FT_Pos>((height - y0) * (1 << 6));
509
510            // now render transformed glyphs
511            typename glyphs_t::iterator pos;
512
513            //make sure we've got reasonable values.
514            if (halo_radius_ > 0 && halo_radius_ < 256)
515            {
516                //render halo
517                for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
518                {
519                    FT_Glyph_Transform(pos->image,0,&start);
520
521                    error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1);
522                    if ( ! error )
523                    {
524
525                        FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image;
526                        render_halo(&bit->bitmap, halo_fill_.rgba(),
527                                    bit->left,
528                                    height - bit->top,halo_radius_);
529                    }
530                }
531            }
532            //render actual text
533            for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos)
534            {
535
536                FT_Glyph_Transform(pos->image,0,&start);
537
538                error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1);
539                if ( ! error )
540                {
541
542                    FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image;
543                    render_bitmap(&bit->bitmap, fill_.rgba(),
544                                  bit->left,
545                                  height - bit->top);
546                }
547            }
548        }
549
550    private:
551
552        void render_halo(FT_Bitmap *bitmap,unsigned rgba,int x,int y,int radius)
553        {
554            int x_max=x+bitmap->width;
555            int y_max=y+bitmap->rows;
556            int i,p,j,q;
557
558            for (i=x,p=0;i<x_max;++i,++p)
559            {
560                for (j=y,q=0;j<y_max;++j,++q)
561                {
562                    int gray = bitmap->buffer[q*bitmap->width+p];
563                    if (gray)
564                    {
565                        for (int n=-halo_radius_; n <=halo_radius_; ++n)
566                            for (int m=-halo_radius_;m <= halo_radius_; ++m)
567                                pixmap_.blendPixel2(i+m,j+n,rgba,gray,opacity_);
568                    }
569                }
570            }
571        }
572
573        void render_bitmap(FT_Bitmap *bitmap,unsigned rgba,int x,int y)
574        {
575            int x_max=x+bitmap->width;
576            int y_max=y+bitmap->rows;
577            int i,p,j,q;
578
579            for (i=x,p=0;i<x_max;++i,++p)
580            {
581                for (j=y,q=0;j<y_max;++j,++q)
582                {
583                    int gray=bitmap->buffer[q*bitmap->width+p];
584                    if (gray)
585                    {
586                        pixmap_.blendPixel2(i,j,rgba,gray,opacity_);
587                    }
588                }
589            }
590        }
591
592        pixmap_type & pixmap_;
593        face_set_ptr faces_;
594        mapnik::color fill_;
595        mapnik::color halo_fill_;
596        int halo_radius_;
597        unsigned text_ratio_;
598        unsigned wrap_width_;
599        glyphs_t glyphs_;
600        double opacity_;
601    };
602}
603
604#endif // FONT_ENGINE_FREETYPE_HPP
Note: See TracBrowser for help on using the browser.