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