113931a8885435408de1ceb055253037ee8710c4
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / font-client-plugin-impl.cpp
1 /*
2  * Copyright (c) 2019 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/text-abstraction/font-client-plugin-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/font-list.h>
23
24 #include <dali/public-api/common/dali-vector.h>
25 #include <dali/public-api/common/vector-wrapper.h>
26 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/platform-abstraction.h>
28 #include <dali/internal/text/text-abstraction/font-client-helper.h>
29 #include <dali/internal/imaging/common/image-operations.h>
30 #include <dali/internal/adaptor/common/adaptor-impl.h>
31 #include <dali/devel-api/adaptor-framework/image-loading.h>
32
33 // EXTERNAL INCLUDES
34 #include <fontconfig/fontconfig.h>
35
36 namespace
37 {
38
39 #if defined(DEBUG_ENABLED)
40 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
41 #endif
42
43 /**
44  * Conversion from Fractional26.6 to float
45  */
46 const float FROM_266 = 1.0f / 64.0f;
47 const float POINTS_PER_INCH = 72.f;
48
49 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
50 const int DEFAULT_FONT_WIDTH  = 100; // normal
51 const int DEFAULT_FONT_WEIGHT =  80; // normal
52 const int DEFAULT_FONT_SLANT  =   0; // normal
53
54 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
55
56 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
57
58 // NONE            -1  --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
59 // ULTRA_CONDENSED 50
60 // EXTRA_CONDENSED 63
61 // CONDENSED       75
62 // SEMI_CONDENSED  87
63 // NORMAL         100
64 // SEMI_EXPANDED  113
65 // EXPANDED       125
66 // EXTRA_EXPANDED 150
67 // ULTRA_EXPANDED 200
68 const int FONT_WIDTH_TYPE_TO_INT[] = { -1, 50, 63, 75, 87, 100, 113, 125, 150, 200 };
69 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / sizeof( int );
70
71 // NONE                       -1  --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
72 // THIN                        0
73 // ULTRA_LIGHT, EXTRA_LIGHT   40
74 // LIGHT                      50
75 // DEMI_LIGHT, SEMI_LIGHT     55
76 // BOOK                       75
77 // NORMAL, REGULAR            80
78 // MEDIUM                    100
79 // DEMI_BOLD, SEMI_BOLD      180
80 // BOLD                      200
81 // ULTRA_BOLD, EXTRA_BOLD    205
82 // BLACK, HEAVY, EXTRA_BLACK 210
83 const int FONT_WEIGHT_TYPE_TO_INT[] = { -1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210 };
84 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof( FONT_WEIGHT_TYPE_TO_INT ) / sizeof( int );
85
86 // NONE             -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
87 // NORMAL, ROMAN     0
88 // ITALIC          100
89 // OBLIQUE         110
90 const int FONT_SLANT_TYPE_TO_INT[] = { -1, 0, 100, 110 };
91 const unsigned int NUM_FONT_SLANT_TYPE = sizeof( FONT_SLANT_TYPE_TO_INT ) / sizeof( int );
92
93 } // namespace
94
95 using Dali::Vector;
96 using namespace std;
97
98 namespace Dali
99 {
100
101 namespace TextAbstraction
102 {
103
104 namespace Internal
105 {
106
107 /**
108  * @brief Returns the FontWidth's enum index for the given width value.
109  *
110  * @param[in] width The width value.
111  *
112  * @return The FontWidth's enum index.
113  */
114 FontWidth::Type IntToWidthType( int width )
115 {
116   return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
117 }
118
119 /**
120  * @brief Returns the FontWeight's enum index for the given weight value.
121  *
122  * @param[in] weight The weight value.
123  *
124  * @return The FontWeight's enum index.
125  */
126 FontWeight::Type IntToWeightType( int weight )
127 {
128   return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
129 }
130
131 /**
132  * @brief Returns the FontSlant's enum index for the given slant value.
133  *
134  * @param[in] slant The slant value.
135  *
136  * @return The FontSlant's enum index.
137  */
138 FontSlant::Type IntToSlantType( int slant )
139 {
140   return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
141 }
142
143 /**
144  * @brief Free the resources allocated by the FcCharSet objects.
145  *
146  * @param[in] characterSets The vector of character sets.
147  */
148 void DestroyCharacterSets( CharacterSetList& characterSets )
149 {
150   for( auto& item : characterSets )
151   {
152     if( item )
153     {
154       FcCharSetDestroy( item );
155     }
156   }
157 }
158
159 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets )
160 : fontDescription{ std::move( font ) },
161   fallbackFonts{ fallbackFonts },
162   characterSets{ characterSets }
163 {
164 }
165
166 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
167                                                                         FontDescriptionId index )
168 : fontDescription{ fontDescription },
169   index{ index }
170 {
171 }
172
173 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( FontDescription&& fontDescription,
174                                                                         FontDescriptionId index )
175 : fontDescription{ std::move( fontDescription ) },
176   index{ index }
177 {
178 }
179
180 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem( FontDescriptionId validatedFontId,
181                                                                                 PointSize26Dot6 requestedPointSize,
182                                                                                 FontId fontId )
183 : validatedFontId( validatedFontId ),
184   requestedPointSize( requestedPointSize ),
185   fontId( fontId )
186 {
187 }
188
189 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
190                                                           const FontPath& path,
191                                                           PointSize26Dot6 requestedPointSize,
192                                                           FaceIndex face,
193                                                           const FontMetrics& metrics )
194 : mFreeTypeFace( ftFace ),
195   mPath( path ),
196   mRequestedPointSize( requestedPointSize ),
197   mFaceIndex( face ),
198   mMetrics( metrics ),
199   mCharacterSet( nullptr ),
200   mFixedSizeIndex( 0 ),
201   mFixedWidthPixels( 0.f ),
202   mFixedHeightPixels( 0.f ),
203   mVectorFontId( 0u ),
204   mFontId( 0u ),
205   mIsFixedSizeBitmap( false ),
206   mHasColorTables( false )
207 {
208 }
209
210 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
211                                                           const FontPath& path,
212                                                           PointSize26Dot6 requestedPointSize,
213                                                           FaceIndex face,
214                                                           const FontMetrics& metrics,
215                                                           int fixedSizeIndex,
216                                                           float fixedWidth,
217                                                           float fixedHeight,
218                                                           bool hasColorTables )
219 : mFreeTypeFace( ftFace ),
220   mPath( path ),
221   mRequestedPointSize( requestedPointSize ),
222   mFaceIndex( face ),
223   mMetrics( metrics ),
224   mCharacterSet( nullptr ),
225   mFixedSizeIndex( fixedSizeIndex ),
226   mFixedWidthPixels( fixedWidth ),
227   mFixedHeightPixels( fixedHeight ),
228   mVectorFontId( 0u ),
229   mFontId( 0u ),
230   mIsFixedSizeBitmap( true ),
231   mHasColorTables( hasColorTables )
232 {
233 }
234
235 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
236                             unsigned int verticalDpi )
237 : mFreeTypeLibrary( nullptr ),
238   mDpiHorizontal( horizontalDpi ),
239   mDpiVertical( verticalDpi ),
240   mDefaultFontDescription(),
241   mSystemFonts(),
242   mDefaultFonts(),
243   mFontIdCache(),
244   mFontFaceCache(),
245   mValidatedFontCache(),
246   mFontDescriptionCache(),
247   mCharacterSetCache(),
248   mFontDescriptionSizeCache(),
249   mVectorFontCache( nullptr ),
250   mEllipsisCache(),
251   mEmbeddedItemCache(),
252   mDefaultFontDescriptionCached( false )
253 {
254   int error = FT_Init_FreeType( &mFreeTypeLibrary );
255   if( FT_Err_Ok != error )
256   {
257     DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Init error: %d\n", error );
258   }
259
260 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
261   mVectorFontCache = new VectorFontCache( mFreeTypeLibrary );
262 #endif
263 }
264
265 FontClient::Plugin::~Plugin()
266 {
267   ClearFallbackCache( mFallbackCache );
268
269   // Free the resources allocated by the FcCharSet objects.
270   DestroyCharacterSets( mDefaultFontCharacterSets );
271   DestroyCharacterSets( mCharacterSetCache );
272   ClearCharacterSetFromFontFaceCache();
273
274 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
275   delete mVectorFontCache;
276 #endif
277   FT_Done_FreeType( mFreeTypeLibrary );
278 }
279
280 void FontClient::Plugin::ClearCache()
281 {
282   mDefaultFontDescription = FontDescription();
283
284   mSystemFonts.clear();
285   mDefaultFonts.clear();
286
287   DestroyCharacterSets( mDefaultFontCharacterSets );
288   mDefaultFontCharacterSets.Clear();
289
290   ClearFallbackCache( mFallbackCache );
291   mFallbackCache.clear();
292
293   mFontIdCache.Clear();
294
295   ClearCharacterSetFromFontFaceCache();
296   mFontFaceCache.clear();
297
298   mValidatedFontCache.clear();
299   mFontDescriptionCache.clear();
300
301   DestroyCharacterSets( mCharacterSetCache );
302   mCharacterSetCache.Clear();
303
304   mFontDescriptionSizeCache.clear();
305
306   mEllipsisCache.Clear();
307   mPixelBufferCache.clear();
308   mEmbeddedItemCache.Clear();
309   mBitmapFontCache.clear();
310
311   mDefaultFontDescriptionCached = false;
312 }
313
314 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
315                                  unsigned int verticalDpi )
316 {
317   mDpiHorizontal = horizontalDpi;
318   mDpiVertical = verticalDpi;
319 }
320
321 void FontClient::Plugin::ResetSystemDefaults()
322 {
323   mDefaultFontDescriptionCached = false;
324 }
325
326 void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList )
327 {
328   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n" );
329   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
330   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
331   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
332   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
333
334   fontList.clear();
335
336   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
337
338   FcResult result = FcResultMatch;
339
340   // Match the pattern.
341   FcFontSet* fontSet = FcFontSort( nullptr /* use default configure */,
342                                    fontFamilyPattern,
343                                    false /* don't trim */,
344                                    nullptr,
345                                    &result ); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
346
347   if( nullptr != fontSet )
348   {
349     DALI_LOG_INFO( gLogFilter, Debug::General, "  number of fonts found : [%d]\n", fontSet->nfont );
350     // Reserve some space to avoid reallocations.
351     fontList.reserve( fontSet->nfont );
352
353     for( int i = 0u; i < fontSet->nfont; ++i )
354     {
355       FcPattern* fontPattern = fontSet->fonts[i];
356
357       FontPath path;
358
359       // Skip fonts with no path
360       if( GetFcString( fontPattern, FC_FILE, path ) )
361       {
362         // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
363         FcCharSet* characterSet = nullptr;
364         FcPatternGetCharSet( fontPattern, FC_CHARSET, 0u, &characterSet );
365
366         // Increase the reference counter of the character set.
367         characterSetList.PushBack( FcCharSetCopy( characterSet ) );
368
369         fontList.push_back( FontDescription() );
370         FontDescription& newFontDescription = fontList.back();
371
372         newFontDescription.path = std::move( path );
373
374         int width = 0;
375         int weight = 0;
376         int slant = 0;
377         GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
378         GetFcInt( fontPattern, FC_WIDTH, width );
379         GetFcInt( fontPattern, FC_WEIGHT, weight );
380         GetFcInt( fontPattern, FC_SLANT, slant );
381         newFontDescription.width = IntToWidthType( width );
382         newFontDescription.weight = IntToWeightType( weight );
383         newFontDescription.slant = IntToSlantType( slant );
384
385         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", newFontDescription.family.c_str() );
386         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", newFontDescription.path.c_str() );
387         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[newFontDescription.width] );
388         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[newFontDescription.weight] );
389         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant] );
390       }
391     }
392
393     // Destroys the font set created by FcFontSort.
394     FcFontSetDestroy( fontSet );
395   }
396   else
397   {
398     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  No fonts found.\n" );
399   }
400
401   // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
402   FcPatternDestroy( fontFamilyPattern );
403
404   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n" );
405 }
406
407 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
408 {
409   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n" );
410
411   if( mDefaultFonts.empty() )
412   {
413     FontDescription fontDescription;
414     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;  // todo This could be set to the Platform font
415     fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
416     fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
417     fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
418     SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
419   }
420
421   defaultFonts = mDefaultFonts;
422
423   DALI_LOG_INFO( gLogFilter, Debug::General, "  number of default fonts : [%d]\n", mDefaultFonts.size() );
424   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n" );
425 }
426
427 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
428 {
429   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
430
431   if( !mDefaultFontDescriptionCached )
432   {
433     // Clear any font config stored info in the caches.
434
435     // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
436     DestroyCharacterSets( mDefaultFontCharacterSets );
437     DestroyCharacterSets( mCharacterSetCache );
438     mDefaultFontCharacterSets.Clear();
439     mCharacterSetCache.Clear();
440
441     for( auto& item : mFallbackCache )
442     {
443       // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
444       DestroyCharacterSets( *item.characterSets );
445
446       delete item.characterSets;
447       item.characterSets = nullptr;
448     }
449
450     // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
451     ClearCharacterSetFromFontFaceCache();
452
453     // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
454     FcInitReinitialize();
455
456     FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
457
458     if( nullptr != matchPattern )
459     {
460       FcConfigSubstitute( nullptr, matchPattern, FcMatchPattern );
461       FcDefaultSubstitute( matchPattern );
462
463       FcCharSet* characterSet = nullptr;
464       MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription, &characterSet );
465       // Decrease the reference counter of the character set as it's not stored.
466       FcCharSetDestroy( characterSet );
467
468       // Destroys the pattern created.
469       FcPatternDestroy( matchPattern );
470     }
471
472     // Create again the character sets as they are not valid after FcInitReinitialize()
473
474     for( const auto& description : mDefaultFonts )
475     {
476       mDefaultFontCharacterSets.PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
477     }
478
479     for( const auto& description : mFontDescriptionCache )
480     {
481       mCharacterSetCache.PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
482     }
483
484     for( auto& item : mFallbackCache )
485     {
486       if( nullptr != item.fallbackFonts )
487       {
488         if( nullptr == item.characterSets )
489         {
490           item.characterSets = new CharacterSetList;
491         }
492
493         for( const auto& description : *( item.fallbackFonts ) )
494         {
495           item.characterSets->PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
496         }
497       }
498     }
499
500     mDefaultFontDescriptionCached = true;
501   }
502
503   fontDescription.path   = mDefaultFontDescription.path;
504   fontDescription.family = mDefaultFontDescription.family;
505   fontDescription.width  = mDefaultFontDescription.width;
506   fontDescription.weight = mDefaultFontDescription.weight;
507   fontDescription.slant  = mDefaultFontDescription.slant;
508
509   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
510   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
511   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
512   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
513   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
514   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
515 }
516
517 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
518 {
519   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
520
521   if( mSystemFonts.empty() )
522   {
523     InitSystemFonts();
524   }
525
526   systemFonts = mSystemFonts;
527   DALI_LOG_INFO( gLogFilter, Debug::General, "  number of system fonts : [%d]\n", mSystemFonts.size() );
528   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
529 }
530
531 void FontClient::Plugin::GetDescription( FontId id,
532                                          FontDescription& fontDescription ) const
533 {
534   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
535   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
536   const FontId index = id - 1u;
537
538   if( ( id > 0u ) && ( index < mFontIdCache.Count() ) )
539   {
540     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
541     switch( fontIdCacheItem.type )
542     {
543       case FontDescription::FACE_FONT:
544       {
545         for( const auto& item : mFontDescriptionSizeCache )
546         {
547           if( item.fontId == fontIdCacheItem.id )
548           {
549             fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId - 1u );
550
551             DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
552             DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
553             DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
554             DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
555             DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
556             DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
557             return;
558           }
559         }
560         break;
561       }
562       case FontDescription::BITMAP_FONT:
563       {
564         fontDescription.type = FontDescription::BITMAP_FONT;
565         fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
566         break;
567       }
568       default:
569       {
570         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
571         fontDescription.type = FontDescription::INVALID;
572         fontDescription.family.clear();
573       }
574     }
575   }
576
577   DALI_LOG_INFO( gLogFilter, Debug::General, "  No description found for the font ID %d\n", id );
578   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
579 }
580
581 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
582 {
583   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
584   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
585   const FontId index = id - 1u;
586
587   if( ( id > 0u ) &&
588       ( index < mFontIdCache.Count() ) )
589   {
590     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
591
592     switch( fontIdCacheItem.type )
593     {
594       case FontDescription::FACE_FONT:
595       {
596         DALI_LOG_INFO( gLogFilter, Debug::General, "  point size : %d\n", ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize );
597         DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
598         return ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize;
599       }
600       case FontDescription::BITMAP_FONT:
601       {
602         return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
603       }
604       default:
605       {
606         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
607       }
608     }
609   }
610   else
611   {
612     DALI_LOG_INFO( gLogFilter, Debug::General, "  Invalid font ID %d\n", id );
613   }
614
615   DALI_LOG_INFO( gLogFilter, Debug::General, "  default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE );
616   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
617   return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
618 }
619
620 bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character character )
621 {
622   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
623   DALI_LOG_INFO( gLogFilter, Debug::General, "    font id : %d\n", fontId );
624   DALI_LOG_INFO( gLogFilter, Debug::General, "  character : %p\n", character );
625
626   if( ( fontId < 1u ) || ( fontId > mFontIdCache.Count() ) )
627   {
628     DALI_LOG_INFO( gLogFilter, Debug::General, "  Invalid font id. Number of items in the cache: %d\n",mFontIdCache.Count());
629     DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
630     return false;
631   }
632
633   --fontId;
634
635   bool isSupported = false;
636
637   const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
638
639   switch( fontIdCacheItem.type )
640   {
641     case FontDescription::FACE_FONT:
642     {
643       if( fontIdCacheItem.id < mFontFaceCache.size() )
644       {
645         FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
646
647         if( nullptr == cacheItem.mCharacterSet )
648         {
649           // Create again the character set.
650           // It can be null if the ResetSystemDefaults() method has been called.
651
652           FontDescription description;
653           description.path = cacheItem.mPath;
654           description.family = std::move( FontFamily( cacheItem.mFreeTypeFace->family_name ) );
655           description.weight = FontWeight::NONE;
656           description.width = FontWidth::NONE;
657           description.slant = FontSlant::NONE;
658
659           // Note FreeType doesn't give too much info to build a proper font style.
660           if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC )
661           {
662             description.slant = FontSlant::ITALIC;
663           }
664           if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD )
665           {
666             description.weight = FontWeight::BOLD;
667           }
668
669           cacheItem.mCharacterSet = FcCharSetCopy( CreateCharacterSetFromDescription( description ) );
670         }
671
672         isSupported = FcCharSetHasChar( cacheItem.mCharacterSet, character );
673       }
674       break;
675     }
676     case FontDescription::BITMAP_FONT:
677     {
678       const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
679
680       for( const auto& glyph : bitmapFont.glyphs )
681       {
682         if( glyph.utf32 == character )
683         {
684           isSupported = true;
685           break;
686         }
687       }
688       break;
689     }
690     default:
691     {
692       DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
693     }
694   }
695
696   DALI_LOG_INFO( gLogFilter, Debug::General, "  is supported : %s\n", (isSupported ? "true" : "false") );
697   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
698   return isSupported;
699 }
700
701 FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
702                                                  const CharacterSetList& characterSetList,
703                                                  Character character,
704                                                  PointSize26Dot6 requestedPointSize,
705                                                  bool preferColor )
706 {
707   DALI_ASSERT_DEBUG( ( fontList.size() == characterSetList.Count() ) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets." );
708
709   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n" );
710   DALI_LOG_INFO( gLogFilter, Debug::General, "           character : %p\n", character );
711   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
712   DALI_LOG_INFO( gLogFilter, Debug::General, "         preferColor : %s\n", ( preferColor ? "true" : "false" ) );
713
714   FontId fontId = 0u;
715   bool foundColor = false;
716
717   DALI_LOG_INFO( gLogFilter, Debug::General, "  number of fonts : %d\n", fontList.size() );
718
719   // Traverse the list of fonts.
720   // Check for each font if supports the character.
721   for( unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index )
722   {
723     const FontDescription& description = fontList[index];
724     const FcCharSet* const characterSet = characterSetList[index];
725
726     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", description.family.c_str() );
727     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", description.path.c_str() );
728     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[description.width] );
729     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[description.weight] );
730     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[description.slant] );
731
732     bool foundInRanges = false;
733     if( nullptr != characterSet )
734     {
735       foundInRanges = FcCharSetHasChar( characterSet, character );
736     }
737
738     if( foundInRanges )
739     {
740       fontId = GetFontId( description,
741                           requestedPointSize,
742                           0u );
743
744       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "     font id : %d\n", fontId );
745
746       if( preferColor )
747       {
748         if( ( fontId > 0 ) &&
749             ( fontId - 1u < mFontIdCache.Count() ) )
750         {
751           const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
752
753           foundColor = item.mHasColorTables;
754         }
755
756         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  foundColor : %s\n", ( foundColor ? "true" : "false" ) );
757       }
758
759       // Keep going unless we prefer a different (color) font.
760       if( !preferColor || foundColor )
761       {
762         break;
763       }
764     }
765   }
766
767   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
768   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n" );
769   return fontId;
770 }
771
772 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
773                                             PointSize26Dot6 requestedPointSize,
774                                             bool preferColor )
775 {
776   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n" );
777   DALI_LOG_INFO( gLogFilter, Debug::General, "           character : %p\n", charcode );
778   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
779   DALI_LOG_INFO( gLogFilter, Debug::General, "         preferColor : %s\n", ( preferColor ? "true" : "false" ) );
780
781   FontId fontId(0);
782
783   // Create the list of default fonts if it has not been created.
784   if( mDefaultFonts.empty() )
785   {
786     FontDescription fontDescription;
787     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
788     fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
789     fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
790     fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
791
792     SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
793   }
794   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of default fonts : %d\n", mDefaultFonts.size() );
795
796
797   // Traverse the list of default fonts.
798   // Check for each default font if supports the character.
799   fontId = FindFontForCharacter( mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor );
800
801   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
802   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n" );
803
804   return fontId;
805 }
806
807 FontId FontClient::Plugin::FindFallbackFont( Character charcode,
808                                              const FontDescription& preferredFontDescription,
809                                              PointSize26Dot6 requestedPointSize,
810                                              bool preferColor )
811 {
812   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n" );
813   DALI_LOG_INFO( gLogFilter, Debug::General, "           character : %p\n", charcode );
814   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
815   DALI_LOG_INFO( gLogFilter, Debug::General, "         preferColor : %s\n", ( preferColor ? "true" : "false" ) );
816
817   // The font id to be returned.
818   FontId fontId = 0u;
819
820   FontDescription fontDescription;
821
822   // Fill the font description with the preferred font description and complete with the defaults.
823   fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
824   fontDescription.weight = ( ( FontWeight::NONE == preferredFontDescription.weight ) ? IntToWeightType( DEFAULT_FONT_WEIGHT ) : preferredFontDescription.weight );
825   fontDescription.width = ( ( FontWidth::NONE == preferredFontDescription.width ) ? IntToWidthType( DEFAULT_FONT_WIDTH ) : preferredFontDescription.width );
826   fontDescription.slant = ( ( FontSlant::NONE == preferredFontDescription.slant ) ? IntToSlantType( DEFAULT_FONT_SLANT ) : preferredFontDescription.slant );
827
828   DALI_LOG_INFO( gLogFilter, Debug::General, "  preferredFontDescription --> fontDescription\n" );
829   DALI_LOG_INFO( gLogFilter, Debug::General, "  [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str() );
830   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight] );
831   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width] );
832   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant] );
833
834   // Check first if the font's description has been queried before.
835   FontList* fontList = nullptr;
836   CharacterSetList* characterSetList = nullptr;
837
838   if( !FindFallbackFontList( fontDescription, fontList, characterSetList ) )
839   {
840     fontList = new FontList;
841     characterSetList = new CharacterSetList;
842
843     SetFontList( fontDescription, *fontList, *characterSetList );
844
845     // Add the font-list to the cache.
846     mFallbackCache.push_back( std::move( FallbackCacheItem( std::move( fontDescription ), fontList, characterSetList ) ) );
847   }
848
849   if( fontList && characterSetList )
850   {
851     fontId = FindFontForCharacter( *fontList, *characterSetList, charcode, requestedPointSize, preferColor );
852   }
853
854   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
855   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
856   return fontId;
857 }
858
859 FontId FontClient::Plugin::GetFontId( const FontPath& path,
860                                       PointSize26Dot6 requestedPointSize,
861                                       FaceIndex faceIndex,
862                                       bool cacheDescription )
863 {
864   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
865   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
866   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
867
868   FontId id = 0u;
869
870   if( nullptr != mFreeTypeLibrary )
871   {
872     FontId foundId = 0u;
873     if( FindFont( path, requestedPointSize, faceIndex, foundId ) )
874     {
875       id = foundId;
876     }
877     else
878     {
879       id = CreateFont( path, requestedPointSize, faceIndex, cacheDescription );
880     }
881   }
882
883   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
884   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
885
886   return id;
887 }
888
889 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
890                                       PointSize26Dot6 requestedPointSize,
891                                       FaceIndex faceIndex )
892 {
893   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
894   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
895   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
896   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
897   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
898   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
899   DALI_LOG_INFO( gLogFilter, Debug::General, "   requestedPointSize : %d\n", requestedPointSize );
900
901   // This method uses three vectors which caches:
902   // * The bitmap font cache
903   // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
904   // * The path to font file names.
905   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
906
907   // 1) Checks if the font description matches with a previously loaded bitmap font.
908   //    Returns if a font is found.
909   // 2) Checks in the cache if the font's description has been validated before.
910   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
911   //    retrieves using font config a path to a font file name which matches with the
912   //    font's description. The path is stored in the cache.
913   //
914   // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
915   //    font file names' exists. If exists, it gets the font id. If it doesn't it calls
916   //    the GetFontId() method with the path to the font file name and the point size to
917   //    get the font id.
918
919   // The font id to be returned.
920   FontId fontId = 0u;
921
922   // Check first if the font description matches with a previously loaded bitmap font.
923   if( FindBitmapFont( fontDescription.family, fontId ) )
924   {
925     return fontId;
926   }
927
928   // Check if the font's description have been validated before.
929   FontDescriptionId validatedFontId = 0u;
930
931   if( !FindValidatedFont( fontDescription,
932                           validatedFontId ) )
933   {
934     // Use font config to validate the font's description.
935     ValidateFont( fontDescription,
936                   validatedFontId );
937   }
938
939   FontId fontFaceId = 0u;
940   // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
941   if( !FindFont( validatedFontId, requestedPointSize, fontFaceId ) )
942   {
943     // Retrieve the font file name path.
944     const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId - 1u );
945
946     // Retrieve the font id. Do not cache the description as it has been already cached.
947     fontId = GetFontId( description.path,
948                         requestedPointSize,
949                         faceIndex,
950                         false );
951
952     fontFaceId = mFontIdCache[fontId-1u].id;
953     mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( mCharacterSetCache[validatedFontId - 1u] );
954
955     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
956     mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
957                                                                        requestedPointSize,
958                                                                        fontFaceId ) );
959   }
960   else
961   {
962     fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
963   }
964
965   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
966   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
967
968   return fontId;
969 }
970
971 FontId FontClient::Plugin::GetFontId( const BitmapFont& bitmapFont )
972 {
973   for( const auto& item : mBitmapFontCache )
974   {
975     if( bitmapFont.name == item.font.name )
976     {
977       return item.id + 1u;
978     }
979   }
980
981   BitmapFontCacheItem bitmapFontCacheItem;
982   bitmapFontCacheItem.font = bitmapFont;
983   bitmapFontCacheItem.id = mFontIdCache.Count();
984
985   // Resize the vector with the pixel buffers.
986   bitmapFontCacheItem.pixelBuffers.resize( bitmapFont.glyphs.size() );
987
988   // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
989   unsigned int index = 0u;
990   for( auto& glyph : bitmapFontCacheItem.font.glyphs )
991   {
992     Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
993
994     if( EqualsZero( glyph.ascender ) && EqualsZero( glyph.descender ) )
995     {
996       // Load the glyph.
997       pixelBuffer = LoadImageFromFile( glyph.url );
998
999       if( pixelBuffer )
1000       {
1001         glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1002       }
1003     }
1004
1005     bitmapFontCacheItem.font.ascender = std::max( glyph.ascender, bitmapFontCacheItem.font.ascender );
1006     bitmapFontCacheItem.font.descender = std::min( glyph.descender, bitmapFontCacheItem.font.descender );
1007
1008     ++index;
1009   }
1010
1011   FontIdCacheItem fontIdCacheItem;
1012   fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1013   fontIdCacheItem.id = mBitmapFontCache.size();
1014
1015   mBitmapFontCache.push_back( std::move( bitmapFontCacheItem ) );
1016   mFontIdCache.PushBack( fontIdCacheItem );
1017
1018   return bitmapFontCacheItem.id + 1u;
1019 }
1020
1021 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
1022                                        FontDescriptionId& validatedFontId )
1023 {
1024   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n" );
1025   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1026   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1027   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1028   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1029   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1030
1031   // Create a font pattern.
1032   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1033
1034   FontDescription description;
1035
1036   FcCharSet* characterSet = nullptr;
1037   bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description, &characterSet );
1038   FcPatternDestroy( fontFamilyPattern );
1039
1040   if( matched && ( nullptr != characterSet ) )
1041   {
1042     // Add the path to the cache.
1043     description.type = FontDescription::FACE_FONT;
1044     mFontDescriptionCache.push_back( description );
1045
1046     // Set the index to the vector of paths to font file names.
1047     validatedFontId = mFontDescriptionCache.size();
1048
1049     DALI_LOG_INFO( gLogFilter, Debug::General, "  matched description; family : [%s]\n", description.family.c_str() );
1050     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                         path : [%s]\n", description.path.c_str() );
1051     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                        width : [%s]\n", FontWidth::Name[description.width] );
1052     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                       weight : [%s]\n", FontWeight::Name[description.weight] );
1053     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                        slant : [%s]\n\n", FontSlant::Name[description.slant] );
1054     DALI_LOG_INFO( gLogFilter, Debug::General, "  validatedFontId : %d\n", validatedFontId );
1055
1056     // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1057     mCharacterSetCache.PushBack( characterSet );
1058
1059     // Cache the index and the matched font's description.
1060     FontDescriptionCacheItem item( description,
1061                                    validatedFontId );
1062
1063     mValidatedFontCache.push_back( std::move( item ) );
1064
1065     if( ( fontDescription.family != description.family ) ||
1066         ( fontDescription.width != description.width )   ||
1067         ( fontDescription.weight != description.weight ) ||
1068         ( fontDescription.slant != description.slant ) )
1069     {
1070       // Cache the given font's description if it's different than the matched.
1071       FontDescriptionCacheItem item( fontDescription,
1072                                      validatedFontId );
1073
1074       mValidatedFontCache.push_back( std::move( item ) );
1075     }
1076   }
1077   else
1078   {
1079     DALI_LOG_INFO( gLogFilter, Debug::General, "  font validation failed for font [%s]\n", fontDescription.family.c_str() );
1080   }
1081
1082   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n" );
1083 }
1084
1085 void FontClient::Plugin::GetFontMetrics( FontId fontId,
1086                                          FontMetrics& metrics )
1087 {
1088   const FontId index = fontId - 1u;
1089
1090   if( ( fontId > 0 ) &&
1091       ( index < mFontIdCache.Count() ) )
1092   {
1093     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1094
1095     switch( fontIdCacheItem.type )
1096     {
1097       case FontDescription::FACE_FONT:
1098       {
1099         const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1100
1101         metrics = font.mMetrics;
1102
1103         // Adjust the metrics if the fixed-size font should be down-scaled
1104         if( font.mIsFixedSizeBitmap )
1105         {
1106           const float desiredFixedSize =  static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1107
1108           if( desiredFixedSize > 0.f )
1109           {
1110             const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1111
1112             metrics.ascender = metrics.ascender * scaleFactor;
1113             metrics.descender = metrics.descender * scaleFactor;
1114             metrics.height = metrics.height * scaleFactor;
1115             metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1116             metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1117           }
1118         }
1119         break;
1120       }
1121       case FontDescription::BITMAP_FONT:
1122       {
1123         const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1124
1125         metrics.ascender = bitmapFontCacheItem.font.ascender;
1126         metrics.descender = bitmapFontCacheItem.font.descender;
1127         metrics.height = metrics.ascender - metrics.descender;
1128         metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1129         metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1130         break;
1131       }
1132       default:
1133       {
1134         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1135       }
1136     }
1137   }
1138   else
1139   {
1140     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
1141   }
1142 }
1143
1144 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
1145                                               Character charcode )
1146 {
1147   GlyphIndex glyphIndex = 0u;
1148   const FontId index = fontId - 1u;
1149
1150   if( ( fontId > 0u ) &&
1151       ( index < mFontIdCache.Count() ) )
1152   {
1153     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1154
1155     if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1156     {
1157       FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1158
1159       glyphIndex = FT_Get_Char_Index( ftFace, charcode );
1160     }
1161   }
1162
1163   return glyphIndex;
1164 }
1165
1166 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
1167                                           uint32_t size,
1168                                           GlyphType type,
1169                                           bool horizontal )
1170 {
1171   if( VECTOR_GLYPH == type )
1172   {
1173     return GetVectorMetrics( array, size, horizontal );
1174   }
1175
1176   return GetBitmapMetrics( array, size, horizontal );
1177 }
1178
1179 bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
1180                                            uint32_t size,
1181                                            bool horizontal )
1182 {
1183   bool success( true );
1184
1185   for( unsigned int i=0; i<size; ++i )
1186   {
1187     GlyphInfo& glyph = array[i];
1188
1189     FontId index = glyph.fontId - 1u;
1190
1191     if( ( glyph.fontId > 0u ) &&
1192         ( index < mFontIdCache.Count() ) )
1193     {
1194       const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1195
1196       switch( fontIdCacheItem.type )
1197       {
1198         case FontDescription::FACE_FONT:
1199         {
1200           const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1201
1202           FT_Face ftFace = font.mFreeTypeFace;
1203
1204 #ifdef FREETYPE_BITMAP_SUPPORT
1205           // Check to see if we should be loading a Fixed Size bitmap?
1206           if( font.mIsFixedSizeBitmap )
1207           {
1208             FT_Select_Size( ftFace, font.mFixedSizeIndex ); ///< @todo: needs to be investigated why it's needed to select the size again.
1209             int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR );
1210             if ( FT_Err_Ok == error )
1211             {
1212               glyph.width = font.mFixedWidthPixels;
1213               glyph.height = font.mFixedHeightPixels;
1214               glyph.advance = font.mFixedWidthPixels;
1215               glyph.xBearing = 0.0f;
1216               glyph.yBearing = font.mFixedHeightPixels;
1217
1218               // Adjust the metrics if the fixed-size font should be down-scaled
1219               const float desiredFixedSize =  static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1220
1221               if( desiredFixedSize > 0.f )
1222               {
1223                 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1224
1225                 glyph.width = glyph.width * scaleFactor ;
1226                 glyph.height = glyph.height * scaleFactor;
1227                 glyph.advance = glyph.advance * scaleFactor;
1228                 glyph.xBearing = glyph.xBearing * scaleFactor;
1229                 glyph.yBearing = glyph.yBearing * scaleFactor;
1230
1231                 glyph.scaleFactor = scaleFactor;
1232               }
1233             }
1234             else
1235             {
1236               DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error );
1237               success = false;
1238             }
1239           }
1240           else
1241 #endif
1242           {
1243             // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1244             // i.e. with the SNum-3R font.
1245             // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1246             int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT );
1247
1248             // Keep the width of the glyph before doing the software emboldening.
1249             // It will be used to calculate a scale factor to be applied to the
1250             // advance as Harfbuzz doesn't apply any SW emboldening to calculate
1251             // the advance of the glyph.
1252             const float width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1253
1254             if( FT_Err_Ok == error )
1255             {
1256               const bool isEmboldeningRequired = glyph.isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD );
1257               if( isEmboldeningRequired )
1258               {
1259                 // Does the software bold.
1260                 FT_GlyphSlot_Embolden( ftFace->glyph );
1261               }
1262
1263               glyph.width  = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1264               glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266;
1265               if( horizontal )
1266               {
1267                 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
1268                 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
1269               }
1270               else
1271               {
1272                 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
1273                 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
1274               }
1275
1276               if( isEmboldeningRequired && !Dali::EqualsZero( width ) )
1277               {
1278                 // If the glyph is emboldened by software, the advance is multiplied by a
1279                 // scale factor to make it slightly bigger.
1280                 glyph.advance *= ( glyph.width / width );
1281               }
1282
1283               // Use the bounding box of the bitmap to correct the metrics.
1284               // For some fonts i.e the SNum-3R the metrics need to be corrected,
1285               // otherwise the glyphs 'dance' up and down depending on the
1286               // font's point size.
1287
1288               FT_Glyph ftGlyph;
1289               error = FT_Get_Glyph( ftFace->glyph, &ftGlyph );
1290
1291               FT_BBox bbox;
1292               FT_Glyph_Get_CBox( ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox );
1293
1294               const float descender = glyph.height - glyph.yBearing;
1295               glyph.height = ( bbox.yMax -  bbox.yMin) * FROM_266;
1296               glyph.yBearing = glyph.height - round( descender );
1297
1298               // Created FT_Glyph object must be released with FT_Done_Glyph
1299               FT_Done_Glyph( ftGlyph );
1300             }
1301             else
1302             {
1303               success = false;
1304             }
1305           }
1306           break;
1307         }
1308         case FontDescription::BITMAP_FONT:
1309         {
1310           BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1311
1312           unsigned int index = 0u;
1313           for( auto& item : bitmapFontCacheItem.font.glyphs )
1314           {
1315             if( item.utf32 == glyph.index )
1316             {
1317               Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1318               if( !pixelBuffer )
1319               {
1320                 pixelBuffer = LoadImageFromFile( item.url );
1321               }
1322
1323               glyph.width  = static_cast< float >( pixelBuffer.GetWidth() );
1324               glyph.height = static_cast< float >( pixelBuffer.GetHeight() );
1325               glyph.xBearing = 0.f;
1326               glyph.yBearing = glyph.height + item.descender;
1327               glyph.advance = glyph.width;
1328               glyph.scaleFactor = 1.f;
1329               break;
1330             }
1331             ++index;
1332           }
1333
1334           success = true;
1335           break;
1336         }
1337         default:
1338         {
1339           DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1340         }
1341       }
1342     }
1343     else
1344     {
1345       // Check if it's an embedded image.
1346       if( ( 0u == glyph.fontId ) && ( 0u != glyph.index ) && ( glyph.index <= mEmbeddedItemCache.Count() ) )
1347       {
1348         const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1349
1350         glyph.width = static_cast<float>( item.width );
1351         glyph.height = static_cast<float>( item.height );
1352         glyph.xBearing = 0.f;
1353         glyph.yBearing = glyph.height;
1354         glyph.advance = glyph.width;
1355         glyph.scaleFactor = 1.f;
1356       }
1357       else
1358       {
1359         success = false;
1360       }
1361     }
1362   }
1363
1364   return success;
1365 }
1366
1367 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
1368                                            uint32_t size,
1369                                            bool horizontal )
1370 {
1371 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1372   bool success( true );
1373
1374   for( unsigned int i = 0u; i < size; ++i )
1375   {
1376     FontId fontId = array[i].fontId;
1377
1378     if( ( fontId > 0u ) &&
1379         ( fontId - 1u ) < mFontIdCache.Count() )
1380     {
1381       FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1382
1383       if( ! font.mVectorFontId )
1384       {
1385         font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1386       }
1387
1388       mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
1389
1390       // Vector metrics are in EMs, convert to pixels
1391       const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
1392       array[i].width    *= scale;
1393       array[i].height   *= scale;
1394       array[i].xBearing *= scale;
1395       array[i].yBearing *= scale;
1396       array[i].advance  *= scale;
1397     }
1398     else
1399     {
1400       success = false;
1401     }
1402   }
1403
1404   return success;
1405 #else
1406   return false;
1407 #endif
1408 }
1409
1410 void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth )
1411 {
1412   const FontId index = fontId - 1u;
1413
1414   if( ( fontId > 0u ) &&
1415       ( index < mFontIdCache.Count() ) )
1416   {
1417     data.isColorBitmap = false;
1418     data.isColorEmoji = false;
1419
1420     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1421
1422     switch( fontIdCacheItem.type )
1423     {
1424       case FontDescription::FACE_FONT:
1425       {
1426         // For the software italics.
1427         bool isShearRequired = false;
1428
1429         const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1430         FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1431
1432         FT_Error error;
1433
1434 #ifdef FREETYPE_BITMAP_SUPPORT
1435         // Check to see if this is fixed size bitmap
1436         if( fontFaceCacheItem.mIsFixedSizeBitmap )
1437         {
1438           error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1439         }
1440         else
1441 #endif
1442         {
1443           // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1444           // i.e. with the SNum-3R font.
1445           // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1446           error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT );
1447         }
1448         if( FT_Err_Ok == error )
1449         {
1450           if( isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) )
1451           {
1452             // Does the software bold.
1453             FT_GlyphSlot_Embolden( ftFace->glyph );
1454           }
1455
1456           if( isItalicRequired && !( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) )
1457           {
1458             // Will do the software italic.
1459             isShearRequired = true;
1460           }
1461
1462           FT_Glyph glyph;
1463           error = FT_Get_Glyph( ftFace->glyph, &glyph );
1464
1465           // Convert to bitmap if necessary
1466           if( FT_Err_Ok == error )
1467           {
1468             if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
1469             {
1470               int offsetX = 0, offsetY = 0;
1471               bool isOutlineGlyph = ( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 );
1472
1473               // Create a bitmap for the outline
1474               if( isOutlineGlyph )
1475               {
1476                 // Retrieve the horizontal and vertical distance from the current pen position to the
1477                 // left and top border of the glyph bitmap for a normal glyph before applying the outline.
1478                 if( FT_Err_Ok == error )
1479                 {
1480                   FT_Glyph normalGlyph;
1481                   error = FT_Get_Glyph( ftFace->glyph, &normalGlyph );
1482
1483                   error = FT_Glyph_To_Bitmap( &normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1 );
1484                   if( FT_Err_Ok == error )
1485                   {
1486                     FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( normalGlyph );
1487
1488                     offsetX = bitmapGlyph->left;
1489                     offsetY = bitmapGlyph->top;
1490                   }
1491
1492                   // Created FT_Glyph object must be released with FT_Done_Glyph
1493                   FT_Done_Glyph( normalGlyph );
1494                 }
1495
1496                 // Now apply the outline
1497
1498                 // Set up a stroker
1499                 FT_Stroker stroker;
1500                 error = FT_Stroker_New( mFreeTypeLibrary, &stroker );
1501
1502                 if( FT_Err_Ok == error )
1503                 {
1504                   FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 );
1505                   error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 );
1506
1507                   if( FT_Err_Ok == error )
1508                   {
1509                     FT_Stroker_Done( stroker );
1510                   }
1511                   else
1512                   {
1513                     DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error );
1514                   }
1515                 }
1516                 else
1517                 {
1518                   DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error );
1519                 }
1520               }
1521
1522               error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
1523               if( FT_Err_Ok == error )
1524               {
1525                 FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph );
1526
1527                 if( isOutlineGlyph )
1528                 {
1529                   // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
1530                   data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
1531                   data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
1532                 }
1533
1534                 ConvertBitmap( data, bitmapGlyph->bitmap, isShearRequired );
1535               }
1536               else
1537               {
1538                 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error );
1539               }
1540             }
1541             else
1542             {
1543               ConvertBitmap( data, ftFace->glyph->bitmap, isShearRequired );
1544             }
1545
1546             data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1547
1548             // Created FT_Glyph object must be released with FT_Done_Glyph
1549             FT_Done_Glyph( glyph );
1550           }
1551         }
1552         else
1553         {
1554           DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error );
1555         }
1556         break;
1557       }
1558       case FontDescription::BITMAP_FONT:
1559       {
1560         BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1561
1562         unsigned int index = 0u;
1563         for( auto& item : bitmapFontCacheItem.font.glyphs )
1564         {
1565           if( item.utf32 == glyphIndex )
1566           {
1567             Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1568             if( !pixelBuffer )
1569             {
1570               pixelBuffer = LoadImageFromFile( item.url );
1571             }
1572
1573             data.width = pixelBuffer.GetWidth();
1574             data.height = pixelBuffer.GetHeight();
1575
1576             data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1577
1578             ConvertBitmap( data, data.width, data.height, pixelBuffer.GetBuffer() );
1579
1580             // Sets the pixel format.
1581             data.format = pixelBuffer.GetPixelFormat();
1582             break;
1583           }
1584           ++index;
1585         }
1586         break;
1587       }
1588       default:
1589       {
1590         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1591       }
1592     }
1593   }
1594   else
1595   {
1596     if( ( 0u != glyphIndex ) && ( glyphIndex <= mEmbeddedItemCache.Count() ) )
1597     {
1598       // It's an embedded item.
1599       const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1600
1601       data.width = item.width;
1602       data.height = item.height;
1603       if( 0u != item.pixelBufferId )
1604       {
1605         Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId-1u].pixelBuffer;
1606         if( pixelBuffer )
1607         {
1608           ConvertBitmap( data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer() );
1609
1610           // Sets the pixel format.
1611           data.format = pixelBuffer.GetPixelFormat();
1612         }
1613       }
1614       else
1615       {
1616         // Creates the output buffer
1617         const unsigned int bufferSize = data.width * data.height * 4u;
1618         data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1619
1620         memset( data.buffer, 0u, bufferSize );
1621
1622         // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1623       }
1624     }
1625   }
1626 }
1627
1628 PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
1629 {
1630   TextAbstraction::FontClient::GlyphBufferData data;
1631
1632   CreateBitmap( fontId, glyphIndex, false, false, data, outlineWidth );
1633
1634   return PixelData::New( data.buffer,
1635                          data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
1636                          data.width,
1637                          data.height,
1638                          data.format,
1639                          PixelData::DELETE_ARRAY );
1640 }
1641
1642 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
1643 {
1644   blob = nullptr;
1645   blobLength = 0;
1646
1647 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1648   if( ( fontId > 0u ) &&
1649       ( fontId - 1u < mFontIdCache.Count() ) )
1650   {
1651     const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1652     FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1653
1654     if( ! font.mVectorFontId )
1655     {
1656       font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1657     }
1658
1659     mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
1660   }
1661 #endif
1662 }
1663
1664 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
1665 {
1666   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" );
1667   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize );
1668
1669   // First look into the cache if there is an ellipsis glyph for the requested point size.
1670   for( const auto& item : mEllipsisCache )
1671   {
1672     if( item.requestedPointSize == requestedPointSize )
1673     {
1674       // Use the glyph in the cache.
1675
1676       DALI_LOG_INFO( gLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index );
1677       DALI_LOG_INFO( gLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId );
1678       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1679
1680       return item.glyph;
1681     }
1682   }
1683
1684   // No glyph has been found. Create one.
1685   mEllipsisCache.PushBack( EllipsisItem() );
1686   EllipsisItem& item = *( mEllipsisCache.End() - 1u );
1687
1688   item.requestedPointSize = requestedPointSize;
1689
1690   // Find a font for the ellipsis glyph.
1691   item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
1692                                        requestedPointSize,
1693                                        false );
1694
1695   // Set the character index to access the glyph inside the font.
1696   item.glyph.index = FT_Get_Char_Index( mFontFaceCache[mFontIdCache[item.glyph.fontId-1u].id].mFreeTypeFace,
1697                                         ELLIPSIS_CHARACTER );
1698
1699   GetBitmapMetrics( &item.glyph, 1u, true );
1700
1701   DALI_LOG_INFO( gLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index );
1702   DALI_LOG_INFO( gLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId );
1703   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1704
1705   return item.glyph;
1706 }
1707
1708 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1709 {
1710   FT_Error error = -1;
1711
1712   const FontId index = fontId - 1u;
1713
1714   if( ( fontId > 0u ) &&
1715       ( index < mFontIdCache.Count() ) )
1716   {
1717     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1718
1719     switch( fontIdCacheItem.type )
1720     {
1721       case FontDescription::FACE_FONT:
1722       {
1723 #ifdef FREETYPE_BITMAP_SUPPORT
1724         const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1725         FT_Face ftFace = item.mFreeTypeFace;
1726
1727         // Check to see if this is fixed size bitmap
1728         if( item.mHasColorTables )
1729         {
1730           error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1731         }
1732 #endif
1733         break;
1734       }
1735       case FontDescription::BITMAP_FONT:
1736       {
1737         error = FT_Err_Ok; // Will return true;
1738         break;
1739       }
1740       default:
1741       {
1742         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1743       }
1744     }
1745   }
1746
1747   return FT_Err_Ok == error;
1748 }
1749
1750 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId )
1751 {
1752   FT_Face fontFace = nullptr;
1753
1754   const FontId index = fontId - 1u;
1755   if( ( fontId > 0u ) &&
1756       ( index < mFontIdCache.Count() ) )
1757   {
1758     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1759
1760     if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1761     {
1762       fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1763     }
1764   }
1765   return fontFace;
1766 }
1767
1768 FontDescription::Type FontClient::Plugin::GetFontType( FontId fontId )
1769 {
1770   const FontId index = fontId - 1u;
1771   if( ( fontId > 0u ) &&
1772       ( index < mFontIdCache.Count() ) )
1773   {
1774     return mFontIdCache[index].type;
1775   }
1776   return FontDescription::INVALID;
1777 }
1778
1779 bool FontClient::Plugin::AddCustomFontDirectory( const FontPath& path )
1780 {
1781   // nullptr as first parameter means the current configuration is used.
1782   return FcConfigAppFontAddDir( nullptr, reinterpret_cast<const FcChar8 *>( path.c_str() ) );
1783 }
1784
1785 GlyphIndex FontClient::Plugin::CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat )
1786 {
1787   EmbeddedItem embeddedItem;
1788
1789   embeddedItem.pixelBufferId = 0u;
1790   embeddedItem.width = description.width;
1791   embeddedItem.height = description.height;
1792
1793   pixelFormat = Pixel::A8;
1794
1795   if( !description.url.empty() )
1796   {
1797     // Check if the url is in the cache.
1798     PixelBufferId index = 0u;
1799
1800     for( const auto& cacheItem : mPixelBufferCache )
1801     {
1802       ++index;
1803       if( cacheItem.url == description.url )
1804       {
1805         // The url is in the pixel buffer cache.
1806         // Set the index +1 to the vector.
1807         embeddedItem.pixelBufferId = index;
1808         break;
1809       }
1810     }
1811
1812     Devel::PixelBuffer pixelBuffer;
1813     if( 0u == embeddedItem.pixelBufferId )
1814     {
1815       // The pixel buffer is not in the cache. Create one and cache it.
1816
1817       // Load the image from the url.
1818       pixelBuffer = LoadImageFromFile( description.url );
1819
1820       // Create the cache item.
1821       PixelBufferCacheItem pixelBufferCacheItem;
1822       pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1823       pixelBufferCacheItem.url = description.url;
1824
1825       // Store the cache item in the cache.
1826       mPixelBufferCache.push_back( std::move( pixelBufferCacheItem ) );
1827
1828       // Set the pixel buffer id to the embedded item.
1829       embeddedItem.pixelBufferId = mPixelBufferCache.size();
1830     }
1831     else
1832     {
1833       // Retrieve the pixel buffer from the cache to set the pixel format.
1834       pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId-1u].pixelBuffer;
1835     }
1836
1837     if( pixelBuffer )
1838     {
1839       // Set the size of the embedded item if it has not been set.
1840       if( 0u == embeddedItem.width )
1841       {
1842         embeddedItem.width = static_cast<unsigned int>( pixelBuffer.GetWidth() );
1843       }
1844
1845       if( 0u == embeddedItem.height )
1846       {
1847         embeddedItem.height = static_cast<unsigned int>( pixelBuffer.GetHeight() );
1848       }
1849
1850       // Set the pixel format.
1851       pixelFormat = pixelBuffer.GetPixelFormat();
1852     }
1853   }
1854
1855   // Find if the same embeddedItem has already been created.
1856   unsigned int index = 0u;
1857   for( const auto& item : mEmbeddedItemCache )
1858   {
1859     ++index;
1860     if( ( item.pixelBufferId == embeddedItem.pixelBufferId ) &&
1861         ( item.width == embeddedItem.width ) &&
1862         ( item.height == embeddedItem.height ) )
1863     {
1864       return index;
1865     }
1866   }
1867
1868   // Cache the embedded item.
1869   mEmbeddedItemCache.PushBack( embeddedItem );
1870
1871   return mEmbeddedItemCache.Count();
1872 }
1873
1874 void FontClient::Plugin::InitSystemFonts()
1875 {
1876   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1877
1878   FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1879
1880   if( fontSet )
1881   {
1882     DALI_LOG_INFO( gLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont );
1883
1884     // Reserve some space to avoid reallocations.
1885     mSystemFonts.reserve( fontSet->nfont );
1886
1887     for( int i = 0u; i < fontSet->nfont; ++i )
1888     {
1889       FcPattern* fontPattern = fontSet->fonts[i];
1890
1891       FontPath path;
1892
1893       // Skip fonts with no path
1894       if( GetFcString( fontPattern, FC_FILE, path ) )
1895       {
1896         mSystemFonts.push_back( FontDescription() );
1897         FontDescription& fontDescription = mSystemFonts.back();
1898
1899         fontDescription.path = std::move( path );
1900
1901         int width = 0;
1902         int weight = 0;
1903         int slant = 0;
1904         GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1905         GetFcInt( fontPattern, FC_WIDTH, width );
1906         GetFcInt( fontPattern, FC_WEIGHT, weight );
1907         GetFcInt( fontPattern, FC_SLANT, slant );
1908         fontDescription.width = IntToWidthType( width );
1909         fontDescription.weight = IntToWeightType( weight );
1910         fontDescription.slant = IntToSlantType( slant );
1911
1912         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", fontDescription.family.c_str() );
1913         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1914         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1915         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1916         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1917       }
1918     }
1919
1920     // Destroys the font set created.
1921     FcFontSetDestroy( fontSet );
1922   }
1923   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1924 }
1925
1926 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1927 {
1928   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1929
1930   FcResult result = FcResultMatch;
1931   FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result ); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1932
1933   const bool matched = nullptr != match;
1934   DALI_LOG_INFO( gLogFilter, Debug::General, "  pattern matched : %s\n", ( matched ? "true" : "false" ) );
1935
1936   if( matched )
1937   {
1938     int width = 0;
1939     int weight = 0;
1940     int slant = 0;
1941     GetFcString( match, FC_FILE, fontDescription.path );
1942     GetFcString( match, FC_FAMILY, fontDescription.family );
1943     GetFcInt( match, FC_WIDTH, width );
1944     GetFcInt( match, FC_WEIGHT, weight );
1945     GetFcInt( match, FC_SLANT, slant );
1946     fontDescription.width = IntToWidthType( width );
1947     fontDescription.weight = IntToWeightType( weight );
1948     fontDescription.slant = IntToSlantType( slant );
1949
1950     // Retrieve the character set and increase the reference counter.
1951     FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1952     *characterSet = FcCharSetCopy( *characterSet );
1953
1954     // destroyed the matched pattern
1955     FcPatternDestroy( match );
1956
1957     DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1958     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1959     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1960     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1961     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1962   }
1963
1964   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1965   return matched;
1966 }
1967
1968 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1969 {
1970   // create the cached font family lookup pattern
1971   // a pattern holds a set of names, each name refers to a property of the font
1972   FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1973
1974   if( !fontFamilyPattern )
1975   {
1976     return nullptr;
1977   }
1978
1979   // add a property to the pattern for the font family
1980   FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1981
1982   // add a property to the pattern for local setting.
1983   const char* locale = setlocale( LC_MESSAGES, nullptr );
1984   if( locale != nullptr)
1985   {
1986     FcPatternAddString( fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>( locale ) );
1987   }
1988
1989   int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1990   if( width < 0 )
1991   {
1992     // Use default.
1993     width = DEFAULT_FONT_WIDTH;
1994   }
1995
1996   int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1997   if( weight < 0 )
1998   {
1999     // Use default.
2000     weight = DEFAULT_FONT_WEIGHT;
2001   }
2002
2003   int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
2004   if( slant < 0 )
2005   {
2006     // Use default.
2007     slant = DEFAULT_FONT_SLANT;
2008   }
2009
2010   FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
2011   FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
2012   FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
2013
2014   // modify the config, with the mFontFamilyPatterm
2015   FcConfigSubstitute( nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern );
2016
2017   // provide default values for unspecified properties in the font pattern
2018   // e.g. patterns without a specified style or weight are set to Medium
2019   FcDefaultSubstitute( fontFamilyPattern );
2020
2021   return fontFamilyPattern;
2022 }
2023
2024 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
2025 {
2026   FcFontSet* fontset = nullptr;
2027
2028   // create a new pattern.
2029   // a pattern holds a set of names, each name refers to a property of the font
2030   FcPattern* pattern = FcPatternCreate();
2031
2032   if( nullptr != pattern )
2033   {
2034     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
2035     FcObjectSet* objectSet = FcObjectSetCreate();
2036
2037     if( nullptr != objectSet )
2038     {
2039       // build an object set from a list of property names
2040       FcObjectSetAdd( objectSet, FC_FILE );
2041       FcObjectSetAdd( objectSet, FC_FAMILY );
2042       FcObjectSetAdd( objectSet, FC_WIDTH );
2043       FcObjectSetAdd( objectSet, FC_WEIGHT );
2044       FcObjectSetAdd( objectSet, FC_SLANT );
2045
2046       // get a list of fonts
2047       // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
2048       fontset = FcFontList( nullptr /* the default configuration is checked to be up to date, and used */, pattern, objectSet ); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
2049
2050       // clear up the object set
2051       FcObjectSetDestroy( objectSet );
2052     }
2053
2054     // clear up the pattern
2055     FcPatternDestroy( pattern );
2056   }
2057
2058   return fontset;
2059 }
2060
2061 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
2062                                       const char* const n,
2063                                       std::string& string )
2064 {
2065   FcChar8* file = nullptr;
2066   const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
2067
2068   if( FcResultMatch == retVal )
2069   {
2070     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
2071     string.assign( reinterpret_cast<const char*>( file ) );
2072
2073     return true;
2074   }
2075
2076   return false;
2077 }
2078
2079 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
2080 {
2081   const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
2082
2083   if( FcResultMatch == retVal )
2084   {
2085     return true;
2086   }
2087
2088   return false;
2089 }
2090
2091 FontId FontClient::Plugin::CreateFont( const FontPath& path,
2092                                        PointSize26Dot6 requestedPointSize,
2093                                        FaceIndex faceIndex,
2094                                        bool cacheDescription )
2095 {
2096   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" );
2097   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
2098   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
2099
2100   FontId id = 0u;
2101
2102   // Create & cache new font face
2103   FT_Face ftFace;
2104   int error = FT_New_Face( mFreeTypeLibrary,
2105                            path.c_str(),
2106                            0,
2107                            &ftFace );
2108
2109   if( FT_Err_Ok == error )
2110   {
2111     // Check if a font is scalable.
2112     const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) );
2113     const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes );
2114     const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) );
2115     FontId fontFaceId = 0u;
2116
2117     DALI_LOG_INFO( gLogFilter, Debug::General, "            isScalable : [%s]\n", ( isScalable ? "true" : "false" ) );
2118     DALI_LOG_INFO( gLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) );
2119     DALI_LOG_INFO( gLogFilter, Debug::General, "        hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) );
2120
2121     // Check to see if the font contains fixed sizes?
2122     if( !isScalable && hasFixedSizedBitmaps )
2123     {
2124       PointSize26Dot6 actualPointSize = 0u;
2125       int fixedSizeIndex = 0;
2126       for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex )
2127       {
2128         const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2129         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize );
2130
2131         if( fixedSize >= requestedPointSize )
2132         {
2133           actualPointSize = fixedSize;
2134           break;
2135         }
2136       }
2137
2138       if( 0u == actualPointSize )
2139       {
2140         // The requested point size is bigger than the bigest fixed size.
2141         fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2142         actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2143       }
2144
2145       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize );
2146
2147       // Tell Freetype to use this size
2148       error = FT_Select_Size( ftFace, fixedSizeIndex );
2149       if ( FT_Err_Ok != error )
2150       {
2151         DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error );
2152       }
2153       else
2154       {
2155         const float fixedWidth  = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].width );
2156         const float fixedHeight = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].height );
2157
2158         // Indicate that the font is a fixed sized bitmap
2159         FontMetrics metrics( fixedHeight, // The ascender in pixels.
2160                              0.0f,
2161                              fixedHeight, // The height in pixels.
2162                              0.0f,
2163                              0.0f );
2164
2165         // Create the FreeType font face item to cache.
2166         FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables );
2167
2168         // Set the index to the font's id cache.
2169         fontFaceCacheItem.mFontId = mFontIdCache.Count();
2170
2171         // Create the font id item to cache.
2172         FontIdCacheItem fontIdCacheItem;
2173         fontIdCacheItem.type = FontDescription::FACE_FONT;
2174
2175         // Set the index to the FreeType font face cache.
2176         fontIdCacheItem.id = mFontFaceCache.size();
2177         fontFaceId = fontIdCacheItem.id + 1u;
2178
2179         // Cache the items.
2180         mFontFaceCache.push_back( fontFaceCacheItem );
2181         mFontIdCache.PushBack( fontIdCacheItem );
2182
2183         // Set the font id to be returned.
2184         id = mFontIdCache.Count();
2185       }
2186     }
2187     else
2188     {
2189       error = FT_Set_Char_Size( ftFace,
2190                                 0,
2191                                 requestedPointSize,
2192                                 mDpiHorizontal,
2193                                 mDpiVertical );
2194
2195       if( FT_Err_Ok == error )
2196       {
2197
2198         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2199
2200         FontMetrics metrics( static_cast< float >( ftMetrics.ascender  ) * FROM_266,
2201                              static_cast< float >( ftMetrics.descender ) * FROM_266,
2202                              static_cast< float >( ftMetrics.height    ) * FROM_266,
2203                              static_cast< float >( ftFace->underline_position ) * FROM_266,
2204                              static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
2205
2206         // Create the FreeType font face item to cache.
2207         FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics );
2208
2209         // Set the index to the font's id cache.
2210         fontFaceCacheItem.mFontId = mFontIdCache.Count();
2211
2212         // Create the font id item to cache.
2213         FontIdCacheItem fontIdCacheItem;
2214         fontIdCacheItem.type = FontDescription::FACE_FONT;
2215
2216         // Set the index to the FreeType font face cache.
2217         fontIdCacheItem.id = mFontFaceCache.size();
2218         fontFaceId = fontIdCacheItem.id + 1u;
2219
2220         // Cache the items.
2221         mFontFaceCache.push_back( fontFaceCacheItem );
2222         mFontIdCache.PushBack( fontIdCacheItem );
2223
2224         // Set the font id to be returned.
2225         id = mFontIdCache.Count();
2226       }
2227       else
2228       {
2229         DALI_LOG_INFO( gLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize );
2230       }
2231     }
2232
2233     if( 0u != fontFaceId )
2234     {
2235       if( cacheDescription )
2236       {
2237         CacheFontPath( ftFace, fontFaceId, requestedPointSize, path );
2238       }
2239     }
2240   }
2241   else
2242   {
2243     DALI_LOG_INFO( gLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str() );
2244   }
2245
2246   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
2247   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" );
2248
2249   return id;
2250 }
2251
2252 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer )
2253 {
2254   // Set the input dimensions.
2255   const ImageDimensions inputDimensions( srcWidth, srcHeight );
2256
2257   // Set the output dimensions.
2258   // If the output dimension is not given, the input dimension is set
2259   // and won't be downscaling.
2260   data.width = ( data.width == 0 ) ? srcWidth : data.width;
2261   data.height = ( data.height == 0 ) ? srcHeight : data.height;
2262   const ImageDimensions desiredDimensions( data.width, data.height );
2263
2264   // Creates the output buffer
2265   const unsigned int bufferSize = data.width * data.height * 4u;
2266   data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2267
2268   if( inputDimensions == desiredDimensions )
2269   {
2270     // There isn't downscaling.
2271     memcpy( data.buffer, srcBuffer, bufferSize );
2272   }
2273   else
2274   {
2275     Dali::Internal::Platform::LanczosSample4BPP( srcBuffer,
2276                                                  inputDimensions,
2277                                                  data.buffer,
2278                                                  desiredDimensions );
2279   }
2280 }
2281
2282 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired )
2283 {
2284   if( srcBitmap.width*srcBitmap.rows > 0 )
2285   {
2286     switch( srcBitmap.pixel_mode )
2287     {
2288       case FT_PIXEL_MODE_GRAY:
2289       {
2290         if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
2291         {
2292           uint8_t* pixelsIn = srcBitmap.buffer;
2293           unsigned int width = srcBitmap.width;
2294           unsigned height = srcBitmap.rows;
2295
2296           std::unique_ptr<uint8_t, void(*)(void*)> pixelsOutPtr( nullptr, free );
2297
2298           if( isShearRequired )
2299           {
2300             /**
2301              * Glyphs' bitmaps with no slant retrieved from FreeType:
2302              * __________     ____
2303              * |XXXXXXXX|     |XX|
2304              * |   XX   |     |XX|
2305              * |   XX   |     |XX|
2306              * |   XX   |     |XX|
2307              * |   XX   |     |XX|
2308              * |   XX   |     |XX|
2309              * ----------     ----
2310              *
2311              * Expected glyphs' bitmaps with italic slant:
2312              * ____________   ______
2313              * |  XXXXXXXX|   |  XX|
2314              * |     XX   |   |  XX|
2315              * |    XX    |   | XX |
2316              * |    XX    |   | XX |
2317              * |   XX     |   |XX  |
2318              * |   XX     |   |XX  |
2319              * ------------   ------
2320              *
2321              * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2322              * __________     ______
2323              * |XXXXXXXX|     |  XX|
2324              * |   XX   |     |  XX|
2325              * |  XX    |     | XX |
2326              * |  XX    |     | XX |
2327              * | XX     |     |XX  |
2328              * | XX     |     |XX  |
2329              * ----------     ------
2330              *
2331              * This difference in some bitmaps' width causes an overlap of some glyphs. This is the reason why a shear operation is done here instead of relying on the experimental FT_GlyphSlot_Oblique() implementation.
2332              */
2333             unsigned int widthOut = 0u;
2334             unsigned int heightOut = 0u;
2335             uint8_t* pixelsOut = nullptr;
2336
2337             Dali::Internal::Platform::HorizontalShear( pixelsIn,
2338                                                        width,
2339                                                        height,
2340                                                        1u,
2341                                                        -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2342                                                        pixelsOut,
2343                                                        widthOut,
2344                                                        heightOut );
2345
2346             width = widthOut;
2347             height = heightOut;
2348             pixelsIn = pixelsOut;
2349             pixelsOutPtr.reset( pixelsOut );
2350           }
2351
2352           const unsigned int bufferSize = width * height;
2353           data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2354           data.width = width;
2355           data.height = height;
2356           data.format = Pixel::L8; // Sets the pixel format.
2357           memcpy( data.buffer, pixelsIn, bufferSize );
2358         }
2359         break;
2360       }
2361
2362 #ifdef FREETYPE_BITMAP_SUPPORT
2363       case FT_PIXEL_MODE_BGRA:
2364       {
2365         if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
2366         {
2367           ConvertBitmap( data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer );
2368
2369           // Sets the pixel format.
2370           data.format = Pixel::BGRA8888;
2371         }
2372         break;
2373       }
2374 #endif
2375       default:
2376       {
2377         DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" );
2378         break;
2379       }
2380     }
2381   }
2382 }
2383
2384 bool FontClient::Plugin::FindFont( const FontPath& path,
2385                                    PointSize26Dot6 requestedPointSize,
2386                                    FaceIndex faceIndex,
2387                                    FontId& fontId ) const
2388 {
2389   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2390   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
2391   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
2392   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of fonts in the cache : %d\n", mFontFaceCache.size() );
2393
2394   fontId = 0u;
2395   for( const auto& cacheItem : mFontFaceCache )
2396   {
2397     if( cacheItem.mRequestedPointSize == requestedPointSize &&
2398         cacheItem.mFaceIndex == faceIndex &&
2399         cacheItem.mPath == path )
2400     {
2401       fontId = cacheItem.mFontId + 1u;
2402
2403       DALI_LOG_INFO( gLogFilter, Debug::General, "  font found, id : %d\n", fontId );
2404       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2405
2406       return true;
2407     }
2408   }
2409
2410   DALI_LOG_INFO( gLogFilter, Debug::General, "  font not found\n" );
2411   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2412
2413   return false;
2414 }
2415
2416 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
2417                                             FontDescriptionId& validatedFontId )
2418 {
2419   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" );
2420   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
2421   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
2422   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
2423   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2424   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2425   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %d\n", mValidatedFontCache.size() );
2426
2427   validatedFontId = 0u;
2428
2429   for( const auto& item : mValidatedFontCache )
2430   {
2431     if( !fontDescription.family.empty() &&
2432         ( fontDescription.family == item.fontDescription.family ) &&
2433         ( fontDescription.width == item.fontDescription.width ) &&
2434         ( fontDescription.weight == item.fontDescription.weight ) &&
2435         ( fontDescription.slant == item.fontDescription.slant ) )
2436     {
2437       validatedFontId = item.index;
2438
2439       DALI_LOG_INFO( gLogFilter, Debug::General, "  validated font found, id : %d\n", validatedFontId );
2440       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2441       return true;
2442     }
2443   }
2444
2445   DALI_LOG_INFO( gLogFilter, Debug::General, "  validated font not found\n" );
2446   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2447   return false;
2448 }
2449
2450 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
2451                                                FontList*& fontList,
2452                                                CharacterSetList*& characterSetList )
2453 {
2454   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" );
2455   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
2456   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
2457   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
2458   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2459   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2460   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of fallback font lists in the cache : %d\n", mFallbackCache.size() );
2461
2462   fontList = nullptr;
2463
2464   for( const auto& item : mFallbackCache )
2465   {
2466     if( !fontDescription.family.empty() &&
2467         ( fontDescription.family == item.fontDescription.family ) &&
2468         ( fontDescription.width == item.fontDescription.width ) &&
2469         ( fontDescription.weight == item.fontDescription.weight ) &&
2470         ( fontDescription.slant == item.fontDescription.slant ) )
2471     {
2472       fontList = item.fallbackFonts;
2473       characterSetList = item.characterSets;
2474
2475       DALI_LOG_INFO( gLogFilter, Debug::General, "  fallback font list found.\n" );
2476       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2477       return true;
2478     }
2479   }
2480
2481   DALI_LOG_INFO( gLogFilter, Debug::General, "  fallback font list not found.\n" );
2482   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2483   return false;
2484 }
2485
2486 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
2487                                    PointSize26Dot6 requestedPointSize,
2488                                    FontId& fontId )
2489 {
2490   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2491   DALI_LOG_INFO( gLogFilter, Debug::General, "    validatedFontId  : %d\n", validatedFontId );
2492   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
2493
2494   fontId = 0u;
2495
2496   for( const auto& item : mFontDescriptionSizeCache )
2497   {
2498     if( ( validatedFontId == item.validatedFontId ) &&
2499         ( requestedPointSize == item.requestedPointSize ) )
2500     {
2501       fontId = item.fontId;
2502
2503       DALI_LOG_INFO( gLogFilter, Debug::General, "  font found, id : %d\n", fontId );
2504       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2505       return true;
2506     }
2507   }
2508
2509   DALI_LOG_INFO( gLogFilter, Debug::General, "  font not found.\n" );
2510   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2511   return false;
2512 }
2513
2514 bool FontClient::Plugin::FindBitmapFont( const FontFamily& bitmapFont, FontId& fontId ) const
2515 {
2516   fontId = 0u;
2517
2518   for( const auto& item : mBitmapFontCache )
2519   {
2520     if( bitmapFont == item.font.name )
2521     {
2522       fontId = item.id + 1u;
2523       return true;
2524     }
2525   }
2526
2527   return false;
2528 }
2529
2530 bool FontClient::Plugin::IsScalable( const FontPath& path )
2531 {
2532   bool isScalable = false;
2533
2534   FT_Face ftFace;
2535   int error = FT_New_Face( mFreeTypeLibrary,
2536                            path.c_str(),
2537                            0,
2538                            &ftFace );
2539   if( FT_Err_Ok != error )
2540   {
2541     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() );
2542   }
2543   else
2544   {
2545     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2546   }
2547
2548   return isScalable;
2549 }
2550
2551 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
2552 {
2553   // Create a font pattern.
2554   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2555
2556   FcResult result = FcResultMatch;
2557
2558   // match the pattern
2559   FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2560   bool isScalable = false;
2561
2562   if( match )
2563   {
2564     // Get the path to the font file name.
2565     FontPath path;
2566     GetFcString( match, FC_FILE, path );
2567     isScalable = IsScalable( path );
2568   }
2569   else
2570   {
2571     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2572   }
2573
2574   // Destroys the created patterns.
2575   FcPatternDestroy( match );
2576   FcPatternDestroy( fontFamilyPattern );
2577
2578   return isScalable;
2579 }
2580
2581 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
2582 {
2583   // Empty the caller container
2584   sizes.Clear();
2585
2586   FT_Face ftFace;
2587   int error = FT_New_Face( mFreeTypeLibrary,
2588                            path.c_str(),
2589                            0,
2590                            &ftFace );
2591   if( FT_Err_Ok != error )
2592   {
2593     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() );
2594   }
2595
2596   // Fetch the number of fixed sizes available
2597   if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
2598   {
2599     for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
2600     {
2601       sizes.PushBack( ftFace->available_sizes[ i ].size );
2602     }
2603   }
2604 }
2605
2606 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
2607                                         Vector< PointSize26Dot6 >& sizes )
2608 {
2609   // Create a font pattern.
2610   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2611
2612   FcResult result = FcResultMatch;
2613
2614   // match the pattern
2615   FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2616
2617   if( match )
2618   {
2619     // Get the path to the font file name.
2620     FontPath path;
2621     GetFcString( match, FC_FILE, path );
2622     GetFixedSizes( path, sizes );
2623   }
2624   else
2625   {
2626     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2627   }
2628
2629   // Destroys the created patterns.
2630   FcPatternDestroy( match );
2631   FcPatternDestroy( fontFamilyPattern );
2632 }
2633
2634 bool FontClient::Plugin::HasItalicStyle( FontId fontId ) const
2635 {
2636   bool hasItalicStyle = false;
2637
2638   const FontId index = fontId - 1u;
2639
2640   if( ( fontId > 0 ) &&
2641       ( index < mFontIdCache.Count() ) )
2642   {
2643     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2644
2645     if( FontDescription::FACE_FONT == fontIdCacheItem.type )
2646     {
2647       const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2648
2649       hasItalicStyle = 0u != ( font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC );
2650     }
2651   }
2652   else
2653   {
2654     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
2655   }
2656
2657   return hasItalicStyle;
2658 }
2659
2660 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize,  const FontPath& path )
2661 {
2662   FontDescription description;
2663   description.path = path;
2664   description.family = std::move( FontFamily( ftFace->family_name ) );
2665   description.weight = FontWeight::NONE;
2666   description.width = FontWidth::NONE;
2667   description.slant = FontSlant::NONE;
2668
2669   // Note FreeType doesn't give too much info to build a proper font style.
2670   if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
2671   {
2672     description.slant = FontSlant::ITALIC;
2673   }
2674   if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
2675   {
2676     description.weight = FontWeight::BOLD;
2677   }
2678
2679   FontDescriptionId validatedFontId = 0u;
2680   if( !FindValidatedFont( description,
2681                           validatedFontId ) )
2682   {
2683     FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2684
2685     FcResult result = FcResultMatch;
2686     FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2687
2688     FcCharSet* characterSet = nullptr;
2689     FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2690
2691     const FontId fontFaceId = id - 1u;
2692     mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( characterSet ); // Increases the reference counter.
2693
2694     // Destroys the created patterns.
2695     FcPatternDestroy( match );
2696     FcPatternDestroy( pattern );
2697
2698     // Add the path to the cache.
2699     description.type = FontDescription::FACE_FONT;
2700     mFontDescriptionCache.push_back( description );
2701
2702     // Set the index to the vector of paths to font file names.
2703     validatedFontId = mFontDescriptionCache.size();
2704
2705     // Increase the reference counter and add the character set to the cache.
2706     mCharacterSetCache.PushBack( FcCharSetCopy( characterSet ) );
2707
2708     // Cache the index and the font's description.
2709     mValidatedFontCache.push_back( std::move( FontDescriptionCacheItem( std::move( description ),
2710                                                                         validatedFontId) ) );
2711
2712     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2713     mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
2714                                                                        requestedPointSize,
2715                                                                        fontFaceId ) );
2716   }
2717 }
2718
2719 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description )
2720 {
2721   FcCharSet* characterSet = nullptr;
2722
2723   FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2724
2725   if( nullptr != pattern )
2726   {
2727     FcResult result = FcResultMatch;
2728     FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2729
2730     FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2731
2732     // Destroys the created patterns.
2733     FcPatternDestroy( match );
2734     FcPatternDestroy( pattern );
2735   }
2736
2737   return characterSet;
2738 }
2739
2740 void FontClient::Plugin::ClearFallbackCache( std::vector<FallbackCacheItem>& fallbackCache )
2741 {
2742   for( auto& item : fallbackCache )
2743   {
2744     if( nullptr != item.fallbackFonts )
2745     {
2746       delete item.fallbackFonts;
2747     }
2748
2749     if( nullptr != item.characterSets )
2750     {
2751       // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2752       DestroyCharacterSets( *item.characterSets );
2753       delete item.characterSets;
2754     }
2755   }
2756 }
2757
2758 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2759 {
2760   for( auto& item : mFontFaceCache )
2761   {
2762     FcCharSetDestroy( item.mCharacterSet );
2763     item.mCharacterSet = nullptr;
2764   }
2765 }
2766
2767 } // namespace Internal
2768
2769 } // namespace TextAbstraction
2770
2771 } // namespace Dali