Merge "Fix a bug caching color fonts." into devel/master
[platform/core/uifw/dali-adaptor.git] / text / dali / internal / text-abstraction / font-client-plugin-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 <dali/internal/text-abstraction/font-client-plugin-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/font-list.h>
23 #include <dali/public-api/common/dali-vector.h>
24 #include <dali/public-api/common/vector-wrapper.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/integration-api/platform-abstraction.h>
27 #include <adaptor-impl.h>
28
29 // EXTERNAL INCLUDES
30 #include <fontconfig/fontconfig.h>
31
32 namespace
33 {
34
35 #if defined(DEBUG_ENABLED)
36 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
37 #endif
38
39 /**
40  * Conversion from Fractional26.6 to float
41  */
42 const float FROM_266 = 1.0f / 64.0f;
43
44 const std::string FONT_FORMAT( "TrueType" );
45 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
46 const int DEFAULT_FONT_WIDTH  = 100; // normal
47 const int DEFAULT_FONT_WEIGHT =  80; // normal
48 const int DEFAULT_FONT_SLANT  =   0; // normal
49
50 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
51
52 const bool FONT_FIXED_SIZE_BITMAP( true );
53
54 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
55
56 // ULTRA_CONDENSED 50
57 // EXTRA_CONDENSED 63
58 // CONDENSED       75
59 // SEMI_CONDENSED  87
60 // NORMAL         100
61 // SEMI_EXPANDED  113
62 // EXPANDED       125
63 // EXTRA_EXPANDED 150
64 // ULTRA_EXPANDED 200
65 const int FONT_WIDTH_TYPE_TO_INT[] = { 50, 63, 75, 87, 100, 113, 125, 150, 200 };
66 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / sizeof( int );
67
68 // THIN                        0
69 // ULTRA_LIGHT, EXTRA_LIGHT   40
70 // LIGHT                      50
71 // DEMI_LIGHT, SEMI_LIGHT     55
72 // BOOK                       75
73 // NORMAL, REGULAR            80
74 // MEDIUM                    100
75 // DEMI_BOLD, SEMI_BOLD      180
76 // BOLD                      200
77 // ULTRA_BOLD, EXTRA_BOLD    205
78 // BLACK, HEAVY, EXTRA_BLACK 210
79 const int FONT_WEIGHT_TYPE_TO_INT[] = { 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210 };
80 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof( FONT_WEIGHT_TYPE_TO_INT ) / sizeof( int );
81
82 // NORMAL, ROMAN   0
83 // ITALIC        100
84 // OBLIQUE       110
85 const int FONT_SLANT_TYPE_TO_INT[] = { 0, 100, 110 };
86 const unsigned int NUM_FONT_SLANT_TYPE = sizeof( FONT_SLANT_TYPE_TO_INT ) / sizeof( int );
87
88 /**
89  * @brief Retrieves a table index for a given value.
90  *
91  * @param[in] value The value.
92  * @param[in] table The table.
93  * @param[in] maxIndex The maximum valid index of the table.
94  *
95  * @return The index to the closest available value
96  */
97 int ValueToIndex( int value, const int* const table, unsigned int maxIndex )
98 {
99   if( ( NULL == table ) ||
100       ( value <= table[0] ) )
101   {
102     return 0;
103   }
104
105   if( value >= table[maxIndex] )
106   {
107     return maxIndex;
108   }
109
110   for( unsigned int index = 0u; index < maxIndex; )
111   {
112     const unsigned int indexPlus = ++index;
113     const int v1 = table[index];
114     const int v2 = table[indexPlus];
115     if( ( v1 < value ) && ( value <= v2 ) )
116     {
117       return ( ( value - v1 ) < ( v2 - value ) ) ? index : indexPlus;
118     }
119
120     index = indexPlus;
121   }
122
123   return 0;
124 }
125
126 }
127
128 using Dali::Vector;
129
130 namespace Dali
131 {
132
133 namespace TextAbstraction
134 {
135
136 namespace Internal
137 {
138
139 /**
140  * @brief Returns the FontWidth's enum index for the given width value.
141  *
142  * @param[in] width The width value.
143  *
144  * @return The FontWidth's enum index.
145  */
146 FontWidth::Type IntToWidthType( int width )
147 {
148   return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
149 }
150
151 /**
152  * @brief Returns the FontWeight's enum index for the given weight value.
153  *
154  * @param[in] weight The weight value.
155  *
156  * @return The FontWeight's enum index.
157  */
158 FontWeight::Type IntToWeightType( int weight )
159 {
160   return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
161 }
162
163 /**
164  * @brief Returns the FontSlant's enum index for the given slant value.
165  *
166  * @param[in] slant The slant value.
167  *
168  * @return The FontSlant's enum index.
169  */
170 FontSlant::Type IntToSlantType( int slant )
171 {
172   return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
173 }
174
175 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
176                                                                         FontDescriptionId index )
177 : fontDescription( fontDescription ),
178   index( index )
179 {
180 }
181
182 FontClient::Plugin::FontIdCacheItem::FontIdCacheItem( FontDescriptionId validatedFontId,
183                                                       PointSize26Dot6 pointSize,
184                                                       FontId fontId )
185 : validatedFontId( validatedFontId ),
186   pointSize( pointSize ),
187   fontId( fontId )
188 {
189 }
190
191 FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace,
192                                           const FontPath& path,
193                                           PointSize26Dot6 pointSize,
194                                           FaceIndex face,
195                                           const FontMetrics& metrics )
196 : mFreeTypeFace( ftFace ),
197   mPath( path ),
198   mPointSize( pointSize ),
199   mFaceIndex( face ),
200   mMetrics( metrics ),
201   mFixedWidthPixels( 0.0f ),
202   mFixedHeightPixels( 0.0f ),
203   mIsFixedSizeBitmap( false )
204 {
205 }
206
207 FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace,
208                                           const FontPath& path,
209                                           PointSize26Dot6 pointSize,
210                                           FaceIndex face,
211                                           const FontMetrics& metrics,
212                                           float fixedWidth,
213                                           float fixedHeight )
214 : mFreeTypeFace( ftFace ),
215   mPath( path ),
216   mPointSize( pointSize ),
217   mFaceIndex( face ),
218   mMetrics( metrics ),
219   mFixedWidthPixels( fixedWidth ),
220   mFixedHeightPixels( fixedHeight ),
221   mIsFixedSizeBitmap( true )
222 {
223 }
224
225 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
226                             unsigned int verticalDpi )
227 : mFreeTypeLibrary( NULL ),
228   mDpiHorizontal( horizontalDpi ),
229   mDpiVertical( verticalDpi ),
230   mSystemFonts(),
231   mDefaultFonts(),
232   mFontCache(),
233   mValidatedFontCache(),
234   mFontDescriptionCache( 1u ),
235   mFontIdCache(),
236   mEllipsisCache()
237 {
238   int error = FT_Init_FreeType( &mFreeTypeLibrary );
239   if( FT_Err_Ok != error )
240   {
241     DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
242   }
243 }
244
245 FontClient::Plugin::~Plugin()
246 {
247   FT_Done_FreeType( mFreeTypeLibrary );
248 }
249
250 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
251                                  unsigned int verticalDpi )
252 {
253   mDpiHorizontal = horizontalDpi;
254   mDpiVertical = verticalDpi;
255 }
256
257 void FontClient::Plugin::SetDefaultFont( const FontDescription& fontDescription )
258 {
259   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::SetDefaultFont family(%s)\n", fontDescription.family.c_str() );
260
261   mDefaultFonts.clear();
262
263   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
264
265   FcResult result = FcResultMatch;
266
267   // Match the pattern.
268   FcFontSet* fontSet = FcFontSort( NULL /* use default configure */,
269                                    fontFamilyPattern,
270                                    false /* don't trim */,
271                                    NULL,
272                                    &result );
273
274   if( NULL != fontSet )
275   {
276     // Reserve some space to avoid reallocations.
277     mDefaultFonts.reserve( fontSet->nfont );
278
279     for( int i = 0u; i < fontSet->nfont; ++i )
280     {
281       FcPattern* fontPattern = fontSet->fonts[i];
282
283       FontPath path;
284
285       // Skip fonts with no path
286       if( GetFcString( fontPattern, FC_FILE, path ) )
287       {
288         mDefaultFonts.push_back( FontDescription() );
289         FontDescription& newFontDescription = mDefaultFonts.back();
290
291         newFontDescription.path = path;
292
293         int width = 0;
294         int weight = 0;
295         int slant = 0;
296         GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
297         GetFcInt( fontPattern, FC_WIDTH, width );
298         GetFcInt( fontPattern, FC_WEIGHT, weight );
299         GetFcInt( fontPattern, FC_SLANT, slant );
300         newFontDescription.width = IntToWidthType( width );
301         newFontDescription.weight = IntToWeightType( weight );
302         newFontDescription.slant = IntToSlantType( slant );
303       }
304     }
305
306     FcFontSetDestroy( fontSet );
307   }
308
309   FcPatternDestroy( fontFamilyPattern );
310 }
311
312 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
313 {
314   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultFonts mDefaultFonts(%s)\n", ( mDefaultFonts.empty()?"empty":"valid" ) );
315
316   if( mDefaultFonts.empty() )
317   {
318     FontDescription fontDescription;
319     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;  // todo This could be set to the Platform font
320     fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
321     fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
322     fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
323     SetDefaultFont( fontDescription );
324   }
325
326   defaultFonts = mDefaultFonts;
327 }
328
329 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
330 {
331   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultPlatformFontDescription\n");
332
333   if ( Adaptor::IsAvailable() )
334   {
335     std::string weight; // todo convert weight into enum
336     Dali::Internal::Adaptor::Adaptor& adaptorImpl( Dali::Internal::Adaptor::Adaptor::GetImplementation( Adaptor::Get() ) );
337     adaptorImpl.GetPlatformAbstraction().GetDefaultFontDescription( fontDescription.family, weight );
338     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultPlatformFontDescription Retreived fontFamily:%s\n", fontDescription.family.c_str() );
339   }
340 }
341
342 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
343 {
344   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetSystemFonts\n");
345
346   if( mSystemFonts.empty() )
347   {
348     InitSystemFonts();
349   }
350
351   systemFonts = mSystemFonts;
352 }
353
354 void FontClient::Plugin::GetDescription( FontId id,
355                                          FontDescription& fontDescription ) const
356 {
357   for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
358          endIt = mFontIdCache.end();
359        it != endIt;
360        ++it )
361   {
362     const FontIdCacheItem& item = *it;
363
364     if( item.fontId == id )
365     {
366       fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
367       return;
368     }
369   }
370
371   DALI_LOG_ERROR( "FontClient::Plugin::GetDescription. No description found for the font ID %d\n", id );
372 }
373
374 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
375 {
376   const FontId index = id - 1u;
377
378   if( id > 0u &&
379       index < mFontCache.size() )
380   {
381     return ( *( mFontCache.begin() + index ) ).mPointSize;
382   }
383   else
384   {
385     DALI_LOG_ERROR( "FontClient::Plugin::GetPointSize. Invalid font ID %d\n", id );
386   }
387
388   return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
389 }
390
391 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
392                                             PointSize26Dot6 requestedSize,
393                                             bool preferColor )
394 {
395   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindDefaultFont DefaultFontsList(%s)\n", (mDefaultFonts.empty()?"empty":"created") );
396
397   FontId fontId(0);
398   bool foundColor(false);
399
400   // Create the list of default fonts if it has not been created.
401   if( mDefaultFonts.empty() )
402   {
403     FontDescription fontDescription;
404     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
405     fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
406     fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
407     fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
408     SetDefaultFont( fontDescription );
409   }
410
411   // Traverse the list of default fonts.
412   // Check for each default font if supports the character.
413
414   for( FontList::const_iterator it = mDefaultFonts.begin(), endIt = mDefaultFonts.end();
415        it != endIt;
416        ++it )
417   {
418     const FontDescription& description = *it;
419
420     FcPattern* pattern = CreateFontFamilyPattern( description );
421
422     FcResult result = FcResultMatch;
423     FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result );
424
425     FcCharSet* charSet = NULL;
426     FcPatternGetCharSet( match, FC_CHARSET, 0u, &charSet );
427
428     if( FcCharSetHasChar( charSet, charcode ) )
429     {
430       Vector< PointSize26Dot6 > fixedSizes;
431       GetFixedSizes( description,
432                      fixedSizes );
433
434       const Vector< PointSize26Dot6 >::SizeType count = fixedSizes.Count();
435       if( 0 != count )
436       {
437         // If the font is not scalable, pick the largest size <= requestedSize
438         PointSize26Dot6 size = fixedSizes[0];
439         for( unsigned int i=1; i<count; ++i )
440         {
441           if( fixedSizes[i] <= requestedSize &&
442               fixedSizes[i] > size )
443           {
444             size = fixedSizes[i];
445           }
446         }
447         requestedSize = size;
448       }
449
450       fontId = GetFontId( description,
451                           requestedSize,
452                           0u );
453
454       if( preferColor )
455       {
456         BufferImage bitmap = CreateBitmap( fontId, GetGlyphIndex(fontId,charcode) );
457         if( bitmap &&
458             Pixel::BGRA8888 == bitmap.GetPixelFormat() )
459         {
460           foundColor = true;
461         }
462       }
463
464       // Keep going unless we prefer a different (color) font
465       if( !preferColor || foundColor )
466       {
467         FcPatternDestroy( match );
468         FcPatternDestroy( pattern );
469         break;
470       }
471     }
472
473     FcPatternDestroy( match );
474     FcPatternDestroy( pattern );
475   }
476   return fontId;
477 }
478
479 FontId FontClient::Plugin::GetFontId( const FontPath& path,
480                                       PointSize26Dot6 pointSize,
481                                       FaceIndex faceIndex,
482                                       bool cacheDescription )
483 {
484   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId fontPatch:%s\n", path.c_str() );
485
486   FontId id( 0 );
487
488   if( NULL != mFreeTypeLibrary )
489   {
490     FontId foundId(0);
491     if( FindFont( path, pointSize, faceIndex, foundId ) )
492     {
493       id = foundId;
494     }
495     else
496     {
497       id = CreateFont( path, pointSize, faceIndex, cacheDescription );
498     }
499   }
500
501   return id;
502 }
503
504 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
505                                       PointSize26Dot6 pointSize,
506                                       FaceIndex faceIndex )
507 {
508   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId font family(%s)\n", fontDescription.family.c_str() );
509
510   // This method uses three vectors which caches:
511   // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
512   // * The path to font file names.
513   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
514
515   // 1) Checks in the cache if the font's description has been validated before.
516   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
517   //    retrieves using font config a path to a font file name which matches with the
518   //    font's description. The path is stored in the cache.
519   //
520   // 2) Checks in the cache if the pair 'font point size, index to the vector with paths to
521   //    font file names' exists. If exists, it gets the font id. If it doesn't it calls
522   //    the GetFontId() method with the path to the font file name and the point size to
523   //    get the font id.
524
525   // The font id to be returned.
526   FontId fontId = 0u;
527
528   // Check first if the font's description have been validated before.
529   FontDescriptionId validatedFontId = 0u;
530
531   if( !FindValidatedFont( fontDescription,
532                           validatedFontId ) )
533   {
534     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId Validating Font\n");
535
536     // Use font config to validate the font's description.
537     ValidateFont( fontDescription,
538                   validatedFontId );
539   }
540
541   // Check if exists a pair 'validatedFontId, pointSize' in the cache.
542   if( !FindFont( validatedFontId, pointSize, fontId ) )
543   {
544     // Retrieve the font file name path.
545     const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
546
547     // Retrieve the font id. Do not cache the description as it has been already cached.
548     fontId = GetFontId( description.path,
549                         pointSize,
550                         faceIndex,
551                         false );
552
553     // Cache the pair 'validatedFontId, pointSize' to improve the following queries.
554     mFontIdCache.push_back( FontIdCacheItem( validatedFontId,
555                                              pointSize,
556                                              fontId ) );
557   }
558
559   return fontId;
560 }
561
562 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
563                                        FontDescriptionId& validatedFontId )
564 {
565   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont Validating Font family(%s) \n", fontDescription.family.c_str() );
566
567   // Create a font pattern.
568   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
569
570   FcResult result = FcResultMatch;
571
572   // match the pattern
573   FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
574
575   if( match )
576   {
577     // Get the path to the font file name.
578     int width = 0;
579     int weight = 0;
580     int slant = 0;
581     FontDescription description;
582     GetFcString( match, FC_FILE, description.path );
583     GetFcString( match, FC_FAMILY, description.family );
584     GetFcInt( match, FC_WIDTH, width );
585     GetFcInt( match, FC_WEIGHT, weight );
586     GetFcInt( match, FC_SLANT, slant );
587     description.width = IntToWidthType( width );
588     description.weight = IntToWeightType( weight );
589     description.slant = IntToSlantType( slant );
590
591     // Set the index to the vector of paths to font file names.
592     validatedFontId = mFontDescriptionCache.size();
593
594     // Add the path to the cache.
595     mFontDescriptionCache.push_back( description );
596
597     // Cache the index and the font's description.
598     FontDescriptionCacheItem item( description,
599                                    validatedFontId );
600
601     mValidatedFontCache.push_back( item );
602
603     // destroyed the matched pattern
604     FcPatternDestroy( match );
605   }
606   else
607   {
608     DALI_LOG_ERROR( "FontClient::Plugin::ValidateFont failed for font %s %d %d %d\n",
609                     fontDescription.family.c_str(),
610                     fontDescription.width,
611                     fontDescription.weight,
612                     fontDescription.slant );
613   }
614
615   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont validatedFontId(%u) font family(%s)\n", validatedFontId, fontDescription.family.c_str() );
616
617   // destroy the pattern
618   FcPatternDestroy( fontFamilyPattern );
619 }
620
621 void FontClient::Plugin::GetFontMetrics( FontId fontId,
622                                          FontMetrics& metrics,
623                                          int maxFixedSize )
624 {
625   if( fontId > 0 &&
626       fontId-1 < mFontCache.size() )
627   {
628     const CacheItem& font = mFontCache[fontId-1];
629
630     metrics = font.mMetrics;
631
632     // Adjust the metrics if the fixed-size font should be down-scaled
633     if( font.mIsFixedSizeBitmap &&
634         ( maxFixedSize > 0 ) &&
635         ( font.mFixedHeightPixels > maxFixedSize ) )
636     {
637       float scaleFactor = static_cast<float>(maxFixedSize) / static_cast<float>(font.mFixedHeightPixels);
638
639       metrics.ascender           *= scaleFactor;
640       metrics.descender          *= scaleFactor;
641       metrics.height             *= scaleFactor;
642       metrics.underlinePosition  *= scaleFactor;
643       metrics.underlineThickness *= scaleFactor;
644     }
645   }
646   else
647   {
648     DALI_LOG_ERROR( "Invalid font ID %d\n", fontId );
649   }
650 }
651
652 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
653                                               Character charcode )
654 {
655   GlyphIndex index( 0 );
656
657   if( fontId > 0 &&
658       fontId-1 < mFontCache.size() )
659   {
660     FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
661
662     index = FT_Get_Char_Index( ftFace, charcode );
663   }
664
665   return index;
666 }
667
668 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
669                                           uint32_t size,
670                                           bool horizontal,
671                                           int maxFixedSize )
672 {
673   bool success( true );
674
675   for( unsigned int i=0; i<size; ++i )
676   {
677     FontId fontId = array[i].fontId;
678
679     if( fontId > 0 &&
680         fontId-1 < mFontCache.size() )
681     {
682       const CacheItem& font = mFontCache[fontId-1];
683
684       FT_Face ftFace = font.mFreeTypeFace;
685
686 #ifdef FREETYPE_BITMAP_SUPPORT
687       // Check to see if we should be loading a Fixed Size bitmap?
688       if ( font.mIsFixedSizeBitmap )
689       {
690         int error = FT_Load_Glyph( ftFace, array[i].index, FT_LOAD_COLOR );
691         if ( FT_Err_Ok == error )
692         {
693           array[i].width = font.mFixedWidthPixels;
694           array[i].height = font.mFixedHeightPixels;
695           array[i].advance = font.mFixedWidthPixels;
696           array[i].xBearing = 0.0f;
697           array[i].yBearing = font.mFixedHeightPixels;
698
699           // Adjust the metrics if the fixed-size font should be down-scaled
700           if( ( maxFixedSize > 0 ) &&
701               ( font.mFixedHeightPixels > maxFixedSize ) )
702           {
703             float scaleFactor = static_cast<float>(maxFixedSize) / static_cast<float>(font.mFixedHeightPixels);
704
705             array[i].width    *= scaleFactor;
706             array[i].height   *= scaleFactor;
707             array[i].advance  *= scaleFactor;
708             array[i].xBearing *= scaleFactor;
709             array[i].yBearing *= scaleFactor;
710
711             array[i].scaleFactor = scaleFactor;
712           }
713         }
714         else
715         {
716           DALI_LOG_ERROR( "FreeType Bitmap Load_Glyph error %d\n", error );
717           success = false;
718         }
719       }
720       else
721 #endif
722       {
723         int error = FT_Load_Glyph( ftFace, array[i].index, FT_LOAD_DEFAULT );
724
725         if( FT_Err_Ok == error )
726         {
727           array[i].width  = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
728           array[i].height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
729           if( horizontal )
730           {
731             array[i].xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
732             array[i].yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
733           }
734           else
735           {
736             array[i].xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
737             array[i].yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
738           }
739         }
740         else
741         {
742           success = false;
743         }
744       }
745     }
746     else
747     {
748       success = false;
749     }
750   }
751
752   return success;
753 }
754
755 BufferImage FontClient::Plugin::CreateBitmap( FontId fontId,
756                                               GlyphIndex glyphIndex )
757 {
758   BufferImage bitmap;
759
760   if( fontId > 0 &&
761       fontId-1 < mFontCache.size() )
762   {
763     FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
764
765     FT_Error error;
766
767 #ifdef FREETYPE_BITMAP_SUPPORT
768     // Check to see if this is fixed size bitmap
769     if ( mFontCache[fontId-1].mIsFixedSizeBitmap )
770     {
771       error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
772     }
773     else
774 #endif
775     {
776       error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_DEFAULT );
777     }
778     if( FT_Err_Ok == error )
779     {
780       FT_Glyph glyph;
781       error = FT_Get_Glyph( ftFace->glyph, &glyph );
782
783       // Convert to bitmap if necessary
784       if ( FT_Err_Ok == error )
785       {
786         if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
787         {
788           error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
789           if ( FT_Err_Ok == error )
790           {
791             FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph;
792             ConvertBitmap( bitmap, bitmapGlyph->bitmap );
793           }
794           else
795           {
796             DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error );
797           }
798         }
799         else
800         {
801           ConvertBitmap( bitmap, ftFace->glyph->bitmap );
802         }
803
804         // Created FT_Glyph object must be released with FT_Done_Glyph
805         FT_Done_Glyph( glyph );
806       }
807     }
808     else
809     {
810       DALI_LOG_ERROR( "FT_Load_Glyph Failed with error: %d\n", error );
811     }
812   }
813
814   return bitmap;
815 }
816
817 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 pointSize )
818 {
819   // First look into the cache if there is an ellipsis glyph for the requested point size.
820   for( Vector<EllipsisItem>::ConstIterator it = mEllipsisCache.Begin(),
821          endIt = mEllipsisCache.End();
822        it != endIt;
823        ++it )
824   {
825     const EllipsisItem& item = *it;
826
827     if( fabsf( item.size - pointSize ) < Math::MACHINE_EPSILON_1000 )
828     {
829       // Use the glyph in the cache.
830       return item.glyph;
831     }
832   }
833
834   // No glyph has been found. Create one.
835   mEllipsisCache.PushBack( EllipsisItem() );
836   EllipsisItem& item = *( mEllipsisCache.End() - 1u );
837
838   item.size = pointSize;
839
840   // Find a font for the ellipsis glyph.
841   item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
842                                        pointSize,
843                                        false );
844
845   // Set the character index to access the glyph inside the font.
846   item.glyph.index = FT_Get_Char_Index( mFontCache[item.glyph.fontId-1].mFreeTypeFace,
847                                         ELLIPSIS_CHARACTER );
848
849   GetGlyphMetrics( &item.glyph, 1u, true, 0 );
850
851   return item.glyph;
852 }
853
854 void FontClient::Plugin::InitSystemFonts()
855 {
856   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::InitSystemFonts \n");
857
858   FcFontSet* fontSet = GetFcFontSet();
859
860   if( fontSet )
861   {
862     // Reserve some space to avoid reallocations.
863     mSystemFonts.reserve( fontSet->nfont );
864
865     for( int i = 0u; i < fontSet->nfont; ++i )
866     {
867       FcPattern* fontPattern = fontSet->fonts[i];
868
869       FontPath path;
870
871       // Skip fonts with no path
872       if( GetFcString( fontPattern, FC_FILE, path ) )
873       {
874         mSystemFonts.push_back( FontDescription() );
875         FontDescription& fontDescription = mSystemFonts.back();
876
877         fontDescription.path = path;
878
879         int width = 0;
880         int weight = 0;
881         int slant = 0;
882         GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
883         GetFcInt( fontPattern, FC_WIDTH, width );
884         GetFcInt( fontPattern, FC_WEIGHT, weight );
885         GetFcInt( fontPattern, FC_SLANT, slant );
886         fontDescription.width = IntToWidthType( width );
887         fontDescription.weight = IntToWeightType( weight );
888         fontDescription.slant = IntToSlantType( slant );
889         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::InitSystemFonts font family(%s)\n", fontDescription.family.c_str() );
890
891       }
892     }
893
894     FcFontSetDestroy( fontSet );
895   }
896 }
897
898 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription )
899 {
900   // create the cached font family lookup pattern
901   // a pattern holds a set of names, each name refers to a property of the font
902   FcPattern* fontFamilyPattern = FcPatternCreate();
903
904   // add a property to the pattern for the font family
905   FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
906
907   FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, FONT_WIDTH_TYPE_TO_INT[fontDescription.width] );
908   FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight] );
909   FcPatternAddInteger( fontFamilyPattern, FC_SLANT, FONT_SLANT_TYPE_TO_INT[fontDescription.slant] );
910
911   // Add a property of the pattern, to say we want to match TrueType fonts
912   FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
913
914   // modify the config, with the mFontFamilyPatterm
915   FcConfigSubstitute( NULL /* use default configure */, fontFamilyPattern, FcMatchPattern );
916
917   // provide default values for unspecified properties in the font pattern
918   // e.g. patterns without a specified style or weight are set to Medium
919   FcDefaultSubstitute( fontFamilyPattern );
920
921   return fontFamilyPattern;
922 }
923
924 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
925 {
926   // create a new pattern.
927   // a pattern holds a set of names, each name refers to a property of the font
928   FcPattern* pattern = FcPatternCreate();
929
930   // create an object set used to define which properties are to be returned in the patterns from FcFontList.
931   FcObjectSet* objectSet = FcObjectSetCreate();
932
933   // build an object set from a list of property names
934   FcObjectSetAdd( objectSet, FC_FILE );
935   FcObjectSetAdd( objectSet, FC_FAMILY );
936   FcObjectSetAdd( objectSet, FC_WIDTH );
937   FcObjectSetAdd( objectSet, FC_WEIGHT );
938   FcObjectSetAdd( objectSet, FC_SLANT );
939
940   // get a list of fonts
941   // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
942   FcFontSet* fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
943
944   // clear up the object set
945   if( objectSet )
946   {
947     FcObjectSetDestroy( objectSet );
948   }
949   // clear up the pattern
950   if( pattern )
951   {
952     FcPatternDestroy( pattern );
953   }
954
955   return fontset;
956 }
957
958 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
959                                       const char* const n,
960                                       std::string& string )
961 {
962   FcChar8* file = NULL;
963   const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
964
965   if( FcResultMatch == retVal )
966   {
967     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
968     string.assign( reinterpret_cast<const char*>( file ) );
969
970     return true;
971   }
972
973   return false;
974 }
975
976 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
977 {
978   const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
979
980   if( FcResultMatch == retVal )
981   {
982     return true;
983   }
984
985   return false;
986 }
987
988 FontId FontClient::Plugin::CreateFont( const FontPath& path,
989                                        PointSize26Dot6 pointSize,
990                                        FaceIndex faceIndex,
991                                        bool cacheDescription )
992 {
993   FontId id( 0 );
994
995   // Create & cache new font face
996   FT_Face ftFace;
997   int error = FT_New_Face( mFreeTypeLibrary,
998                            path.c_str(),
999                            0,
1000                            &ftFace );
1001
1002   if( FT_Err_Ok == error )
1003   {
1004     // Check to see if the font contains fixed sizes?
1005     if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1006     {
1007       // Ensure this size is available
1008       for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1009       {
1010         if ( static_cast<FT_Pos>(pointSize) == ftFace->available_sizes[ i ].size )
1011         {
1012           // Tell Freetype to use this size
1013           error = FT_Select_Size( ftFace, i );
1014           if ( FT_Err_Ok != error )
1015           {
1016             DALI_LOG_ERROR( "FreeType Select_Size error: %d\n", error );
1017           }
1018           else
1019           {
1020             float fixedWidth  = static_cast< float >( ftFace->available_sizes[ i ].width );
1021             float fixedHeight = static_cast< float >( ftFace->available_sizes[ i ].height );
1022
1023             // Indicate that the font is a fixed sized bitmap
1024             FontMetrics metrics( fixedHeight, // The ascender in pixels.
1025                                  0.0f,
1026                                  fixedHeight, // The height in pixels.
1027                                  0.0f,
1028                                  0.0f );
1029
1030             mFontCache.push_back( CacheItem( ftFace, path, pointSize, faceIndex, metrics, fixedWidth, fixedHeight ) );
1031             id = mFontCache.size();
1032
1033             if( cacheDescription )
1034             {
1035               FontDescription description;
1036               description.path = path;
1037               description.family = FontFamily( ftFace->family_name );
1038
1039               // Note FreeType doesn't give too much info to build a proper font style.
1040               if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
1041               {
1042                 description.slant = FontSlant::ITALIC;
1043               }
1044               if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
1045               {
1046                 description.weight = FontWeight::BOLD;
1047               }
1048
1049               mFontDescriptionCache.push_back( description );
1050             }
1051             return id;
1052           }
1053         }
1054       }
1055
1056       // Can't find this size
1057       std::stringstream sizes;
1058       for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1059       {
1060         if ( i )
1061         {
1062           sizes << ", ";
1063         }
1064         sizes << ftFace->available_sizes[ i ].size;
1065       }
1066       DALI_LOG_ERROR( "FreeType Font: %s, does not contain Bitmaps of size: %d. Available sizes are: %s\n",
1067                        path.c_str(), pointSize, sizes.str().c_str() );
1068     }
1069     else
1070     {
1071       error = FT_Set_Char_Size( ftFace,
1072                               0,
1073                               pointSize,
1074                               mDpiHorizontal,
1075                               mDpiVertical );
1076
1077       if( FT_Err_Ok == error )
1078       {
1079
1080         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1081
1082         FontMetrics metrics( static_cast< float >( ftMetrics.ascender  ) * FROM_266,
1083                              static_cast< float >( ftMetrics.descender ) * FROM_266,
1084                              static_cast< float >( ftMetrics.height    ) * FROM_266,
1085                              static_cast< float >( ftFace->underline_position ) * FROM_266,
1086                              static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
1087
1088         mFontCache.push_back( CacheItem( ftFace, path, pointSize, faceIndex, metrics ) );
1089         id = mFontCache.size();
1090
1091         if( cacheDescription )
1092         {
1093           FontDescription description;
1094           description.path = path;
1095           description.family = FontFamily( ftFace->family_name );
1096
1097           // Note FreeType doesn't give too much info to build a proper font style.
1098           if( ftFace->style_flags | FT_STYLE_FLAG_ITALIC )
1099           {
1100             description.slant = FontSlant::ITALIC;
1101           }
1102           if( ftFace->style_flags | FT_STYLE_FLAG_BOLD )
1103           {
1104             description.weight = FontWeight::BOLD;
1105           }
1106
1107           mFontDescriptionCache.push_back( description );
1108         }
1109       }
1110       else
1111       {
1112         DALI_LOG_ERROR( "FreeType Set_Char_Size error: %d for pointSize %d\n", error, pointSize );
1113       }
1114     }
1115   }
1116   else
1117   {
1118     DALI_LOG_ERROR( "FreeType New_Face error: %d for %s\n", error, path.c_str() );
1119   }
1120
1121   return id;
1122 }
1123
1124 void FontClient::Plugin::ConvertBitmap( BufferImage& destBitmap,
1125                                         FT_Bitmap srcBitmap )
1126 {
1127   if( srcBitmap.width*srcBitmap.rows > 0 )
1128   {
1129     switch( srcBitmap.pixel_mode )
1130     {
1131       case FT_PIXEL_MODE_GRAY:
1132       {
1133         if( srcBitmap.pitch == static_cast< int >( srcBitmap.width ) )
1134         {
1135           destBitmap = BufferImage::New( srcBitmap.width, srcBitmap.rows, Pixel::L8 );
1136
1137           PixelBuffer* destBuffer = destBitmap.GetBuffer();
1138           if( destBuffer )
1139           {
1140             memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows );
1141           }
1142           else
1143           {
1144             DALI_LOG_ERROR( "GetBuffer returns null\n" );
1145           }
1146         }
1147         break;
1148       }
1149
1150 #ifdef FREETYPE_BITMAP_SUPPORT
1151       case FT_PIXEL_MODE_BGRA:
1152       {
1153         if ( srcBitmap.pitch == static_cast< int >( srcBitmap.width << 2 ) )
1154         {
1155           destBitmap = BufferImage::New( srcBitmap.width, srcBitmap.rows, Pixel::BGRA8888 );
1156
1157           PixelBuffer* destBuffer = destBitmap.GetBuffer();
1158           if( destBuffer )
1159           {
1160             memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows*4 );
1161           }
1162           else
1163           {
1164             DALI_LOG_ERROR( "GetBuffer returns null\n" );
1165           }
1166         }
1167         break;
1168       }
1169 #endif
1170       default:
1171       {
1172         DALI_LOG_ERROR( "FontClient Unable to create Bitmap of this PixelType\n" );
1173         break;
1174       }
1175     }
1176   }
1177 }
1178
1179 bool FontClient::Plugin::FindFont( const FontPath& path,
1180                                    PointSize26Dot6 pointSize,
1181                                    FaceIndex faceIndex,
1182                                    FontId& fontId ) const
1183 {
1184   fontId = 0u;
1185   for( std::vector<CacheItem>::const_iterator it = mFontCache.begin(),
1186          endIt = mFontCache.end();
1187        it != endIt;
1188        ++it, ++fontId )
1189   {
1190     const CacheItem& cacheItem = *it;
1191
1192     if( cacheItem.mPointSize == pointSize &&
1193         cacheItem.mFaceIndex == faceIndex &&
1194         cacheItem.mPath == path )
1195     {
1196       ++fontId;
1197       return true;
1198     }
1199   }
1200
1201   return false;
1202 }
1203
1204 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
1205                                             FontDescriptionId& validatedFontId )
1206 {
1207   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont fontDescription family(%s)\n", fontDescription.family.c_str() );
1208
1209   validatedFontId = 0u;
1210
1211   for( std::vector<FontDescriptionCacheItem>::const_iterator it = mValidatedFontCache.begin(),
1212          endIt = mValidatedFontCache.end();
1213        it != endIt;
1214        ++it )
1215   {
1216     const FontDescriptionCacheItem& item = *it;
1217
1218     if( !fontDescription.family.empty() &&
1219         ( fontDescription.family == item.fontDescription.family ) &&
1220         ( fontDescription.width == item.fontDescription.width ) &&
1221         ( fontDescription.weight == item.fontDescription.weight ) &&
1222         ( fontDescription.slant == item.fontDescription.slant ) )
1223     {
1224       validatedFontId = item.index;
1225
1226       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont validated font family(%s) font id (%u) \n", fontDescription.family.c_str(), validatedFontId );
1227
1228       return true;
1229     }
1230   }
1231
1232   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont NOT VALIDATED return false\n" );
1233
1234   return false;
1235 }
1236
1237 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
1238                                    PointSize26Dot6 pointSize,
1239                                    FontId& fontId )
1240 {
1241   fontId = 0u;
1242
1243   for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
1244          endIt = mFontIdCache.end();
1245        it != endIt;
1246        ++it )
1247   {
1248     const FontIdCacheItem& item = *it;
1249
1250     if( ( validatedFontId == item.validatedFontId ) &&
1251         ( pointSize == item.pointSize ) )
1252     {
1253       fontId = item.fontId;
1254       return true;
1255     }
1256   }
1257
1258   return false;
1259 }
1260
1261 bool FontClient::Plugin::IsScalable( const FontPath& path )
1262 {
1263   FT_Face ftFace;
1264   int error = FT_New_Face( mFreeTypeLibrary,
1265                            path.c_str(),
1266                            0,
1267                            &ftFace );
1268   if( FT_Err_Ok != error )
1269   {
1270     DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() );
1271   }
1272   return ( ftFace->num_fixed_sizes == 0 );
1273 }
1274
1275 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
1276 {
1277   // Create a font pattern.
1278   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1279
1280   FcResult result = FcResultMatch;
1281
1282   // match the pattern
1283   FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
1284   bool isScalable = true;
1285
1286   if( match )
1287   {
1288     // Get the path to the font file name.
1289     FontPath path;
1290     GetFcString( match, FC_FILE, path );
1291     isScalable = IsScalable( path );
1292   }
1293   else
1294   {
1295     DALI_LOG_ERROR( "FreeType Cannot check font: %s %d %d %d\n",
1296                     fontDescription.family.c_str(),
1297                     fontDescription.width,
1298                     fontDescription.weight,
1299                     fontDescription.slant );
1300   }
1301   FcPatternDestroy( fontFamilyPattern );
1302   FcPatternDestroy( match );
1303   return isScalable;
1304 }
1305
1306 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
1307 {
1308   // Empty the caller container
1309   sizes.Clear();
1310
1311   FT_Face ftFace;
1312   int error = FT_New_Face( mFreeTypeLibrary,
1313                            path.c_str(),
1314                            0,
1315                            &ftFace );
1316   if( FT_Err_Ok != error )
1317   {
1318     DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() );
1319   }
1320
1321   // Fetch the number of fixed sizes available
1322   if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1323   {
1324     for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1325     {
1326       sizes.PushBack( ftFace->available_sizes[ i ].size );
1327     }
1328   }
1329 }
1330
1331 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
1332                                         Vector< PointSize26Dot6 >& sizes )
1333 {
1334   // Create a font pattern.
1335   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1336
1337   FcResult result = FcResultMatch;
1338
1339   // match the pattern
1340   FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
1341
1342   if( match )
1343   {
1344     // Get the path to the font file name.
1345     FontPath path;
1346     GetFcString( match, FC_FILE, path );
1347     GetFixedSizes( path, sizes );
1348   }
1349   else
1350   {
1351     DALI_LOG_ERROR( "FreeType Cannot check font: %s %d %d %d\n",
1352                     fontDescription.family.c_str(),
1353                     fontDescription.width,
1354                     fontDescription.weight,
1355                     fontDescription.slant );
1356   }
1357   FcPatternDestroy( match );
1358   FcPatternDestroy( fontFamilyPattern );
1359 }
1360
1361 } // namespace Internal
1362
1363 } // namespace TextAbstraction
1364
1365 } // namespace Dali