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