99caffed6093a92bf921821f049afcf06a8c4309
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-client-plugin-impl.cpp
1 /*
2  * Copyright (c) 2023 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 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/platform-abstraction.h>
25 #include <dali/integration-api/trace.h>
26 #include <dali/internal/adaptor/common/adaptor-impl.h>
27 #include <dali/internal/imaging/common/image-operations.h>
28 #include <dali/internal/system/common/logging.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-plugin-cache-handler.h>
32 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
33 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
34 #include <dali/public-api/common/dali-vector.h>
35 #include <dali/public-api/common/vector-wrapper.h>
36
37 // EXTERNAL INCLUDES
38 #include <fontconfig/fontconfig.h>
39 #include <algorithm>
40 #include <iterator>
41
42 // Use this macro only if need to log messages before the log function is set.
43 #define FONT_LOG_MESSAGE(level, format, ...)                                    \
44   do                                                                            \
45   {                                                                             \
46     char buffer[256];                                                           \
47     int  result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
48     if(result >= static_cast<int>(sizeof(buffer)))                              \
49     {                                                                           \
50       std::string log("Font log message is too long to fit in the buffer.\n");  \
51       Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log);      \
52       break;                                                                    \
53     }                                                                           \
54     std::string log(buffer);                                                    \
55     Dali::TizenPlatform::LogMessage(level, log);                                \
56   } while(0)
57
58 #if defined(DEBUG_ENABLED)
59
60 // Note, to turn on trace and verbose logging, use "export LOG_FONT_CLIENT=3,true"
61 // Or re-define the following filter using Verbose,true instead of NoLogging,false,
62 // Or, add DALI_LOG_FILTER_ENABLE_TRACE(gFontClientLogFilter) in the code below.
63
64 Dali::Integration::Log::Filter* gFontClientLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
65
66 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)                                                                            \
67   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix "  description; family : [%s]\n", fontDescription.family.c_str()); \
68   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose,                                                                            \
69                 "                 path : [%s]\n"                                                                                 \
70                 "                width : [%s]\n"                                                                                 \
71                 "               weight : [%s]\n"                                                                                 \
72                 "                slant : [%s]\n\n",                                                                              \
73                 fontDescription.path.c_str(),                                                                                    \
74                 FontWidth::Name[fontDescription.width],                                                                          \
75                 FontWeight::Name[fontDescription.weight],                                                                        \
76                 FontSlant::Name[fontDescription.slant])
77
78 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
79   DALI_LOG_INFO(gFontClientLogFilter, Debug::General,               \
80                 "           character : %p\n"                       \
81                 "  requestedPointSize : %d\n"                       \
82                 "         preferColor : %s\n",                      \
83                 charcode,                                           \
84                 requestedPointSize,                                 \
85                 (preferColor ? "true" : "false"))
86
87 #else
88
89 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
90 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
91
92 #endif
93
94 namespace
95 {
96 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_FONT_PERFORMANCE_MARKER, false);
97
98 /**
99  * Conversion from Fractional26.6 to float
100  */
101 const float FROM_266        = 1.0f / 64.0f;
102 const float POINTS_PER_INCH = 72.f;
103
104 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
105
106 } // namespace
107
108 using Dali::Vector;
109
110 namespace Dali::TextAbstraction::Internal
111 {
112 namespace
113 {
114 /**
115  * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
116  *
117  * @param[in/out] ftFace Face type object.
118  * @param[in] horizontalDpi The horizontal dpi.
119  * @param[in] verticalDpi The vertical dpi.
120  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
121  * @param[in] requestedPointSize The requested point-size.
122  * @return whether the  ftFace's block can fit into atlas
123  */
124 bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
125 {
126   bool isFit = false;
127
128   error = FT_Set_Char_Size(ftFace,
129                            0,
130                            FT_F26Dot6(requestedPointSize),
131                            horizontalDpi,
132                            verticalDpi);
133
134   if(error == FT_Err_Ok)
135   {
136     //Check width and height of block for requestedPointSize
137     //If the width or height is greater than the maximum-size then decrement by one unit of point-size.
138     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)
139     {
140       isFit = true;
141     }
142   }
143
144   return isFit;
145 }
146
147 /**
148  * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
149  *
150  * @param[in/out] ftFace Face type object.
151  * @param[in] horizontalDpi The horizontal dpi.
152  * @param[in] verticalDpi The vertical dpi.
153  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
154  * @param[in/out] requestedPointSize The requested point-size.
155  * @return FreeType error code. 0 means success when requesting the nominal size (in points).
156  */
157 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
158 {
159   //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
160   const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
161   bool            canFitInAtlas;
162   int             error; // FreeType error code.
163
164   canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
165   if(FT_Err_Ok != error)
166   {
167     return error;
168   }
169
170   if(!canFitInAtlas)
171   {
172     //Exponential search
173     uint32_t exponentialDecrement = 1;
174
175     while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit * exponentialDecrement)
176     {
177       requestedPointSize -= (pointSizePerOneUnit * exponentialDecrement);
178       canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
179       if(FT_Err_Ok != error)
180       {
181         return error;
182       }
183
184       exponentialDecrement *= 2;
185     }
186
187     //Binary search
188     uint32_t minPointSize;
189     uint32_t maxPointSize;
190
191     if(canFitInAtlas)
192     {
193       exponentialDecrement /= 2;
194       minPointSize = requestedPointSize;
195       maxPointSize = requestedPointSize + (pointSizePerOneUnit * exponentialDecrement);
196     }
197     else
198     {
199       minPointSize = 0;
200       maxPointSize = requestedPointSize;
201     }
202
203     while(minPointSize < maxPointSize)
204     {
205       requestedPointSize = ((maxPointSize / pointSizePerOneUnit - minPointSize / pointSizePerOneUnit) / 2) * pointSizePerOneUnit + minPointSize;
206       canFitInAtlas      = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
207       if(FT_Err_Ok != error)
208       {
209         return error;
210       }
211
212       if(canFitInAtlas)
213       {
214         if(minPointSize == requestedPointSize)
215         {
216           //Found targeted point-size
217           return error;
218         }
219
220         minPointSize = requestedPointSize;
221       }
222       else
223       {
224         maxPointSize = requestedPointSize;
225       }
226     }
227   }
228
229   return error;
230 }
231
232 } // namespace
233
234 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
235                            unsigned int verticalDpi)
236 : mFreeTypeLibrary(nullptr),
237   mDpiHorizontal(horizontalDpi),
238   mDpiVertical(verticalDpi),
239   mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
240   mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS),
241   mVectorFontCache(nullptr),
242   mCacheHandler(new CacheHandler())
243 {
244   int error = FT_Init_FreeType(&mFreeTypeLibrary);
245   if(FT_Err_Ok != error)
246   {
247     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Init error: %d\n", error);
248   }
249
250 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
251   mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
252 #endif
253 }
254
255 FontClient::Plugin::~Plugin()
256 {
257   // Delete cache hanlder before remove mFreeTypeLibrary
258   delete mCacheHandler;
259
260 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
261   delete mVectorFontCache;
262 #endif
263
264   FT_Done_FreeType(mFreeTypeLibrary);
265 }
266
267 void FontClient::Plugin::ClearCache() const
268 {
269   mCacheHandler->ClearCache();
270 }
271
272 void FontClient::Plugin::ClearCacheOnLocaleChanged() const
273 {
274   mCacheHandler->ClearCacheOnLocaleChanged();
275 }
276
277 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
278                                 unsigned int verticalDpi)
279 {
280   mDpiHorizontal = horizontalDpi;
281   mDpiVertical   = verticalDpi;
282 }
283
284 void FontClient::Plugin::ResetSystemDefaults() const
285 {
286   mCacheHandler->ResetSystemDefaults();
287 }
288
289 void FontClient::Plugin::CacheFontDataFromFile(const std::string& fontPath) const
290 {
291   if(fontPath.empty())
292   {
293     return;
294   }
295
296   if(mCacheHandler->FindFontData(fontPath))
297   {
298     // Font data is already cached, no need to reload
299     return;
300   }
301
302   Dali::Vector<uint8_t> fontDataBuffer;
303   std::streampos        dataSize = 0;
304   if(!mCacheHandler->LoadFontDataFromFile(fontPath, fontDataBuffer, dataSize))
305   {
306     fontDataBuffer.Clear();
307     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "Failed to load font data : %s\n", fontPath.c_str());
308     return;
309   }
310
311   // Cache font data
312   mCacheHandler->CacheFontData(fontPath, fontDataBuffer, dataSize);
313 }
314
315 void FontClient::Plugin::CacheFontFaceFromFile(const std::string& fontPath) const
316 {
317   if(fontPath.empty())
318   {
319     return;
320   }
321
322   if(mCacheHandler->FindFontFace(fontPath))
323   {
324     // Font face is already cached, no need to reload
325     return;
326   }
327
328   FT_Face ftFace;
329   int     error = FT_New_Face(mFreeTypeLibrary, fontPath.c_str(), 0, &ftFace);
330   if(FT_Err_Ok != error)
331   {
332     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "Failed to load font face : %s\n", fontPath.c_str());
333     return;
334   }
335
336   // Cache font face
337   mCacheHandler->CacheFontFace(fontPath, ftFace);
338   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "PreLoad font new face : %s\n", fontPath.c_str());
339 }
340
341 void FontClient::Plugin::FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList) const
342 {
343   for(const auto& fontPath : fontPathList)
344   {
345     CacheFontFaceFromFile(fontPath);
346   }
347
348   for(const auto& memoryFontPath : memoryFontPathList)
349   {
350     CacheFontDataFromFile(memoryFontPath);
351   }
352 }
353
354 void FontClient::Plugin::FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily) const
355 {
356   mCacheHandler->InitDefaultFontDescription();
357
358   FontFamilyList familyList;
359   familyList.reserve(extraFamilyList.size() + 1);
360
361   for(const auto& fallbackFont : fallbackFamilyList)
362   {
363     FontList*         fontList          = nullptr;
364     CharacterSetList* characterSetList  = nullptr;
365     FontDescriptionId fontDescriptionId = 0u;
366     FontDescription   fontDescription;
367     fontDescription.family = FontFamily(fallbackFont);
368     fontDescription.weight = DefaultFontWeight();
369     fontDescription.width  = DefaultFontWidth();
370     fontDescription.slant  = DefaultFontSlant();
371
372     if(!mCacheHandler->FindFallbackFontList(fontDescription, fontList, characterSetList))
373     {
374       FontDescription copiedFontDescription = fontDescription;
375       mCacheHandler->CacheFallbackFontList(std::move(copiedFontDescription), fontList, characterSetList);
376     }
377     if(!mCacheHandler->FindValidatedFont(fontDescription, fontDescriptionId))
378     {
379       mCacheHandler->ValidateFont(fontDescription, fontDescriptionId);
380     }
381
382     if(extraFamilyList.empty() && localeFamily.empty())
383     {
384       continue;
385     }
386
387     familyList.clear();
388     familyList.insert(familyList.end(), extraFamilyList.begin(), extraFamilyList.end());
389     if(!localeFamily.empty())
390     {
391       familyList.push_back(localeFamily);
392     }
393
394     for(const auto& font : *fontList)
395     {
396       auto it = std::find(familyList.begin(), familyList.end(), font.family);
397       if(it != familyList.end())
398       {
399         if(!mCacheHandler->FindValidatedFont(font, fontDescriptionId))
400         {
401           mCacheHandler->ValidateFont(font, fontDescriptionId);
402         }
403         familyList.erase(it);
404       }
405     }
406   }
407 }
408
409 void FontClient::Plugin::InitDefaultFontDescription() const
410 {
411   mCacheHandler->InitDefaultFontDescription();
412 }
413
414 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription) const
415 {
416   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
417
418   mCacheHandler->InitDefaultFontDescription();
419   fontDescription = mCacheHandler->mDefaultFontDescription;
420
421   FONT_LOG_DESCRIPTION(fontDescription, "");
422 }
423
424 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts) const
425 {
426   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
427
428   mCacheHandler->InitDefaultFonts();
429   defaultFonts = mCacheHandler->mDefaultFonts;
430
431   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of default fonts : [%d]\n", mCacheHandler->mDefaultFonts.size());
432 }
433
434 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts) const
435 {
436   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
437
438   mCacheHandler->InitSystemFonts();
439   systemFonts = mCacheHandler->mSystemFonts;
440
441   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : [%d]\n", mCacheHandler->mSystemFonts.size());
442 }
443
444 void FontClient::Plugin::GetDescription(FontId           fontId,
445                                         FontDescription& fontDescription) const
446 {
447   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
448   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
449
450   if((fontId > 0u) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
451   {
452     const auto& fontIdCacheItem = mCacheHandler->mFontIdCache[fontId - 1u];
453     switch(fontIdCacheItem.type)
454     {
455       case FontDescription::FACE_FONT:
456       {
457         for(const auto& item : mCacheHandler->mFontDescriptionSizeCache)
458         {
459           if(item.second == fontIdCacheItem.index)
460           {
461             fontDescription = *(mCacheHandler->mFontDescriptionCache.begin() + item.first.fontDescriptionId - 1u);
462
463             FONT_LOG_DESCRIPTION(fontDescription, "");
464             return;
465           }
466         }
467         break;
468       }
469       case FontDescription::BITMAP_FONT:
470       {
471         fontDescription.type   = FontDescription::BITMAP_FONT;
472         fontDescription.family = mCacheHandler->mBitmapFontCache[fontIdCacheItem.index].font.name;
473         break;
474       }
475       default:
476       {
477         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Invalid type of font\n");
478         fontDescription.type = FontDescription::INVALID;
479         fontDescription.family.clear();
480       }
481     }
482   }
483
484   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  No description found for the font id %d\n", fontId);
485 }
486
487 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId fontId) const
488 {
489   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
490   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
491
492   PointSize26Dot6               pointSize     = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
493   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
494   if(fontCacheItem != nullptr)
495   {
496     pointSize = fontCacheItem->GetPointSize();
497   }
498   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  point size : %d\n", pointSize);
499
500   return pointSize;
501 }
502
503 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character) const
504 {
505   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
506   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "    font id : %d\n", fontId);
507   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  character : %p\n", character);
508
509   bool isSupported   = false;
510   auto fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
511   if(fontCacheItem != nullptr)
512   {
513     isSupported = fontCacheItem->IsCharacterSupported(character); // May cache
514   }
515
516   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  is supported : %s\n", (isSupported ? "true" : "false"));
517   return isSupported;
518 }
519
520 const FontCacheItemInterface* FontClient::Plugin::GetCachedFontItem(FontId fontId) const
521 {
522   if((fontId > 0u) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
523   {
524     const auto& fontIdCacheItem = mCacheHandler->mFontIdCache[fontId - 1u];
525     switch(fontIdCacheItem.type)
526     {
527       case FontDescription::FACE_FONT:
528       {
529         return &mCacheHandler->mFontFaceCache[fontIdCacheItem.index];
530       }
531       case FontDescription::BITMAP_FONT:
532       {
533         return &mCacheHandler->mBitmapFontCache[fontIdCacheItem.index];
534       }
535       default:
536       {
537         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Invalid type of font\n");
538       }
539     }
540   }
541   return nullptr;
542 }
543
544 FontId FontClient::Plugin::FindFontForCharacter(const FontList&         fontList,
545                                                 const CharacterSetList& characterSetList,
546                                                 Character               character,
547                                                 PointSize26Dot6         requestedPointSize,
548                                                 bool                    preferColor) const
549 {
550   DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
551   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
552   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "           character : %p\n", character);
553   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
554   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "         preferColor : %s\n", (preferColor ? "true" : "false"));
555
556   FontId fontId     = 0u;
557   bool   foundColor = false;
558
559   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of fonts : %d\n", fontList.size());
560
561   // Traverse the list of fonts.
562   // Check for each font if supports the character.
563   for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
564   {
565     const FontDescription& description  = fontList[index];
566     const FcCharSet* const characterSet = characterSetList[index];
567
568     FONT_LOG_DESCRIPTION(description, "");
569
570     bool foundInRanges = false;
571     if(nullptr != characterSet)
572     {
573       foundInRanges = FcCharSetHasChar(characterSet, character);
574     }
575
576     if(foundInRanges)
577     {
578       fontId = GetFontId(description, requestedPointSize, 0u);
579
580       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "     font id : %d\n", fontId);
581
582       if(preferColor)
583       {
584         if((fontId > 0) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
585         {
586           const FontFaceCacheItem& item = mCacheHandler->mFontFaceCache[mCacheHandler->mFontIdCache[fontId - 1u].index];
587
588           foundColor = item.mHasColorTables;
589         }
590
591         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  foundColor : %s\n", (foundColor ? "true" : "false"));
592       }
593
594       // Keep going unless we prefer a different (color) font.
595       if(!preferColor || foundColor)
596       {
597         break;
598       }
599     }
600   }
601
602   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
603   return fontId;
604 }
605
606 FontId FontClient::Plugin::FindDefaultFont(Character       charcode,
607                                            PointSize26Dot6 requestedPointSize,
608                                            bool            preferColor) const
609 {
610   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
611   FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
612
613   FontId fontId(0);
614
615   // Create the list of default fonts if it has not been created.
616   mCacheHandler->InitDefaultFonts();
617   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of default fonts : %d\n", mCacheHandler->mDefaultFonts.size());
618
619   // Traverse the list of default fonts.
620   // Check for each default font if supports the character.
621   fontId = FindFontForCharacter(mCacheHandler->mDefaultFonts, mCacheHandler->mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
622
623   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
624   return fontId;
625 }
626
627 FontId FontClient::Plugin::FindFallbackFont(Character              charcode,
628                                             const FontDescription& preferredFontDescription,
629                                             PointSize26Dot6        requestedPointSize,
630                                             bool                   preferColor) const
631 {
632   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
633   FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
634
635   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_FIND_FALLBACKFONT");
636
637   // The font id to be returned.
638   FontId fontId = 0u;
639
640   FontDescription fontDescription;
641
642   // Fill the font description with the preferred font description and complete with the defaults.
643   fontDescription.family = preferredFontDescription.family.empty() ? DefaultFontFamily() : preferredFontDescription.family;
644   fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? DefaultFontWeight() : preferredFontDescription.weight);
645   fontDescription.width  = ((FontWidth::NONE == preferredFontDescription.width) ? DefaultFontWidth() : preferredFontDescription.width);
646   fontDescription.slant  = ((FontSlant::NONE == preferredFontDescription.slant) ? DefaultFontSlant() : preferredFontDescription.slant);
647
648   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  preferredFontDescription --> fontDescription\n");
649   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
650   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
651   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
652   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
653
654 #if defined(TRACE_ENABLED)
655   if(gTraceFilter && gTraceFilter->IsTraceEnabled())
656   {
657     DALI_LOG_DEBUG_INFO("DALI_TEXT_FIND_FALLBACKFONT : %s -> %s\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
658   }
659 #endif
660
661   // Check first if the font's description has been queried before.
662   FontList*         fontList         = nullptr;
663   CharacterSetList* characterSetList = nullptr;
664
665   if(!mCacheHandler->FindFallbackFontList(fontDescription, fontList, characterSetList))
666   {
667     mCacheHandler->CacheFallbackFontList(std::move(fontDescription), fontList, characterSetList);
668   }
669
670   if(fontList && characterSetList)
671   {
672     fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
673   }
674
675   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
676   return fontId;
677 }
678
679 FontId FontClient::Plugin::GetFontIdByPath(const FontPath& path,
680                                            PointSize26Dot6 requestedPointSize,
681                                            FaceIndex       faceIndex,
682                                            bool            cacheDescription) const
683 {
684   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
685   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
686   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
687
688   FontId id = 0u;
689
690   if(nullptr != mFreeTypeLibrary)
691   {
692     FontId foundId = 0u;
693     if(mCacheHandler->FindFontByPath(path, requestedPointSize, faceIndex, foundId))
694     {
695       id = foundId;
696     }
697     else
698     {
699       id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
700     }
701   }
702
703   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
704   return id;
705 }
706
707 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
708                                      PointSize26Dot6        requestedPointSize,
709                                      FaceIndex              faceIndex) const
710 {
711   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
712   FONT_LOG_DESCRIPTION(fontDescription, "");
713
714   // Special case when font Description don't have family information.
715   // In this case, we just use default description family and path.
716   const FontDescription& realFontDescription = fontDescription.family.empty() ? FontDescription(mCacheHandler->mDefaultFontDescription.path,
717                                                                                                 mCacheHandler->mDefaultFontDescription.family,
718                                                                                                 fontDescription.width,
719                                                                                                 fontDescription.weight,
720                                                                                                 fontDescription.slant,
721                                                                                                 fontDescription.type)
722                                                                               : fontDescription;
723
724   FONT_LOG_DESCRIPTION(realFontDescription, "");
725   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "   requestedPointSize : %d\n", requestedPointSize);
726
727   // This method uses three vectors which caches:
728   // * The bitmap font cache
729   // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
730   // * The path to font file names.
731   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
732
733   // 1) Checks if the font description matches with a previously loaded bitmap font.
734   //    Returns if a font is found.
735   // 2) Checks in the cache if the font's description has been validated before.
736   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
737   //    retrieves using font config a path to a font file name which matches with the
738   //    font's description. The path is stored in the cache.
739   //
740   // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
741   //    font file names' exists. If exists, it gets the font id. If it doesn't it calls
742   //    the GetFontId() method with the path to the font file name and the point size to
743   //    get the font id.
744
745   // The font id to be returned.
746   FontId fontId = 0u;
747
748   // Check first if the font description matches with a previously loaded bitmap font.
749   if(mCacheHandler->FindBitmapFont(realFontDescription.family, fontId))
750   {
751     return fontId;
752   }
753
754   // Check if the font's description have been validated before.
755   FontDescriptionId fontDescriptionId = 0u;
756
757   if(!mCacheHandler->FindValidatedFont(realFontDescription, fontDescriptionId))
758   {
759     // Use font config to validate the font's description.
760     mCacheHandler->ValidateFont(realFontDescription, fontDescriptionId);
761   }
762
763   using FontCacheIndex          = CacheHandler::FontCacheIndex;
764   FontCacheIndex fontCacheIndex = 0u;
765   // Check if exists a pair 'fontDescriptionId, requestedPointSize' in the cache.
766   if(!mCacheHandler->FindFont(fontDescriptionId, requestedPointSize, fontCacheIndex))
767   {
768     // Retrieve the font file name path.
769     const FontDescription& description = *(mCacheHandler->mFontDescriptionCache.begin() + fontDescriptionId - 1u);
770
771     // Retrieve the font id. Do not cache the description as it has been already cached.
772     // Note : CacheFontPath() API call ValidateFont() + setup CharacterSet + cache the font description.
773     // So set cacheDescription=false, that we don't call CacheFontPath().
774     fontId = GetFontIdByPath(description.path, requestedPointSize, faceIndex, false);
775
776     if((fontId > 0u) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
777     {
778       fontCacheIndex                                              = mCacheHandler->mFontIdCache[fontId - 1u].index;
779       mCacheHandler->mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCacheHandler->mCharacterSetCache[fontDescriptionId - 1u]);
780
781       // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
782       mCacheHandler->CacheFontDescriptionSize(fontDescriptionId, requestedPointSize, fontCacheIndex);
783     }
784   }
785   else
786   {
787     fontId = mCacheHandler->mFontFaceCache[fontCacheIndex].mFontId + 1u;
788   }
789
790   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
791   return fontId;
792 }
793
794 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont) const
795 {
796   // The font id to be returned.
797   FontId fontId = 0u;
798   if(!mCacheHandler->FindBitmapFont(bitmapFont.name, fontId))
799   {
800     BitmapFontCacheItem bitmapFontCacheItem(bitmapFont);
801
802     fontId = mCacheHandler->CacheBitmapFontCacheItem(std::move(bitmapFontCacheItem));
803   }
804   return fontId;
805 }
806
807 void FontClient::Plugin::GetFontMetrics(FontId       fontId,
808                                         FontMetrics& metrics) const
809 {
810   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
811   if(fontCacheItem != nullptr)
812   {
813     fontCacheItem->GetFontMetrics(metrics, mDpiVertical);
814   }
815 }
816
817 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
818                                              Character charcode) const
819 {
820   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
821   if(fontCacheItem != nullptr)
822   {
823     return fontCacheItem->GetGlyphIndex(charcode);
824   }
825
826   return 0u;
827 }
828
829 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
830                                              Character charcode,
831                                              Character variantSelector) const
832 {
833   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
834   if(fontCacheItem != nullptr)
835   {
836     return fontCacheItem->GetGlyphIndex(charcode, variantSelector);
837   }
838
839   return 0u;
840 }
841
842 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
843                                          uint32_t   size,
844                                          GlyphType  type,
845                                          bool       horizontal) const
846 {
847   if(VECTOR_GLYPH == type)
848   {
849     return GetVectorMetrics(array, size, horizontal);
850   }
851
852   return GetBitmapMetrics(array, size, horizontal);
853 }
854
855 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
856                                           uint32_t   size,
857                                           bool       horizontal) const
858 {
859   bool success(size > 0u);
860
861   for(unsigned int i = 0; i < size; ++i)
862   {
863     GlyphInfo& glyph = array[i];
864
865     const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(glyph.fontId);
866     if(fontCacheItem != nullptr)
867     {
868       success &= fontCacheItem->GetGlyphMetrics(glyph, mDpiVertical, horizontal);
869     }
870     // Check if it's an embedded image.
871     else if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mCacheHandler->mEmbeddedItemCache.size()))
872     {
873       mCacheHandler->mEmbeddedItemCache[glyph.index - 1u].GetGlyphMetrics(glyph);
874     }
875     else
876     {
877       success = false;
878     }
879   }
880
881   return success;
882 }
883
884 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
885                                           uint32_t   size,
886                                           bool       horizontal) const
887 {
888 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
889   bool success(true);
890
891   for(unsigned int i = 0u; i < size; ++i)
892   {
893     FontId fontId = array[i].fontId;
894
895     if((fontId > 0u) &&
896        (fontId - 1u) < mCacheHandler->mFontIdCache.size())
897     {
898       FontFaceCacheItem& font = mCacheHandler->mFontFaceCache[mCacheHandler->mFontIdCache[fontId - 1u].index];
899
900       if(!font.mVectorFontId)
901       {
902         font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
903       }
904
905       mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
906
907       // Vector metrics are in EMs, convert to pixels
908       const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
909       array[i].width *= scale;
910       array[i].height *= scale;
911       array[i].xBearing *= scale;
912       array[i].yBearing *= scale;
913       array[i].advance *= scale;
914     }
915     else
916     {
917       success = false;
918     }
919   }
920
921   return success;
922 #else
923   return false;
924 #endif
925 }
926
927 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::GlyphBufferData& data, int outlineWidth) const
928 {
929   data.isColorBitmap                          = false;
930   data.isColorEmoji                           = false;
931   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
932   if(fontCacheItem != nullptr)
933   {
934     fontCacheItem->CreateBitmap(glyphIndex, data, outlineWidth, isItalicRequired, isBoldRequired);
935   }
936   else if((0u != glyphIndex) && (glyphIndex <= mCacheHandler->mEmbeddedItemCache.size()))
937   {
938     // It's an embedded item.
939     mCacheHandler->mEmbeddedItemCache[glyphIndex - 1u].CreateBitmap(mCacheHandler->mPixelBufferCache, data);
940   }
941 }
942
943 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth) const
944 {
945   TextAbstraction::GlyphBufferData data;
946
947   CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
948
949   // If data is compressed or not owned buffer, copy this.
950   if(!data.isBufferOwned || data.compressionType != TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION)
951   {
952     uint8_t* newBuffer = (uint8_t*)malloc(data.width * data.height * Pixel::GetBytesPerPixel(data.format));
953     TextAbstraction::GlyphBufferData::Decompress(data, newBuffer);
954     if(data.isBufferOwned)
955     {
956       free(data.buffer);
957     }
958
959     data.buffer          = newBuffer;
960     data.isBufferOwned   = true;
961     data.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
962   }
963
964   return PixelData::New(data.buffer,
965                         data.width * data.height * Pixel::GetBytesPerPixel(data.format),
966                         data.width,
967                         data.height,
968                         data.format,
969                         PixelData::FREE);
970 }
971
972 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight) const
973 {
974   blob       = nullptr;
975   blobLength = 0;
976
977 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
978   if((fontId > 0u) &&
979      (fontId - 1u < mCacheHandler->mFontIdCache.size()))
980   {
981     using FontCacheIndex                = CacheHandler::FontCacheIndex;
982     const FontCacheIndex fontCacheIndex = mCacheHandler->mFontIdCache[fontId - 1u].index;
983     FontFaceCacheItem&   font           = mCacheHandler->mFontFaceCache[fontCacheIndex];
984
985     if(!font.mVectorFontId)
986     {
987       font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
988     }
989
990     mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontCacheIndex, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
991   }
992 #endif
993 }
994
995 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize) const
996 {
997   using EllipsisCacheIndex              = CacheHandler::EllipsisCacheIndex;
998   using EllipsisItem                    = CacheHandler::EllipsisItem;
999   EllipsisCacheIndex ellipsisCacheIndex = 0u;
1000
1001   if(!mCacheHandler->FindEllipsis(requestedPointSize, ellipsisCacheIndex))
1002   {
1003     // No glyph has been found. Create one.
1004     EllipsisItem item;
1005
1006     item.requestedPointSize = requestedPointSize;
1007     item.index              = ellipsisCacheIndex;
1008
1009     // Find a font for the ellipsis glyph.
1010     item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1011                                         requestedPointSize,
1012                                         false);
1013
1014     // Set the character index to access the glyph inside the font.
1015     item.glyph.index = GetGlyphIndex(item.glyph.fontId, ELLIPSIS_CHARACTER);
1016
1017     // Get glyph informations.
1018     GetBitmapMetrics(&item.glyph, 1u, true);
1019
1020     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
1021     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
1022
1023     ellipsisCacheIndex = mCacheHandler->CacheEllipsis(std::move(item));
1024   }
1025   return mCacheHandler->mEllipsisCache[ellipsisCacheIndex].glyph;
1026 }
1027
1028 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex) const
1029 {
1030   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1031   return fontCacheItem && fontCacheItem->IsColorGlyph(glyphIndex);
1032 }
1033
1034 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId) const
1035 {
1036   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1037   if(fontCacheItem != nullptr)
1038   {
1039     return fontCacheItem->GetTypeface();
1040   }
1041   return nullptr;
1042 }
1043
1044 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId) const
1045 {
1046   const FontId index = fontId - 1u;
1047   if((fontId > 0u) && (index < mCacheHandler->mFontIdCache.size()))
1048   {
1049     return mCacheHandler->mFontIdCache[index].type;
1050   }
1051   return FontDescription::INVALID;
1052 }
1053
1054 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1055 {
1056   // nullptr as first parameter means the current configuration is used.
1057   return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1058 }
1059
1060 HarfBuzzFontHandle FontClient::Plugin::GetHarfBuzzFont(FontId fontId) const
1061 {
1062   FontCacheItemInterface* fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
1063   if(fontCacheItem != nullptr)
1064   {
1065     return fontCacheItem->GetHarfBuzzFont(mDpiHorizontal, mDpiVertical); // May cache
1066   }
1067   return nullptr;
1068 }
1069
1070 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat) const
1071 {
1072   EmbeddedItem embeddedItem;
1073
1074   embeddedItem.pixelBufferId = 0u;
1075   embeddedItem.width         = description.width;
1076   embeddedItem.height        = description.height;
1077
1078   pixelFormat = Pixel::A8;
1079
1080   if(!description.url.empty())
1081   {
1082     // Check if the url is in the cache.
1083     Devel::PixelBuffer pixelBuffer;
1084     if(!mCacheHandler->FindEmbeddedPixelBufferId(description.url, embeddedItem.pixelBufferId))
1085     {
1086       // The pixel buffer is not in the cache. Create one and cache it.
1087       embeddedItem.pixelBufferId = mCacheHandler->CacheEmbeddedPixelBuffer(description.url);
1088     }
1089
1090     if((embeddedItem.pixelBufferId > 0u) && (embeddedItem.pixelBufferId - 1u) < mCacheHandler->mPixelBufferCache.size())
1091     {
1092       // Retrieve the pixel buffer from the cache to set the pixel format.
1093       pixelBuffer = mCacheHandler->mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1094     }
1095
1096     if(pixelBuffer)
1097     {
1098       // Set the size of the embedded item if it has not been set.
1099       if(0u == embeddedItem.width)
1100       {
1101         embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1102       }
1103
1104       if(0u == embeddedItem.height)
1105       {
1106         embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1107       }
1108
1109       // Set the pixel format.
1110       pixelFormat = pixelBuffer.GetPixelFormat();
1111     }
1112   }
1113
1114   // Find if the same embeddedItem has already been created.
1115   GlyphIndex index = 0u;
1116   if(!mCacheHandler->FindEmbeddedItem(embeddedItem.pixelBufferId, embeddedItem.width, embeddedItem.height, index))
1117   {
1118     index = mCacheHandler->CacheEmbeddedItem(std::move(embeddedItem));
1119   }
1120   return index;
1121 }
1122
1123 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
1124 {
1125   mIsAtlasLimitationEnabled = enabled;
1126 }
1127
1128 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
1129 {
1130   return mIsAtlasLimitationEnabled;
1131 }
1132
1133 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
1134 {
1135   return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1136 }
1137
1138 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
1139 {
1140   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
1141 }
1142
1143 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
1144 {
1145   return mCurrentMaximumBlockSizeFitInAtlas;
1146 }
1147
1148 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
1149 {
1150   bool            isChanged        = false;
1151   const Size&     maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1152   const uint16_t& padding          = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
1153
1154   if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
1155   {
1156     mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
1157     isChanged                          = true;
1158   }
1159
1160   return isChanged;
1161 }
1162
1163 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
1164 {
1165   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
1166 }
1167
1168 FontId FontClient::Plugin::CreateFont(const FontPath& path,
1169                                       PointSize26Dot6 requestedPointSize,
1170                                       FaceIndex       faceIndex,
1171                                       bool            cacheDescription) const
1172 {
1173   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1174   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
1175   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1176
1177   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_CREATE_FONT");
1178
1179   FontId   fontId = 0u;
1180   FT_Face  ftFace;
1181   FT_Error error;
1182
1183   uint8_t*       fontDataPtr   = nullptr;
1184   std::streampos dataSize      = 0;
1185   bool           fontDataFound = mCacheHandler->FindFontData(path, fontDataPtr, dataSize);
1186
1187   if(fontDataFound)
1188   {
1189     // Create & cache new font face from pre-loaded font
1190     error = FT_New_Memory_Face(mFreeTypeLibrary, reinterpret_cast<FT_Byte*>(fontDataPtr), static_cast<FT_Long>(dataSize), 0, &ftFace);
1191 #if defined(TRACE_ENABLED)
1192     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
1193     {
1194       DALI_LOG_DEBUG_INFO("DALI_TEXT_CREATE_FONT : FT_New_Memory_Face : %s\n", path.c_str());
1195     }
1196 #endif
1197   }
1198   else
1199   {
1200     // Create & cache new font face
1201     error = FT_New_Face(mFreeTypeLibrary, path.c_str(), 0, &ftFace);
1202 #if defined(TRACE_ENABLED)
1203     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
1204     {
1205       DALI_LOG_DEBUG_INFO("DALI_TEXT_CREATE_FONT : FT_New_Face : %s\n", path.c_str());
1206     }
1207 #endif
1208   }
1209
1210   if(FT_Err_Ok == error)
1211   {
1212     // Check if a font is scalable.
1213     const bool isScalable           = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
1214     const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
1215     const bool hasColorTables       = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
1216
1217     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "            isScalable : [%s]\n", (isScalable ? "true" : "false"));
1218     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
1219     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "        hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
1220
1221     // Check to see if the font contains fixed sizes?
1222     if(!isScalable && hasFixedSizedBitmaps)
1223     {
1224       PointSize26Dot6 actualPointSize = 0u;
1225       int             fixedSizeIndex  = 0;
1226       for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
1227       {
1228         const PointSize26Dot6 fixedSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
1229         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
1230
1231         if(fixedSize >= requestedPointSize)
1232         {
1233           actualPointSize = fixedSize;
1234           break;
1235         }
1236       }
1237
1238       if(0u == actualPointSize)
1239       {
1240         // The requested point size is bigger than the bigest fixed size.
1241         fixedSizeIndex  = ftFace->num_fixed_sizes - 1;
1242         actualPointSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
1243       }
1244
1245       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
1246
1247       // Tell Freetype to use this size
1248       error = FT_Select_Size(ftFace, fixedSizeIndex);
1249       if(FT_Err_Ok != error)
1250       {
1251         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
1252       }
1253       else
1254       {
1255         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1256
1257         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1258                             static_cast<float>(ftMetrics.descender) * FROM_266,
1259                             static_cast<float>(ftMetrics.height) * FROM_266,
1260                             static_cast<float>(ftFace->underline_position) * FROM_266,
1261                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1262
1263         const float fixedWidth  = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
1264         const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
1265
1266         // Create the FreeType font face item to cache.
1267         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
1268
1269         fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
1270       }
1271     }
1272     else
1273     {
1274       if(mIsAtlasLimitationEnabled)
1275       {
1276         //There is limitation on block size to fit in predefined atlas size.
1277         //If the block size cannot fit into atlas size, then the system cannot draw block.
1278         //This is workaround to avoid issue in advance
1279         //Decrementing point-size until arriving to maximum allowed block size.
1280         auto        requestedPointSizeBackup = requestedPointSize;
1281         const Size& maxSizeFitInAtlas        = GetCurrentMaximumBlockSizeFitInAtlas();
1282         error                                = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
1283
1284         if(requestedPointSize != requestedPointSizeBackup)
1285         {
1286           DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
1287         }
1288       }
1289       else
1290       {
1291         error = FT_Set_Char_Size(ftFace,
1292                                  0,
1293                                  FT_F26Dot6(requestedPointSize),
1294                                  mDpiHorizontal,
1295                                  mDpiVertical);
1296       }
1297
1298       if(FT_Err_Ok == error)
1299       {
1300         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1301
1302         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1303                             static_cast<float>(ftMetrics.descender) * FROM_266,
1304                             static_cast<float>(ftMetrics.height) * FROM_266,
1305                             static_cast<float>(ftFace->underline_position) * FROM_266,
1306                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1307
1308         // Create the FreeType font face item to cache.
1309         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics);
1310
1311         fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
1312       }
1313       else
1314       {
1315         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
1316       }
1317     }
1318
1319     if(0u != fontId)
1320     {
1321       if(cacheDescription)
1322       {
1323         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Cache Font Path at font id : %d [%s]\n", fontId, path.c_str());
1324         mCacheHandler->CacheFontPath(ftFace, fontId, requestedPointSize, path);
1325       }
1326     }
1327   }
1328   else
1329   {
1330     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str());
1331   }
1332
1333   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
1334   return fontId;
1335 }
1336
1337 bool FontClient::Plugin::IsScalable(const FontPath& path) const
1338 {
1339   bool isScalable = false;
1340
1341   FT_Face ftFace = nullptr;
1342   int     error  = FT_New_Face(mFreeTypeLibrary,
1343                           path.c_str(),
1344                           0,
1345                           &ftFace);
1346   if(FT_Err_Ok != error)
1347   {
1348     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
1349   }
1350   else
1351   {
1352     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
1353   }
1354
1355   if(ftFace)
1356   {
1357     FT_Done_Face(ftFace);
1358   }
1359
1360   return isScalable;
1361 }
1362
1363 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription) const
1364 {
1365   // Create a font pattern.
1366   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1367
1368   FcResult result = FcResultMatch;
1369
1370   // match the pattern
1371   FcPattern* match      = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1372   bool       isScalable = false;
1373
1374   if(match)
1375   {
1376     // Get the path to the font file name.
1377     FontPath path;
1378     GetFcString(match, FC_FILE, path);
1379     isScalable = IsScalable(path);
1380   }
1381   else
1382   {
1383     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1384   }
1385
1386   // Destroys the created patterns.
1387   FcPatternDestroy(match);
1388   FcPatternDestroy(fontFamilyPattern);
1389
1390   return isScalable;
1391 }
1392
1393 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes) const
1394 {
1395   // Empty the caller container
1396   sizes.Clear();
1397
1398   FT_Face ftFace = nullptr;
1399   int     error  = FT_New_Face(mFreeTypeLibrary,
1400                           path.c_str(),
1401                           0,
1402                           &ftFace);
1403   if(FT_Err_Ok != error)
1404   {
1405     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
1406   }
1407
1408   if(ftFace)
1409   {
1410     // Fetch the number of fixed sizes available
1411     if(ftFace->num_fixed_sizes && ftFace->available_sizes)
1412     {
1413       for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
1414       {
1415         sizes.PushBack(ftFace->available_sizes[i].size);
1416       }
1417     }
1418
1419     FT_Done_Face(ftFace);
1420   }
1421 }
1422
1423 void FontClient::Plugin::GetFixedSizes(const FontDescription&   fontDescription,
1424                                        Vector<PointSize26Dot6>& sizes) const
1425 {
1426   // Create a font pattern.
1427   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1428
1429   FcResult result = FcResultMatch;
1430
1431   // match the pattern
1432   FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1433
1434   if(match)
1435   {
1436     // Get the path to the font file name.
1437     FontPath path;
1438     GetFcString(match, FC_FILE, path);
1439     GetFixedSizes(path, sizes);
1440   }
1441   else
1442   {
1443     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1444   }
1445
1446   // Destroys the created patterns.
1447   FcPatternDestroy(match);
1448   FcPatternDestroy(fontFamilyPattern);
1449 }
1450
1451 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
1452 {
1453   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1454   if(fontCacheItem != nullptr)
1455   {
1456     return fontCacheItem->HasItalicStyle();
1457   }
1458   return false;
1459 }
1460
1461 } // namespace Dali::TextAbstraction::Internal