clear cache when locale is changed
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / font-client-plugin-impl.cpp
1 /*
2  * Copyright (c) 2018 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 FONT_FORMAT( "TrueType" );
50 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
51 const int DEFAULT_FONT_WIDTH  = 100; // normal
52 const int DEFAULT_FONT_WEIGHT =  80; // normal
53 const int DEFAULT_FONT_SLANT  =   0; // normal
54
55 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
56
57 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
58
59 // NONE            -1  --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
60 // ULTRA_CONDENSED 50
61 // EXTRA_CONDENSED 63
62 // CONDENSED       75
63 // SEMI_CONDENSED  87
64 // NORMAL         100
65 // SEMI_EXPANDED  113
66 // EXPANDED       125
67 // EXTRA_EXPANDED 150
68 // ULTRA_EXPANDED 200
69 const int FONT_WIDTH_TYPE_TO_INT[] = { -1, 50, 63, 75, 87, 100, 113, 125, 150, 200 };
70 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / sizeof( int );
71
72 // NONE                       -1  --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
73 // THIN                        0
74 // ULTRA_LIGHT, EXTRA_LIGHT   40
75 // LIGHT                      50
76 // DEMI_LIGHT, SEMI_LIGHT     55
77 // BOOK                       75
78 // NORMAL, REGULAR            80
79 // MEDIUM                    100
80 // DEMI_BOLD, SEMI_BOLD      180
81 // BOLD                      200
82 // ULTRA_BOLD, EXTRA_BOLD    205
83 // BLACK, HEAVY, EXTRA_BLACK 210
84 const int FONT_WEIGHT_TYPE_TO_INT[] = { -1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210 };
85 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof( FONT_WEIGHT_TYPE_TO_INT ) / sizeof( int );
86
87 // NONE             -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
88 // NORMAL, ROMAN     0
89 // ITALIC          100
90 // OBLIQUE         110
91 const int FONT_SLANT_TYPE_TO_INT[] = { -1, 0, 100, 110 };
92 const unsigned int NUM_FONT_SLANT_TYPE = sizeof( FONT_SLANT_TYPE_TO_INT ) / sizeof( int );
93
94 } // namespace
95
96
97
98 using Dali::Vector;
99
100 namespace Dali
101 {
102
103 namespace TextAbstraction
104 {
105
106 namespace Internal
107 {
108
109 /**
110  * @brief Returns the FontWidth's enum index for the given width value.
111  *
112  * @param[in] width The width value.
113  *
114  * @return The FontWidth's enum index.
115  */
116 FontWidth::Type IntToWidthType( int width )
117 {
118   return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
119 }
120
121 /**
122  * @brief Returns the FontWeight's enum index for the given weight value.
123  *
124  * @param[in] weight The weight value.
125  *
126  * @return The FontWeight's enum index.
127  */
128 FontWeight::Type IntToWeightType( int weight )
129 {
130   return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
131 }
132
133 /**
134  * @brief Returns the FontSlant's enum index for the given slant value.
135  *
136  * @param[in] slant The slant value.
137  *
138  * @return The FontSlant's enum index.
139  */
140 FontSlant::Type IntToSlantType( int slant )
141 {
142   return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
143 }
144
145 /**
146  * @brief Free the resources allocated by the FcCharSet objects.
147  *
148  * @param[in] characterSets The vector of character sets.
149  */
150 void DestroyCharacterSets( CharacterSetList& characterSets )
151 {
152   for( auto& item : characterSets )
153   {
154     FcCharSetDestroy( item );
155   }
156 }
157
158 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets )
159 : fontDescription{ std::move( font ) },
160   fallbackFonts{ fallbackFonts },
161   characterSets{ characterSets }
162 {
163 }
164
165 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
166                                                                         FontDescriptionId index )
167 : fontDescription{ fontDescription },
168   index{ index }
169 {
170 }
171
172 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( FontDescription&& fontDescription,
173                                                                         FontDescriptionId index )
174 : fontDescription{ std::move( fontDescription ) },
175   index{ index }
176 {
177 }
178
179 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem( FontDescriptionId validatedFontId,
180                                                                                 PointSize26Dot6 requestedPointSize,
181                                                                                 FontId fontId )
182 : validatedFontId( validatedFontId ),
183   requestedPointSize( requestedPointSize ),
184   fontId( fontId )
185 {
186 }
187
188 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
189                                                           const FontPath& path,
190                                                           PointSize26Dot6 requestedPointSize,
191                                                           FaceIndex face,
192                                                           const FontMetrics& metrics )
193 : mFreeTypeFace( ftFace ),
194   mPath( path ),
195   mRequestedPointSize( requestedPointSize ),
196   mFaceIndex( face ),
197   mMetrics( metrics ),
198   mCharacterSet( nullptr ),
199   mFixedSizeIndex( 0 ),
200   mFixedWidthPixels( 0.f ),
201   mFixedHeightPixels( 0.f ),
202   mVectorFontId( 0u ),
203   mFontId( 0u ),
204   mIsFixedSizeBitmap( false ),
205   mHasColorTables( false )
206 {
207 }
208
209 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
210                                                           const FontPath& path,
211                                                           PointSize26Dot6 requestedPointSize,
212                                                           FaceIndex face,
213                                                           const FontMetrics& metrics,
214                                                           int fixedSizeIndex,
215                                                           float fixedWidth,
216                                                           float fixedHeight,
217                                                           bool hasColorTables )
218 : mFreeTypeFace( ftFace ),
219   mPath( path ),
220   mRequestedPointSize( requestedPointSize ),
221   mFaceIndex( face ),
222   mMetrics( metrics ),
223   mCharacterSet( nullptr ),
224   mFixedSizeIndex( fixedSizeIndex ),
225   mFixedWidthPixels( fixedWidth ),
226   mFixedHeightPixels( fixedHeight ),
227   mVectorFontId( 0u ),
228   mFontId( 0u ),
229   mIsFixedSizeBitmap( true ),
230   mHasColorTables( hasColorTables )
231 {
232 }
233
234 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
235                             unsigned int verticalDpi )
236 : mFreeTypeLibrary( nullptr ),
237   mDpiHorizontal( horizontalDpi ),
238   mDpiVertical( verticalDpi ),
239   mDefaultFontDescription(),
240   mSystemFonts(),
241   mDefaultFonts(),
242   mFontIdCache(),
243   mFontFaceCache(),
244   mValidatedFontCache(),
245   mFontDescriptionCache( 1u ),
246   mCharacterSetCache(),
247   mFontDescriptionSizeCache(),
248   mVectorFontCache( nullptr ),
249   mEllipsisCache(),
250   mEmbeddedItemCache(),
251   mDefaultFontDescriptionCached( false )
252 {
253   mCharacterSetCache.Resize( 1u );
254
255   int error = FT_Init_FreeType( &mFreeTypeLibrary );
256   if( FT_Err_Ok != error )
257   {
258     DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Init error: %d\n", error );
259   }
260
261 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
262   mVectorFontCache = new VectorFontCache( mFreeTypeLibrary );
263 #endif
264
265 }
266
267 FontClient::Plugin::~Plugin()
268 {
269   ClearFallbackCache( mFallbackCache );
270
271   // Free the resources allocated by the FcCharSet objects.
272   DestroyCharacterSets( mDefaultFontCharacterSets );
273   DestroyCharacterSets( mCharacterSetCache );
274   ClearCharacterSetFromFontFaceCache();
275
276 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
277   delete mVectorFontCache;
278 #endif
279   FT_Done_FreeType( mFreeTypeLibrary );
280 }
281
282 void FontClient::Plugin::ClearCache()
283 {
284   mDefaultFontDescription = FontDescription();
285
286   mSystemFonts.clear();
287   mDefaultFonts.clear();
288
289   DestroyCharacterSets( mDefaultFontCharacterSets );
290   mDefaultFontCharacterSets.Clear();
291
292   ClearFallbackCache( mFallbackCache );
293   mFallbackCache.clear();
294
295   mFontIdCache.Clear();
296
297   ClearCharacterSetFromFontFaceCache();
298   mFontFaceCache.clear();
299
300   mValidatedFontCache.clear();
301   mFontDescriptionCache.clear();
302   mFontDescriptionCache.resize( 1u );
303
304   DestroyCharacterSets( mCharacterSetCache );
305   mCharacterSetCache.Clear();
306   mCharacterSetCache.Resize( 1u );
307
308   mFontDescriptionSizeCache.clear();
309
310   mEllipsisCache.Clear();
311   mPixelBufferCache.clear();
312   mEmbeddedItemCache.Clear();
313   mBitmapFontCache.clear();
314
315   mDefaultFontDescriptionCached = false;
316 }
317
318 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
319                                  unsigned int verticalDpi )
320 {
321   mDpiHorizontal = horizontalDpi;
322   mDpiVertical = verticalDpi;
323 }
324
325 void FontClient::Plugin::ResetSystemDefaults()
326 {
327   mDefaultFontDescriptionCached = false;
328 }
329
330 void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList )
331 {
332   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n" );
333   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
334   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
335   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
336   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
337
338   fontList.clear();
339
340   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
341
342   FcResult result = FcResultMatch;
343
344   // Match the pattern.
345   FcFontSet* fontSet = FcFontSort( nullptr /* use default configure */,
346                                    fontFamilyPattern,
347                                    false /* don't trim */,
348                                    nullptr,
349                                    &result ); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
350
351   if( nullptr != fontSet )
352   {
353     DALI_LOG_INFO( gLogFilter, Debug::General, "  number of fonts found : [%d]\n", fontSet->nfont );
354     // Reserve some space to avoid reallocations.
355     fontList.reserve( fontSet->nfont );
356
357     for( int i = 0u; i < fontSet->nfont; ++i )
358     {
359       FcPattern* fontPattern = fontSet->fonts[i];
360
361       FontPath path;
362
363       // Skip fonts with no path
364       if( GetFcString( fontPattern, FC_FILE, path ) )
365       {
366         // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
367         FcCharSet* characterSet = nullptr;
368         FcPatternGetCharSet( fontPattern, FC_CHARSET, 0u, &characterSet );
369
370         // Increase the reference counter of the character set.
371         characterSetList.PushBack( FcCharSetCopy( characterSet ) );
372
373         fontList.push_back( FontDescription() );
374         FontDescription& newFontDescription = fontList.back();
375
376         newFontDescription.path = std::move( path );
377
378         int width = 0;
379         int weight = 0;
380         int slant = 0;
381         GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
382         GetFcInt( fontPattern, FC_WIDTH, width );
383         GetFcInt( fontPattern, FC_WEIGHT, weight );
384         GetFcInt( fontPattern, FC_SLANT, slant );
385         newFontDescription.width = IntToWidthType( width );
386         newFontDescription.weight = IntToWeightType( weight );
387         newFontDescription.slant = IntToSlantType( slant );
388
389         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", newFontDescription.family.c_str() );
390         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", newFontDescription.path.c_str() );
391         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[newFontDescription.width] );
392         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[newFontDescription.weight] );
393         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant] );
394       }
395     }
396
397     // Destroys the font set created by FcFontSort.
398     FcFontSetDestroy( fontSet );
399   }
400   else
401   {
402     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  No fonts found.\n" );
403   }
404
405   // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
406   FcPatternDestroy( fontFamilyPattern );
407
408   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n" );
409 }
410
411 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
412 {
413   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n" );
414
415   if( mDefaultFonts.empty() )
416   {
417     FontDescription fontDescription;
418     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;  // todo This could be set to the Platform font
419     fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
420     fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
421     fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
422     SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
423   }
424
425   defaultFonts = mDefaultFonts;
426
427   DALI_LOG_INFO( gLogFilter, Debug::General, "  number of default fonts : [%d]\n", mDefaultFonts.size() );
428   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n" );
429 }
430
431 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
432 {
433   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
434
435   if( !mDefaultFontDescriptionCached )
436   {
437     // Clear any font config stored info in the caches.
438
439     // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
440     DestroyCharacterSets( mDefaultFontCharacterSets );
441     DestroyCharacterSets( mCharacterSetCache );
442     mDefaultFontCharacterSets.Clear();
443     mCharacterSetCache.Clear();
444
445     for( auto& item : mFallbackCache )
446     {
447       // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
448       DestroyCharacterSets( *item.characterSets );
449
450       delete item.characterSets;
451       item.characterSets = nullptr;
452     }
453
454     // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
455     ClearCharacterSetFromFontFaceCache();
456
457     // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
458     FcInitReinitialize();
459
460     FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
461
462     if( nullptr != matchPattern )
463     {
464       FcConfigSubstitute( nullptr, matchPattern, FcMatchPattern );
465       FcDefaultSubstitute( matchPattern );
466
467       FcCharSet* characterSet = nullptr;
468       MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription, &characterSet );
469       // Decrease the reference counter of the character set as it's not stored.
470       FcCharSetDestroy( characterSet );
471
472       // Destroys the pattern created.
473       FcPatternDestroy( matchPattern );
474     }
475
476     // Create again the character sets as they are not valid after FcInitReinitialize()
477
478     for( const auto& description : mDefaultFonts )
479     {
480       mDefaultFontCharacterSets.PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
481     }
482
483     for( const auto& description : mFontDescriptionCache )
484     {
485       mCharacterSetCache.PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
486     }
487
488     for( auto& item : mFallbackCache )
489     {
490       if( nullptr != item.fallbackFonts )
491       {
492         if( nullptr == item.characterSets )
493         {
494           item.characterSets = new CharacterSetList;
495         }
496
497         for( const auto& description : *( item.fallbackFonts ) )
498         {
499           item.characterSets->PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
500         }
501       }
502     }
503
504     mDefaultFontDescriptionCached = true;
505   }
506
507   fontDescription.path   = mDefaultFontDescription.path;
508   fontDescription.family = mDefaultFontDescription.family;
509   fontDescription.width  = mDefaultFontDescription.width;
510   fontDescription.weight = mDefaultFontDescription.weight;
511   fontDescription.slant  = mDefaultFontDescription.slant;
512
513   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
514   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
515   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
516   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
517   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
518   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
519 }
520
521 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
522 {
523   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
524
525   if( mSystemFonts.empty() )
526   {
527     InitSystemFonts();
528   }
529
530   systemFonts = mSystemFonts;
531   DALI_LOG_INFO( gLogFilter, Debug::General, "  number of system fonts : [%d]\n", mSystemFonts.size() );
532   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
533 }
534
535 void FontClient::Plugin::GetDescription( FontId id,
536                                          FontDescription& fontDescription ) const
537 {
538   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
539   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
540   const FontId index = id - 1u;
541
542   if( ( id > 0u ) && ( index < mFontIdCache.Count() ) )
543   {
544     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
545     switch( fontIdCacheItem.type )
546     {
547       case FontDescription::FACE_FONT:
548       {
549         for( const auto& item : mFontDescriptionSizeCache )
550         {
551           if( item.fontId == fontIdCacheItem.id )
552           {
553             fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
554
555             DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
556             DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
557             DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
558             DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
559             DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
560             DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
561             return;
562           }
563         }
564         break;
565       }
566       case FontDescription::BITMAP_FONT:
567       {
568         fontDescription.type = FontDescription::BITMAP_FONT;
569         fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
570         break;
571       }
572       default:
573       {
574         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
575         fontDescription.type = FontDescription::INVALID;
576         fontDescription.family.clear();
577       }
578     }
579   }
580
581   DALI_LOG_INFO( gLogFilter, Debug::General, "  No description found for the font ID %d\n", id );
582   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
583 }
584
585 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
586 {
587   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
588   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
589   const FontId index = id - 1u;
590
591   if( ( id > 0u ) &&
592       ( index < mFontIdCache.Count() ) )
593   {
594     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
595
596     switch( fontIdCacheItem.type )
597     {
598       case FontDescription::FACE_FONT:
599       {
600         DALI_LOG_INFO( gLogFilter, Debug::General, "  point size : %d\n", ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize );
601         DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
602         return ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize;
603       }
604       case FontDescription::BITMAP_FONT:
605       {
606         return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
607       }
608       default:
609       {
610         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
611       }
612     }
613   }
614   else
615   {
616     DALI_LOG_INFO( gLogFilter, Debug::General, "  Invalid font ID %d\n", id );
617   }
618
619   DALI_LOG_INFO( gLogFilter, Debug::General, "  default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE );
620   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
621   return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
622 }
623
624 bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character character )
625 {
626   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
627   DALI_LOG_INFO( gLogFilter, Debug::General, "    font id : %d\n", fontId );
628   DALI_LOG_INFO( gLogFilter, Debug::General, "  character : %p\n", character );
629
630   if( ( fontId < 1u ) || ( fontId > mFontIdCache.Count() ) )
631   {
632     DALI_LOG_INFO( gLogFilter, Debug::General, "  Invalid font id. Number of items in the cache: %d\n",mFontIdCache.Count());
633     DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
634     return false;
635   }
636
637   --fontId;
638
639   bool isSupported = false;
640
641   const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
642
643   switch( fontIdCacheItem.type )
644   {
645     case FontDescription::FACE_FONT:
646     {
647       if( fontIdCacheItem.id < mFontFaceCache.size() )
648       {
649         FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
650
651         if( nullptr == cacheItem.mCharacterSet )
652         {
653           // Create again the character set.
654           // It can be null if the ResetSystemDefaults() method has been called.
655
656           FontDescription description;
657           description.path = cacheItem.mPath;
658           description.family = std::move( FontFamily( cacheItem.mFreeTypeFace->family_name ) );
659           description.weight = FontWeight::NONE;
660           description.width = FontWidth::NONE;
661           description.slant = FontSlant::NONE;
662
663           // Note FreeType doesn't give too much info to build a proper font style.
664           if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC )
665           {
666             description.slant = FontSlant::ITALIC;
667           }
668           if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD )
669           {
670             description.weight = FontWeight::BOLD;
671           }
672
673           cacheItem.mCharacterSet = FcCharSetCopy( CreateCharacterSetFromDescription( description ) );
674         }
675
676         isSupported = FcCharSetHasChar( cacheItem.mCharacterSet, character );
677       }
678       break;
679     }
680     case FontDescription::BITMAP_FONT:
681     {
682       const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
683
684       for( const auto& glyph : bitmapFont.glyphs )
685       {
686         if( glyph.utf32 == character )
687         {
688           isSupported = true;
689           break;
690         }
691       }
692       break;
693     }
694     default:
695     {
696       DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
697     }
698   }
699
700   DALI_LOG_INFO( gLogFilter, Debug::General, "  is supported : %s\n", (isSupported ? "true" : "false") );
701   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
702   return isSupported;
703 }
704
705 FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
706                                                  const CharacterSetList& characterSetList,
707                                                  Character character,
708                                                  PointSize26Dot6 requestedPointSize,
709                                                  bool preferColor )
710 {
711   DALI_ASSERT_DEBUG( ( fontList.size() == characterSetList.Count() ) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets." );
712
713   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n" );
714   DALI_LOG_INFO( gLogFilter, Debug::General, "           character : %p\n", character );
715   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
716   DALI_LOG_INFO( gLogFilter, Debug::General, "         preferColor : %s\n", ( preferColor ? "true" : "false" ) );
717
718   FontId fontId = 0u;
719   bool foundColor = false;
720
721   DALI_LOG_INFO( gLogFilter, Debug::General, "  number of fonts : %d\n", fontList.size() );
722
723   // Traverse the list of fonts.
724   // Check for each font if supports the character.
725   for( unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index )
726   {
727     const FontDescription& description = fontList[index];
728     const FcCharSet* const characterSet = characterSetList[index];
729
730     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", description.family.c_str() );
731     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", description.path.c_str() );
732     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[description.width] );
733     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[description.weight] );
734     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[description.slant] );
735
736     bool foundInRanges = false;
737     if( nullptr != characterSet )
738     {
739       foundInRanges = FcCharSetHasChar( characterSet, character );
740     }
741
742     if( foundInRanges )
743     {
744       fontId = GetFontId( description,
745                           requestedPointSize,
746                           0u );
747
748       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "     font id : %d\n", fontId );
749
750       if( preferColor )
751       {
752         if( ( fontId > 0 ) &&
753             ( fontId - 1u < mFontIdCache.Count() ) )
754         {
755           const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
756
757           foundColor = item.mHasColorTables;
758         }
759
760         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  foundColor : %s\n", ( foundColor ? "true" : "false" ) );
761       }
762
763       // Keep going unless we prefer a different (color) font.
764       if( !preferColor || foundColor )
765       {
766         break;
767       }
768     }
769   }
770
771   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
772   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n" );
773   return fontId;
774 }
775
776 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
777                                             PointSize26Dot6 requestedPointSize,
778                                             bool preferColor )
779 {
780   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n" );
781   DALI_LOG_INFO( gLogFilter, Debug::General, "           character : %p\n", charcode );
782   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
783   DALI_LOG_INFO( gLogFilter, Debug::General, "         preferColor : %s\n", ( preferColor ? "true" : "false" ) );
784
785   FontId fontId(0);
786
787   // Create the list of default fonts if it has not been created.
788   if( mDefaultFonts.empty() )
789   {
790     FontDescription fontDescription;
791     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
792     fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
793     fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
794     fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
795
796     SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
797   }
798   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of default fonts : %d\n", mDefaultFonts.size() );
799
800
801   // Traverse the list of default fonts.
802   // Check for each default font if supports the character.
803   fontId = FindFontForCharacter( mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor );
804
805   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
806   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n" );
807
808   return fontId;
809 }
810
811 FontId FontClient::Plugin::FindFallbackFont( Character charcode,
812                                              const FontDescription& preferredFontDescription,
813                                              PointSize26Dot6 requestedPointSize,
814                                              bool preferColor )
815 {
816   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n" );
817   DALI_LOG_INFO( gLogFilter, Debug::General, "           character : %p\n", charcode );
818   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
819   DALI_LOG_INFO( gLogFilter, Debug::General, "         preferColor : %s\n", ( preferColor ? "true" : "false" ) );
820
821   // The font id to be returned.
822   FontId fontId = 0u;
823
824   FontDescription fontDescription;
825
826   // Fill the font description with the preferred font description and complete with the defaults.
827   fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
828   fontDescription.weight = ( ( FontWeight::NONE == preferredFontDescription.weight ) ? IntToWeightType( DEFAULT_FONT_WEIGHT ) : preferredFontDescription.weight );
829   fontDescription.width = ( ( FontWidth::NONE == preferredFontDescription.width ) ? IntToWidthType( DEFAULT_FONT_WIDTH ) : preferredFontDescription.width );
830   fontDescription.slant = ( ( FontSlant::NONE == preferredFontDescription.slant ) ? IntToSlantType( DEFAULT_FONT_SLANT ) : preferredFontDescription.slant );
831
832   DALI_LOG_INFO( gLogFilter, Debug::General, "  preferredFontDescription --> fontDescription\n" );
833   DALI_LOG_INFO( gLogFilter, Debug::General, "  [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str() );
834   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight] );
835   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width] );
836   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant] );
837
838   // Check first if the font's description has been queried before.
839   FontList* fontList = nullptr;
840   CharacterSetList* characterSetList = nullptr;
841
842   if( !FindFallbackFontList( fontDescription, fontList, characterSetList ) )
843   {
844     fontList = new FontList;
845     characterSetList = new CharacterSetList;
846
847     SetFontList( fontDescription, *fontList, *characterSetList );
848
849     // Add the font-list to the cache.
850     mFallbackCache.push_back( std::move( FallbackCacheItem( std::move( fontDescription ), fontList, characterSetList ) ) );
851   }
852
853   if( fontList && characterSetList )
854   {
855     fontId = FindFontForCharacter( *fontList, *characterSetList, charcode, requestedPointSize, preferColor );
856   }
857
858   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
859   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
860   return fontId;
861 }
862
863 FontId FontClient::Plugin::GetFontId( const FontPath& path,
864                                       PointSize26Dot6 requestedPointSize,
865                                       FaceIndex faceIndex,
866                                       bool cacheDescription )
867 {
868   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
869   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
870   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
871
872   FontId id = 0u;
873
874   if( nullptr != mFreeTypeLibrary )
875   {
876     FontId foundId = 0u;
877     if( FindFont( path, requestedPointSize, faceIndex, foundId ) )
878     {
879       id = foundId;
880     }
881     else
882     {
883       id = CreateFont( path, requestedPointSize, faceIndex, cacheDescription );
884     }
885   }
886
887   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
888   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
889
890   return id;
891 }
892
893 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
894                                       PointSize26Dot6 requestedPointSize,
895                                       FaceIndex faceIndex )
896 {
897   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
898   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
899   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
900   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
901   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
902   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
903   DALI_LOG_INFO( gLogFilter, Debug::General, "   requestedPointSize : %d\n", requestedPointSize );
904
905   // This method uses three vectors which caches:
906   // * The bitmap font cache
907   // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
908   // * The path to font file names.
909   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
910
911   // 1) Checks if the font description matches with a previously loaded bitmap font.
912   //    Returns if a font is found.
913   // 2) Checks in the cache if the font's description has been validated before.
914   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
915   //    retrieves using font config a path to a font file name which matches with the
916   //    font's description. The path is stored in the cache.
917   //
918   // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
919   //    font file names' exists. If exists, it gets the font id. If it doesn't it calls
920   //    the GetFontId() method with the path to the font file name and the point size to
921   //    get the font id.
922
923   // The font id to be returned.
924   FontId fontId = 0u;
925
926   // Check first if the font description matches with a previously loaded bitmap font.
927   if( FindBitmapFont( fontDescription.family, fontId ) )
928   {
929     return fontId;
930   }
931
932   // Check if the font's description have been validated before.
933   FontDescriptionId validatedFontId = 0u;
934
935   if( !FindValidatedFont( fontDescription,
936                           validatedFontId ) )
937   {
938     // Use font config to validate the font's description.
939     ValidateFont( fontDescription,
940                   validatedFontId );
941   }
942
943   FontId fontFaceId = 0u;
944   // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
945   if( !FindFont( validatedFontId, requestedPointSize, fontFaceId ) )
946   {
947     // Retrieve the font file name path.
948     const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
949
950     // Retrieve the font id. Do not cache the description as it has been already cached.
951     fontId = GetFontId( description.path,
952                         requestedPointSize,
953                         faceIndex,
954                         false );
955
956     fontFaceId = mFontIdCache[fontId-1u].id;
957     mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( mCharacterSetCache[validatedFontId] );
958
959     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
960     mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
961                                                                        requestedPointSize,
962                                                                        fontFaceId ) );
963   }
964   else
965   {
966     fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
967   }
968
969   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
970   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
971
972   return fontId;
973 }
974
975 FontId FontClient::Plugin::GetFontId( const BitmapFont& bitmapFont )
976 {
977   for( const auto& item : mBitmapFontCache )
978   {
979     if( bitmapFont.name == item.font.name )
980     {
981       return item.id + 1u;
982     }
983   }
984
985   BitmapFontCacheItem bitmapFontCacheItem;
986   bitmapFontCacheItem.font = bitmapFont;
987   bitmapFontCacheItem.id = mFontIdCache.Count();
988
989   // Resize the vector with the pixel buffers.
990   bitmapFontCacheItem.pixelBuffers.resize( bitmapFont.glyphs.size() );
991
992   // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
993   unsigned int index = 0u;
994   for( auto& glyph : bitmapFontCacheItem.font.glyphs )
995   {
996     Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
997
998     if( EqualsZero( glyph.ascender ) && EqualsZero( glyph.descender ) )
999     {
1000       // Load the glyph.
1001       pixelBuffer = LoadImageFromFile( glyph.url );
1002
1003       if( pixelBuffer )
1004       {
1005         glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1006       }
1007     }
1008
1009     bitmapFontCacheItem.font.ascender = std::max( glyph.ascender, bitmapFontCacheItem.font.ascender );
1010     bitmapFontCacheItem.font.descender = std::min( glyph.descender, bitmapFontCacheItem.font.descender );
1011
1012     ++index;
1013   }
1014
1015   FontIdCacheItem fontIdCacheItem;
1016   fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1017   fontIdCacheItem.id = mBitmapFontCache.size();
1018
1019   mBitmapFontCache.push_back( std::move( bitmapFontCacheItem ) );
1020   mFontIdCache.PushBack( fontIdCacheItem );
1021
1022   return bitmapFontCacheItem.id + 1u;
1023 }
1024
1025 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
1026                                        FontDescriptionId& validatedFontId )
1027 {
1028   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n" );
1029   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1030   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1031   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1032   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1033   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1034
1035   // Create a font pattern.
1036   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1037
1038   FontDescription description;
1039
1040   FcCharSet* characterSet = nullptr;
1041   bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description, &characterSet );
1042   FcPatternDestroy( fontFamilyPattern );
1043
1044   if( matched && ( nullptr != characterSet ) )
1045   {
1046     // Set the index to the vector of paths to font file names.
1047     validatedFontId = mFontDescriptionCache.size();
1048
1049     DALI_LOG_INFO( gLogFilter, Debug::General, "  matched description; family : [%s]\n", description.family.c_str() );
1050     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                         path : [%s]\n", description.path.c_str() );
1051     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                        width : [%s]\n", FontWidth::Name[description.width] );
1052     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                       weight : [%s]\n", FontWeight::Name[description.weight] );
1053     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                        slant : [%s]\n\n", FontSlant::Name[description.slant] );
1054     DALI_LOG_INFO( gLogFilter, Debug::General, "  validatedFontId : %d\n", validatedFontId );
1055
1056     // Add the path to the cache.
1057     description.type = FontDescription::FACE_FONT;
1058     mFontDescriptionCache.push_back( description );
1059
1060     // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1061     mCharacterSetCache.PushBack( characterSet );
1062
1063     // Cache the index and the matched font's description.
1064     FontDescriptionCacheItem item( description,
1065                                    validatedFontId );
1066
1067     mValidatedFontCache.push_back( std::move( item ) );
1068
1069     if( ( fontDescription.family != description.family ) ||
1070         ( fontDescription.width != description.width )   ||
1071         ( fontDescription.weight != description.weight ) ||
1072         ( fontDescription.slant != description.slant ) )
1073     {
1074       // Cache the given font's description if it's different than the matched.
1075       FontDescriptionCacheItem item( fontDescription,
1076                                      validatedFontId );
1077
1078       mValidatedFontCache.push_back( std::move( item ) );
1079     }
1080   }
1081   else
1082   {
1083     DALI_LOG_INFO( gLogFilter, Debug::General, "  font validation failed for font [%s]\n", fontDescription.family.c_str() );
1084   }
1085
1086   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n" );
1087 }
1088
1089 void FontClient::Plugin::GetFontMetrics( FontId fontId,
1090                                          FontMetrics& metrics )
1091 {
1092   const FontId index = fontId - 1u;
1093
1094   if( ( fontId > 0 ) &&
1095       ( index < mFontIdCache.Count() ) )
1096   {
1097     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1098
1099     switch( fontIdCacheItem.type )
1100     {
1101       case FontDescription::FACE_FONT:
1102       {
1103         const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1104
1105         metrics = font.mMetrics;
1106
1107         // Adjust the metrics if the fixed-size font should be down-scaled
1108         if( font.mIsFixedSizeBitmap )
1109         {
1110           const float desiredFixedSize =  static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1111
1112           if( desiredFixedSize > 0.f )
1113           {
1114             const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1115
1116             metrics.ascender = metrics.ascender * scaleFactor;
1117             metrics.descender = metrics.descender * scaleFactor;
1118             metrics.height = metrics.height * scaleFactor;
1119             metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1120             metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1121           }
1122         }
1123         break;
1124       }
1125       case FontDescription::BITMAP_FONT:
1126       {
1127         const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1128
1129         metrics.ascender = bitmapFontCacheItem.font.ascender;
1130         metrics.descender = bitmapFontCacheItem.font.descender;
1131         metrics.height = metrics.ascender - metrics.descender;
1132         metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1133         metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1134         break;
1135       }
1136       default:
1137       {
1138         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1139       }
1140     }
1141   }
1142   else
1143   {
1144     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
1145   }
1146 }
1147
1148 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
1149                                               Character charcode )
1150 {
1151   GlyphIndex glyphIndex = 0u;
1152   const FontId index = fontId - 1u;
1153
1154   if( ( fontId > 0u ) &&
1155       ( index < mFontIdCache.Count() ) )
1156   {
1157     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1158
1159     if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1160     {
1161       FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1162
1163       glyphIndex = FT_Get_Char_Index( ftFace, charcode );
1164     }
1165   }
1166
1167   return glyphIndex;
1168 }
1169
1170 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
1171                                           uint32_t size,
1172                                           GlyphType type,
1173                                           bool horizontal )
1174 {
1175   if( VECTOR_GLYPH == type )
1176   {
1177     return GetVectorMetrics( array, size, horizontal );
1178   }
1179
1180   return GetBitmapMetrics( array, size, horizontal );
1181 }
1182
1183 bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
1184                                            uint32_t size,
1185                                            bool horizontal )
1186 {
1187   bool success( true );
1188
1189   for( unsigned int i=0; i<size; ++i )
1190   {
1191     GlyphInfo& glyph = array[i];
1192
1193     FontId index = glyph.fontId - 1u;
1194
1195     if( ( glyph.fontId > 0u ) &&
1196         ( index < mFontIdCache.Count() ) )
1197     {
1198       const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1199
1200       switch( fontIdCacheItem.type )
1201       {
1202       case FontDescription::FACE_FONT:
1203       {
1204         const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1205
1206         FT_Face ftFace = font.mFreeTypeFace;
1207
1208 #ifdef FREETYPE_BITMAP_SUPPORT
1209         // Check to see if we should be loading a Fixed Size bitmap?
1210         if( font.mIsFixedSizeBitmap )
1211         {
1212           FT_Select_Size( ftFace, font.mFixedSizeIndex ); ///< @todo: needs to be investigated why it's needed to select the size again.
1213           int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR );
1214           if ( FT_Err_Ok == error )
1215           {
1216             glyph.width = font.mFixedWidthPixels;
1217             glyph.height = font.mFixedHeightPixels;
1218             glyph.advance = font.mFixedWidthPixels;
1219             glyph.xBearing = 0.0f;
1220             glyph.yBearing = font.mFixedHeightPixels;
1221
1222             // Adjust the metrics if the fixed-size font should be down-scaled
1223             const float desiredFixedSize =  static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1224
1225             if( desiredFixedSize > 0.f )
1226             {
1227               const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1228
1229               glyph.width = glyph.width * scaleFactor ;
1230               glyph.height = glyph.height * scaleFactor;
1231               glyph.advance = glyph.advance * scaleFactor;
1232               glyph.xBearing = glyph.xBearing * scaleFactor;
1233               glyph.yBearing = glyph.yBearing * scaleFactor;
1234
1235               glyph.scaleFactor = scaleFactor;
1236             }
1237           }
1238           else
1239           {
1240             DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error );
1241             success = false;
1242           }
1243         }
1244         else
1245 #endif
1246         {
1247           int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT );
1248
1249           if( FT_Err_Ok == error )
1250           {
1251             glyph.width  = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1252             glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
1253             if( horizontal )
1254             {
1255               glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
1256               glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
1257             }
1258             else
1259             {
1260               glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
1261               glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
1262             }
1263           }
1264           else
1265           {
1266             success = false;
1267           }
1268         }
1269         break;
1270       }
1271       case FontDescription::BITMAP_FONT:
1272       {
1273         BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1274
1275         unsigned int index = 0u;
1276         for( auto& item : bitmapFontCacheItem.font.glyphs )
1277         {
1278           if( item.utf32 == glyph.index )
1279           {
1280             Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1281             if( !pixelBuffer )
1282             {
1283               pixelBuffer = LoadImageFromFile( item.url );
1284             }
1285
1286             glyph.width  = static_cast< float >( pixelBuffer.GetWidth() );
1287             glyph.height = static_cast< float >( pixelBuffer.GetHeight() );
1288             glyph.xBearing = 0.f;
1289             glyph.yBearing = glyph.height + item.descender;
1290             glyph.advance = glyph.width;
1291             glyph.scaleFactor = 1.f;
1292             break;
1293           }
1294           ++index;
1295         }
1296
1297         success = true;
1298         break;
1299       }
1300       default:
1301       {
1302         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1303       }
1304       }
1305     }
1306     else
1307     {
1308       // Check if it's an embedded image.
1309       if( ( 0u == glyph.fontId ) && ( 0u != glyph.index ) && ( glyph.index <= mEmbeddedItemCache.Count() ) )
1310       {
1311         const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1312
1313         glyph.width = static_cast<float>( item.width );
1314         glyph.height = static_cast<float>( item.height );
1315         glyph.xBearing = 0.f;
1316         glyph.yBearing = glyph.height;
1317         glyph.advance = glyph.width;
1318         glyph.scaleFactor = 1.f;
1319       }
1320       else
1321       {
1322         success = false;
1323       }
1324     }
1325   }
1326
1327   return success;
1328 }
1329
1330 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
1331                                            uint32_t size,
1332                                            bool horizontal )
1333 {
1334 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1335   bool success( true );
1336
1337   for( unsigned int i = 0u; i < size; ++i )
1338   {
1339     FontId fontId = array[i].fontId;
1340
1341     if( ( fontId > 0u ) &&
1342         ( fontId - 1u ) < mFontIdCache.Count() )
1343     {
1344       FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1345
1346       if( ! font.mVectorFontId )
1347       {
1348         font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1349       }
1350
1351       mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
1352
1353       // Vector metrics are in EMs, convert to pixels
1354       const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
1355       array[i].width    *= scale;
1356       array[i].height   *= scale;
1357       array[i].xBearing *= scale;
1358       array[i].yBearing *= scale;
1359       array[i].advance  *= scale;
1360     }
1361     else
1362     {
1363       success = false;
1364     }
1365   }
1366
1367   return success;
1368 #else
1369   return false;
1370 #endif
1371 }
1372
1373 void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth )
1374 {
1375   const FontId index = fontId - 1u;
1376
1377   if( ( fontId > 0u ) &&
1378       ( index < mFontIdCache.Count() ) )
1379   {
1380     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1381
1382     switch( fontIdCacheItem.type )
1383     {
1384     case FontDescription::FACE_FONT:
1385     {
1386       // For the software italics.
1387       bool isShearRequired = false;
1388
1389       const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1390       FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1391
1392       FT_Error error;
1393
1394 #ifdef FREETYPE_BITMAP_SUPPORT
1395       // Check to see if this is fixed size bitmap
1396       if( fontFaceCacheItem.mIsFixedSizeBitmap )
1397       {
1398         error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1399       }
1400       else
1401 #endif
1402       {
1403         error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT );
1404       }
1405       if( FT_Err_Ok == error )
1406       {
1407         if( isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) )
1408         {
1409           // Does the software bold.
1410           FT_GlyphSlot_Embolden( ftFace->glyph );
1411         }
1412
1413         if( isItalicRequired && !( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) )
1414         {
1415           // Will do the software italic.
1416           isShearRequired = true;
1417         }
1418
1419         FT_Glyph glyph;
1420         error = FT_Get_Glyph( ftFace->glyph, &glyph );
1421
1422         // Convert to bitmap if necessary
1423         if ( FT_Err_Ok == error )
1424         {
1425           if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
1426           {
1427             // Check whether we should create a bitmap for the outline
1428             if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 )
1429             {
1430               // Set up a stroker
1431               FT_Stroker stroker;
1432               error = FT_Stroker_New( mFreeTypeLibrary, &stroker );
1433
1434               if( FT_Err_Ok == error )
1435               {
1436                 FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 );
1437                 error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 );
1438
1439                 if( FT_Err_Ok == error )
1440                 {
1441                   FT_Stroker_Done( stroker );
1442                 }
1443                 else
1444                 {
1445                   DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error );
1446                 }
1447               }
1448               else
1449               {
1450                 DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error );
1451               }
1452             }
1453
1454             error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
1455             if( FT_Err_Ok == error )
1456             {
1457               FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph );
1458               ConvertBitmap( data, bitmapGlyph->bitmap, isShearRequired );
1459             }
1460             else
1461             {
1462               DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error );
1463             }
1464           }
1465           else
1466           {
1467             ConvertBitmap( data, ftFace->glyph->bitmap, isShearRequired );
1468           }
1469
1470           // Created FT_Glyph object must be released with FT_Done_Glyph
1471           FT_Done_Glyph( glyph );
1472         }
1473       }
1474       else
1475       {
1476         DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error );
1477       }
1478       break;
1479     }
1480     case FontDescription::BITMAP_FONT:
1481     {
1482       BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1483
1484       unsigned int index = 0u;
1485       for( auto& item : bitmapFontCacheItem.font.glyphs )
1486       {
1487         if( item.utf32 == glyphIndex )
1488         {
1489           Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1490           if( !pixelBuffer )
1491           {
1492             pixelBuffer = LoadImageFromFile( item.url );
1493           }
1494
1495           data.width = pixelBuffer.GetWidth();
1496           data.height = pixelBuffer.GetHeight();
1497
1498           ConvertBitmap( data, data.width, data.height, pixelBuffer.GetBuffer() );
1499
1500           // Sets the pixel format.
1501           data.format = pixelBuffer.GetPixelFormat();
1502           break;
1503         }
1504         ++index;
1505       }
1506       break;
1507     }
1508     default:
1509     {
1510       DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1511     }
1512     }
1513   }
1514   else
1515   {
1516     if( ( 0u != glyphIndex ) && ( glyphIndex <= mEmbeddedItemCache.Count() ) )
1517     {
1518       // It's an embedded item.
1519       const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1520
1521       data.width = item.width;
1522       data.height = item.height;
1523       if( 0u != item.pixelBufferId )
1524       {
1525         Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId-1u].pixelBuffer;
1526         if( pixelBuffer )
1527         {
1528           ConvertBitmap( data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer() );
1529
1530           // Sets the pixel format.
1531           data.format = pixelBuffer.GetPixelFormat();
1532         }
1533       }
1534       else
1535       {
1536         // Creates the output buffer
1537         const unsigned int bufferSize = data.width * data.height * 4u;
1538         data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1539
1540         memset( data.buffer, 0u, bufferSize );
1541
1542         // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1543       }
1544     }
1545   }
1546 }
1547
1548 PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
1549 {
1550   TextAbstraction::FontClient::GlyphBufferData data;
1551
1552   CreateBitmap( fontId, glyphIndex, false, false, data, outlineWidth );
1553
1554   return PixelData::New( data.buffer,
1555                          data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
1556                          data.width,
1557                          data.height,
1558                          data.format,
1559                          PixelData::DELETE_ARRAY );
1560 }
1561
1562 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
1563 {
1564   blob = nullptr;
1565   blobLength = 0;
1566
1567 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1568   if( ( fontId > 0u ) &&
1569       ( fontId - 1u < mFontIdCache.Count() ) )
1570   {
1571     const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1572     FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1573
1574     if( ! font.mVectorFontId )
1575     {
1576       font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1577     }
1578
1579     mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
1580   }
1581 #endif
1582 }
1583
1584 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
1585 {
1586   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" );
1587   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize );
1588
1589   // First look into the cache if there is an ellipsis glyph for the requested point size.
1590   for( const auto& item : mEllipsisCache )
1591   {
1592     if( item.requestedPointSize != requestedPointSize )
1593     {
1594       // Use the glyph in the cache.
1595
1596       DALI_LOG_INFO( gLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index );
1597       DALI_LOG_INFO( gLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId );
1598       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1599
1600       return item.glyph;
1601     }
1602   }
1603
1604   // No glyph has been found. Create one.
1605   mEllipsisCache.PushBack( EllipsisItem() );
1606   EllipsisItem& item = *( mEllipsisCache.End() - 1u );
1607
1608   item.requestedPointSize = requestedPointSize;
1609
1610   // Find a font for the ellipsis glyph.
1611   item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
1612                                        requestedPointSize,
1613                                        false );
1614
1615   // Set the character index to access the glyph inside the font.
1616   item.glyph.index = FT_Get_Char_Index( mFontFaceCache[mFontIdCache[item.glyph.fontId-1u].id].mFreeTypeFace,
1617                                         ELLIPSIS_CHARACTER );
1618
1619   GetBitmapMetrics( &item.glyph, 1u, true );
1620
1621   DALI_LOG_INFO( gLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index );
1622   DALI_LOG_INFO( gLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId );
1623   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1624
1625   return item.glyph;
1626 }
1627
1628 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1629 {
1630   FT_Error error = -1;
1631
1632   const FontId index = fontId - 1u;
1633
1634 #ifdef FREETYPE_BITMAP_SUPPORT
1635   if( ( fontId > 0u ) &&
1636       ( index < mFontIdCache.Count() ) )
1637   {
1638     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1639
1640     switch( fontIdCacheItem.type )
1641     {
1642     case FontDescription::FACE_FONT:
1643     {
1644       const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1645       FT_Face ftFace = item.mFreeTypeFace;
1646
1647       // Check to see if this is fixed size bitmap
1648       if( item.mHasColorTables )
1649       {
1650         error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1651       }
1652       break;
1653     }
1654     case FontDescription::BITMAP_FONT:
1655     {
1656       error = FT_Err_Ok; // Will return true;
1657       break;
1658     }
1659     default:
1660     {
1661       DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1662     }
1663     }
1664   }
1665 #endif
1666
1667   return FT_Err_Ok == error;
1668 }
1669
1670 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId )
1671 {
1672   FT_Face fontFace = nullptr;
1673
1674   const FontId index = fontId - 1u;
1675   if( ( fontId > 0u ) &&
1676       ( index < mFontIdCache.Count() ) )
1677   {
1678     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1679
1680     if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1681     {
1682       fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1683     }
1684   }
1685   return fontFace;
1686 }
1687
1688 FontDescription::Type FontClient::Plugin::GetFontType( FontId fontId )
1689 {
1690   const FontId index = fontId - 1u;
1691   if( ( fontId > 0u ) &&
1692       ( index < mFontIdCache.Count() ) )
1693   {
1694     return mFontIdCache[index].type;
1695   }
1696   return FontDescription::INVALID;
1697 }
1698
1699 bool FontClient::Plugin::AddCustomFontDirectory( const FontPath& path )
1700 {
1701   // nullptr as first parameter means the current configuration is used.
1702   return FcConfigAppFontAddDir( nullptr, reinterpret_cast<const FcChar8 *>( path.c_str() ) );
1703 }
1704
1705 GlyphIndex FontClient::Plugin::CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat )
1706 {
1707   EmbeddedItem embeddedItem;
1708
1709   embeddedItem.pixelBufferId = 0u;
1710   embeddedItem.width = description.width;
1711   embeddedItem.height = description.height;
1712
1713   pixelFormat = Pixel::A8;
1714
1715   if( !description.url.empty() )
1716   {
1717     // Check if the url is in the cache.
1718     PixelBufferId index = 0u;
1719
1720     for( const auto& cacheItem : mPixelBufferCache )
1721     {
1722       ++index;
1723       if( cacheItem.url == description.url )
1724       {
1725         // The url is in the pixel buffer cache.
1726         // Set the index +1 to the vector.
1727         embeddedItem.pixelBufferId = index;
1728         break;
1729       }
1730     }
1731
1732     Devel::PixelBuffer pixelBuffer;
1733     if( 0u == embeddedItem.pixelBufferId )
1734     {
1735       // The pixel buffer is not in the cache. Create one and cache it.
1736
1737       // Load the image from the url.
1738       pixelBuffer = LoadImageFromFile( description.url );
1739
1740       // Create the cache item.
1741       PixelBufferCacheItem pixelBufferCacheItem;
1742       pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1743       pixelBufferCacheItem.url = description.url;
1744
1745       // Store the cache item in the cache.
1746       mPixelBufferCache.push_back( std::move( pixelBufferCacheItem ) );
1747
1748       // Set the pixel buffer id to the embedded item.
1749       embeddedItem.pixelBufferId = mPixelBufferCache.size();
1750     }
1751     else
1752     {
1753       // Retrieve the pixel buffer from the cache to set the pixel format.
1754       pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId-1u].pixelBuffer;
1755     }
1756
1757     if( pixelBuffer )
1758     {
1759       // Set the size of the embedded item if it has not been set.
1760       if( 0u == embeddedItem.width )
1761       {
1762         embeddedItem.width = static_cast<unsigned int>( pixelBuffer.GetWidth() );
1763       }
1764
1765       if( 0u == embeddedItem.height )
1766       {
1767         embeddedItem.height = static_cast<unsigned int>( pixelBuffer.GetHeight() );
1768       }
1769
1770       // Set the pixel format.
1771       pixelFormat = pixelBuffer.GetPixelFormat();
1772     }
1773   }
1774
1775   // Find if the same embeddedItem has already been created.
1776   unsigned int index = 0u;
1777   for( const auto& item : mEmbeddedItemCache )
1778   {
1779     ++index;
1780     if( ( item.pixelBufferId == embeddedItem.pixelBufferId ) &&
1781         ( item.width == embeddedItem.width ) &&
1782         ( item.height == embeddedItem.height ) )
1783     {
1784       return index;
1785     }
1786   }
1787
1788   // Cache the embedded item.
1789   mEmbeddedItemCache.PushBack( embeddedItem );
1790
1791   return mEmbeddedItemCache.Count();
1792 }
1793
1794 void FontClient::Plugin::InitSystemFonts()
1795 {
1796   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1797
1798   FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1799
1800   if( fontSet )
1801   {
1802     DALI_LOG_INFO( gLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont );
1803
1804     // Reserve some space to avoid reallocations.
1805     mSystemFonts.reserve( fontSet->nfont );
1806
1807     for( int i = 0u; i < fontSet->nfont; ++i )
1808     {
1809       FcPattern* fontPattern = fontSet->fonts[i];
1810
1811       FontPath path;
1812
1813       // Skip fonts with no path
1814       if( GetFcString( fontPattern, FC_FILE, path ) )
1815       {
1816         mSystemFonts.push_back( FontDescription() );
1817         FontDescription& fontDescription = mSystemFonts.back();
1818
1819         fontDescription.path = std::move( path );
1820
1821         int width = 0;
1822         int weight = 0;
1823         int slant = 0;
1824         GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1825         GetFcInt( fontPattern, FC_WIDTH, width );
1826         GetFcInt( fontPattern, FC_WEIGHT, weight );
1827         GetFcInt( fontPattern, FC_SLANT, slant );
1828         fontDescription.width = IntToWidthType( width );
1829         fontDescription.weight = IntToWeightType( weight );
1830         fontDescription.slant = IntToSlantType( slant );
1831
1832         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", fontDescription.family.c_str() );
1833         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1834         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1835         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1836         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1837       }
1838     }
1839
1840     // Destroys the font set created.
1841     FcFontSetDestroy( fontSet );
1842   }
1843   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1844 }
1845
1846 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1847 {
1848   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1849
1850   FcResult result = FcResultMatch;
1851   FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result ); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1852
1853   const bool matched = nullptr != match;
1854   DALI_LOG_INFO( gLogFilter, Debug::General, "  pattern matched : %s\n", ( matched ? "true" : "false" ) );
1855
1856   if( matched )
1857   {
1858     int width = 0;
1859     int weight = 0;
1860     int slant = 0;
1861     GetFcString( match, FC_FILE, fontDescription.path );
1862     GetFcString( match, FC_FAMILY, fontDescription.family );
1863     GetFcInt( match, FC_WIDTH, width );
1864     GetFcInt( match, FC_WEIGHT, weight );
1865     GetFcInt( match, FC_SLANT, slant );
1866     fontDescription.width = IntToWidthType( width );
1867     fontDescription.weight = IntToWeightType( weight );
1868     fontDescription.slant = IntToSlantType( slant );
1869
1870     // Retrieve the character set and increase the reference counter.
1871     FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1872     *characterSet = FcCharSetCopy( *characterSet );
1873
1874     // destroyed the matched pattern
1875     FcPatternDestroy( match );
1876
1877     DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1878     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1879     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1880     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1881     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1882   }
1883
1884   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1885   return matched;
1886 }
1887
1888 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1889 {
1890   // create the cached font family lookup pattern
1891   // a pattern holds a set of names, each name refers to a property of the font
1892   FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1893
1894   if( !fontFamilyPattern )
1895   {
1896     return nullptr;
1897   }
1898
1899   // add a property to the pattern for the font family
1900   FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1901
1902   // add a property to the pattern for local setting.
1903   const char* locale = setlocale( LC_MESSAGES, nullptr );
1904   if( locale != nullptr)
1905   {
1906     FcPatternAddString( fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>( locale ) );
1907   }
1908
1909   int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1910   if( width < 0 )
1911   {
1912     // Use default.
1913     width = DEFAULT_FONT_WIDTH;
1914   }
1915
1916   int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1917   if( weight < 0 )
1918   {
1919     // Use default.
1920     weight = DEFAULT_FONT_WEIGHT;
1921   }
1922
1923   int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1924   if( slant < 0 )
1925   {
1926     // Use default.
1927     slant = DEFAULT_FONT_SLANT;
1928   }
1929
1930   FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1931   FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1932   FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1933
1934   // Add a property of the pattern, to say we want to match TrueType fonts
1935   FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
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