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