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