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