Merge remote-tracking branch 'origin/tizen' into new_text
[platform/core/uifw/dali-adaptor.git] / text / dali / internal / text-abstraction / font-client-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS  HEADER
19 #include "font-client-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <vector>
23 #include <ft2build.h>
24 #include FT_FREETYPE_H
25 #include FT_GLYPH_H
26 #include <fontconfig/fontconfig.h>
27 #include <dali/integration-api/debug.h>
28
29 // INTERNAL INCLUDES
30 #include <singleton-service-impl.h>
31
32 /**
33  * Conversion from Fractional26.6 to float
34  */
35 namespace
36 {
37 const float FROM_266 = 1.0f / 64.0f;
38 }
39
40 namespace Dali
41 {
42
43 namespace TextAbstraction
44 {
45
46 namespace Internal
47 {
48
49 struct FontClient::Plugin
50 {
51   struct CacheItem
52   {
53     CacheItem( FontId id, FT_Face ftFace, const std::string& path, PointSize26Dot6 pointSize, FaceIndex face, const FontMetrics& metrics )
54     : mFreeTypeFace( ftFace ),
55       mPath( path ),
56       mPointSize( pointSize ),
57       mFaceIndex( face ),
58       mMetrics( metrics )
59     {
60     }
61
62     FT_Face mFreeTypeFace;
63     std::string mPath;
64     PointSize26Dot6 mPointSize;
65     FaceIndex mFaceIndex;
66     FontMetrics mMetrics;
67   };
68
69   Plugin( unsigned int horizontalDpi, unsigned int verticalDpi )
70   : mFreeTypeLibrary( NULL ),
71     mDpiHorizontal( horizontalDpi ),
72     mDpiVertical( verticalDpi )
73   {
74   }
75
76   ~Plugin()
77   {
78     FT_Done_FreeType( mFreeTypeLibrary );
79   }
80
81   void Initialize()
82   {
83     int error = FT_Init_FreeType( &mFreeTypeLibrary );
84     if( FT_Err_Ok != error )
85     {
86       DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
87     }
88   }
89
90   void SetDpi( unsigned int horizontalDpi, unsigned int verticalDpi )
91   {
92     mDpiHorizontal = horizontalDpi;
93     mDpiVertical = verticalDpi;
94   }
95
96   void GetSystemFonts( FontList& systemFonts )
97   {
98     if( mSystemFonts.empty() )
99     {
100       InitSystemFonts();
101     }
102
103     systemFonts = mSystemFonts;
104   }
105
106   void InitSystemFonts()
107   {
108     FcFontSet* fontSet = GetFcFontSet();
109
110     if( fontSet )
111     {
112       mSystemFonts.reserve( fontSet->nfont );
113
114       for( int i = 0u; i < fontSet->nfont; ++i )
115       {
116         FcPattern* fontPattern = fontSet->fonts[i];
117
118         std::string path;
119
120         // Skip fonts with no path
121         if( GetFcString( fontPattern, FC_FILE, path ) )
122         {
123           mSystemFonts.push_back( FontDescription() );
124           FontDescription& fontDescription = mSystemFonts.back();
125
126           fontDescription.path = path;
127
128           GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
129           GetFcString( fontPattern, FC_STYLE, fontDescription.style );
130         }
131       }
132
133       FcFontSetDestroy( fontSet );
134     }
135   }
136
137   _FcFontSet* GetFcFontSet() const
138   {
139     // create a new pattern.
140     // a pattern holds a set of names, each name refers to a property of the font
141     FcPattern* pattern = FcPatternCreate();
142
143     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
144     FcObjectSet* objectSet = FcObjectSetCreate();
145
146     // build an object set from a list of property names
147     FcObjectSetAdd( objectSet, FC_FILE );
148     FcObjectSetAdd( objectSet, FC_FAMILY );
149     FcObjectSetAdd( objectSet, FC_STYLE );
150
151     // get a list of fonts
152     // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
153     FcFontSet* fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
154
155     // clear up the object set
156     if( objectSet )
157     {
158       FcObjectSetDestroy( objectSet );
159     }
160     // clear up the pattern
161     if( pattern )
162     {
163       FcPatternDestroy( pattern );
164     }
165
166     return fontset;
167   }
168
169   bool GetFcString( const FcPattern* pattern, const char* n, std::string& string )
170   {
171     FcChar8* file = NULL;
172     const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
173
174     if( FcResultMatch == retVal )
175     {
176       // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
177       string.assign( reinterpret_cast<const char*>( file ) );
178
179       return true;
180     }
181
182     return false;
183   }
184
185   void GetDescription( FontId id, FontDescription& fontDescription ) const
186   {
187     // TODO
188   }
189
190   PointSize26Dot6 GetPointSize( FontId id )
191   {
192     return 12*64;
193   }
194
195   FontId FindDefaultFont( Character charcode, PointSize26Dot6 pointSize )
196   {
197     // TODO - Use FcCharSetHasChar()
198     return FontId(0);
199   }
200
201   FontId GetFontId( const std::string& path, PointSize26Dot6 pointSize, FaceIndex faceIndex )
202   {
203     FontId id( 0 );
204
205     if( NULL != mFreeTypeLibrary )
206     {
207       FontId foundId(0);
208       if( FindFont( path, pointSize, faceIndex, foundId ) )
209       {
210         id = foundId;
211       }
212       else
213       {
214         id = CreateFont( path, pointSize, faceIndex );
215       }
216     }
217
218     return id;
219   }
220
221   FontId GetFontId( const FontFamily& fontFamily,
222                     const FontStyle& fontStyle,
223                     PointSize26Dot6 pointSize,
224                     FaceIndex faceIndex )
225   {
226     return 0u;
227   }
228
229   GlyphIndex GetGlyphIndex( FontId fontId, Character charcode )
230   {
231     GlyphIndex index( 0 );
232
233     if( fontId > 0 &&
234         fontId-1 < mFontCache.size() )
235     {
236       FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
237
238       index = FT_Get_Char_Index( ftFace, charcode );
239     }
240
241     return index;
242   }
243
244   FontId CreateFont( const std::string& path, PointSize26Dot6 pointSize, FaceIndex faceIndex )
245   {
246     FontId id( 0 );
247
248     // Create & cache new font face
249     FT_Face ftFace;
250     int error = FT_New_Face( mFreeTypeLibrary,
251                              path.c_str(),
252                              0,
253                              &ftFace );
254
255     if( FT_Err_Ok == error )
256     {
257       error = FT_Set_Char_Size( ftFace,
258                                 0,
259                                 pointSize,
260                                 mDpiHorizontal,
261                                 mDpiVertical );
262
263       if( FT_Err_Ok == error )
264       {
265         id = mFontCache.size() + 1;
266
267         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
268
269         FontMetrics metrics( static_cast< float >( ftMetrics.ascender  ) * FROM_266,
270                              static_cast< float >( ftMetrics.descender ) * FROM_266,
271                              static_cast< float >( ftMetrics.height    ) * FROM_266 );
272
273         mFontCache.push_back( CacheItem( id, ftFace, path, pointSize, faceIndex, metrics ) );
274       }
275       else
276       {
277         DALI_LOG_ERROR( "FreeType Set_Char_Size error: %d for pointSize %d\n", pointSize );
278       }
279     }
280     else
281     {
282       DALI_LOG_ERROR( "FreeType New_Face error: %d for %s\n", error, path.c_str() );
283     }
284
285     return id;
286   }
287
288   void GetFontMetrics( FontId fontId, FontMetrics& metrics )
289   {
290     if( fontId > 0 &&
291         fontId-1 < mFontCache.size() )
292     {
293       metrics = mFontCache[fontId-1].mMetrics;
294     }
295     else
296     {
297       DALI_LOG_ERROR( "Invalid font ID %d\n", fontId );
298     }
299   }
300
301   bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal )
302   {
303     bool success( true );
304
305     for( unsigned int i=0; i<size; ++i )
306     {
307       FontId fontId = array[i].fontId;
308
309       if( fontId > 0 &&
310           fontId-1 < mFontCache.size() )
311       {
312         FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
313
314         int error = FT_Load_Glyph( ftFace, array[i].index, FT_LOAD_DEFAULT );
315
316         if( FT_Err_Ok == error )
317         {
318           array[i].width  = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
319           array[i].height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
320           if( horizontal )
321           {
322             array[i].xBearing = static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
323             array[i].yBearing = static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
324             array[i].advance  = static_cast< float >( ftFace->glyph->metrics.horiAdvance ) * FROM_266;
325           }
326           else
327           {
328             array[i].xBearing = static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
329             array[i].yBearing = static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
330             array[i].advance  = static_cast< float >( ftFace->glyph->metrics.vertAdvance ) * FROM_266;
331           }
332         }
333         else
334         {
335           success = false;
336         }
337       }
338       else
339       {
340         success = false;
341       }
342     }
343
344     return success;
345   }
346
347   BitmapImage CreateBitmap( FontId fontId, GlyphIndex glyphIndex )
348   {
349     BitmapImage bitmap;
350
351     if( fontId > 0 &&
352         fontId-1 < mFontCache.size() )
353     {
354       FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
355
356       FT_Error error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_DEFAULT );
357       if( FT_Err_Ok == error )
358       {
359         FT_Glyph glyph;
360         error = FT_Get_Glyph( ftFace->glyph, &glyph );
361
362         // Convert to bitmap if necessary
363         if ( FT_Err_Ok == error )
364         {
365           if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
366           {
367             error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
368           }
369           else
370           {
371             DALI_LOG_ERROR( "FT_Glyph_To_Bitmap Failed with error: %d\n", error );
372           }
373         }
374         else
375         {
376           DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error );
377         }
378
379         if( FT_Err_Ok == error )
380         {
381           // Access the underlying bitmap data
382           FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph;
383           ConvertBitmap( bitmap, bitmapGlyph->bitmap );
384         }
385
386         // Created FT_Glyph object must be released with FT_Done_Glyph
387         FT_Done_Glyph( glyph );
388       }
389       else
390       {
391         DALI_LOG_ERROR( "FT_Load_Glyph Failed with error: %d\n", error );
392       }
393     }
394
395     return bitmap;
396   }
397
398   void ConvertBitmap( BitmapImage& destBitmap, FT_Bitmap srcBitmap )
399   {
400     if( srcBitmap.width*srcBitmap.rows > 0 )
401     {
402       // TODO - Support all pixel modes
403       if( FT_PIXEL_MODE_GRAY == srcBitmap.pixel_mode )
404       {
405         if( srcBitmap.pitch == srcBitmap.width )
406         {
407           destBitmap = BitmapImage::New( srcBitmap.width, srcBitmap.rows, Pixel::L8 );
408
409           PixelBuffer* destBuffer = destBitmap.GetBuffer();
410           memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows );
411         }
412       }
413     }
414   }
415
416 private:
417
418   bool FindFont( const std::string& path, PointSize26Dot6 pointSize, FaceIndex faceIndex, FontId& found ) const
419   {
420     for( unsigned int i=0; i<mFontCache.size(); ++i )
421     {
422       if( mFontCache[i].mPointSize == pointSize &&
423           mFontCache[i].mPath == path &&
424           mFontCache[i].mFaceIndex == faceIndex )
425       {
426         found = i + 1;
427         return true;
428       }
429     }
430
431     return false;
432   }
433
434   FT_Library mFreeTypeLibrary;
435
436   FontList mSystemFonts;
437
438   std::vector<CacheItem> mFontCache;
439
440   unsigned int mDpiHorizontal;
441   unsigned int mDpiVertical;
442 };
443
444 FontClient::FontClient()
445 : mPlugin( NULL ),
446   mDpiHorizontal( 0 ),
447   mDpiVertical( 0 )
448 {
449 }
450
451 FontClient::~FontClient()
452 {
453   delete mPlugin;
454 }
455
456 Dali::TextAbstraction::FontClient FontClient::Get()
457 {
458   Dali::TextAbstraction::FontClient fontClientHandle;
459
460   Dali::SingletonService service( SingletonService::Get() );
461   if ( service )
462   {
463     // Check whether the singleton is already created
464     Dali::BaseHandle handle = service.GetSingleton( typeid( Dali::TextAbstraction::FontClient ) );
465     if(handle)
466     {
467       // If so, downcast the handle
468       FontClient* impl = dynamic_cast< Dali::TextAbstraction::Internal::FontClient* >( handle.GetObjectPtr() );
469       fontClientHandle = Dali::TextAbstraction::FontClient( impl );
470     }
471     else // create and register the object
472     {
473       fontClientHandle = Dali::TextAbstraction::FontClient( new FontClient );
474       service.Register( typeid( fontClientHandle ), fontClientHandle );
475     }
476   }
477
478   return fontClientHandle;
479 }
480
481 void FontClient::SetDpi( unsigned int horizontalDpi, unsigned int verticalDpi  )
482 {
483   mDpiHorizontal = horizontalDpi;
484   mDpiVertical = verticalDpi;
485
486   // Allow DPI to be set without loading plugin
487   if( mPlugin )
488   {
489     mPlugin->SetDpi( horizontalDpi, verticalDpi  );
490   }
491 }
492
493 void FontClient::GetDescription( FontId id, FontDescription& fontDescription )
494 {
495   CreatePlugin();
496 }
497
498 PointSize26Dot6 FontClient::GetPointSize( FontId id )
499 {
500   CreatePlugin();
501
502   return mPlugin->GetPointSize( id );
503 }
504
505 void FontClient::GetSystemFonts( FontList& systemFonts )
506 {
507   CreatePlugin();
508
509   mPlugin->GetSystemFonts( systemFonts );
510 }
511
512 FontId FontClient::FindDefaultFont( Character charcode, PointSize26Dot6 pointSize )
513 {
514   CreatePlugin();
515
516   return mPlugin->FindDefaultFont( charcode, pointSize );
517 }
518
519 FontId FontClient::GetFontId( const FontPath& path, PointSize26Dot6 pointSize, FaceIndex faceIndex )
520 {
521   CreatePlugin();
522
523   return mPlugin->GetFontId( path, pointSize, faceIndex );
524 }
525
526 FontId FontClient::GetFontId( const FontFamily& fontFamily,
527                               const FontStyle& fontStyle,
528                               PointSize26Dot6 pointSize,
529                               FaceIndex faceIndex )
530 {
531   CreatePlugin();
532
533   return mPlugin->GetFontId( fontFamily,
534                              fontStyle,
535                              pointSize,
536                              faceIndex );
537 }
538
539 void FontClient::GetFontMetrics( FontId fontId, FontMetrics& metrics )
540 {
541   CreatePlugin();
542
543   return mPlugin->GetFontMetrics( fontId, metrics );
544 }
545
546 GlyphIndex FontClient::GetGlyphIndex( FontId fontId, Character charcode )
547 {
548   CreatePlugin();
549
550   return mPlugin->GetGlyphIndex( fontId, charcode );
551 }
552
553 bool FontClient::GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal )
554 {
555   CreatePlugin();
556
557   return mPlugin->GetGlyphMetrics( array, size, horizontal );
558 }
559
560 BitmapImage FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex )
561 {
562   CreatePlugin();
563
564   return mPlugin->CreateBitmap( fontId, glyphIndex );
565 }
566
567 void FontClient::CreatePlugin()
568 {
569   if( !mPlugin )
570   {
571     mPlugin = new Plugin( mDpiHorizontal, mDpiVertical );
572     mPlugin->Initialize();
573   }
574 }
575
576 } // namespace Internal
577
578 } // namespace FontClient
579
580 } // namespace Dali