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