5f5a3046c976dfab51b55ad521e3989b9c97902d
[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 void FontClient::Plugin::InitSystemFonts()
1292 {
1293   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1294
1295   FcFontSet* fontSet = GetFcFontSet();
1296
1297   if( fontSet )
1298   {
1299     DALI_LOG_INFO( gLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont );
1300
1301     // Reserve some space to avoid reallocations.
1302     mSystemFonts.reserve( fontSet->nfont );
1303
1304     for( int i = 0u; i < fontSet->nfont; ++i )
1305     {
1306       FcPattern* fontPattern = fontSet->fonts[i];
1307
1308       FontPath path;
1309
1310       // Skip fonts with no path
1311       if( GetFcString( fontPattern, FC_FILE, path ) )
1312       {
1313         mSystemFonts.push_back( FontDescription() );
1314         FontDescription& fontDescription = mSystemFonts.back();
1315
1316         fontDescription.path = std::move( path );
1317
1318         int width = 0;
1319         int weight = 0;
1320         int slant = 0;
1321         GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1322         GetFcInt( fontPattern, FC_WIDTH, width );
1323         GetFcInt( fontPattern, FC_WEIGHT, weight );
1324         GetFcInt( fontPattern, FC_SLANT, slant );
1325         fontDescription.width = IntToWidthType( width );
1326         fontDescription.weight = IntToWeightType( weight );
1327         fontDescription.slant = IntToSlantType( slant );
1328
1329         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", fontDescription.family.c_str() );
1330         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1331         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1332         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1333         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1334       }
1335     }
1336
1337     FcFontSetDestroy( fontSet );
1338   }
1339   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1340 }
1341
1342 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1343 {
1344   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1345
1346   FcResult result = FcResultMatch;
1347   FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result );
1348
1349   const bool matched = nullptr != match;
1350   DALI_LOG_INFO( gLogFilter, Debug::General, "  pattern matched : %s\n", ( matched ? "true" : "false" ) );
1351
1352   if( matched )
1353   {
1354     int width = 0;
1355     int weight = 0;
1356     int slant = 0;
1357     GetFcString( match, FC_FILE, fontDescription.path );
1358     GetFcString( match, FC_FAMILY, fontDescription.family );
1359     GetFcInt( match, FC_WIDTH, width );
1360     GetFcInt( match, FC_WEIGHT, weight );
1361     GetFcInt( match, FC_SLANT, slant );
1362     fontDescription.width = IntToWidthType( width );
1363     fontDescription.weight = IntToWeightType( weight );
1364     fontDescription.slant = IntToSlantType( slant );
1365
1366     // Cache the character ranges.
1367     FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1368
1369     // destroyed the matched pattern
1370     FcPatternDestroy( match );
1371
1372     DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1373     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1374     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1375     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1376     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1377   }
1378
1379   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1380   return matched;
1381 }
1382
1383 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1384 {
1385   // create the cached font family lookup pattern
1386   // a pattern holds a set of names, each name refers to a property of the font
1387   FcPattern* fontFamilyPattern = FcPatternCreate();
1388
1389   if( !fontFamilyPattern )
1390   {
1391     return nullptr;
1392   }
1393
1394   // add a property to the pattern for the font family
1395   FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1396
1397   // add a property to the pattern for local setting.
1398   const char* locale = setlocale( LC_MESSAGES, nullptr );
1399   if( locale != nullptr)
1400   {
1401     FcPatternAddString( fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>( locale ) );
1402   }
1403
1404   int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1405   if( width < 0 )
1406   {
1407     // Use default.
1408     width = DEFAULT_FONT_WIDTH;
1409   }
1410
1411   int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1412   if( weight < 0 )
1413   {
1414     // Use default.
1415     weight = DEFAULT_FONT_WEIGHT;
1416   }
1417
1418   int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1419   if( slant < 0 )
1420   {
1421     // Use default.
1422     slant = DEFAULT_FONT_SLANT;
1423   }
1424
1425   FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1426   FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1427   FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1428
1429   // Add a property of the pattern, to say we want to match TrueType fonts
1430   FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
1431
1432   // modify the config, with the mFontFamilyPatterm
1433   FcConfigSubstitute( nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern );
1434
1435   // provide default values for unspecified properties in the font pattern
1436   // e.g. patterns without a specified style or weight are set to Medium
1437   FcDefaultSubstitute( fontFamilyPattern );
1438
1439   return fontFamilyPattern;
1440 }
1441
1442 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1443 {
1444   // create a new pattern.
1445   // a pattern holds a set of names, each name refers to a property of the font
1446   FcPattern* pattern = FcPatternCreate();
1447
1448   // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1449   FcObjectSet* objectSet = FcObjectSetCreate();
1450
1451   // build an object set from a list of property names
1452   FcObjectSetAdd( objectSet, FC_FILE );
1453   FcObjectSetAdd( objectSet, FC_FAMILY );
1454   FcObjectSetAdd( objectSet, FC_WIDTH );
1455   FcObjectSetAdd( objectSet, FC_WEIGHT );
1456   FcObjectSetAdd( objectSet, FC_SLANT );
1457
1458   // get a list of fonts
1459   // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1460   FcFontSet* fontset = FcFontList( nullptr /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
1461
1462   // clear up the object set
1463   if( objectSet )
1464   {
1465     FcObjectSetDestroy( objectSet );
1466   }
1467   // clear up the pattern
1468   if( pattern )
1469   {
1470     FcPatternDestroy( pattern );
1471   }
1472
1473   return fontset;
1474 }
1475
1476 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
1477                                       const char* const n,
1478                                       std::string& string )
1479 {
1480   FcChar8* file = nullptr;
1481   const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
1482
1483   if( FcResultMatch == retVal )
1484   {
1485     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1486     string.assign( reinterpret_cast<const char*>( file ) );
1487
1488     return true;
1489   }
1490
1491   return false;
1492 }
1493
1494 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
1495 {
1496   const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
1497
1498   if( FcResultMatch == retVal )
1499   {
1500     return true;
1501   }
1502
1503   return false;
1504 }
1505
1506 FontId FontClient::Plugin::CreateFont( const FontPath& path,
1507                                        PointSize26Dot6 requestedPointSize,
1508                                        FaceIndex faceIndex,
1509                                        bool cacheDescription )
1510 {
1511   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" );
1512   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
1513   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
1514
1515   FontId id = 0u;
1516
1517   // Create & cache new font face
1518   FT_Face ftFace;
1519   int error = FT_New_Face( mFreeTypeLibrary,
1520                            path.c_str(),
1521                            0,
1522                            &ftFace );
1523
1524   if( FT_Err_Ok == error )
1525   {
1526     // Check if a font is scalable.
1527     const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) );
1528     const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes );
1529     const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) );
1530
1531     DALI_LOG_INFO( gLogFilter, Debug::General, "            isScalable : [%s]\n", ( isScalable ? "true" : "false" ) );
1532     DALI_LOG_INFO( gLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) );
1533     DALI_LOG_INFO( gLogFilter, Debug::General, "        hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) );
1534
1535     // Check to see if the font contains fixed sizes?
1536     if( !isScalable && hasFixedSizedBitmaps )
1537     {
1538       PointSize26Dot6 actualPointSize = 0u;
1539       int fixedSizeIndex = 0;
1540       for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex )
1541       {
1542         const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
1543         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize );
1544
1545         if( fixedSize >= requestedPointSize )
1546         {
1547           actualPointSize = fixedSize;
1548           break;
1549         }
1550       }
1551
1552       if( 0u == actualPointSize )
1553       {
1554         // The requested point size is bigger than the bigest fixed size.
1555         fixedSizeIndex = ftFace->num_fixed_sizes - 1;
1556         actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
1557       }
1558
1559       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize );
1560
1561       // Tell Freetype to use this size
1562       error = FT_Select_Size( ftFace, fixedSizeIndex );
1563       if ( FT_Err_Ok != error )
1564       {
1565         DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error );
1566       }
1567       else
1568       {
1569         float fixedWidth  = static_cast< float >( ftFace->available_sizes[ fixedSizeIndex ].width );
1570         float fixedHeight = static_cast< float >( ftFace->available_sizes[ fixedSizeIndex ].height );
1571
1572         // Indicate that the font is a fixed sized bitmap
1573         FontMetrics metrics( fixedHeight, // The ascender in pixels.
1574                              0.0f,
1575                              fixedHeight, // The height in pixels.
1576                              0.0f,
1577                              0.0f );
1578
1579         mFontFaceCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedWidth, fixedHeight, hasColorTables ) );
1580         id = mFontFaceCache.size();
1581       }
1582     }
1583     else
1584     {
1585       error = FT_Set_Char_Size( ftFace,
1586                                 0,
1587                                 requestedPointSize,
1588                                 mDpiHorizontal,
1589                                 mDpiVertical );
1590
1591       if( FT_Err_Ok == error )
1592       {
1593
1594         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1595
1596         FontMetrics metrics( static_cast< float >( ftMetrics.ascender  ) * FROM_266,
1597                              static_cast< float >( ftMetrics.descender ) * FROM_266,
1598                              static_cast< float >( ftMetrics.height    ) * FROM_266,
1599                              static_cast< float >( ftFace->underline_position ) * FROM_266,
1600                              static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
1601
1602         mFontFaceCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics ) );
1603         id = mFontFaceCache.size();
1604       }
1605       else
1606       {
1607         DALI_LOG_INFO( gLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize );
1608       }
1609     }
1610
1611     if( 0u != id )
1612     {
1613       if( cacheDescription )
1614       {
1615         CacheFontPath( ftFace, id, requestedPointSize, path );
1616       }
1617     }
1618   }
1619   else
1620   {
1621     DALI_LOG_INFO( gLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str() );
1622   }
1623
1624   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
1625   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" );
1626
1627   return id;
1628 }
1629
1630 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap )
1631 {
1632   if( srcBitmap.width*srcBitmap.rows > 0 )
1633   {
1634     switch( srcBitmap.pixel_mode )
1635     {
1636       case FT_PIXEL_MODE_GRAY:
1637       {
1638         if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
1639         {
1640           const unsigned int bufferSize = srcBitmap.width * srcBitmap.rows;
1641           data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1642           data.width = srcBitmap.width;
1643           data.height = srcBitmap.rows;
1644           data.format = Pixel::L8;
1645           memcpy( data.buffer, srcBitmap.buffer, bufferSize );
1646         }
1647         break;
1648       }
1649
1650 #ifdef FREETYPE_BITMAP_SUPPORT
1651       case FT_PIXEL_MODE_BGRA:
1652       {
1653         if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
1654         {
1655           // Set the input dimensions.
1656           const ImageDimensions inputDimensions( srcBitmap.width, srcBitmap.rows );
1657
1658           // Set the output dimensions.
1659           // If the output dimension is not given, the input dimension is set
1660           // and won't be downscaling.
1661           data.width = ( data.width == 0 ) ? srcBitmap.width : data.width;
1662           data.height = ( data.height == 0 ) ? srcBitmap.rows : data.height;
1663           const ImageDimensions desiredDimensions( data.width, data.height );
1664
1665           // Creates the output buffer
1666           const unsigned int bufferSize = data.width * data.height * 4u;
1667           data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1668
1669           if( inputDimensions == desiredDimensions )
1670           {
1671             // There isn't downscaling.
1672             memcpy( data.buffer, srcBitmap.buffer, bufferSize );
1673           }
1674           else
1675           {
1676             Dali::Internal::Platform::LanczosSample4BPP( srcBitmap.buffer,
1677                                                          inputDimensions,
1678                                                          data.buffer,
1679                                                          desiredDimensions );
1680           }
1681           data.format = Pixel::BGRA8888;
1682         }
1683         break;
1684       }
1685 #endif
1686       default:
1687       {
1688         DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" );
1689         break;
1690       }
1691     }
1692   }
1693 }
1694
1695 bool FontClient::Plugin::FindFont( const FontPath& path,
1696                                    PointSize26Dot6 requestedPointSize,
1697                                    FaceIndex faceIndex,
1698                                    FontId& fontId ) const
1699 {
1700   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
1701   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
1702   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
1703   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of fonts in the cache : %d\n", mFontFaceCache.size() );
1704
1705   fontId = 0u;
1706   for( const auto& cacheItem : mFontFaceCache )
1707   {
1708     ++fontId;
1709     if( cacheItem.mRequestedPointSize == requestedPointSize &&
1710         cacheItem.mFaceIndex == faceIndex &&
1711         cacheItem.mPath == path )
1712     {
1713       DALI_LOG_INFO( gLogFilter, Debug::General, "  font found, id : %d\n", fontId );
1714       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
1715
1716       return true;
1717     }
1718   }
1719
1720   DALI_LOG_INFO( gLogFilter, Debug::General, "  font not found\n" );
1721   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
1722
1723   fontId = 0u;
1724   return false;
1725 }
1726
1727 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
1728                                             FontDescriptionId& validatedFontId )
1729 {
1730   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" );
1731   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1732   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1733   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1734   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1735   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1736   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %d\n", mValidatedFontCache.size() );
1737
1738   validatedFontId = 0u;
1739
1740   for( const auto& item : mValidatedFontCache )
1741   {
1742     if( !fontDescription.family.empty() &&
1743         ( fontDescription.family == item.fontDescription.family ) &&
1744         ( fontDescription.width == item.fontDescription.width ) &&
1745         ( fontDescription.weight == item.fontDescription.weight ) &&
1746         ( fontDescription.slant == item.fontDescription.slant ) )
1747     {
1748       validatedFontId = item.index;
1749
1750       DALI_LOG_INFO( gLogFilter, Debug::General, "  validated font found, id : %d\n", validatedFontId );
1751       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
1752       return true;
1753     }
1754   }
1755
1756   DALI_LOG_INFO( gLogFilter, Debug::General, "  validated font not found\n" );
1757   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
1758   return false;
1759 }
1760
1761 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
1762                                                FontList*& fontList,
1763                                                CharacterSetList*& characterSetList )
1764 {
1765   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" );
1766   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1767   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1768   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1769   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1770   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1771   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of fallback font lists in the cache : %d\n", mFallbackCache.size() );
1772
1773   fontList = nullptr;
1774
1775   for( const auto& item : mFallbackCache )
1776   {
1777     if( !fontDescription.family.empty() &&
1778         ( fontDescription.family == item.fontDescription.family ) &&
1779         ( fontDescription.width == item.fontDescription.width ) &&
1780         ( fontDescription.weight == item.fontDescription.weight ) &&
1781         ( fontDescription.slant == item.fontDescription.slant ) )
1782     {
1783       fontList = item.fallbackFonts;
1784       characterSetList = item.characterSets;
1785
1786       DALI_LOG_INFO( gLogFilter, Debug::General, "  fallback font list found.\n" );
1787       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
1788       return true;
1789     }
1790   }
1791
1792   DALI_LOG_INFO( gLogFilter, Debug::General, "  fallback font list not found.\n" );
1793   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
1794   return false;
1795 }
1796
1797 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
1798                                    PointSize26Dot6 requestedPointSize,
1799                                    FontId& fontId )
1800 {
1801   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
1802   DALI_LOG_INFO( gLogFilter, Debug::General, "    validatedFontId  : %d\n", validatedFontId );
1803   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
1804
1805   fontId = 0u;
1806
1807   for( const auto& item : mFontDescriptionSizeCache )
1808   {
1809     if( ( validatedFontId == item.validatedFontId ) &&
1810         ( requestedPointSize == item.requestedPointSize ) )
1811     {
1812       fontId = item.fontId;
1813
1814       DALI_LOG_INFO( gLogFilter, Debug::General, "  font found, id : %d\n", fontId );
1815       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
1816       return true;
1817     }
1818   }
1819
1820   DALI_LOG_INFO( gLogFilter, Debug::General, "  font not found.\n" );
1821   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
1822   return false;
1823 }
1824
1825 bool FontClient::Plugin::IsScalable( const FontPath& path )
1826 {
1827   bool isScalable = false;
1828
1829   FT_Face ftFace;
1830   int error = FT_New_Face( mFreeTypeLibrary,
1831                            path.c_str(),
1832                            0,
1833                            &ftFace );
1834   if( FT_Err_Ok != error )
1835   {
1836     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() );
1837   }
1838   else
1839   {
1840     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
1841   }
1842
1843   return isScalable;
1844 }
1845
1846 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
1847 {
1848   // Create a font pattern.
1849   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1850
1851   FcResult result = FcResultMatch;
1852
1853   // match the pattern
1854   FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result );
1855   bool isScalable = false;
1856
1857   if( match )
1858   {
1859     // Get the path to the font file name.
1860     FontPath path;
1861     GetFcString( match, FC_FILE, path );
1862     isScalable = IsScalable( path );
1863   }
1864   else
1865   {
1866     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
1867   }
1868   FcPatternDestroy( fontFamilyPattern );
1869   FcPatternDestroy( match );
1870   return isScalable;
1871 }
1872
1873 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
1874 {
1875   // Empty the caller container
1876   sizes.Clear();
1877
1878   FT_Face ftFace;
1879   int error = FT_New_Face( mFreeTypeLibrary,
1880                            path.c_str(),
1881                            0,
1882                            &ftFace );
1883   if( FT_Err_Ok != error )
1884   {
1885     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() );
1886   }
1887
1888   // Fetch the number of fixed sizes available
1889   if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1890   {
1891     for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1892     {
1893       sizes.PushBack( ftFace->available_sizes[ i ].size );
1894     }
1895   }
1896 }
1897
1898 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
1899                                         Vector< PointSize26Dot6 >& sizes )
1900 {
1901   // Create a font pattern.
1902   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1903
1904   FcResult result = FcResultMatch;
1905
1906   // match the pattern
1907   FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result );
1908
1909   if( match )
1910   {
1911     // Get the path to the font file name.
1912     FontPath path;
1913     GetFcString( match, FC_FILE, path );
1914     GetFixedSizes( path, sizes );
1915   }
1916   else
1917   {
1918     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
1919   }
1920   FcPatternDestroy( match );
1921   FcPatternDestroy( fontFamilyPattern );
1922 }
1923
1924 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize,  const FontPath& path )
1925 {
1926   FontDescription description;
1927   description.path = path;
1928   description.family = std::move( FontFamily( ftFace->family_name ) );
1929   description.weight = FontWeight::NONE;
1930   description.width = FontWidth::NONE;
1931   description.slant = FontSlant::NONE;
1932
1933   // Note FreeType doesn't give too much info to build a proper font style.
1934   if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
1935   {
1936     description.slant = FontSlant::ITALIC;
1937   }
1938   if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
1939   {
1940     description.weight = FontWeight::BOLD;
1941   }
1942
1943   FontDescriptionId validatedFontId = 0u;
1944   if( !FindValidatedFont( description,
1945                           validatedFontId ) )
1946   {
1947     // Set the index to the vector of paths to font file names.
1948     validatedFontId = mFontDescriptionCache.size();
1949
1950     FcPattern* pattern = CreateFontFamilyPattern( description );
1951
1952     FcResult result = FcResultMatch;
1953     FcPattern* match = FcFontMatch( nullptr, pattern, &result );
1954
1955     FcCharSet* characterSet = nullptr;
1956     FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
1957
1958     FcPatternDestroy( pattern );
1959
1960     mMatchedFcPatternCache.PushBack( match );
1961
1962     mFontFaceCache[id-1u].mCharacterSet = characterSet;
1963
1964     // Add the path to the cache.
1965     mFontDescriptionCache.push_back( description );
1966     mCharacterSetCache.PushBack( characterSet );
1967
1968     // Cache the index and the font's description.
1969     mValidatedFontCache.push_back( std::move( FontDescriptionCacheItem( std::move( description ),
1970                                                                         validatedFontId) ) );
1971
1972     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
1973     mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
1974                                                                        requestedPointSize,
1975                                                                        id ) );
1976   }
1977 }
1978
1979 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description )
1980 {
1981   FcCharSet* characterSet = nullptr;
1982
1983   FcPattern* pattern = CreateFontFamilyPattern( description );
1984
1985   if( nullptr != pattern )
1986   {
1987     FcResult result = FcResultMatch;
1988     FcPattern* match = FcFontMatch( nullptr, pattern, &result );
1989
1990     FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
1991
1992     mMatchedFcPatternCache.PushBack( match );
1993
1994     FcPatternDestroy( pattern );
1995   }
1996
1997   return characterSet;
1998 }
1999
2000 void FontClient::Plugin::DestroyMatchedPatterns()
2001 {
2002   for (auto & object : mMatchedFcPatternCache)
2003   {
2004     FcPatternDestroy(reinterpret_cast<FcPattern*>(object));
2005   }
2006   mMatchedFcPatternCache.Clear();
2007 }
2008
2009 } // namespace Internal
2010
2011 } // namespace TextAbstraction
2012
2013 } // namespace Dali