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