Refactored font-client-plugin-impl
[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/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       MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
482       // Decrease the reference counter of the character set as it's not stored.
483       FcCharSetDestroy(characterSet);
484
485       // Destroys the pattern created.
486       FcPatternDestroy(matchPattern);
487     }
488
489     // Create again the character sets as they are not valid after FcInitReinitialize()
490
491     for(const auto& description : mDefaultFonts)
492     {
493       mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
494     }
495
496     for(const auto& description : mFontDescriptionCache)
497     {
498       mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
499     }
500
501     for(auto& item : mFallbackCache)
502     {
503       if(nullptr != item.fallbackFonts)
504       {
505         if(nullptr == item.characterSets)
506         {
507           item.characterSets = new CharacterSetList;
508         }
509
510         for(const auto& description : *(item.fallbackFonts))
511         {
512           item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
513         }
514       }
515     }
516
517     mDefaultFontDescriptionCached = true;
518   }
519
520   fontDescription.path   = mDefaultFontDescription.path;
521   fontDescription.family = mDefaultFontDescription.family;
522   fontDescription.width  = mDefaultFontDescription.width;
523   fontDescription.weight = mDefaultFontDescription.weight;
524   fontDescription.slant  = mDefaultFontDescription.slant;
525
526   FONT_LOG_DESCRIPTION(fontDescription, "");
527 }
528
529 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts)
530 {
531   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
532
533   if(mSystemFonts.empty())
534   {
535     InitSystemFonts();
536   }
537
538   systemFonts = mSystemFonts;
539   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : [%d]\n", mSystemFonts.size());
540 }
541
542 void FontClient::Plugin::GetDescription(FontId           id,
543                                         FontDescription& fontDescription) const
544 {
545   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
546   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
547   const FontId index = id - 1u;
548
549   if((id > 0u) && (index < mFontIdCache.Count()))
550   {
551     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
552     switch(fontIdCacheItem.type)
553     {
554       case FontDescription::FACE_FONT:
555       {
556         for(const auto& item : mFontDescriptionSizeCache)
557         {
558           if(item.fontId == fontIdCacheItem.id)
559           {
560             fontDescription = *(mFontDescriptionCache.begin() + item.validatedFontId - 1u);
561
562             FONT_LOG_DESCRIPTION(fontDescription, "");
563             return;
564           }
565         }
566         break;
567       }
568       case FontDescription::BITMAP_FONT:
569       {
570         fontDescription.type   = FontDescription::BITMAP_FONT;
571         fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
572         break;
573       }
574       default:
575       {
576         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Invalid type of font\n");
577         fontDescription.type = FontDescription::INVALID;
578         fontDescription.family.clear();
579       }
580     }
581   }
582
583   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  No description found for the font ID %d\n", id);
584 }
585
586 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id)
587 {
588   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
589   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
590
591   PointSize26Dot6               pointSize     = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
592   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(id);
593   if(fontCacheItem != nullptr)
594   {
595     pointSize = fontCacheItem->GetPointSize();
596   }
597   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  point size : %d\n", pointSize);
598
599   return pointSize;
600 }
601
602 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character)
603 {
604   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
605   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "    font id : %d\n", fontId);
606   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  character : %p\n", character);
607
608   bool isSupported   = false;
609   auto fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
610   if(fontCacheItem != nullptr)
611   {
612     isSupported = fontCacheItem->IsCharacterSupported(character); // May cache
613   }
614
615   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  is supported : %s\n", (isSupported ? "true" : "false"));
616   return isSupported;
617 }
618
619 const FontCacheItemInterface* FontClient::Plugin::GetCachedFontItem(FontId id) const
620 {
621   const FontId index = id - 1u;
622   if((id > 0u) && (index < mFontIdCache.Count()))
623   {
624     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
625     switch(fontIdCacheItem.type)
626     {
627       case FontDescription::FACE_FONT:
628       {
629         return &mFontFaceCache[fontIdCacheItem.id];
630       }
631       case FontDescription::BITMAP_FONT:
632       {
633         return &mBitmapFontCache[fontIdCacheItem.id];
634       }
635       default:
636       {
637         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Invalid type of font\n");
638       }
639     }
640   }
641   return nullptr;
642 }
643
644 FontId FontClient::Plugin::FindFontForCharacter(const FontList&         fontList,
645                                                 const CharacterSetList& characterSetList,
646                                                 Character               character,
647                                                 PointSize26Dot6         requestedPointSize,
648                                                 bool                    preferColor)
649 {
650   DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
651   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
652   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "           character : %p\n", character);
653   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
654   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "         preferColor : %s\n", (preferColor ? "true" : "false"));
655
656   FontId fontId     = 0u;
657   bool   foundColor = false;
658
659   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of fonts : %d\n", fontList.size());
660
661   // Traverse the list of fonts.
662   // Check for each font if supports the character.
663   for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
664   {
665     const FontDescription& description  = fontList[index];
666     const FcCharSet* const characterSet = characterSetList[index];
667
668     FONT_LOG_DESCRIPTION(description, "");
669
670     bool foundInRanges = false;
671     if(nullptr != characterSet)
672     {
673       foundInRanges = FcCharSetHasChar(characterSet, character);
674     }
675
676     if(foundInRanges)
677     {
678       fontId = GetFontId(description, requestedPointSize, 0u);
679
680       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "     font id : %d\n", fontId);
681
682       if(preferColor)
683       {
684         if((fontId > 0) &&
685            (fontId - 1u < mFontIdCache.Count()))
686         {
687           const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
688
689           foundColor = item.mHasColorTables;
690         }
691
692         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  foundColor : %s\n", (foundColor ? "true" : "false"));
693       }
694
695       // Keep going unless we prefer a different (color) font.
696       if(!preferColor || foundColor)
697       {
698         break;
699       }
700     }
701   }
702
703   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
704   return fontId;
705 }
706
707 FontId FontClient::Plugin::FindDefaultFont(Character       charcode,
708                                            PointSize26Dot6 requestedPointSize,
709                                            bool            preferColor)
710 {
711   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
712   FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
713
714   FontId fontId(0);
715
716   // Create the list of default fonts if it has not been created.
717   if(mDefaultFonts.empty())
718   {
719     FontDescription fontDescription;
720     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
721     fontDescription.width  = DefaultFontWidth();
722     fontDescription.weight = DefaultFontWeight();
723     fontDescription.slant  = DefaultFontSlant();
724
725     SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
726   }
727   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of default fonts : %d\n", mDefaultFonts.size());
728
729   // Traverse the list of default fonts.
730   // Check for each default font if supports the character.
731   fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
732
733   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
734   return fontId;
735 }
736
737 FontId FontClient::Plugin::FindFallbackFont(Character              charcode,
738                                             const FontDescription& preferredFontDescription,
739                                             PointSize26Dot6        requestedPointSize,
740                                             bool                   preferColor)
741 {
742   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
743   FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
744
745   // The font id to be returned.
746   FontId fontId = 0u;
747
748   FontDescription fontDescription;
749
750   // Fill the font description with the preferred font description and complete with the defaults.
751   fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
752   fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? DefaultFontWeight() : preferredFontDescription.weight);
753   fontDescription.width  = ((FontWidth::NONE == preferredFontDescription.width) ? DefaultFontWidth() : preferredFontDescription.width);
754   fontDescription.slant  = ((FontSlant::NONE == preferredFontDescription.slant) ? DefaultFontSlant() : preferredFontDescription.slant);
755
756   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  preferredFontDescription --> fontDescription\n");
757   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
758   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
759   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
760   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
761
762   // Check first if the font's description has been queried before.
763   FontList*         fontList         = nullptr;
764   CharacterSetList* characterSetList = nullptr;
765
766   if(!FindFallbackFontList(fontDescription, fontList, characterSetList))
767   {
768     fontList         = new FontList;
769     characterSetList = new CharacterSetList;
770
771     SetFontList(fontDescription, *fontList, *characterSetList);
772 #ifdef __APPLE__
773     FontDescription appleColorEmoji;
774     appleColorEmoji.family = "Apple Color Emoji";
775     appleColorEmoji.width  = fontDescription.width;
776     appleColorEmoji.weight = fontDescription.weight;
777     appleColorEmoji.slant  = fontDescription.slant;
778     FontList         emojiFontList;
779     CharacterSetList emojiCharSetList;
780     SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
781
782     std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
783     emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
784     *fontList         = std::move(emojiFontList);
785     *characterSetList = std::move(emojiCharSetList);
786 #endif
787
788     // Add the font-list to the cache.
789     mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
790   }
791
792   if(fontList && characterSetList)
793   {
794     fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
795   }
796
797   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
798   return fontId;
799 }
800
801 FontId FontClient::Plugin::GetFontId(const FontPath& path,
802                                      PointSize26Dot6 requestedPointSize,
803                                      FaceIndex       faceIndex,
804                                      bool            cacheDescription)
805 {
806   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
807   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
808   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
809
810   FontId id = 0u;
811
812   if(nullptr != mFreeTypeLibrary)
813   {
814     FontId foundId = 0u;
815     if(FindFont(path, requestedPointSize, faceIndex, foundId))
816     {
817       id = foundId;
818     }
819     else
820     {
821       id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
822     }
823   }
824
825   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
826   return id;
827 }
828
829 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
830                                      PointSize26Dot6        requestedPointSize,
831                                      FaceIndex              faceIndex)
832 {
833   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
834   FONT_LOG_DESCRIPTION(fontDescription, "");
835   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "   requestedPointSize : %d\n", requestedPointSize);
836
837   // This method uses three vectors which caches:
838   // * The bitmap font cache
839   // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
840   // * The path to font file names.
841   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
842
843   // 1) Checks if the font description matches with a previously loaded bitmap font.
844   //    Returns if a font is found.
845   // 2) Checks in the cache if the font's description has been validated before.
846   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
847   //    retrieves using font config a path to a font file name which matches with the
848   //    font's description. The path is stored in the cache.
849   //
850   // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
851   //    font file names' exists. If exists, it gets the font id. If it doesn't it calls
852   //    the GetFontId() method with the path to the font file name and the point size to
853   //    get the font id.
854
855   // The font id to be returned.
856   FontId fontId = 0u;
857
858   // Check first if the font description matches with a previously loaded bitmap font.
859   if(FindBitmapFont(fontDescription.family, fontId))
860   {
861     return fontId;
862   }
863
864   // Check if the font's description have been validated before.
865   FontDescriptionId validatedFontId = 0u;
866
867   if(!FindValidatedFont(fontDescription,
868                         validatedFontId))
869   {
870     // Use font config to validate the font's description.
871     ValidateFont(fontDescription,
872                  validatedFontId);
873   }
874
875   FontId fontFaceId = 0u;
876   // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
877   if(!FindFont(validatedFontId, requestedPointSize, fontFaceId))
878   {
879     // Retrieve the font file name path.
880     const FontDescription& description = *(mFontDescriptionCache.begin() + validatedFontId - 1u);
881
882     // Retrieve the font id. Do not cache the description as it has been already cached.
883     fontId = GetFontId(description.path,
884                        requestedPointSize,
885                        faceIndex,
886                        false);
887
888     fontFaceId                               = mFontIdCache[fontId - 1u].id;
889     mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(mCharacterSetCache[validatedFontId - 1u]);
890
891     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
892     mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
893                                                                      requestedPointSize,
894                                                                      fontFaceId));
895   }
896   else
897   {
898     fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
899   }
900
901   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
902   return fontId;
903 }
904
905 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont)
906 {
907   for(const auto& item : mBitmapFontCache)
908   {
909     if(bitmapFont.name == item.font.name)
910     {
911       return item.id + 1u;
912     }
913   }
914
915   BitmapFontCacheItem bitmapFontCacheItem(bitmapFont, mFontIdCache.Count());
916
917   FontIdCacheItem fontIdCacheItem;
918   fontIdCacheItem.type = FontDescription::BITMAP_FONT;
919   fontIdCacheItem.id   = mBitmapFontCache.size();
920
921   mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
922   mFontIdCache.PushBack(fontIdCacheItem);
923
924   return bitmapFontCacheItem.id + 1u;
925 }
926
927 void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
928                                       FontDescriptionId&     validatedFontId)
929 {
930   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
931   FONT_LOG_DESCRIPTION(fontDescription, "");
932
933   // Create a font pattern.
934   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
935
936   FontDescription description;
937
938   FcCharSet* characterSet = nullptr;
939   bool       matched      = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
940   FcPatternDestroy(fontFamilyPattern);
941
942   if(matched && (nullptr != characterSet))
943   {
944     // Add the path to the cache.
945     description.type = FontDescription::FACE_FONT;
946     mFontDescriptionCache.push_back(description);
947
948     // Set the index to the vector of paths to font file names.
949     validatedFontId = mFontDescriptionCache.size();
950
951     FONT_LOG_DESCRIPTION(description, "matched");
952     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validatedFontId : %d\n", validatedFontId);
953
954     // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
955     mCharacterSetCache.PushBack(characterSet);
956
957     // Cache the index and the matched font's description.
958     FontDescriptionCacheItem item(description,
959                                   validatedFontId);
960
961     mValidatedFontCache.push_back(std::move(item));
962
963     if((fontDescription.family != description.family) ||
964        (fontDescription.width != description.width) ||
965        (fontDescription.weight != description.weight) ||
966        (fontDescription.slant != description.slant))
967     {
968       // Cache the given font's description if it's different than the matched.
969       FontDescriptionCacheItem item(fontDescription,
970                                     validatedFontId);
971
972       mValidatedFontCache.push_back(std::move(item));
973     }
974   }
975   else
976   {
977     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font validation failed for font [%s]\n", fontDescription.family.c_str());
978   }
979 }
980
981 void FontClient::Plugin::GetFontMetrics(FontId       fontId,
982                                         FontMetrics& metrics)
983 {
984   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
985   if(fontCacheItem != nullptr)
986   {
987     fontCacheItem->GetFontMetrics(metrics, mDpiVertical);
988   }
989 }
990
991 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
992                                              Character charcode)
993 {
994   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
995   if(fontCacheItem != nullptr)
996   {
997     return fontCacheItem->GetGlyphIndex(charcode);
998   }
999
1000   return 0u;
1001 }
1002
1003 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
1004                                          uint32_t   size,
1005                                          GlyphType  type,
1006                                          bool       horizontal)
1007 {
1008   if(VECTOR_GLYPH == type)
1009   {
1010     return GetVectorMetrics(array, size, horizontal);
1011   }
1012
1013   return GetBitmapMetrics(array, size, horizontal);
1014 }
1015
1016 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
1017                                           uint32_t   size,
1018                                           bool       horizontal)
1019 {
1020   bool success(false);
1021
1022   for(unsigned int i = 0; i < size; ++i)
1023   {
1024     GlyphInfo& glyph = array[i];
1025
1026     const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(glyph.fontId);
1027     if(fontCacheItem != nullptr)
1028     {
1029       success = fontCacheItem->GetGlyphMetrics(glyph, mDpiVertical, horizontal);
1030     }
1031     // Check if it's an embedded image.
1032     else if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count()))
1033     {
1034       mEmbeddedItemCache[glyph.index - 1u].GetGlyphMetrics(glyph);
1035       success = true;
1036     }
1037   }
1038
1039   return success;
1040 }
1041
1042 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
1043                                           uint32_t   size,
1044                                           bool       horizontal)
1045 {
1046 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1047   bool success(true);
1048
1049   for(unsigned int i = 0u; i < size; ++i)
1050   {
1051     FontId fontId = array[i].fontId;
1052
1053     if((fontId > 0u) &&
1054        (fontId - 1u) < mFontIdCache.Count())
1055     {
1056       FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1057
1058       if(!font.mVectorFontId)
1059       {
1060         font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1061       }
1062
1063       mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
1064
1065       // Vector metrics are in EMs, convert to pixels
1066       const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
1067       array[i].width *= scale;
1068       array[i].height *= scale;
1069       array[i].xBearing *= scale;
1070       array[i].yBearing *= scale;
1071       array[i].advance *= scale;
1072     }
1073     else
1074     {
1075       success = false;
1076     }
1077   }
1078
1079   return success;
1080 #else
1081   return false;
1082 #endif
1083 }
1084
1085 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
1086 {
1087   data.isColorBitmap                          = false;
1088   data.isColorEmoji                           = false;
1089   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1090   if(fontCacheItem != nullptr)
1091   {
1092     fontCacheItem->CreateBitmap(glyphIndex, data, outlineWidth, isItalicRequired, isBoldRequired);
1093   }
1094   else if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count()))
1095   {
1096     // It's an embedded item.
1097     mEmbeddedItemCache[glyphIndex - 1u].CreateBitmap(mPixelBufferCache, data);
1098   }
1099 }
1100
1101 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
1102 {
1103   TextAbstraction::FontClient::GlyphBufferData data;
1104
1105   CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
1106
1107   return PixelData::New(data.buffer,
1108                         data.width * data.height * Pixel::GetBytesPerPixel(data.format),
1109                         data.width,
1110                         data.height,
1111                         data.format,
1112                         PixelData::DELETE_ARRAY);
1113 }
1114
1115 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
1116 {
1117   blob       = nullptr;
1118   blobLength = 0;
1119
1120 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1121   if((fontId > 0u) &&
1122      (fontId - 1u < mFontIdCache.Count()))
1123   {
1124     const FontId       fontFaceId = mFontIdCache[fontId - 1u].id;
1125     FontFaceCacheItem& font       = mFontFaceCache[fontFaceId];
1126
1127     if(!font.mVectorFontId)
1128     {
1129       font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1130     }
1131
1132     mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1133   }
1134 #endif
1135 }
1136
1137 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
1138 {
1139   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1140   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize);
1141
1142   // First look into the cache if there is an ellipsis glyph for the requested point size.
1143   for(const auto& item : mEllipsisCache)
1144   {
1145     if(item.requestedPointSize == requestedPointSize)
1146     {
1147       // Use the glyph in the cache.
1148       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
1149       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
1150       return item.glyph;
1151     }
1152   }
1153
1154   // No glyph has been found. Create one.
1155   mEllipsisCache.PushBack(EllipsisItem());
1156   EllipsisItem& item = *(mEllipsisCache.End() - 1u);
1157
1158   item.requestedPointSize = requestedPointSize;
1159
1160   // Find a font for the ellipsis glyph.
1161   item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1162                                       requestedPointSize,
1163                                       false);
1164
1165   // Set the character index to access the glyph inside the font.
1166   item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].id].mFreeTypeFace,
1167                                        ELLIPSIS_CHARACTER);
1168
1169   GetBitmapMetrics(&item.glyph, 1u, true);
1170
1171   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
1172   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
1173   return item.glyph;
1174 }
1175
1176 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
1177 {
1178   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1179   return fontCacheItem && fontCacheItem->IsColorGlyph(glyphIndex);
1180 }
1181
1182 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId)
1183 {
1184   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1185   if(fontCacheItem != nullptr)
1186   {
1187     return fontCacheItem->GetTypeface();
1188   }
1189   return nullptr;
1190 }
1191
1192 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId)
1193 {
1194   const FontId index = fontId - 1u;
1195   if((fontId > 0u) &&
1196      (index < mFontIdCache.Count()))
1197   {
1198     return mFontIdCache[index].type;
1199   }
1200   return FontDescription::INVALID;
1201 }
1202
1203 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1204 {
1205   // nullptr as first parameter means the current configuration is used.
1206   return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1207 }
1208
1209 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
1210 {
1211   EmbeddedItem embeddedItem;
1212
1213   embeddedItem.pixelBufferId = 0u;
1214   embeddedItem.width         = description.width;
1215   embeddedItem.height        = description.height;
1216
1217   pixelFormat = Pixel::A8;
1218
1219   if(!description.url.empty())
1220   {
1221     // Check if the url is in the cache.
1222     PixelBufferId index = 0u;
1223
1224     for(const auto& cacheItem : mPixelBufferCache)
1225     {
1226       ++index;
1227       if(cacheItem.url == description.url)
1228       {
1229         // The url is in the pixel buffer cache.
1230         // Set the index +1 to the vector.
1231         embeddedItem.pixelBufferId = index;
1232         break;
1233       }
1234     }
1235
1236     Devel::PixelBuffer pixelBuffer;
1237     if(0u == embeddedItem.pixelBufferId)
1238     {
1239       // The pixel buffer is not in the cache. Create one and cache it.
1240
1241       // Load the image from the url.
1242       pixelBuffer = LoadImageFromFile(description.url);
1243
1244       // Create the cache item.
1245       PixelBufferCacheItem pixelBufferCacheItem;
1246       pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1247       pixelBufferCacheItem.url         = description.url;
1248
1249       // Store the cache item in the cache.
1250       mPixelBufferCache.push_back(std::move(pixelBufferCacheItem));
1251
1252       // Set the pixel buffer id to the embedded item.
1253       embeddedItem.pixelBufferId = mPixelBufferCache.size();
1254     }
1255     else
1256     {
1257       // Retrieve the pixel buffer from the cache to set the pixel format.
1258       pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1259     }
1260
1261     if(pixelBuffer)
1262     {
1263       // Set the size of the embedded item if it has not been set.
1264       if(0u == embeddedItem.width)
1265       {
1266         embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1267       }
1268
1269       if(0u == embeddedItem.height)
1270       {
1271         embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1272       }
1273
1274       // Set the pixel format.
1275       pixelFormat = pixelBuffer.GetPixelFormat();
1276     }
1277   }
1278
1279   // Find if the same embeddedItem has already been created.
1280   unsigned int index = 0u;
1281   for(const auto& item : mEmbeddedItemCache)
1282   {
1283     ++index;
1284     if((item.pixelBufferId == embeddedItem.pixelBufferId) &&
1285        (item.width == embeddedItem.width) &&
1286        (item.height == embeddedItem.height))
1287     {
1288       return index;
1289     }
1290   }
1291
1292   // Cache the embedded item.
1293   mEmbeddedItemCache.PushBack(embeddedItem);
1294
1295   return mEmbeddedItemCache.Count();
1296 }
1297
1298 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
1299 {
1300   mIsAtlasLimitationEnabled = enabled;
1301 }
1302
1303 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
1304 {
1305   return mIsAtlasLimitationEnabled;
1306 }
1307
1308 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
1309 {
1310   return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1311 }
1312
1313 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
1314 {
1315   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
1316 }
1317
1318 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
1319 {
1320   return mCurrentMaximumBlockSizeFitInAtlas;
1321 }
1322
1323 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
1324 {
1325   bool            isChanged        = false;
1326   const Size&     maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1327   const uint16_t& padding          = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
1328
1329   if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
1330   {
1331     mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
1332     isChanged                          = true;
1333   }
1334
1335   return isChanged;
1336 }
1337
1338 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
1339 {
1340   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
1341   ;
1342 }
1343
1344 void FontClient::Plugin::InitSystemFonts()
1345 {
1346   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1347
1348   FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1349
1350   if(fontSet)
1351   {
1352     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont);
1353
1354     // Reserve some space to avoid reallocations.
1355     mSystemFonts.reserve(fontSet->nfont);
1356
1357     for(int i = 0u; i < fontSet->nfont; ++i)
1358     {
1359       FcPattern* fontPattern = fontSet->fonts[i];
1360
1361       FontPath path;
1362
1363       // Skip fonts with no path
1364       if(GetFcString(fontPattern, FC_FILE, path))
1365       {
1366         mSystemFonts.push_back(FontDescription());
1367         FontDescription& fontDescription = mSystemFonts.back();
1368
1369         fontDescription.path = std::move(path);
1370
1371         int width  = 0;
1372         int weight = 0;
1373         int slant  = 0;
1374         GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
1375         GetFcInt(fontPattern, FC_WIDTH, width);
1376         GetFcInt(fontPattern, FC_WEIGHT, weight);
1377         GetFcInt(fontPattern, FC_SLANT, slant);
1378         fontDescription.width  = IntToWidthType(width);
1379         fontDescription.weight = IntToWeightType(weight);
1380         fontDescription.slant  = IntToSlantType(slant);
1381
1382         FONT_LOG_DESCRIPTION(fontDescription, "");
1383       }
1384     }
1385
1386     // Destroys the font set created.
1387     FcFontSetDestroy(fontSet);
1388   }
1389 }
1390
1391 bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
1392 {
1393   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1394
1395   FcResult   result = FcResultMatch;
1396   FcPattern* match  = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1397
1398   const bool matched = nullptr != match;
1399   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  pattern matched : %s\n", (matched ? "true" : "false"));
1400
1401   if(matched)
1402   {
1403     int width  = 0;
1404     int weight = 0;
1405     int slant  = 0;
1406     GetFcString(match, FC_FILE, fontDescription.path);
1407     GetFcString(match, FC_FAMILY, fontDescription.family);
1408     GetFcInt(match, FC_WIDTH, width);
1409     GetFcInt(match, FC_WEIGHT, weight);
1410     GetFcInt(match, FC_SLANT, slant);
1411     fontDescription.width  = IntToWidthType(width);
1412     fontDescription.weight = IntToWeightType(weight);
1413     fontDescription.slant  = IntToSlantType(slant);
1414
1415     // Retrieve the character set and increase the reference counter.
1416     FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
1417     *characterSet = FcCharSetCopy(*characterSet);
1418
1419     // destroyed the matched pattern
1420     FcPatternDestroy(match);
1421     FONT_LOG_DESCRIPTION(fontDescription, "");
1422   }
1423   return matched;
1424 }
1425
1426 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1427 {
1428   FcFontSet* fontset = nullptr;
1429
1430   // create a new pattern.
1431   // a pattern holds a set of names, each name refers to a property of the font
1432   FcPattern* pattern = FcPatternCreate();
1433
1434   if(nullptr != pattern)
1435   {
1436     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1437     FcObjectSet* objectSet = FcObjectSetCreate();
1438
1439     if(nullptr != objectSet)
1440     {
1441       // build an object set from a list of property names
1442       FcObjectSetAdd(objectSet, FC_FILE);
1443       FcObjectSetAdd(objectSet, FC_FAMILY);
1444       FcObjectSetAdd(objectSet, FC_WIDTH);
1445       FcObjectSetAdd(objectSet, FC_WEIGHT);
1446       FcObjectSetAdd(objectSet, FC_SLANT);
1447
1448       // get a list of fonts
1449       // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1450       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.
1451
1452       // clear up the object set
1453       FcObjectSetDestroy(objectSet);
1454     }
1455
1456     // clear up the pattern
1457     FcPatternDestroy(pattern);
1458   }
1459
1460   return fontset;
1461 }
1462
1463 bool FontClient::Plugin::GetFcString(const FcPattern* const pattern,
1464                                      const char* const      n,
1465                                      std::string&           string)
1466 {
1467   FcChar8*       file   = nullptr;
1468   const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
1469
1470   if(FcResultMatch == retVal)
1471   {
1472     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1473     string.assign(reinterpret_cast<const char*>(file));
1474
1475     return true;
1476   }
1477
1478   return false;
1479 }
1480
1481 bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
1482 {
1483   const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
1484
1485   if(FcResultMatch == retVal)
1486   {
1487     return true;
1488   }
1489
1490   return false;
1491 }
1492
1493 FontId FontClient::Plugin::CreateFont(const FontPath& path,
1494                                       PointSize26Dot6 requestedPointSize,
1495                                       FaceIndex       faceIndex,
1496                                       bool            cacheDescription)
1497 {
1498   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1499   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
1500   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1501
1502   FontId id = 0u;
1503
1504   // Create & cache new font face
1505   FT_Face ftFace;
1506   int     error = FT_New_Face(mFreeTypeLibrary,
1507                           path.c_str(),
1508                           0,
1509                           &ftFace);
1510
1511   if(FT_Err_Ok == error)
1512   {
1513     // Check if a font is scalable.
1514     const bool isScalable           = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
1515     const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
1516     const bool hasColorTables       = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
1517     FontId     fontFaceId           = 0u;
1518
1519     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "            isScalable : [%s]\n", (isScalable ? "true" : "false"));
1520     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
1521     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "        hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
1522
1523     // Check to see if the font contains fixed sizes?
1524     if(!isScalable && hasFixedSizedBitmaps)
1525     {
1526       PointSize26Dot6 actualPointSize = 0u;
1527       int             fixedSizeIndex  = 0;
1528       for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
1529       {
1530         const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
1531         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
1532
1533         if(fixedSize >= requestedPointSize)
1534         {
1535           actualPointSize = fixedSize;
1536           break;
1537         }
1538       }
1539
1540       if(0u == actualPointSize)
1541       {
1542         // The requested point size is bigger than the bigest fixed size.
1543         fixedSizeIndex  = ftFace->num_fixed_sizes - 1;
1544         actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
1545       }
1546
1547       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
1548
1549       // Tell Freetype to use this size
1550       error = FT_Select_Size(ftFace, fixedSizeIndex);
1551       if(FT_Err_Ok != error)
1552       {
1553         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
1554       }
1555       else
1556       {
1557         const float fixedWidth  = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
1558         const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
1559
1560         // Indicate that the font is a fixed sized bitmap
1561         FontMetrics metrics(fixedHeight, // The ascender in pixels.
1562                             0.0f,
1563                             fixedHeight, // The height in pixels.
1564                             0.0f,
1565                             0.0f);
1566
1567         // Create the FreeType font face item to cache.
1568         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
1569
1570         // Set the index to the font's id cache.
1571         fontFaceCacheItem.mFontId = mFontIdCache.Count();
1572
1573         // Create the font id item to cache.
1574         FontIdCacheItem fontIdCacheItem;
1575         fontIdCacheItem.type = FontDescription::FACE_FONT;
1576
1577         // Set the index to the FreeType font face cache.
1578         fontIdCacheItem.id = mFontFaceCache.size();
1579         fontFaceId         = fontIdCacheItem.id + 1u;
1580
1581         // Cache the items.
1582         mFontFaceCache.push_back(fontFaceCacheItem);
1583         mFontIdCache.PushBack(fontIdCacheItem);
1584
1585         // Set the font id to be returned.
1586         id = mFontIdCache.Count();
1587       }
1588     }
1589     else
1590     {
1591       if(mIsAtlasLimitationEnabled)
1592       {
1593         //There is limitation on block size to fit in predefined atlas size.
1594         //If the block size cannot fit into atlas size, then the system cannot draw block.
1595         //This is workaround to avoid issue in advance
1596         //Decrementing point-size until arriving to maximum allowed block size.
1597         auto        requestedPointSizeBackup = requestedPointSize;
1598         const Size& maxSizeFitInAtlas        = GetCurrentMaximumBlockSizeFitInAtlas();
1599         error                                = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
1600
1601         if(requestedPointSize != requestedPointSizeBackup)
1602         {
1603           DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
1604         }
1605       }
1606       else
1607       {
1608         error = FT_Set_Char_Size(ftFace,
1609                                  0,
1610                                  requestedPointSize,
1611                                  mDpiHorizontal,
1612                                  mDpiVertical);
1613       }
1614
1615       if(FT_Err_Ok == error)
1616       {
1617         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1618
1619         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1620                             static_cast<float>(ftMetrics.descender) * FROM_266,
1621                             static_cast<float>(ftMetrics.height) * FROM_266,
1622                             static_cast<float>(ftFace->underline_position) * FROM_266,
1623                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1624
1625         // Create the FreeType font face item to cache.
1626         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics);
1627
1628         // Set the index to the font's id cache.
1629         fontFaceCacheItem.mFontId = mFontIdCache.Count();
1630
1631         // Create the font id item to cache.
1632         FontIdCacheItem fontIdCacheItem;
1633         fontIdCacheItem.type = FontDescription::FACE_FONT;
1634
1635         // Set the index to the FreeType font face cache.
1636         fontIdCacheItem.id = mFontFaceCache.size();
1637         fontFaceId         = fontIdCacheItem.id + 1u;
1638
1639         // Cache the items.
1640         mFontFaceCache.push_back(fontFaceCacheItem);
1641         mFontIdCache.PushBack(fontIdCacheItem);
1642
1643         // Set the font id to be returned.
1644         id = mFontIdCache.Count();
1645       }
1646       else
1647       {
1648         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
1649       }
1650     }
1651
1652     if(0u != fontFaceId)
1653     {
1654       if(cacheDescription)
1655       {
1656         CacheFontPath(ftFace, fontFaceId, requestedPointSize, path);
1657       }
1658     }
1659   }
1660   else
1661   {
1662     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str());
1663   }
1664
1665   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
1666   return id;
1667 }
1668
1669 bool FontClient::Plugin::FindFont(const FontPath& path,
1670                                   PointSize26Dot6 requestedPointSize,
1671                                   FaceIndex       faceIndex,
1672                                   FontId&         fontId) const
1673 {
1674   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1675   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
1676   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1677   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of fonts in the cache : %d\n", mFontFaceCache.size());
1678
1679   fontId = 0u;
1680   for(const auto& cacheItem : mFontFaceCache)
1681   {
1682     if(cacheItem.mRequestedPointSize == requestedPointSize &&
1683        cacheItem.mFaceIndex == faceIndex &&
1684        cacheItem.mPath == path)
1685     {
1686       fontId = cacheItem.mFontId + 1u;
1687
1688       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, id : %d\n", fontId);
1689       return true;
1690     }
1691   }
1692
1693   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found\n");
1694   return false;
1695 }
1696
1697 bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
1698                                            FontDescriptionId&     validatedFontId)
1699 {
1700   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1701   FONT_LOG_DESCRIPTION(fontDescription, "");
1702   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
1703
1704   validatedFontId = 0u;
1705
1706   for(const auto& item : mValidatedFontCache)
1707   {
1708     if(!fontDescription.family.empty() &&
1709        (fontDescription.family == item.fontDescription.family) &&
1710        (fontDescription.width == item.fontDescription.width) &&
1711        (fontDescription.weight == item.fontDescription.weight) &&
1712        (fontDescription.slant == item.fontDescription.slant))
1713     {
1714       validatedFontId = item.index;
1715
1716       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font found, id : %d\n", validatedFontId);
1717       return true;
1718     }
1719   }
1720
1721   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font not found\n");
1722   return false;
1723 }
1724
1725 bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription,
1726                                               FontList*&             fontList,
1727                                               CharacterSetList*&     characterSetList)
1728 {
1729   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1730   FONT_LOG_DESCRIPTION(fontDescription, "");
1731   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of fallback font lists in the cache : %d\n", mFallbackCache.size());
1732
1733   fontList = nullptr;
1734
1735   for(const auto& item : mFallbackCache)
1736   {
1737     if(!fontDescription.family.empty() &&
1738        (fontDescription.family == item.fontDescription.family) &&
1739        (fontDescription.width == item.fontDescription.width) &&
1740        (fontDescription.weight == item.fontDescription.weight) &&
1741        (fontDescription.slant == item.fontDescription.slant))
1742     {
1743       fontList         = item.fallbackFonts;
1744       characterSetList = item.characterSets;
1745
1746       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list found.\n");
1747       return true;
1748     }
1749   }
1750
1751   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list not found.\n");
1752   return false;
1753 }
1754
1755 bool FontClient::Plugin::FindFont(FontDescriptionId validatedFontId,
1756                                   PointSize26Dot6   requestedPointSize,
1757                                   FontId&           fontId)
1758 {
1759   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1760   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "    validatedFontId  : %d\n", validatedFontId);
1761   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1762
1763   fontId = 0u;
1764
1765   for(const auto& item : mFontDescriptionSizeCache)
1766   {
1767     if((validatedFontId == item.validatedFontId) &&
1768        (requestedPointSize == item.requestedPointSize))
1769     {
1770       fontId = item.fontId;
1771
1772       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, id : %d\n", fontId);
1773       return true;
1774     }
1775   }
1776
1777   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found.\n");
1778   return false;
1779 }
1780
1781 bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const
1782 {
1783   fontId = 0u;
1784
1785   for(const auto& item : mBitmapFontCache)
1786   {
1787     if(bitmapFont == item.font.name)
1788     {
1789       fontId = item.id + 1u;
1790       return true;
1791     }
1792   }
1793
1794   return false;
1795 }
1796
1797 bool FontClient::Plugin::IsScalable(const FontPath& path)
1798 {
1799   bool isScalable = false;
1800
1801   FT_Face ftFace;
1802   int     error = FT_New_Face(mFreeTypeLibrary,
1803                           path.c_str(),
1804                           0,
1805                           &ftFace);
1806   if(FT_Err_Ok != error)
1807   {
1808     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
1809   }
1810   else
1811   {
1812     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
1813   }
1814
1815   return isScalable;
1816 }
1817
1818 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription)
1819 {
1820   // Create a font pattern.
1821   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1822
1823   FcResult result = FcResultMatch;
1824
1825   // match the pattern
1826   FcPattern* match      = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1827   bool       isScalable = false;
1828
1829   if(match)
1830   {
1831     // Get the path to the font file name.
1832     FontPath path;
1833     GetFcString(match, FC_FILE, path);
1834     isScalable = IsScalable(path);
1835   }
1836   else
1837   {
1838     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1839   }
1840
1841   // Destroys the created patterns.
1842   FcPatternDestroy(match);
1843   FcPatternDestroy(fontFamilyPattern);
1844
1845   return isScalable;
1846 }
1847
1848 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes)
1849 {
1850   // Empty the caller container
1851   sizes.Clear();
1852
1853   FT_Face ftFace;
1854   int     error = FT_New_Face(mFreeTypeLibrary,
1855                           path.c_str(),
1856                           0,
1857                           &ftFace);
1858   if(FT_Err_Ok != error)
1859   {
1860     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
1861   }
1862
1863   // Fetch the number of fixed sizes available
1864   if(ftFace->num_fixed_sizes && ftFace->available_sizes)
1865   {
1866     for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
1867     {
1868       sizes.PushBack(ftFace->available_sizes[i].size);
1869     }
1870   }
1871 }
1872
1873 void FontClient::Plugin::GetFixedSizes(const FontDescription&   fontDescription,
1874                                        Vector<PointSize26Dot6>& sizes)
1875 {
1876   // Create a font pattern.
1877   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1878
1879   FcResult result = FcResultMatch;
1880
1881   // match the pattern
1882   FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1883
1884   if(match)
1885   {
1886     // Get the path to the font file name.
1887     FontPath path;
1888     GetFcString(match, FC_FILE, path);
1889     GetFixedSizes(path, sizes);
1890   }
1891   else
1892   {
1893     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1894   }
1895
1896   // Destroys the created patterns.
1897   FcPatternDestroy(match);
1898   FcPatternDestroy(fontFamilyPattern);
1899 }
1900
1901 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
1902 {
1903   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1904   if(fontCacheItem != nullptr)
1905   {
1906     return fontCacheItem->HasItalicStyle();
1907   }
1908   return false;
1909 }
1910
1911 void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path)
1912 {
1913   FontDescription description;
1914   description.path   = path;
1915   description.family = std::move(FontFamily(ftFace->family_name));
1916   description.weight = FontWeight::NONE;
1917   description.width  = FontWidth::NONE;
1918   description.slant  = FontSlant::NONE;
1919
1920   // Note FreeType doesn't give too much info to build a proper font style.
1921   if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
1922   {
1923     description.slant = FontSlant::ITALIC;
1924   }
1925   if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
1926   {
1927     description.weight = FontWeight::BOLD;
1928   }
1929
1930   FontDescriptionId validatedFontId = 0u;
1931   if(!FindValidatedFont(description,
1932                         validatedFontId))
1933   {
1934     FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1935
1936     FcResult   result = FcResultMatch;
1937     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1938
1939     FcCharSet* characterSet = nullptr;
1940     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
1941
1942     const FontId fontFaceId                  = id - 1u;
1943     mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
1944
1945     // Destroys the created patterns.
1946     FcPatternDestroy(match);
1947     FcPatternDestroy(pattern);
1948
1949     // Add the path to the cache.
1950     description.type = FontDescription::FACE_FONT;
1951     mFontDescriptionCache.push_back(description);
1952
1953     // Set the index to the vector of paths to font file names.
1954     validatedFontId = mFontDescriptionCache.size();
1955
1956     // Increase the reference counter and add the character set to the cache.
1957     mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
1958
1959     // Cache the index and the font's description.
1960     mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
1961                                                                      validatedFontId)));
1962
1963     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
1964     mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
1965                                                                      requestedPointSize,
1966                                                                      fontFaceId));
1967   }
1968 }
1969
1970 void FontClient::Plugin::ClearFallbackCache(std::vector<FallbackCacheItem>& fallbackCache)
1971 {
1972   for(auto& item : fallbackCache)
1973   {
1974     if(nullptr != item.fallbackFonts)
1975     {
1976       delete item.fallbackFonts;
1977     }
1978
1979     if(nullptr != item.characterSets)
1980     {
1981       // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
1982       DestroyCharacterSets(*item.characterSets);
1983       delete item.characterSets;
1984     }
1985   }
1986 }
1987
1988 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
1989 {
1990   for(auto& item : mFontFaceCache)
1991   {
1992     FcCharSetDestroy(item.mCharacterSet);
1993     item.mCharacterSet = nullptr;
1994   }
1995 }
1996
1997 } // namespace Dali::TextAbstraction::Internal