Merge "[AT-SPI] Rework intercepting key events" into devel/master
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-client-plugin-impl.cpp
1 /*
2  * Copyright (c) 2024 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     if(fontDescriptionId > 0u && fontDescriptionId <= mCacheHandler->mCharacterSetCache.Count())
769     {
770       // Retrieve the font file name path.
771       const FontDescription& description = *(mCacheHandler->mFontDescriptionCache.begin() + fontDescriptionId - 1u);
772
773       // Retrieve the font id. Do not cache the description as it has been already cached.
774       // Note : CacheFontPath() API call ValidateFont() + setup CharacterSet + cache the font description.
775       // So set cacheDescription=false, that we don't call CacheFontPath().
776       fontId = GetFontIdByPath(description.path, requestedPointSize, faceIndex, false);
777
778       if((fontId > 0u) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
779       {
780         fontCacheIndex                                              = mCacheHandler->mFontIdCache[fontId - 1u].index;
781         mCacheHandler->mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCacheHandler->mCharacterSetCache[fontDescriptionId - 1u]);
782
783         // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
784         mCacheHandler->CacheFontDescriptionSize(fontDescriptionId, requestedPointSize, fontCacheIndex);
785       }
786     }
787   }
788   else
789   {
790     fontId = mCacheHandler->mFontFaceCache[fontCacheIndex].mFontId + 1u;
791   }
792
793   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
794   return fontId;
795 }
796
797 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont) const
798 {
799   // The font id to be returned.
800   FontId fontId = 0u;
801   if(!mCacheHandler->FindBitmapFont(bitmapFont.name, fontId))
802   {
803     BitmapFontCacheItem bitmapFontCacheItem(bitmapFont);
804
805     fontId = mCacheHandler->CacheBitmapFontCacheItem(std::move(bitmapFontCacheItem));
806   }
807   return fontId;
808 }
809
810 void FontClient::Plugin::GetFontMetrics(FontId       fontId,
811                                         FontMetrics& metrics) const
812 {
813   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
814   if(fontCacheItem != nullptr)
815   {
816     fontCacheItem->GetFontMetrics(metrics, mDpiVertical);
817   }
818 }
819
820 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
821                                              Character charcode) const
822 {
823   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
824   if(fontCacheItem != nullptr)
825   {
826     return fontCacheItem->GetGlyphIndex(charcode);
827   }
828
829   return 0u;
830 }
831
832 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
833                                              Character charcode,
834                                              Character variantSelector) const
835 {
836   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
837   if(fontCacheItem != nullptr)
838   {
839     return fontCacheItem->GetGlyphIndex(charcode, variantSelector);
840   }
841
842   return 0u;
843 }
844
845 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
846                                          uint32_t   size,
847                                          GlyphType  type,
848                                          bool       horizontal) const
849 {
850   if(VECTOR_GLYPH == type)
851   {
852     return GetVectorMetrics(array, size, horizontal);
853   }
854
855   return GetBitmapMetrics(array, size, horizontal);
856 }
857
858 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
859                                           uint32_t   size,
860                                           bool       horizontal) const
861 {
862   bool success(size > 0u);
863
864   for(unsigned int i = 0; i < size; ++i)
865   {
866     GlyphInfo& glyph = array[i];
867
868     const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(glyph.fontId);
869     if(fontCacheItem != nullptr)
870     {
871       success &= fontCacheItem->GetGlyphMetrics(glyph, mDpiVertical, horizontal);
872     }
873     // Check if it's an embedded image.
874     else if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mCacheHandler->mEmbeddedItemCache.size()))
875     {
876       mCacheHandler->mEmbeddedItemCache[glyph.index - 1u].GetGlyphMetrics(glyph);
877     }
878     else
879     {
880       success = false;
881     }
882   }
883
884   return success;
885 }
886
887 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
888                                           uint32_t   size,
889                                           bool       horizontal) const
890 {
891 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
892   bool success(true);
893
894   for(unsigned int i = 0u; i < size; ++i)
895   {
896     FontId fontId = array[i].fontId;
897
898     if((fontId > 0u) &&
899        (fontId - 1u) < mCacheHandler->mFontIdCache.size())
900     {
901       FontFaceCacheItem& font = mCacheHandler->mFontFaceCache[mCacheHandler->mFontIdCache[fontId - 1u].index];
902
903       if(!font.mVectorFontId)
904       {
905         font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
906       }
907
908       mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
909
910       // Vector metrics are in EMs, convert to pixels
911       const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
912       array[i].width *= scale;
913       array[i].height *= scale;
914       array[i].xBearing *= scale;
915       array[i].yBearing *= scale;
916       array[i].advance *= scale;
917     }
918     else
919     {
920       success = false;
921     }
922   }
923
924   return success;
925 #else
926   return false;
927 #endif
928 }
929
930 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::GlyphBufferData& data, int outlineWidth) const
931 {
932   data.isColorBitmap                          = false;
933   data.isColorEmoji                           = false;
934   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
935   if(fontCacheItem != nullptr)
936   {
937     fontCacheItem->CreateBitmap(glyphIndex, data, outlineWidth, isItalicRequired, isBoldRequired);
938   }
939   else if((0u != glyphIndex) && (glyphIndex <= mCacheHandler->mEmbeddedItemCache.size()))
940   {
941     // It's an embedded item.
942     mCacheHandler->mEmbeddedItemCache[glyphIndex - 1u].CreateBitmap(mCacheHandler->mPixelBufferCache, data);
943   }
944 }
945
946 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth) const
947 {
948   TextAbstraction::GlyphBufferData data;
949
950   CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
951
952   // If data is compressed or not owned buffer, copy this.
953   if(!data.isBufferOwned || data.compressionType != TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION)
954   {
955     uint8_t* newBuffer = (uint8_t*)malloc(data.width * data.height * Pixel::GetBytesPerPixel(data.format));
956     if(DALI_UNLIKELY(!newBuffer))
957     {
958       DALI_LOG_ERROR("malloc is failed. request malloc size : %u x %u x %u\n", data.width, data.height, Pixel::GetBytesPerPixel(data.format));
959       return Dali::PixelData();
960     }
961
962     TextAbstraction::GlyphBufferData::Decompress(data, newBuffer);
963     if(data.isBufferOwned)
964     {
965       free(data.buffer);
966     }
967
968     data.buffer          = newBuffer;
969     data.isBufferOwned   = true;
970     data.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
971   }
972
973   return PixelData::New(data.buffer,
974                         data.width * data.height * Pixel::GetBytesPerPixel(data.format),
975                         data.width,
976                         data.height,
977                         data.format,
978                         PixelData::FREE);
979 }
980
981 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight) const
982 {
983   blob       = nullptr;
984   blobLength = 0;
985
986 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
987   if((fontId > 0u) &&
988      (fontId - 1u < mCacheHandler->mFontIdCache.size()))
989   {
990     using FontCacheIndex                = CacheHandler::FontCacheIndex;
991     const FontCacheIndex fontCacheIndex = mCacheHandler->mFontIdCache[fontId - 1u].index;
992     FontFaceCacheItem&   font           = mCacheHandler->mFontFaceCache[fontCacheIndex];
993
994     if(!font.mVectorFontId)
995     {
996       font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
997     }
998
999     mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontCacheIndex, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1000   }
1001 #endif
1002 }
1003
1004 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize) const
1005 {
1006   using EllipsisCacheIndex              = CacheHandler::EllipsisCacheIndex;
1007   using EllipsisItem                    = CacheHandler::EllipsisItem;
1008   EllipsisCacheIndex ellipsisCacheIndex = 0u;
1009
1010   if(!mCacheHandler->FindEllipsis(requestedPointSize, ellipsisCacheIndex))
1011   {
1012     // No glyph has been found. Create one.
1013     EllipsisItem item;
1014
1015     item.requestedPointSize = requestedPointSize;
1016
1017     // Find a font for the ellipsis glyph.
1018     item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1019                                         requestedPointSize,
1020                                         false);
1021
1022     // Set the character index to access the glyph inside the font.
1023     item.glyph.index = GetGlyphIndex(item.glyph.fontId, ELLIPSIS_CHARACTER);
1024
1025     // Get glyph informations.
1026     GetBitmapMetrics(&item.glyph, 1u, true);
1027
1028     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
1029     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
1030
1031     // EllipsisCacheIndex is stored in item.index.
1032     ellipsisCacheIndex = mCacheHandler->CacheEllipsis(std::move(item));
1033     if(!mCacheHandler->mEllipsisCache.empty())
1034     {
1035       mCacheHandler->mEllipsisCache.back().index = ellipsisCacheIndex;
1036     }
1037   }
1038   return mCacheHandler->mEllipsisCache[ellipsisCacheIndex].glyph;
1039 }
1040
1041 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex) const
1042 {
1043   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1044   return fontCacheItem && fontCacheItem->IsColorGlyph(glyphIndex);
1045 }
1046
1047 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId) const
1048 {
1049   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1050   if(fontCacheItem != nullptr)
1051   {
1052     return fontCacheItem->GetTypeface();
1053   }
1054   return nullptr;
1055 }
1056
1057 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId) const
1058 {
1059   const FontId index = fontId - 1u;
1060   if((fontId > 0u) && (index < mCacheHandler->mFontIdCache.size()))
1061   {
1062     return mCacheHandler->mFontIdCache[index].type;
1063   }
1064   return FontDescription::INVALID;
1065 }
1066
1067 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1068 {
1069   // nullptr as first parameter means the current configuration is used.
1070   return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1071 }
1072
1073 HarfBuzzFontHandle FontClient::Plugin::GetHarfBuzzFont(FontId fontId) const
1074 {
1075   FontCacheItemInterface* fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
1076   if(fontCacheItem != nullptr)
1077   {
1078     return fontCacheItem->GetHarfBuzzFont(mDpiHorizontal, mDpiVertical); // May cache
1079   }
1080   return nullptr;
1081 }
1082
1083 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat) const
1084 {
1085   EmbeddedItem embeddedItem;
1086
1087   embeddedItem.pixelBufferId = 0u;
1088   embeddedItem.width         = description.width;
1089   embeddedItem.height        = description.height;
1090
1091   pixelFormat = Pixel::A8;
1092
1093   if(!description.url.empty())
1094   {
1095     // Check if the url is in the cache.
1096     Devel::PixelBuffer pixelBuffer;
1097     if(!mCacheHandler->FindEmbeddedPixelBufferId(description.url, embeddedItem.pixelBufferId))
1098     {
1099       // The pixel buffer is not in the cache. Create one and cache it.
1100       embeddedItem.pixelBufferId = mCacheHandler->CacheEmbeddedPixelBuffer(description.url);
1101     }
1102
1103     if((embeddedItem.pixelBufferId > 0u) && (embeddedItem.pixelBufferId - 1u) < mCacheHandler->mPixelBufferCache.size())
1104     {
1105       // Retrieve the pixel buffer from the cache to set the pixel format.
1106       pixelBuffer = mCacheHandler->mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1107     }
1108
1109     if(pixelBuffer)
1110     {
1111       // Set the size of the embedded item if it has not been set.
1112       if(0u == embeddedItem.width)
1113       {
1114         embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1115       }
1116
1117       if(0u == embeddedItem.height)
1118       {
1119         embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1120       }
1121
1122       // Set the pixel format.
1123       pixelFormat = pixelBuffer.GetPixelFormat();
1124     }
1125   }
1126
1127   // Find if the same embeddedItem has already been created.
1128   GlyphIndex index = 0u;
1129   if(!mCacheHandler->FindEmbeddedItem(embeddedItem.pixelBufferId, embeddedItem.width, embeddedItem.height, index))
1130   {
1131     index = mCacheHandler->CacheEmbeddedItem(std::move(embeddedItem));
1132   }
1133   return index;
1134 }
1135
1136 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
1137 {
1138   mIsAtlasLimitationEnabled = enabled;
1139 }
1140
1141 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
1142 {
1143   return mIsAtlasLimitationEnabled;
1144 }
1145
1146 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
1147 {
1148   return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1149 }
1150
1151 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
1152 {
1153   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
1154 }
1155
1156 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
1157 {
1158   return mCurrentMaximumBlockSizeFitInAtlas;
1159 }
1160
1161 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
1162 {
1163   bool            isChanged        = false;
1164   const Size&     maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1165   const uint16_t& padding          = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
1166
1167   if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
1168   {
1169     mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
1170     isChanged                          = true;
1171   }
1172
1173   return isChanged;
1174 }
1175
1176 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
1177 {
1178   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
1179 }
1180
1181 FontId FontClient::Plugin::CreateFont(const FontPath& path,
1182                                       PointSize26Dot6 requestedPointSize,
1183                                       FaceIndex       faceIndex,
1184                                       bool            cacheDescription) const
1185 {
1186   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1187   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
1188   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1189
1190   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_CREATE_FONT");
1191
1192   FontId   fontId = 0u;
1193   FT_Face  ftFace;
1194   FT_Error error;
1195
1196   uint8_t*       fontDataPtr   = nullptr;
1197   std::streampos dataSize      = 0;
1198   bool           fontDataFound = mCacheHandler->FindFontData(path, fontDataPtr, dataSize);
1199
1200   if(fontDataFound)
1201   {
1202     // Create & cache new font face from pre-loaded font
1203     error = FT_New_Memory_Face(mFreeTypeLibrary, reinterpret_cast<FT_Byte*>(fontDataPtr), static_cast<FT_Long>(dataSize), 0, &ftFace);
1204 #if defined(TRACE_ENABLED)
1205     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
1206     {
1207       DALI_LOG_DEBUG_INFO("DALI_TEXT_CREATE_FONT : FT_New_Memory_Face : %s\n", path.c_str());
1208     }
1209 #endif
1210   }
1211   else
1212   {
1213     // Create & cache new font face
1214     error = FT_New_Face(mFreeTypeLibrary, path.c_str(), 0, &ftFace);
1215 #if defined(TRACE_ENABLED)
1216     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
1217     {
1218       DALI_LOG_DEBUG_INFO("DALI_TEXT_CREATE_FONT : FT_New_Face : %s\n", path.c_str());
1219     }
1220 #endif
1221   }
1222
1223   if(FT_Err_Ok == error)
1224   {
1225     // Check if a font is scalable.
1226     const bool isScalable           = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
1227     const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
1228     const bool hasColorTables       = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
1229
1230     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "            isScalable : [%s]\n", (isScalable ? "true" : "false"));
1231     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
1232     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "        hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
1233
1234     // Check to see if the font contains fixed sizes?
1235     if(!isScalable && hasFixedSizedBitmaps)
1236     {
1237       PointSize26Dot6 actualPointSize = 0u;
1238       int             fixedSizeIndex  = 0;
1239       for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
1240       {
1241         const PointSize26Dot6 fixedSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
1242         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
1243
1244         if(fixedSize >= requestedPointSize)
1245         {
1246           actualPointSize = fixedSize;
1247           break;
1248         }
1249       }
1250
1251       if(0u == actualPointSize)
1252       {
1253         // The requested point size is bigger than the bigest fixed size.
1254         fixedSizeIndex  = ftFace->num_fixed_sizes - 1;
1255         actualPointSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
1256       }
1257
1258       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
1259
1260       // Tell Freetype to use this size
1261       error = FT_Select_Size(ftFace, fixedSizeIndex);
1262       if(FT_Err_Ok != error)
1263       {
1264         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
1265       }
1266       else
1267       {
1268         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1269
1270         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1271                             static_cast<float>(ftMetrics.descender) * FROM_266,
1272                             static_cast<float>(ftMetrics.height) * FROM_266,
1273                             static_cast<float>(ftFace->underline_position) * FROM_266,
1274                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1275
1276         const float fixedWidth  = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
1277         const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
1278
1279         // Create the FreeType font face item to cache.
1280         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
1281
1282         fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
1283       }
1284     }
1285     else
1286     {
1287       if(mIsAtlasLimitationEnabled)
1288       {
1289         //There is limitation on block size to fit in predefined atlas size.
1290         //If the block size cannot fit into atlas size, then the system cannot draw block.
1291         //This is workaround to avoid issue in advance
1292         //Decrementing point-size until arriving to maximum allowed block size.
1293         auto        requestedPointSizeBackup = requestedPointSize;
1294         const Size& maxSizeFitInAtlas        = GetCurrentMaximumBlockSizeFitInAtlas();
1295         error                                = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
1296
1297         if(requestedPointSize != requestedPointSizeBackup)
1298         {
1299           DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
1300         }
1301       }
1302       else
1303       {
1304         error = FT_Set_Char_Size(ftFace,
1305                                  0,
1306                                  FT_F26Dot6(requestedPointSize),
1307                                  mDpiHorizontal,
1308                                  mDpiVertical);
1309       }
1310
1311       if(FT_Err_Ok == error)
1312       {
1313         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1314
1315         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1316                             static_cast<float>(ftMetrics.descender) * FROM_266,
1317                             static_cast<float>(ftMetrics.height) * FROM_266,
1318                             static_cast<float>(ftFace->underline_position) * FROM_266,
1319                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1320
1321         // Create the FreeType font face item to cache.
1322         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics);
1323
1324         fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
1325       }
1326       else
1327       {
1328         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
1329       }
1330     }
1331
1332     if(0u != fontId)
1333     {
1334       if(cacheDescription)
1335       {
1336         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Cache Font Path at font id : %d [%s]\n", fontId, path.c_str());
1337         mCacheHandler->CacheFontPath(ftFace, fontId, requestedPointSize, path);
1338       }
1339     }
1340   }
1341   else
1342   {
1343     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str());
1344   }
1345
1346   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
1347   return fontId;
1348 }
1349
1350 bool FontClient::Plugin::IsScalable(const FontPath& path) const
1351 {
1352   bool isScalable = false;
1353
1354   FT_Face ftFace = nullptr;
1355   int     error  = FT_New_Face(mFreeTypeLibrary,
1356                           path.c_str(),
1357                           0,
1358                           &ftFace);
1359   if(FT_Err_Ok != error)
1360   {
1361     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
1362   }
1363   else
1364   {
1365     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
1366   }
1367
1368   if(ftFace)
1369   {
1370     FT_Done_Face(ftFace);
1371   }
1372
1373   return isScalable;
1374 }
1375
1376 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription) const
1377 {
1378   // Create a font pattern.
1379   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1380
1381   FcResult result = FcResultMatch;
1382
1383   // match the pattern
1384   FcPattern* match      = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1385   bool       isScalable = false;
1386
1387   if(match)
1388   {
1389     // Get the path to the font file name.
1390     FontPath path;
1391     GetFcString(match, FC_FILE, path);
1392     isScalable = IsScalable(path);
1393   }
1394   else
1395   {
1396     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1397   }
1398
1399   // Destroys the created patterns.
1400   FcPatternDestroy(match);
1401   FcPatternDestroy(fontFamilyPattern);
1402
1403   return isScalable;
1404 }
1405
1406 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes) const
1407 {
1408   // Empty the caller container
1409   sizes.Clear();
1410
1411   FT_Face ftFace = nullptr;
1412   int     error  = FT_New_Face(mFreeTypeLibrary,
1413                           path.c_str(),
1414                           0,
1415                           &ftFace);
1416   if(FT_Err_Ok != error)
1417   {
1418     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
1419   }
1420
1421   if(ftFace)
1422   {
1423     // Fetch the number of fixed sizes available
1424     if(ftFace->num_fixed_sizes && ftFace->available_sizes)
1425     {
1426       for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
1427       {
1428         sizes.PushBack(ftFace->available_sizes[i].size);
1429       }
1430     }
1431
1432     FT_Done_Face(ftFace);
1433   }
1434 }
1435
1436 void FontClient::Plugin::GetFixedSizes(const FontDescription&   fontDescription,
1437                                        Vector<PointSize26Dot6>& sizes) const
1438 {
1439   // Create a font pattern.
1440   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1441
1442   FcResult result = FcResultMatch;
1443
1444   // match the pattern
1445   FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1446
1447   if(match)
1448   {
1449     // Get the path to the font file name.
1450     FontPath path;
1451     GetFcString(match, FC_FILE, path);
1452     GetFixedSizes(path, sizes);
1453   }
1454   else
1455   {
1456     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1457   }
1458
1459   // Destroys the created patterns.
1460   FcPatternDestroy(match);
1461   FcPatternDestroy(fontFamilyPattern);
1462 }
1463
1464 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
1465 {
1466   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1467   if(fontCacheItem != nullptr)
1468   {
1469     return fontCacheItem->HasItalicStyle();
1470   }
1471   return false;
1472 }
1473
1474 } // namespace Dali::TextAbstraction::Internal