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