1fb9c673bac21e3fe537d216b3f1b32506cb8411
[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
97 namespace Dali
98 {
99
100 namespace TextAbstraction
101 {
102
103 namespace Internal
104 {
105
106 /**
107  * @brief Returns the FontWidth's enum index for the given width value.
108  *
109  * @param[in] width The width value.
110  *
111  * @return The FontWidth's enum index.
112  */
113 FontWidth::Type IntToWidthType( int width )
114 {
115   return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
116 }
117
118 /**
119  * @brief Returns the FontWeight's enum index for the given weight value.
120  *
121  * @param[in] weight The weight value.
122  *
123  * @return The FontWeight's enum index.
124  */
125 FontWeight::Type IntToWeightType( int weight )
126 {
127   return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
128 }
129
130 /**
131  * @brief Returns the FontSlant's enum index for the given slant value.
132  *
133  * @param[in] slant The slant value.
134  *
135  * @return The FontSlant's enum index.
136  */
137 FontSlant::Type IntToSlantType( int slant )
138 {
139   return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
140 }
141
142 /**
143  * @brief Free the resources allocated by the FcCharSet objects.
144  *
145  * @param[in] characterSets The vector of character sets.
146  */
147 void DestroyCharacterSets( CharacterSetList& characterSets )
148 {
149   for( auto& item : characterSets )
150   {
151     FcCharSetDestroy( item );
152   }
153 }
154
155 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets )
156 : fontDescription{ std::move( font ) },
157   fallbackFonts{ fallbackFonts },
158   characterSets{ characterSets }
159 {
160 }
161
162 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
163                                                                         FontDescriptionId index )
164 : fontDescription{ fontDescription },
165   index{ index }
166 {
167 }
168
169 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( FontDescription&& fontDescription,
170                                                                         FontDescriptionId index )
171 : fontDescription{ std::move( fontDescription ) },
172   index{ index }
173 {
174 }
175
176 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem( FontDescriptionId validatedFontId,
177                                                                                 PointSize26Dot6 requestedPointSize,
178                                                                                 FontId fontId )
179 : validatedFontId( validatedFontId ),
180   requestedPointSize( requestedPointSize ),
181   fontId( fontId )
182 {
183 }
184
185 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
186                                                           const FontPath& path,
187                                                           PointSize26Dot6 requestedPointSize,
188                                                           FaceIndex face,
189                                                           const FontMetrics& metrics )
190 : mFreeTypeFace( ftFace ),
191   mPath( path ),
192   mRequestedPointSize( requestedPointSize ),
193   mFaceIndex( face ),
194   mMetrics( metrics ),
195   mCharacterSet( nullptr ),
196   mFixedSizeIndex( 0 ),
197   mFixedWidthPixels( 0.f ),
198   mFixedHeightPixels( 0.f ),
199   mVectorFontId( 0u ),
200   mFontId( 0u ),
201   mIsFixedSizeBitmap( false ),
202   mHasColorTables( false )
203 {
204 }
205
206 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
207                                                           const FontPath& path,
208                                                           PointSize26Dot6 requestedPointSize,
209                                                           FaceIndex face,
210                                                           const FontMetrics& metrics,
211                                                           int fixedSizeIndex,
212                                                           float fixedWidth,
213                                                           float fixedHeight,
214                                                           bool hasColorTables )
215 : mFreeTypeFace( ftFace ),
216   mPath( path ),
217   mRequestedPointSize( requestedPointSize ),
218   mFaceIndex( face ),
219   mMetrics( metrics ),
220   mCharacterSet( nullptr ),
221   mFixedSizeIndex( fixedSizeIndex ),
222   mFixedWidthPixels( fixedWidth ),
223   mFixedHeightPixels( fixedHeight ),
224   mVectorFontId( 0u ),
225   mFontId( 0u ),
226   mIsFixedSizeBitmap( true ),
227   mHasColorTables( hasColorTables )
228 {
229 }
230
231 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
232                             unsigned int verticalDpi )
233 : mFreeTypeLibrary( nullptr ),
234   mDpiHorizontal( horizontalDpi ),
235   mDpiVertical( verticalDpi ),
236   mDefaultFontDescription(),
237   mSystemFonts(),
238   mDefaultFonts(),
239   mFontIdCache(),
240   mFontFaceCache(),
241   mValidatedFontCache(),
242   mFontDescriptionCache( 1u ),
243   mCharacterSetCache(),
244   mFontDescriptionSizeCache(),
245   mVectorFontCache( nullptr ),
246   mEllipsisCache(),
247   mEmbeddedItemCache(),
248   mDefaultFontDescriptionCached( false )
249 {
250   mCharacterSetCache.Resize( 1u );
251
252   int error = FT_Init_FreeType( &mFreeTypeLibrary );
253   if( FT_Err_Ok != error )
254   {
255     DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Init error: %d\n", error );
256   }
257
258 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
259   mVectorFontCache = new VectorFontCache( mFreeTypeLibrary );
260 #endif
261 }
262
263 FontClient::Plugin::~Plugin()
264 {
265   ClearFallbackCache( mFallbackCache );
266
267   // Free the resources allocated by the FcCharSet objects.
268   DestroyCharacterSets( mDefaultFontCharacterSets );
269   DestroyCharacterSets( mCharacterSetCache );
270   ClearCharacterSetFromFontFaceCache();
271
272 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
273   delete mVectorFontCache;
274 #endif
275   FT_Done_FreeType( mFreeTypeLibrary );
276 }
277
278 void FontClient::Plugin::ClearCache()
279 {
280   mDefaultFontDescription = FontDescription();
281
282   mSystemFonts.clear();
283   mDefaultFonts.clear();
284
285   DestroyCharacterSets( mDefaultFontCharacterSets );
286   mDefaultFontCharacterSets.Clear();
287
288   ClearFallbackCache( mFallbackCache );
289   mFallbackCache.clear();
290
291   mFontIdCache.Clear();
292
293   ClearCharacterSetFromFontFaceCache();
294   mFontFaceCache.clear();
295
296   mValidatedFontCache.clear();
297   mFontDescriptionCache.clear();
298   mFontDescriptionCache.resize( 1u );
299
300   DestroyCharacterSets( mCharacterSetCache );
301   mCharacterSetCache.Clear();
302   mCharacterSetCache.Resize( 1u );
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 );
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 );
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] );
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     // Set the index to the vector of paths to font file names.
1043     validatedFontId = mFontDescriptionCache.size();
1044
1045     DALI_LOG_INFO( gLogFilter, Debug::General, "  matched description; family : [%s]\n", description.family.c_str() );
1046     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                         path : [%s]\n", description.path.c_str() );
1047     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                        width : [%s]\n", FontWidth::Name[description.width] );
1048     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                       weight : [%s]\n", FontWeight::Name[description.weight] );
1049     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                        slant : [%s]\n\n", FontSlant::Name[description.slant] );
1050     DALI_LOG_INFO( gLogFilter, Debug::General, "  validatedFontId : %d\n", validatedFontId );
1051
1052     // Add the path to the cache.
1053     description.type = FontDescription::FACE_FONT;
1054     mFontDescriptionCache.push_back( description );
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             int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT );
1244
1245             if( FT_Err_Ok == error )
1246             {
1247               glyph.width  = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1248               glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
1249               if( horizontal )
1250               {
1251                 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
1252                 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
1253               }
1254               else
1255               {
1256                 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
1257                 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
1258               }
1259             }
1260             else
1261             {
1262               success = false;
1263             }
1264           }
1265           break;
1266         }
1267         case FontDescription::BITMAP_FONT:
1268         {
1269           BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1270
1271           unsigned int index = 0u;
1272           for( auto& item : bitmapFontCacheItem.font.glyphs )
1273           {
1274             if( item.utf32 == glyph.index )
1275             {
1276               Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1277               if( !pixelBuffer )
1278               {
1279                 pixelBuffer = LoadImageFromFile( item.url );
1280               }
1281
1282               glyph.width  = static_cast< float >( pixelBuffer.GetWidth() );
1283               glyph.height = static_cast< float >( pixelBuffer.GetHeight() );
1284               glyph.xBearing = 0.f;
1285               glyph.yBearing = glyph.height + item.descender;
1286               glyph.advance = glyph.width;
1287               glyph.scaleFactor = 1.f;
1288               break;
1289             }
1290             ++index;
1291           }
1292
1293           success = true;
1294           break;
1295         }
1296         default:
1297         {
1298           DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1299         }
1300       }
1301     }
1302     else
1303     {
1304       // Check if it's an embedded image.
1305       if( ( 0u == glyph.fontId ) && ( 0u != glyph.index ) && ( glyph.index <= mEmbeddedItemCache.Count() ) )
1306       {
1307         const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1308
1309         glyph.width = static_cast<float>( item.width );
1310         glyph.height = static_cast<float>( item.height );
1311         glyph.xBearing = 0.f;
1312         glyph.yBearing = glyph.height;
1313         glyph.advance = glyph.width;
1314         glyph.scaleFactor = 1.f;
1315       }
1316       else
1317       {
1318         success = false;
1319       }
1320     }
1321   }
1322
1323   return success;
1324 }
1325
1326 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
1327                                            uint32_t size,
1328                                            bool horizontal )
1329 {
1330 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1331   bool success( true );
1332
1333   for( unsigned int i = 0u; i < size; ++i )
1334   {
1335     FontId fontId = array[i].fontId;
1336
1337     if( ( fontId > 0u ) &&
1338         ( fontId - 1u ) < mFontIdCache.Count() )
1339     {
1340       FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1341
1342       if( ! font.mVectorFontId )
1343       {
1344         font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1345       }
1346
1347       mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
1348
1349       // Vector metrics are in EMs, convert to pixels
1350       const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
1351       array[i].width    *= scale;
1352       array[i].height   *= scale;
1353       array[i].xBearing *= scale;
1354       array[i].yBearing *= scale;
1355       array[i].advance  *= scale;
1356     }
1357     else
1358     {
1359       success = false;
1360     }
1361   }
1362
1363   return success;
1364 #else
1365   return false;
1366 #endif
1367 }
1368
1369 void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth )
1370 {
1371   const FontId index = fontId - 1u;
1372
1373   if( ( fontId > 0u ) &&
1374       ( index < mFontIdCache.Count() ) )
1375   {
1376     data.isColorBitmap = false;
1377     data.isColorEmoji = false;
1378
1379     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1380
1381     switch( fontIdCacheItem.type )
1382     {
1383       case FontDescription::FACE_FONT:
1384       {
1385         // For the software italics.
1386         bool isShearRequired = false;
1387
1388         const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1389         FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1390
1391         FT_Error error;
1392
1393 #ifdef FREETYPE_BITMAP_SUPPORT
1394         // Check to see if this is fixed size bitmap
1395         if( fontFaceCacheItem.mIsFixedSizeBitmap )
1396         {
1397           error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1398         }
1399         else
1400 #endif
1401         {
1402           error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT );
1403         }
1404         if( FT_Err_Ok == error )
1405         {
1406           if( isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) )
1407           {
1408             // Does the software bold.
1409             FT_GlyphSlot_Embolden( ftFace->glyph );
1410           }
1411
1412           if( isItalicRequired && !( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) )
1413           {
1414             // Will do the software italic.
1415             isShearRequired = true;
1416           }
1417
1418           FT_Glyph glyph;
1419           error = FT_Get_Glyph( ftFace->glyph, &glyph );
1420
1421           // Convert to bitmap if necessary
1422           if( FT_Err_Ok == error )
1423           {
1424             if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
1425             {
1426               // Check whether we should create a bitmap for the outline
1427               if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 )
1428               {
1429                 // Set up a stroker
1430                 FT_Stroker stroker;
1431                 error = FT_Stroker_New( mFreeTypeLibrary, &stroker );
1432
1433                 if( FT_Err_Ok == error )
1434                 {
1435                   FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 );
1436                   error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 );
1437
1438                   if( FT_Err_Ok == error )
1439                   {
1440                     FT_Stroker_Done( stroker );
1441                   }
1442                   else
1443                   {
1444                     DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error );
1445                   }
1446                 }
1447                 else
1448                 {
1449                   DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error );
1450                 }
1451               }
1452
1453               error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
1454               if( FT_Err_Ok == error )
1455               {
1456                 FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph );
1457                 ConvertBitmap( data, bitmapGlyph->bitmap, isShearRequired );
1458               }
1459               else
1460               {
1461                 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error );
1462               }
1463             }
1464             else
1465             {
1466               ConvertBitmap( data, ftFace->glyph->bitmap, isShearRequired );
1467             }
1468
1469             data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1470
1471             // Created FT_Glyph object must be released with FT_Done_Glyph
1472             FT_Done_Glyph( glyph );
1473           }
1474         }
1475         else
1476         {
1477           DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error );
1478         }
1479         break;
1480       }
1481       case FontDescription::BITMAP_FONT:
1482       {
1483         BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1484
1485         unsigned int index = 0u;
1486         for( auto& item : bitmapFontCacheItem.font.glyphs )
1487         {
1488           if( item.utf32 == glyphIndex )
1489           {
1490             Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1491             if( !pixelBuffer )
1492             {
1493               pixelBuffer = LoadImageFromFile( item.url );
1494             }
1495
1496             data.width = pixelBuffer.GetWidth();
1497             data.height = pixelBuffer.GetHeight();
1498
1499             data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1500
1501             ConvertBitmap( data, data.width, data.height, pixelBuffer.GetBuffer() );
1502
1503             // Sets the pixel format.
1504             data.format = pixelBuffer.GetPixelFormat();
1505             break;
1506           }
1507           ++index;
1508         }
1509         break;
1510       }
1511       default:
1512       {
1513         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1514       }
1515     }
1516   }
1517   else
1518   {
1519     if( ( 0u != glyphIndex ) && ( glyphIndex <= mEmbeddedItemCache.Count() ) )
1520     {
1521       // It's an embedded item.
1522       const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1523
1524       data.width = item.width;
1525       data.height = item.height;
1526       if( 0u != item.pixelBufferId )
1527       {
1528         Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId-1u].pixelBuffer;
1529         if( pixelBuffer )
1530         {
1531           ConvertBitmap( data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer() );
1532
1533           // Sets the pixel format.
1534           data.format = pixelBuffer.GetPixelFormat();
1535         }
1536       }
1537       else
1538       {
1539         // Creates the output buffer
1540         const unsigned int bufferSize = data.width * data.height * 4u;
1541         data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1542
1543         memset( data.buffer, 0u, bufferSize );
1544
1545         // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1546       }
1547     }
1548   }
1549 }
1550
1551 PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
1552 {
1553   TextAbstraction::FontClient::GlyphBufferData data;
1554
1555   CreateBitmap( fontId, glyphIndex, false, false, data, outlineWidth );
1556
1557   return PixelData::New( data.buffer,
1558                          data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
1559                          data.width,
1560                          data.height,
1561                          data.format,
1562                          PixelData::DELETE_ARRAY );
1563 }
1564
1565 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
1566 {
1567   blob = nullptr;
1568   blobLength = 0;
1569
1570 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1571   if( ( fontId > 0u ) &&
1572       ( fontId - 1u < mFontIdCache.Count() ) )
1573   {
1574     const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1575     FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1576
1577     if( ! font.mVectorFontId )
1578     {
1579       font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1580     }
1581
1582     mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
1583   }
1584 #endif
1585 }
1586
1587 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
1588 {
1589   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" );
1590   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize );
1591
1592   // First look into the cache if there is an ellipsis glyph for the requested point size.
1593   for( const auto& item : mEllipsisCache )
1594   {
1595     if( item.requestedPointSize != requestedPointSize )
1596     {
1597       // Use the glyph in the cache.
1598
1599       DALI_LOG_INFO( gLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index );
1600       DALI_LOG_INFO( gLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId );
1601       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1602
1603       return item.glyph;
1604     }
1605   }
1606
1607   // No glyph has been found. Create one.
1608   mEllipsisCache.PushBack( EllipsisItem() );
1609   EllipsisItem& item = *( mEllipsisCache.End() - 1u );
1610
1611   item.requestedPointSize = requestedPointSize;
1612
1613   // Find a font for the ellipsis glyph.
1614   item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
1615                                        requestedPointSize,
1616                                        false );
1617
1618   // Set the character index to access the glyph inside the font.
1619   item.glyph.index = FT_Get_Char_Index( mFontFaceCache[mFontIdCache[item.glyph.fontId-1u].id].mFreeTypeFace,
1620                                         ELLIPSIS_CHARACTER );
1621
1622   GetBitmapMetrics( &item.glyph, 1u, true );
1623
1624   DALI_LOG_INFO( gLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index );
1625   DALI_LOG_INFO( gLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId );
1626   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1627
1628   return item.glyph;
1629 }
1630
1631 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1632 {
1633   FT_Error error = -1;
1634
1635   const FontId index = fontId - 1u;
1636
1637   if( ( fontId > 0u ) &&
1638       ( index < mFontIdCache.Count() ) )
1639   {
1640     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1641
1642     switch( fontIdCacheItem.type )
1643     {
1644       case FontDescription::FACE_FONT:
1645       {
1646 #ifdef FREETYPE_BITMAP_SUPPORT
1647         const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1648         FT_Face ftFace = item.mFreeTypeFace;
1649
1650         // Check to see if this is fixed size bitmap
1651         if( item.mHasColorTables )
1652         {
1653           error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1654         }
1655 #endif
1656         break;
1657       }
1658       case FontDescription::BITMAP_FONT:
1659       {
1660         error = FT_Err_Ok; // Will return true;
1661         break;
1662       }
1663       default:
1664       {
1665         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1666       }
1667     }
1668   }
1669
1670   return FT_Err_Ok == error;
1671 }
1672
1673 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId )
1674 {
1675   FT_Face fontFace = nullptr;
1676
1677   const FontId index = fontId - 1u;
1678   if( ( fontId > 0u ) &&
1679       ( index < mFontIdCache.Count() ) )
1680   {
1681     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1682
1683     if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1684     {
1685       fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1686     }
1687   }
1688   return fontFace;
1689 }
1690
1691 FontDescription::Type FontClient::Plugin::GetFontType( FontId fontId )
1692 {
1693   const FontId index = fontId - 1u;
1694   if( ( fontId > 0u ) &&
1695       ( index < mFontIdCache.Count() ) )
1696   {
1697     return mFontIdCache[index].type;
1698   }
1699   return FontDescription::INVALID;
1700 }
1701
1702 bool FontClient::Plugin::AddCustomFontDirectory( const FontPath& path )
1703 {
1704   // nullptr as first parameter means the current configuration is used.
1705   return FcConfigAppFontAddDir( nullptr, reinterpret_cast<const FcChar8 *>( path.c_str() ) );
1706 }
1707
1708 GlyphIndex FontClient::Plugin::CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat )
1709 {
1710   EmbeddedItem embeddedItem;
1711
1712   embeddedItem.pixelBufferId = 0u;
1713   embeddedItem.width = description.width;
1714   embeddedItem.height = description.height;
1715
1716   pixelFormat = Pixel::A8;
1717
1718   if( !description.url.empty() )
1719   {
1720     // Check if the url is in the cache.
1721     PixelBufferId index = 0u;
1722
1723     for( const auto& cacheItem : mPixelBufferCache )
1724     {
1725       ++index;
1726       if( cacheItem.url == description.url )
1727       {
1728         // The url is in the pixel buffer cache.
1729         // Set the index +1 to the vector.
1730         embeddedItem.pixelBufferId = index;
1731         break;
1732       }
1733     }
1734
1735     Devel::PixelBuffer pixelBuffer;
1736     if( 0u == embeddedItem.pixelBufferId )
1737     {
1738       // The pixel buffer is not in the cache. Create one and cache it.
1739
1740       // Load the image from the url.
1741       pixelBuffer = LoadImageFromFile( description.url );
1742
1743       // Create the cache item.
1744       PixelBufferCacheItem pixelBufferCacheItem;
1745       pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1746       pixelBufferCacheItem.url = description.url;
1747
1748       // Store the cache item in the cache.
1749       mPixelBufferCache.push_back( std::move( pixelBufferCacheItem ) );
1750
1751       // Set the pixel buffer id to the embedded item.
1752       embeddedItem.pixelBufferId = mPixelBufferCache.size();
1753     }
1754     else
1755     {
1756       // Retrieve the pixel buffer from the cache to set the pixel format.
1757       pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId-1u].pixelBuffer;
1758     }
1759
1760     if( pixelBuffer )
1761     {
1762       // Set the size of the embedded item if it has not been set.
1763       if( 0u == embeddedItem.width )
1764       {
1765         embeddedItem.width = static_cast<unsigned int>( pixelBuffer.GetWidth() );
1766       }
1767
1768       if( 0u == embeddedItem.height )
1769       {
1770         embeddedItem.height = static_cast<unsigned int>( pixelBuffer.GetHeight() );
1771       }
1772
1773       // Set the pixel format.
1774       pixelFormat = pixelBuffer.GetPixelFormat();
1775     }
1776   }
1777
1778   // Find if the same embeddedItem has already been created.
1779   unsigned int index = 0u;
1780   for( const auto& item : mEmbeddedItemCache )
1781   {
1782     ++index;
1783     if( ( item.pixelBufferId == embeddedItem.pixelBufferId ) &&
1784         ( item.width == embeddedItem.width ) &&
1785         ( item.height == embeddedItem.height ) )
1786     {
1787       return index;
1788     }
1789   }
1790
1791   // Cache the embedded item.
1792   mEmbeddedItemCache.PushBack( embeddedItem );
1793
1794   return mEmbeddedItemCache.Count();
1795 }
1796
1797 void FontClient::Plugin::InitSystemFonts()
1798 {
1799   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1800
1801   FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1802
1803   if( fontSet )
1804   {
1805     DALI_LOG_INFO( gLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont );
1806
1807     // Reserve some space to avoid reallocations.
1808     mSystemFonts.reserve( fontSet->nfont );
1809
1810     for( int i = 0u; i < fontSet->nfont; ++i )
1811     {
1812       FcPattern* fontPattern = fontSet->fonts[i];
1813
1814       FontPath path;
1815
1816       // Skip fonts with no path
1817       if( GetFcString( fontPattern, FC_FILE, path ) )
1818       {
1819         mSystemFonts.push_back( FontDescription() );
1820         FontDescription& fontDescription = mSystemFonts.back();
1821
1822         fontDescription.path = std::move( path );
1823
1824         int width = 0;
1825         int weight = 0;
1826         int slant = 0;
1827         GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1828         GetFcInt( fontPattern, FC_WIDTH, width );
1829         GetFcInt( fontPattern, FC_WEIGHT, weight );
1830         GetFcInt( fontPattern, FC_SLANT, slant );
1831         fontDescription.width = IntToWidthType( width );
1832         fontDescription.weight = IntToWeightType( weight );
1833         fontDescription.slant = IntToSlantType( slant );
1834
1835         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", fontDescription.family.c_str() );
1836         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1837         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1838         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1839         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1840       }
1841     }
1842
1843     // Destroys the font set created.
1844     FcFontSetDestroy( fontSet );
1845   }
1846   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1847 }
1848
1849 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1850 {
1851   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1852
1853   FcResult result = FcResultMatch;
1854   FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result ); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1855
1856   const bool matched = nullptr != match;
1857   DALI_LOG_INFO( gLogFilter, Debug::General, "  pattern matched : %s\n", ( matched ? "true" : "false" ) );
1858
1859   if( matched )
1860   {
1861     int width = 0;
1862     int weight = 0;
1863     int slant = 0;
1864     GetFcString( match, FC_FILE, fontDescription.path );
1865     GetFcString( match, FC_FAMILY, fontDescription.family );
1866     GetFcInt( match, FC_WIDTH, width );
1867     GetFcInt( match, FC_WEIGHT, weight );
1868     GetFcInt( match, FC_SLANT, slant );
1869     fontDescription.width = IntToWidthType( width );
1870     fontDescription.weight = IntToWeightType( weight );
1871     fontDescription.slant = IntToSlantType( slant );
1872
1873     // Retrieve the character set and increase the reference counter.
1874     FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1875     *characterSet = FcCharSetCopy( *characterSet );
1876
1877     // destroyed the matched pattern
1878     FcPatternDestroy( match );
1879
1880     DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1881     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1882     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1883     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1884     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1885   }
1886
1887   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1888   return matched;
1889 }
1890
1891 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1892 {
1893   // create the cached font family lookup pattern
1894   // a pattern holds a set of names, each name refers to a property of the font
1895   FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1896
1897   if( !fontFamilyPattern )
1898   {
1899     return nullptr;
1900   }
1901
1902   // add a property to the pattern for the font family
1903   FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1904
1905   // add a property to the pattern for local setting.
1906   const char* locale = setlocale( LC_MESSAGES, nullptr );
1907   if( locale != nullptr)
1908   {
1909     FcPatternAddString( fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>( locale ) );
1910   }
1911
1912   int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1913   if( width < 0 )
1914   {
1915     // Use default.
1916     width = DEFAULT_FONT_WIDTH;
1917   }
1918
1919   int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1920   if( weight < 0 )
1921   {
1922     // Use default.
1923     weight = DEFAULT_FONT_WEIGHT;
1924   }
1925
1926   int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1927   if( slant < 0 )
1928   {
1929     // Use default.
1930     slant = DEFAULT_FONT_SLANT;
1931   }
1932
1933   FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1934   FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1935   FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1936
1937   // modify the config, with the mFontFamilyPatterm
1938   FcConfigSubstitute( nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern );
1939
1940   // provide default values for unspecified properties in the font pattern
1941   // e.g. patterns without a specified style or weight are set to Medium
1942   FcDefaultSubstitute( fontFamilyPattern );
1943
1944   return fontFamilyPattern;
1945 }
1946
1947 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1948 {
1949   FcFontSet* fontset = nullptr;
1950
1951   // create a new pattern.
1952   // a pattern holds a set of names, each name refers to a property of the font
1953   FcPattern* pattern = FcPatternCreate();
1954
1955   if( nullptr != pattern )
1956   {
1957     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1958     FcObjectSet* objectSet = FcObjectSetCreate();
1959
1960     if( nullptr != objectSet )
1961     {
1962       // build an object set from a list of property names
1963       FcObjectSetAdd( objectSet, FC_FILE );
1964       FcObjectSetAdd( objectSet, FC_FAMILY );
1965       FcObjectSetAdd( objectSet, FC_WIDTH );
1966       FcObjectSetAdd( objectSet, FC_WEIGHT );
1967       FcObjectSetAdd( objectSet, FC_SLANT );
1968
1969       // get a list of fonts
1970       // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1971       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.
1972
1973       // clear up the object set
1974       FcObjectSetDestroy( objectSet );
1975     }
1976
1977     // clear up the pattern
1978     FcPatternDestroy( pattern );
1979   }
1980
1981   return fontset;
1982 }
1983
1984 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
1985                                       const char* const n,
1986                                       std::string& string )
1987 {
1988   FcChar8* file = nullptr;
1989   const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
1990
1991   if( FcResultMatch == retVal )
1992   {
1993     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1994     string.assign( reinterpret_cast<const char*>( file ) );
1995
1996     return true;
1997   }
1998
1999   return false;
2000 }
2001
2002 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
2003 {
2004   const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
2005
2006   if( FcResultMatch == retVal )
2007   {
2008     return true;
2009   }
2010
2011   return false;
2012 }
2013
2014 FontId FontClient::Plugin::CreateFont( const FontPath& path,
2015                                        PointSize26Dot6 requestedPointSize,
2016                                        FaceIndex faceIndex,
2017                                        bool cacheDescription )
2018 {
2019   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" );
2020   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
2021   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
2022
2023   FontId id = 0u;
2024
2025   // Create & cache new font face
2026   FT_Face ftFace;
2027   int error = FT_New_Face( mFreeTypeLibrary,
2028                            path.c_str(),
2029                            0,
2030                            &ftFace );
2031
2032   if( FT_Err_Ok == error )
2033   {
2034     // Check if a font is scalable.
2035     const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) );
2036     const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes );
2037     const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) );
2038     FontId fontFaceId = 0u;
2039
2040     DALI_LOG_INFO( gLogFilter, Debug::General, "            isScalable : [%s]\n", ( isScalable ? "true" : "false" ) );
2041     DALI_LOG_INFO( gLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) );
2042     DALI_LOG_INFO( gLogFilter, Debug::General, "        hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) );
2043
2044     // Check to see if the font contains fixed sizes?
2045     if( !isScalable && hasFixedSizedBitmaps )
2046     {
2047       PointSize26Dot6 actualPointSize = 0u;
2048       int fixedSizeIndex = 0;
2049       for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex )
2050       {
2051         const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2052         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize );
2053
2054         if( fixedSize >= requestedPointSize )
2055         {
2056           actualPointSize = fixedSize;
2057           break;
2058         }
2059       }
2060
2061       if( 0u == actualPointSize )
2062       {
2063         // The requested point size is bigger than the bigest fixed size.
2064         fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2065         actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2066       }
2067
2068       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize );
2069
2070       // Tell Freetype to use this size
2071       error = FT_Select_Size( ftFace, fixedSizeIndex );
2072       if ( FT_Err_Ok != error )
2073       {
2074         DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error );
2075       }
2076       else
2077       {
2078         const float fixedWidth  = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].width );
2079         const float fixedHeight = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].height );
2080
2081         // Indicate that the font is a fixed sized bitmap
2082         FontMetrics metrics( fixedHeight, // The ascender in pixels.
2083                              0.0f,
2084                              fixedHeight, // The height in pixels.
2085                              0.0f,
2086                              0.0f );
2087
2088         // Create the FreeType font face item to cache.
2089         FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables );
2090
2091         // Set the index to the font's id cache.
2092         fontFaceCacheItem.mFontId = mFontIdCache.Count();
2093
2094         // Create the font id item to cache.
2095         FontIdCacheItem fontIdCacheItem;
2096         fontIdCacheItem.type = FontDescription::FACE_FONT;
2097
2098         // Set the index to the FreeType font face cache.
2099         fontIdCacheItem.id = mFontFaceCache.size();
2100         fontFaceId = fontIdCacheItem.id + 1u;
2101
2102         // Cache the items.
2103         mFontFaceCache.push_back( fontFaceCacheItem );
2104         mFontIdCache.PushBack( fontIdCacheItem );
2105
2106         // Set the font id to be returned.
2107         id = mFontIdCache.Count();
2108       }
2109     }
2110     else
2111     {
2112       error = FT_Set_Char_Size( ftFace,
2113                                 0,
2114                                 requestedPointSize,
2115                                 mDpiHorizontal,
2116                                 mDpiVertical );
2117
2118       if( FT_Err_Ok == error )
2119       {
2120
2121         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2122
2123         FontMetrics metrics( static_cast< float >( ftMetrics.ascender  ) * FROM_266,
2124                              static_cast< float >( ftMetrics.descender ) * FROM_266,
2125                              static_cast< float >( ftMetrics.height    ) * FROM_266,
2126                              static_cast< float >( ftFace->underline_position ) * FROM_266,
2127                              static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
2128
2129         // Create the FreeType font face item to cache.
2130         FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics );
2131
2132         // Set the index to the font's id cache.
2133         fontFaceCacheItem.mFontId = mFontIdCache.Count();
2134
2135         // Create the font id item to cache.
2136         FontIdCacheItem fontIdCacheItem;
2137         fontIdCacheItem.type = FontDescription::FACE_FONT;
2138
2139         // Set the index to the FreeType font face cache.
2140         fontIdCacheItem.id = mFontFaceCache.size();
2141         fontFaceId = fontIdCacheItem.id + 1u;
2142
2143         // Cache the items.
2144         mFontFaceCache.push_back( fontFaceCacheItem );
2145         mFontIdCache.PushBack( fontIdCacheItem );
2146
2147         // Set the font id to be returned.
2148         id = mFontIdCache.Count();
2149       }
2150       else
2151       {
2152         DALI_LOG_INFO( gLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize );
2153       }
2154     }
2155
2156     if( 0u != fontFaceId )
2157     {
2158       if( cacheDescription )
2159       {
2160         CacheFontPath( ftFace, fontFaceId, requestedPointSize, path );
2161       }
2162     }
2163   }
2164   else
2165   {
2166     DALI_LOG_INFO( gLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str() );
2167   }
2168
2169   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
2170   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" );
2171
2172   return id;
2173 }
2174
2175 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer )
2176 {
2177   // Set the input dimensions.
2178   const ImageDimensions inputDimensions( srcWidth, srcHeight );
2179
2180   // Set the output dimensions.
2181   // If the output dimension is not given, the input dimension is set
2182   // and won't be downscaling.
2183   data.width = ( data.width == 0 ) ? srcWidth : data.width;
2184   data.height = ( data.height == 0 ) ? srcHeight : data.height;
2185   const ImageDimensions desiredDimensions( data.width, data.height );
2186
2187   // Creates the output buffer
2188   const unsigned int bufferSize = data.width * data.height * 4u;
2189   data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2190
2191   if( inputDimensions == desiredDimensions )
2192   {
2193     // There isn't downscaling.
2194     memcpy( data.buffer, srcBuffer, bufferSize );
2195   }
2196   else
2197   {
2198     Dali::Internal::Platform::LanczosSample4BPP( srcBuffer,
2199                                                  inputDimensions,
2200                                                  data.buffer,
2201                                                  desiredDimensions );
2202   }
2203 }
2204
2205 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired )
2206 {
2207   if( srcBitmap.width*srcBitmap.rows > 0 )
2208   {
2209     switch( srcBitmap.pixel_mode )
2210     {
2211       case FT_PIXEL_MODE_GRAY:
2212       {
2213         if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
2214         {
2215           uint8_t* pixelsIn = srcBitmap.buffer;
2216           unsigned int width = srcBitmap.width;
2217           unsigned height = srcBitmap.rows;
2218
2219           std::unique_ptr<uint8_t, void(*)(void*)> pixelsOutPtr( nullptr, free );
2220
2221           if( isShearRequired )
2222           {
2223             /**
2224              * Glyphs' bitmaps with no slant retrieved from FreeType:
2225              * __________     ____
2226              * |XXXXXXXX|     |XX|
2227              * |   XX   |     |XX|
2228              * |   XX   |     |XX|
2229              * |   XX   |     |XX|
2230              * |   XX   |     |XX|
2231              * |   XX   |     |XX|
2232              * ----------     ----
2233              *
2234              * Expected glyphs' bitmaps with italic slant:
2235              * ____________   ______
2236              * |  XXXXXXXX|   |  XX|
2237              * |     XX   |   |  XX|
2238              * |    XX    |   | XX |
2239              * |    XX    |   | XX |
2240              * |   XX     |   |XX  |
2241              * |   XX     |   |XX  |
2242              * ------------   ------
2243              *
2244              * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2245              * __________     ______
2246              * |XXXXXXXX|     |  XX|
2247              * |   XX   |     |  XX|
2248              * |  XX    |     | XX |
2249              * |  XX    |     | XX |
2250              * | XX     |     |XX  |
2251              * | XX     |     |XX  |
2252              * ----------     ------
2253              *
2254              * 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.
2255              */
2256             unsigned int widthOut = 0u;
2257             unsigned int heightOut = 0u;
2258             uint8_t* pixelsOut = nullptr;
2259
2260             Dali::Internal::Platform::HorizontalShear( pixelsIn,
2261                                                        width,
2262                                                        height,
2263                                                        1u,
2264                                                        -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2265                                                        pixelsOut,
2266                                                        widthOut,
2267                                                        heightOut );
2268
2269             width = widthOut;
2270             height = heightOut;
2271             pixelsIn = pixelsOut;
2272             pixelsOutPtr.reset( pixelsOut );
2273           }
2274
2275           const unsigned int bufferSize = width * height;
2276           data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2277           data.width = width;
2278           data.height = height;
2279           data.format = Pixel::L8; // Sets the pixel format.
2280           memcpy( data.buffer, pixelsIn, bufferSize );
2281         }
2282         break;
2283       }
2284
2285 #ifdef FREETYPE_BITMAP_SUPPORT
2286       case FT_PIXEL_MODE_BGRA:
2287       {
2288         if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
2289         {
2290           ConvertBitmap( data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer );
2291
2292           // Sets the pixel format.
2293           data.format = Pixel::BGRA8888;
2294         }
2295         break;
2296       }
2297 #endif
2298       default:
2299       {
2300         DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" );
2301         break;
2302       }
2303     }
2304   }
2305 }
2306
2307 bool FontClient::Plugin::FindFont( const FontPath& path,
2308                                    PointSize26Dot6 requestedPointSize,
2309                                    FaceIndex faceIndex,
2310                                    FontId& fontId ) const
2311 {
2312   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2313   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
2314   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
2315   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of fonts in the cache : %d\n", mFontFaceCache.size() );
2316
2317   fontId = 0u;
2318   for( const auto& cacheItem : mFontFaceCache )
2319   {
2320     if( cacheItem.mRequestedPointSize == requestedPointSize &&
2321         cacheItem.mFaceIndex == faceIndex &&
2322         cacheItem.mPath == path )
2323     {
2324       fontId = cacheItem.mFontId + 1u;
2325
2326       DALI_LOG_INFO( gLogFilter, Debug::General, "  font found, id : %d\n", fontId );
2327       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2328
2329       return true;
2330     }
2331   }
2332
2333   DALI_LOG_INFO( gLogFilter, Debug::General, "  font not found\n" );
2334   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2335
2336   return false;
2337 }
2338
2339 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
2340                                             FontDescriptionId& validatedFontId )
2341 {
2342   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" );
2343   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
2344   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
2345   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
2346   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2347   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2348   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %d\n", mValidatedFontCache.size() );
2349
2350   validatedFontId = 0u;
2351
2352   for( const auto& item : mValidatedFontCache )
2353   {
2354     if( !fontDescription.family.empty() &&
2355         ( fontDescription.family == item.fontDescription.family ) &&
2356         ( fontDescription.width == item.fontDescription.width ) &&
2357         ( fontDescription.weight == item.fontDescription.weight ) &&
2358         ( fontDescription.slant == item.fontDescription.slant ) )
2359     {
2360       validatedFontId = item.index;
2361
2362       DALI_LOG_INFO( gLogFilter, Debug::General, "  validated font found, id : %d\n", validatedFontId );
2363       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2364       return true;
2365     }
2366   }
2367
2368   DALI_LOG_INFO( gLogFilter, Debug::General, "  validated font not found\n" );
2369   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2370   return false;
2371 }
2372
2373 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
2374                                                FontList*& fontList,
2375                                                CharacterSetList*& characterSetList )
2376 {
2377   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" );
2378   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
2379   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
2380   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
2381   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2382   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2383   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of fallback font lists in the cache : %d\n", mFallbackCache.size() );
2384
2385   fontList = nullptr;
2386
2387   for( const auto& item : mFallbackCache )
2388   {
2389     if( !fontDescription.family.empty() &&
2390         ( fontDescription.family == item.fontDescription.family ) &&
2391         ( fontDescription.width == item.fontDescription.width ) &&
2392         ( fontDescription.weight == item.fontDescription.weight ) &&
2393         ( fontDescription.slant == item.fontDescription.slant ) )
2394     {
2395       fontList = item.fallbackFonts;
2396       characterSetList = item.characterSets;
2397
2398       DALI_LOG_INFO( gLogFilter, Debug::General, "  fallback font list found.\n" );
2399       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2400       return true;
2401     }
2402   }
2403
2404   DALI_LOG_INFO( gLogFilter, Debug::General, "  fallback font list not found.\n" );
2405   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2406   return false;
2407 }
2408
2409 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
2410                                    PointSize26Dot6 requestedPointSize,
2411                                    FontId& fontId )
2412 {
2413   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2414   DALI_LOG_INFO( gLogFilter, Debug::General, "    validatedFontId  : %d\n", validatedFontId );
2415   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
2416
2417   fontId = 0u;
2418
2419   for( const auto& item : mFontDescriptionSizeCache )
2420   {
2421     if( ( validatedFontId == item.validatedFontId ) &&
2422         ( requestedPointSize == item.requestedPointSize ) )
2423     {
2424       fontId = item.fontId;
2425
2426       DALI_LOG_INFO( gLogFilter, Debug::General, "  font found, id : %d\n", fontId );
2427       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2428       return true;
2429     }
2430   }
2431
2432   DALI_LOG_INFO( gLogFilter, Debug::General, "  font not found.\n" );
2433   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2434   return false;
2435 }
2436
2437 bool FontClient::Plugin::FindBitmapFont( const FontFamily& bitmapFont, FontId& fontId ) const
2438 {
2439   fontId = 0u;
2440
2441   for( const auto& item : mBitmapFontCache )
2442   {
2443     if( bitmapFont == item.font.name )
2444     {
2445       fontId = item.id + 1u;
2446       return true;
2447     }
2448   }
2449
2450   return false;
2451 }
2452
2453 bool FontClient::Plugin::IsScalable( const FontPath& path )
2454 {
2455   bool isScalable = false;
2456
2457   FT_Face ftFace;
2458   int error = FT_New_Face( mFreeTypeLibrary,
2459                            path.c_str(),
2460                            0,
2461                            &ftFace );
2462   if( FT_Err_Ok != error )
2463   {
2464     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() );
2465   }
2466   else
2467   {
2468     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2469   }
2470
2471   return isScalable;
2472 }
2473
2474 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
2475 {
2476   // Create a font pattern.
2477   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2478
2479   FcResult result = FcResultMatch;
2480
2481   // match the pattern
2482   FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2483   bool isScalable = false;
2484
2485   if( match )
2486   {
2487     // Get the path to the font file name.
2488     FontPath path;
2489     GetFcString( match, FC_FILE, path );
2490     isScalable = IsScalable( path );
2491   }
2492   else
2493   {
2494     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2495   }
2496
2497   // Destroys the created patterns.
2498   FcPatternDestroy( match );
2499   FcPatternDestroy( fontFamilyPattern );
2500
2501   return isScalable;
2502 }
2503
2504 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
2505 {
2506   // Empty the caller container
2507   sizes.Clear();
2508
2509   FT_Face ftFace;
2510   int error = FT_New_Face( mFreeTypeLibrary,
2511                            path.c_str(),
2512                            0,
2513                            &ftFace );
2514   if( FT_Err_Ok != error )
2515   {
2516     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() );
2517   }
2518
2519   // Fetch the number of fixed sizes available
2520   if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
2521   {
2522     for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
2523     {
2524       sizes.PushBack( ftFace->available_sizes[ i ].size );
2525     }
2526   }
2527 }
2528
2529 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
2530                                         Vector< PointSize26Dot6 >& sizes )
2531 {
2532   // Create a font pattern.
2533   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2534
2535   FcResult result = FcResultMatch;
2536
2537   // match the pattern
2538   FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2539
2540   if( match )
2541   {
2542     // Get the path to the font file name.
2543     FontPath path;
2544     GetFcString( match, FC_FILE, path );
2545     GetFixedSizes( path, sizes );
2546   }
2547   else
2548   {
2549     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2550   }
2551
2552   // Destroys the created patterns.
2553   FcPatternDestroy( match );
2554   FcPatternDestroy( fontFamilyPattern );
2555 }
2556
2557 bool FontClient::Plugin::HasItalicStyle( FontId fontId ) const
2558 {
2559   bool hasItalicStyle = false;
2560
2561   const FontId index = fontId - 1u;
2562
2563   if( ( fontId > 0 ) &&
2564       ( index < mFontIdCache.Count() ) )
2565   {
2566     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2567
2568     if( FontDescription::FACE_FONT == fontIdCacheItem.type )
2569     {
2570       const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2571
2572       hasItalicStyle = 0u != ( font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC );
2573     }
2574   }
2575   else
2576   {
2577     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
2578   }
2579
2580   return hasItalicStyle;
2581 }
2582
2583 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize,  const FontPath& path )
2584 {
2585   FontDescription description;
2586   description.path = path;
2587   description.family = std::move( FontFamily( ftFace->family_name ) );
2588   description.weight = FontWeight::NONE;
2589   description.width = FontWidth::NONE;
2590   description.slant = FontSlant::NONE;
2591
2592   // Note FreeType doesn't give too much info to build a proper font style.
2593   if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
2594   {
2595     description.slant = FontSlant::ITALIC;
2596   }
2597   if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
2598   {
2599     description.weight = FontWeight::BOLD;
2600   }
2601
2602   FontDescriptionId validatedFontId = 0u;
2603   if( !FindValidatedFont( description,
2604                           validatedFontId ) )
2605   {
2606     // Set the index to the vector of paths to font file names.
2607     validatedFontId = mFontDescriptionCache.size();
2608
2609     FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2610
2611     FcResult result = FcResultMatch;
2612     FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2613
2614     FcCharSet* characterSet = nullptr;
2615     FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2616
2617     const FontId fontFaceId = id - 1u;
2618     mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( characterSet ); // Increases the reference counter.
2619
2620     // Destroys the created patterns.
2621     FcPatternDestroy( match );
2622     FcPatternDestroy( pattern );
2623
2624     // Add the path to the cache.
2625     description.type = FontDescription::FACE_FONT;
2626     mFontDescriptionCache.push_back( description );
2627
2628     // Increase the reference counter and add the character set to the cache.
2629     mCharacterSetCache.PushBack( FcCharSetCopy( characterSet ) );
2630
2631     // Cache the index and the font's description.
2632     mValidatedFontCache.push_back( std::move( FontDescriptionCacheItem( std::move( description ),
2633                                                                         validatedFontId) ) );
2634
2635     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2636     mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
2637                                                                        requestedPointSize,
2638                                                                        fontFaceId ) );
2639   }
2640 }
2641
2642 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description )
2643 {
2644   FcCharSet* characterSet = nullptr;
2645
2646   FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2647
2648   if( nullptr != pattern )
2649   {
2650     FcResult result = FcResultMatch;
2651     FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2652
2653     FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2654
2655     // Destroys the created patterns.
2656     FcPatternDestroy( match );
2657     FcPatternDestroy( pattern );
2658   }
2659
2660   return characterSet;
2661 }
2662
2663 void FontClient::Plugin::ClearFallbackCache( std::vector<FallbackCacheItem>& fallbackCache )
2664 {
2665   for( auto& item : fallbackCache )
2666   {
2667     if( nullptr != item.fallbackFonts )
2668     {
2669       delete item.fallbackFonts;
2670     }
2671
2672     if( nullptr != item.characterSets )
2673     {
2674       // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2675       DestroyCharacterSets( *item.characterSets );
2676       delete item.characterSets;
2677     }
2678   }
2679 }
2680
2681 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2682 {
2683   for( auto& item : mFontFaceCache )
2684   {
2685     FcCharSetDestroy( item.mCharacterSet );
2686     item.mCharacterSet = nullptr;
2687   }
2688 }
2689
2690 } // namespace Internal
2691
2692 } // namespace TextAbstraction
2693
2694 } // namespace Dali