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