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