Merge "Fix SIGBUS error during downscaling." into devel/master
[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
24 #include <dali/devel-api/adaptor-framework/image-loading.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/integration-api/platform-abstraction.h>
27 #include <dali/internal/adaptor/common/adaptor-impl.h>
28 #include <dali/internal/imaging/common/image-operations.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-utils.h>
32 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
33 #include <dali/public-api/common/dali-vector.h>
34 #include <dali/public-api/common/vector-wrapper.h>
35
36 // EXTERNAL INCLUDES
37 #include <fontconfig/fontconfig.h>
38 #include <algorithm>
39 #include <iterator>
40
41 #if defined(DEBUG_ENABLED)
42
43 // Note, to turn on trace and verbose logging, use "export LOG_FONT_CLIENT=3,true"
44 // Or re-define the following filter using Verbose,true instead of NoLogging,false,
45 // Or, add DALI_LOG_FILTER_ENABLE_TRACE(gFontClientLogFilter) in the code below.
46
47 Dali::Integration::Log::Filter* gFontClientLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
48
49 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)                                                                            \
50   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix "  description; family : [%s]\n", fontDescription.family.c_str()); \
51   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose,                                                                            \
52                 "                 path : [%s]\n"                                                                                 \
53                 "                width : [%s]\n"                                                                                 \
54                 "               weight : [%s]\n"                                                                                 \
55                 "                slant : [%s]\n\n",                                                                              \
56                 fontDescription.path.c_str(),                                                                                    \
57                 FontWidth::Name[fontDescription.width],                                                                          \
58                 FontWeight::Name[fontDescription.weight],                                                                        \
59                 FontSlant::Name[fontDescription.slant])
60
61 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
62   DALI_LOG_INFO(gFontClientLogFilter, Debug::General,               \
63                 "           character : %p\n"                       \
64                 "  requestedPointSize : %d\n"                       \
65                 "         preferColor : %s\n",                      \
66                 charcode,                                           \
67                 requestedPointSize,                                 \
68                 (preferColor ? "true" : "false"))
69
70 #else
71
72 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
73 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
74
75 #endif
76
77 namespace
78 {
79 /**
80  * Conversion from Fractional26.6 to float
81  */
82 const float FROM_266        = 1.0f / 64.0f;
83 const float POINTS_PER_INCH = 72.f;
84
85 const std::string DEFAULT_FONT_FAMILY_NAME("Tizen");
86
87 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
88
89 } // namespace
90
91 using Dali::Vector;
92 using namespace std;
93
94 namespace Dali::TextAbstraction::Internal
95 {
96 /**
97  * @brief Free the resources allocated by the FcCharSet objects.
98  *
99  * @param[in] characterSets The vector of character sets.
100  */
101 void DestroyCharacterSets(CharacterSetList& characterSets)
102 {
103   for(auto& item : characterSets)
104   {
105     if(item)
106     {
107       FcCharSetDestroy(item);
108     }
109   }
110 }
111
112 /**
113  * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
114  *
115  * @param[in/out] ftFace Face type object.
116  * @param[in] horizontalDpi The horizontal dpi.
117  * @param[in] verticalDpi The vertical dpi.
118  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
119  * @param[in] requestedPointSize The requested point-size.
120  * @return whether the  ftFace's block can fit into atlas
121  */
122 bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
123 {
124   bool isFit = false;
125
126   error = FT_Set_Char_Size(ftFace,
127                            0,
128                            requestedPointSize,
129                            horizontalDpi,
130                            verticalDpi);
131
132   if(error == FT_Err_Ok)
133   {
134     //Check width and height of block for requestedPointSize
135     //If the width or height is greater than the maximum-size then decrement by one unit of point-size.
136     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)
137     {
138       isFit = true;
139     }
140   }
141
142   return isFit;
143 }
144
145 /**
146  * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
147  *
148  * @param[in/out] ftFace Face type object.
149  * @param[in] horizontalDpi The horizontal dpi.
150  * @param[in] verticalDpi The vertical dpi.
151  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
152  * @param[in/out] requestedPointSize The requested point-size.
153  * @return FreeType error code. 0 means success when requesting the nominal size (in points).
154  */
155 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
156 {
157   //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
158   const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
159   bool            canFitInAtlas;
160   int             error; // FreeType error code.
161
162   canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
163   if(FT_Err_Ok != error)
164   {
165     return error;
166   }
167
168   if(!canFitInAtlas)
169   {
170     //Exponential search
171     uint32_t exponentialDecrement = 1;
172
173     while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit * exponentialDecrement)
174     {
175       requestedPointSize -= (pointSizePerOneUnit * exponentialDecrement);
176       canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
177       if(FT_Err_Ok != error)
178       {
179         return error;
180       }
181
182       exponentialDecrement *= 2;
183     }
184
185     //Binary search
186     uint32_t minPointSize;
187     uint32_t maxPointSize;
188
189     if(canFitInAtlas)
190     {
191       exponentialDecrement /= 2;
192       minPointSize = requestedPointSize;
193       maxPointSize = requestedPointSize + (pointSizePerOneUnit * exponentialDecrement);
194     }
195     else
196     {
197       minPointSize = 0;
198       maxPointSize = requestedPointSize;
199     }
200
201     while(minPointSize < maxPointSize)
202     {
203       requestedPointSize = ((maxPointSize / pointSizePerOneUnit - minPointSize / pointSizePerOneUnit) / 2) * pointSizePerOneUnit + minPointSize;
204       canFitInAtlas      = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
205       if(FT_Err_Ok != error)
206       {
207         return error;
208       }
209
210       if(canFitInAtlas)
211       {
212         if(minPointSize == requestedPointSize)
213         {
214           //Found targeted point-size
215           return error;
216         }
217
218         minPointSize = requestedPointSize;
219       }
220       else
221       {
222         maxPointSize = requestedPointSize;
223       }
224     }
225   }
226
227   return error;
228 }
229
230 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
231 : fontDescription{std::move(font)},
232   fallbackFonts{fallbackFonts},
233   characterSets{characterSets}
234 {
235 }
236
237 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
238                                                                        FontDescriptionId      index)
239 : fontDescription{fontDescription},
240   index{index}
241 {
242 }
243
244 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
245                                                                        FontDescriptionId index)
246 : fontDescription{std::move(fontDescription)},
247   index{index}
248 {
249 }
250
251 FontClient::Plugin::FontDescriptionSizeCacheKey::FontDescriptionSizeCacheKey(FontDescriptionId fontDescriptionId,
252                                                                              PointSize26Dot6   requestedPointSize)
253 : fontDescriptionId(fontDescriptionId),
254   requestedPointSize(requestedPointSize)
255 {
256 }
257
258 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
259                            unsigned int verticalDpi)
260 : mFreeTypeLibrary(nullptr),
261   mDpiHorizontal(horizontalDpi),
262   mDpiVertical(verticalDpi),
263   mDefaultFontDescription(),
264   mSystemFonts(),
265   mDefaultFonts(),
266   mFontIdCache(),
267   mFontFaceCache(),
268   mValidatedFontCache(),
269   mFontDescriptionCache(),
270   mCharacterSetCache(),
271   mFontDescriptionSizeCache(),
272   mVectorFontCache(nullptr),
273   mEllipsisCache(),
274   mEmbeddedItemCache(),
275   mDefaultFontDescriptionCached(false),
276   mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
277   mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS)
278
279 {
280   int error = FT_Init_FreeType(&mFreeTypeLibrary);
281   if(FT_Err_Ok != error)
282   {
283     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Init error: %d\n", error);
284   }
285
286 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
287   mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
288 #endif
289 }
290
291 FontClient::Plugin::~Plugin()
292 {
293   ClearFallbackCache(mFallbackCache);
294
295   // Free the resources allocated by the FcCharSet objects.
296   DestroyCharacterSets(mDefaultFontCharacterSets);
297   DestroyCharacterSets(mCharacterSetCache);
298   ClearCharacterSetFromFontFaceCache();
299
300   // Clear FontFaceCache here. Due to we sould deallocate FT_Faces before done freetype library
301   mFontFaceCache.clear();
302
303 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
304   delete mVectorFontCache;
305 #endif
306   FT_Done_FreeType(mFreeTypeLibrary);
307 }
308
309 void FontClient::Plugin::ClearCache()
310 {
311   mDefaultFontDescription = FontDescription();
312
313   mSystemFonts.clear();
314   mDefaultFonts.clear();
315
316   DestroyCharacterSets(mDefaultFontCharacterSets);
317   mDefaultFontCharacterSets.Clear();
318
319   ClearFallbackCache(mFallbackCache);
320   mFallbackCache.clear();
321
322   mFontIdCache.Clear();
323
324   ClearCharacterSetFromFontFaceCache();
325   mFontFaceCache.clear();
326
327   mValidatedFontCache.clear();
328   mFontDescriptionCache.clear();
329
330   DestroyCharacterSets(mCharacterSetCache);
331   mCharacterSetCache.Clear();
332
333   mFontDescriptionSizeCache.clear();
334   mFontDescriptionSizeCache.rehash(0); // Note : unordered_map.clear() didn't deallocate memory
335
336   mEllipsisCache.Clear();
337   mPixelBufferCache.clear();
338   mEmbeddedItemCache.Clear();
339   mBitmapFontCache.clear();
340
341   mDefaultFontDescriptionCached = false;
342 }
343
344 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
345                                 unsigned int verticalDpi)
346 {
347   mDpiHorizontal = horizontalDpi;
348   mDpiVertical   = verticalDpi;
349 }
350
351 void FontClient::Plugin::ResetSystemDefaults()
352 {
353   mDefaultFontDescriptionCached = false;
354 }
355
356 void FontClient::Plugin::SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
357 {
358   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
359   FONT_LOG_DESCRIPTION(fontDescription, "");
360   fontList.clear();
361
362   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
363
364   FcResult result = FcResultMatch;
365
366   // Match the pattern.
367   FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
368                                   fontFamilyPattern,
369                                   false /* don't trim */,
370                                   nullptr,
371                                   &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
372
373   if(nullptr != fontSet)
374   {
375     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of fonts found : [%d]\n", fontSet->nfont);
376     // Reserve some space to avoid reallocations.
377     fontList.reserve(fontSet->nfont);
378
379     for(int i = 0u; i < fontSet->nfont; ++i)
380     {
381       FcPattern* fontPattern = fontSet->fonts[i];
382
383       FontPath path;
384
385       // Skip fonts with no path
386       if(GetFcString(fontPattern, FC_FILE, path))
387       {
388         // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
389         FcCharSet* characterSet = nullptr;
390         FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
391
392         // Increase the reference counter of the character set.
393         characterSetList.PushBack(FcCharSetCopy(characterSet));
394
395         fontList.push_back(FontDescription());
396         FontDescription& newFontDescription = fontList.back();
397
398         newFontDescription.path = std::move(path);
399
400         int width  = 0;
401         int weight = 0;
402         int slant  = 0;
403         GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
404         GetFcInt(fontPattern, FC_WIDTH, width);
405         GetFcInt(fontPattern, FC_WEIGHT, weight);
406         GetFcInt(fontPattern, FC_SLANT, slant);
407         newFontDescription.width  = IntToWidthType(width);
408         newFontDescription.weight = IntToWeightType(weight);
409         newFontDescription.slant  = IntToSlantType(slant);
410
411         FONT_LOG_DESCRIPTION(newFontDescription, "");
412       }
413     }
414
415     // Destroys the font set created by FcFontSort.
416     FcFontSetDestroy(fontSet);
417   }
418   else
419   {
420     DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  No fonts found.\n");
421   }
422
423   // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
424   FcPatternDestroy(fontFamilyPattern);
425 }
426
427 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts)
428 {
429   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
430
431   if(mDefaultFonts.empty())
432   {
433     FontDescription fontDescription;
434     fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
435     fontDescription.width  = DefaultFontWidth();
436     fontDescription.weight = DefaultFontWeight();
437     fontDescription.slant  = DefaultFontSlant();
438     SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
439   }
440
441   defaultFonts = mDefaultFonts;
442
443   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of default fonts : [%d]\n", mDefaultFonts.size());
444 }
445
446 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
447 {
448   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
449
450   if(!mDefaultFontDescriptionCached)
451   {
452     // Clear any font config stored info in the caches.
453
454     // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
455     DestroyCharacterSets(mDefaultFontCharacterSets);
456     DestroyCharacterSets(mCharacterSetCache);
457     mDefaultFontCharacterSets.Clear();
458     mCharacterSetCache.Clear();
459
460     for(auto& item : mFallbackCache)
461     {
462       // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
463       DestroyCharacterSets(*item.characterSets);
464
465       delete item.characterSets;
466       item.characterSets = nullptr;
467     }
468
469     // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
470     ClearCharacterSetFromFontFaceCache();
471
472     // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
473     FcInitReinitialize();
474
475     FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
476
477     if(nullptr != matchPattern)
478     {
479       FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
480       FcDefaultSubstitute(matchPattern);
481
482       FcCharSet* characterSet = nullptr;
483       bool       matched      = MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
484
485       // Caching the default font description
486       if(matched)
487       {
488         // Copy default font description info.
489         // Due to the type changed, we need to make some temperal font description.
490         FontDescription tempFontDescription = mDefaultFontDescription;
491
492         // Add the path to the cache.
493         tempFontDescription.type = FontDescription::FACE_FONT;
494         mFontDescriptionCache.push_back(tempFontDescription);
495
496         // Set the index to the vector of paths to font file names.
497         const FontDescriptionId fontDescriptionId = mFontDescriptionCache.size();
498
499         FONT_LOG_DESCRIPTION(tempFontDescription, "default platform font");
500         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  default font fontDescriptionId : %d\n", fontDescriptionId);
501
502         // Cache the index and the matched font's description.
503         FontDescriptionCacheItem item(tempFontDescription,
504                                       fontDescriptionId);
505
506         mValidatedFontCache.push_back(std::move(item));
507       }
508       else
509       {
510         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  default font validation failed for font [%s]\n", mDefaultFontDescription.family.c_str());
511       }
512
513       // Decrease the reference counter of the character set as it's not stored.
514       // Note. the cached default font description will increase reference counter by
515       // mFontDescriptionCache. So we can decrease reference counter here.
516       FcCharSetDestroy(characterSet);
517
518       // Destroys the pattern created.
519       FcPatternDestroy(matchPattern);
520     }
521
522     // Create again the character sets as they are not valid after FcInitReinitialize()
523
524     for(const auto& description : mDefaultFonts)
525     {
526       mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
527     }
528
529     for(const auto& description : mFontDescriptionCache)
530     {
531       mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
532     }
533
534     for(auto& item : mFallbackCache)
535     {
536       if(nullptr != item.fallbackFonts)
537       {
538         if(nullptr == item.characterSets)
539         {
540           item.characterSets = new CharacterSetList;
541         }
542
543         for(const auto& description : *(item.fallbackFonts))
544         {
545           item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
546         }
547       }
548     }
549
550     mDefaultFontDescriptionCached = true;
551   }
552
553   fontDescription.path   = mDefaultFontDescription.path;
554   fontDescription.family = mDefaultFontDescription.family;
555   fontDescription.width  = mDefaultFontDescription.width;
556   fontDescription.weight = mDefaultFontDescription.weight;
557   fontDescription.slant  = mDefaultFontDescription.slant;
558
559   FONT_LOG_DESCRIPTION(fontDescription, "");
560 }
561
562 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts)
563 {
564   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
565
566   if(mSystemFonts.empty())
567   {
568     InitSystemFonts();
569   }
570
571   systemFonts = mSystemFonts;
572   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : [%d]\n", mSystemFonts.size());
573 }
574
575 void FontClient::Plugin::GetDescription(FontId           id,
576                                         FontDescription& fontDescription) const
577 {
578   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
579   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
580   const FontId index = id - 1u;
581
582   if((id > 0u) && (index < mFontIdCache.Count()))
583   {
584     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
585     switch(fontIdCacheItem.type)
586     {
587       case FontDescription::FACE_FONT:
588       {
589         for(const auto& item : mFontDescriptionSizeCache)
590         {
591           if(item.second == fontIdCacheItem.index)
592           {
593             fontDescription = *(mFontDescriptionCache.begin() + item.first.fontDescriptionId - 1u);
594
595             FONT_LOG_DESCRIPTION(fontDescription, "");
596             return;
597           }
598         }
599         break;
600       }
601       case FontDescription::BITMAP_FONT:
602       {
603         fontDescription.type   = FontDescription::BITMAP_FONT;
604         fontDescription.family = mBitmapFontCache[fontIdCacheItem.index].font.name;
605         break;
606       }
607       default:
608       {
609         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Invalid type of font\n");
610         fontDescription.type = FontDescription::INVALID;
611         fontDescription.family.clear();
612       }
613     }
614   }
615
616   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  No description found for the font ID %d\n", id);
617 }
618
619 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id)
620 {
621   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
622   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
623
624   PointSize26Dot6               pointSize     = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
625   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(id);
626   if(fontCacheItem != nullptr)
627   {
628     pointSize = fontCacheItem->GetPointSize();
629   }
630   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  point size : %d\n", pointSize);
631
632   return pointSize;
633 }
634
635 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character)
636 {
637   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
638   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "    font id : %d\n", fontId);
639   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  character : %p\n", character);
640
641   bool isSupported   = false;
642   auto fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
643   if(fontCacheItem != nullptr)
644   {
645     isSupported = fontCacheItem->IsCharacterSupported(character); // May cache
646   }
647
648   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  is supported : %s\n", (isSupported ? "true" : "false"));
649   return isSupported;
650 }
651
652 const FontCacheItemInterface* FontClient::Plugin::GetCachedFontItem(FontId id) const
653 {
654   const FontCacheIndex index = id - 1u;
655   if((id > 0u) && (index < mFontIdCache.Count()))
656   {
657     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
658     switch(fontIdCacheItem.type)
659     {
660       case FontDescription::FACE_FONT:
661       {
662         return &mFontFaceCache[fontIdCacheItem.index];
663       }
664       case FontDescription::BITMAP_FONT:
665       {
666         return &mBitmapFontCache[fontIdCacheItem.index];
667       }
668       default:
669       {
670         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Invalid type of font\n");
671       }
672     }
673   }
674   return nullptr;
675 }
676
677 FontId FontClient::Plugin::FindFontForCharacter(const FontList&         fontList,
678                                                 const CharacterSetList& characterSetList,
679                                                 Character               character,
680                                                 PointSize26Dot6         requestedPointSize,
681                                                 bool                    preferColor)
682 {
683   DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
684   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
685   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "           character : %p\n", character);
686   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
687   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "         preferColor : %s\n", (preferColor ? "true" : "false"));
688
689   FontId fontId     = 0u;
690   bool   foundColor = false;
691
692   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of fonts : %d\n", fontList.size());
693
694   // Traverse the list of fonts.
695   // Check for each font if supports the character.
696   for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
697   {
698     const FontDescription& description  = fontList[index];
699     const FcCharSet* const characterSet = characterSetList[index];
700
701     FONT_LOG_DESCRIPTION(description, "");
702
703     bool foundInRanges = false;
704     if(nullptr != characterSet)
705     {
706       foundInRanges = FcCharSetHasChar(characterSet, character);
707     }
708
709     if(foundInRanges)
710     {
711       fontId = GetFontId(description, requestedPointSize, 0u);
712
713       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "     font id : %d\n", fontId);
714
715       if(preferColor)
716       {
717         if((fontId > 0) &&
718            (fontId - 1u < mFontIdCache.Count()))
719         {
720           const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].index];
721
722           foundColor = item.mHasColorTables;
723         }
724
725         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  foundColor : %s\n", (foundColor ? "true" : "false"));
726       }
727
728       // Keep going unless we prefer a different (color) font.
729       if(!preferColor || foundColor)
730       {
731         break;
732       }
733     }
734   }
735
736   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
737   return fontId;
738 }
739
740 FontId FontClient::Plugin::FindDefaultFont(Character       charcode,
741                                            PointSize26Dot6 requestedPointSize,
742                                            bool            preferColor)
743 {
744   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
745   FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
746
747   FontId fontId(0);
748
749   // Create the list of default fonts if it has not been created.
750   if(mDefaultFonts.empty())
751   {
752     FontDescription fontDescription;
753     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
754     fontDescription.width  = DefaultFontWidth();
755     fontDescription.weight = DefaultFontWeight();
756     fontDescription.slant  = DefaultFontSlant();
757
758     SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
759   }
760   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of default fonts : %d\n", mDefaultFonts.size());
761
762   // Traverse the list of default fonts.
763   // Check for each default font if supports the character.
764   fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
765
766   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
767   return fontId;
768 }
769
770 FontId FontClient::Plugin::FindFallbackFont(Character              charcode,
771                                             const FontDescription& preferredFontDescription,
772                                             PointSize26Dot6        requestedPointSize,
773                                             bool                   preferColor)
774 {
775   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
776   FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
777
778   // The font id to be returned.
779   FontId fontId = 0u;
780
781   FontDescription fontDescription;
782
783   // Fill the font description with the preferred font description and complete with the defaults.
784   fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
785   fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? DefaultFontWeight() : preferredFontDescription.weight);
786   fontDescription.width  = ((FontWidth::NONE == preferredFontDescription.width) ? DefaultFontWidth() : preferredFontDescription.width);
787   fontDescription.slant  = ((FontSlant::NONE == preferredFontDescription.slant) ? DefaultFontSlant() : preferredFontDescription.slant);
788
789   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  preferredFontDescription --> fontDescription\n");
790   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
791   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
792   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
793   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
794
795   // Check first if the font's description has been queried before.
796   FontList*         fontList         = nullptr;
797   CharacterSetList* characterSetList = nullptr;
798
799   if(!FindFallbackFontList(fontDescription, fontList, characterSetList))
800   {
801     fontList         = new FontList;
802     characterSetList = new CharacterSetList;
803
804     SetFontList(fontDescription, *fontList, *characterSetList);
805 #ifdef __APPLE__
806     FontDescription appleColorEmoji;
807     appleColorEmoji.family = "Apple Color Emoji";
808     appleColorEmoji.width  = fontDescription.width;
809     appleColorEmoji.weight = fontDescription.weight;
810     appleColorEmoji.slant  = fontDescription.slant;
811     FontList         emojiFontList;
812     CharacterSetList emojiCharSetList;
813     SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
814
815     std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
816     emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
817     *fontList         = std::move(emojiFontList);
818     *characterSetList = std::move(emojiCharSetList);
819 #endif
820
821     // Add the font-list to the cache.
822     mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
823   }
824
825   if(fontList && characterSetList)
826   {
827     fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
828   }
829
830   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
831   return fontId;
832 }
833
834 FontId FontClient::Plugin::GetFontId(const FontPath& path,
835                                      PointSize26Dot6 requestedPointSize,
836                                      FaceIndex       faceIndex,
837                                      bool            cacheDescription)
838 {
839   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
840   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
841   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
842
843   FontId id = 0u;
844
845   if(nullptr != mFreeTypeLibrary)
846   {
847     FontId foundId = 0u;
848     if(FindFont(path, requestedPointSize, faceIndex, foundId))
849     {
850       id = foundId;
851     }
852     else
853     {
854       id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
855     }
856   }
857
858   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
859   return id;
860 }
861
862 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
863                                      PointSize26Dot6        requestedPointSize,
864                                      FaceIndex              faceIndex)
865 {
866   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
867   FONT_LOG_DESCRIPTION(fontDescription, "");
868
869   // Special case when font Description don't have family information.
870   // In this case, we just use default description family and path.
871   const FontDescription& realFontDescription = fontDescription.family.empty() ? FontDescription(mDefaultFontDescription.path,
872                                                                                                 mDefaultFontDescription.family,
873                                                                                                 fontDescription.width,
874                                                                                                 fontDescription.weight,
875                                                                                                 fontDescription.slant,
876                                                                                                 fontDescription.type)
877                                                                               : fontDescription;
878
879   FONT_LOG_DESCRIPTION(realFontDescription, "");
880   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "   requestedPointSize : %d\n", requestedPointSize);
881
882   // This method uses three vectors which caches:
883   // * The bitmap font cache
884   // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
885   // * The path to font file names.
886   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
887
888   // 1) Checks if the font description matches with a previously loaded bitmap font.
889   //    Returns if a font is found.
890   // 2) Checks in the cache if the font's description has been validated before.
891   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
892   //    retrieves using font config a path to a font file name which matches with the
893   //    font's description. The path is stored in the cache.
894   //
895   // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
896   //    font file names' exists. If exists, it gets the font id. If it doesn't it calls
897   //    the GetFontId() method with the path to the font file name and the point size to
898   //    get the font id.
899
900   // The font id to be returned.
901   FontId fontId = 0u;
902
903   // Check first if the font description matches with a previously loaded bitmap font.
904   if(FindBitmapFont(realFontDescription.family, fontId))
905   {
906     return fontId;
907   }
908
909   // Check if the font's description have been validated before.
910   FontDescriptionId fontDescriptionId = 0u;
911
912   if(!FindValidatedFont(realFontDescription,
913                         fontDescriptionId))
914   {
915     // Use font config to validate the font's description.
916     ValidateFont(realFontDescription,
917                  fontDescriptionId);
918   }
919
920   FontCacheIndex fontCacheIndex = 0u;
921   // Check if exists a pair 'fontDescriptionId, requestedPointSize' in the cache.
922   if(!FindFont(fontDescriptionId, requestedPointSize, fontCacheIndex))
923   {
924     // Retrieve the font file name path.
925     const FontDescription& description = *(mFontDescriptionCache.begin() + fontDescriptionId - 1u);
926
927     // Retrieve the font id. Do not cache the description as it has been already cached.
928     fontId = GetFontId(description.path,
929                        requestedPointSize,
930                        faceIndex,
931                        false);
932
933     fontCacheIndex                               = mFontIdCache[fontId - 1u].index;
934     mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCharacterSetCache[fontDescriptionId - 1u]);
935
936     // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
937     mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
938   }
939   else
940   {
941     fontId = mFontFaceCache[fontCacheIndex].mFontId + 1u;
942   }
943
944   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
945   return fontId;
946 }
947
948 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont)
949 {
950   for(const auto& item : mBitmapFontCache)
951   {
952     if(bitmapFont.name == item.font.name)
953     {
954       return item.id + 1u;
955     }
956   }
957
958   BitmapFontCacheItem bitmapFontCacheItem(bitmapFont, mFontIdCache.Count());
959
960   FontIdCacheItem fontIdCacheItem;
961   fontIdCacheItem.type  = FontDescription::BITMAP_FONT;
962   fontIdCacheItem.index = mBitmapFontCache.size();
963
964   mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
965   mFontIdCache.PushBack(fontIdCacheItem);
966
967   return bitmapFontCacheItem.id + 1u;
968 }
969
970 void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
971                                       FontDescriptionId&     fontDescriptionId)
972 {
973   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
974   FONT_LOG_DESCRIPTION(fontDescription, "");
975
976   // Create a font pattern.
977   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
978
979   FontDescription description;
980
981   FcCharSet* characterSet = nullptr;
982   bool       matched      = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
983   FcPatternDestroy(fontFamilyPattern);
984
985   if(matched && (nullptr != characterSet))
986   {
987     // Add the path to the cache.
988     description.type = FontDescription::FACE_FONT;
989     mFontDescriptionCache.push_back(description);
990
991     // Set the index to the vector of paths to font file names.
992     fontDescriptionId = mFontDescriptionCache.size();
993
994     FONT_LOG_DESCRIPTION(description, "matched");
995     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fontDescriptionId : %d\n", fontDescriptionId);
996
997     // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
998     mCharacterSetCache.PushBack(characterSet);
999
1000     // Cache the index and the matched font's description.
1001     FontDescriptionCacheItem item(description,
1002                                   fontDescriptionId);
1003
1004     mValidatedFontCache.push_back(std::move(item));
1005
1006     if((fontDescription.family != description.family) ||
1007        (fontDescription.width != description.width) ||
1008        (fontDescription.weight != description.weight) ||
1009        (fontDescription.slant != description.slant))
1010     {
1011       // Cache the given font's description if it's different than the matched.
1012       FontDescriptionCacheItem item(fontDescription,
1013                                     fontDescriptionId);
1014
1015       mValidatedFontCache.push_back(std::move(item));
1016     }
1017   }
1018   else
1019   {
1020     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font validation failed for font [%s]\n", fontDescription.family.c_str());
1021   }
1022 }
1023
1024 void FontClient::Plugin::GetFontMetrics(FontId       fontId,
1025                                         FontMetrics& metrics)
1026 {
1027   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1028   if(fontCacheItem != nullptr)
1029   {
1030     fontCacheItem->GetFontMetrics(metrics, mDpiVertical);
1031   }
1032 }
1033
1034 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
1035                                              Character charcode)
1036 {
1037   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1038   if(fontCacheItem != nullptr)
1039   {
1040     return fontCacheItem->GetGlyphIndex(charcode);
1041   }
1042
1043   return 0u;
1044 }
1045
1046 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
1047                                              Character charcode,
1048                                              Character variantSelector)
1049 {
1050   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1051   if(fontCacheItem != nullptr)
1052   {
1053     return fontCacheItem->GetGlyphIndex(charcode, variantSelector);
1054   }
1055
1056   return 0u;
1057 }
1058
1059 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
1060                                          uint32_t   size,
1061                                          GlyphType  type,
1062                                          bool       horizontal)
1063 {
1064   if(VECTOR_GLYPH == type)
1065   {
1066     return GetVectorMetrics(array, size, horizontal);
1067   }
1068
1069   return GetBitmapMetrics(array, size, horizontal);
1070 }
1071
1072 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
1073                                           uint32_t   size,
1074                                           bool       horizontal)
1075 {
1076   bool success(false);
1077
1078   for(unsigned int i = 0; i < size; ++i)
1079   {
1080     GlyphInfo& glyph = array[i];
1081
1082     const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(glyph.fontId);
1083     if(fontCacheItem != nullptr)
1084     {
1085       success = fontCacheItem->GetGlyphMetrics(glyph, mDpiVertical, horizontal);
1086     }
1087     // Check if it's an embedded image.
1088     else if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count()))
1089     {
1090       mEmbeddedItemCache[glyph.index - 1u].GetGlyphMetrics(glyph);
1091       success = true;
1092     }
1093   }
1094
1095   return success;
1096 }
1097
1098 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
1099                                           uint32_t   size,
1100                                           bool       horizontal)
1101 {
1102 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1103   bool success(true);
1104
1105   for(unsigned int i = 0u; i < size; ++i)
1106   {
1107     FontId fontId = array[i].fontId;
1108
1109     if((fontId > 0u) &&
1110        (fontId - 1u) < mFontIdCache.Count())
1111     {
1112       FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].index];
1113
1114       if(!font.mVectorFontId)
1115       {
1116         font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1117       }
1118
1119       mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
1120
1121       // Vector metrics are in EMs, convert to pixels
1122       const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
1123       array[i].width *= scale;
1124       array[i].height *= scale;
1125       array[i].xBearing *= scale;
1126       array[i].yBearing *= scale;
1127       array[i].advance *= scale;
1128     }
1129     else
1130     {
1131       success = false;
1132     }
1133   }
1134
1135   return success;
1136 #else
1137   return false;
1138 #endif
1139 }
1140
1141 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
1142 {
1143   data.isColorBitmap                          = false;
1144   data.isColorEmoji                           = false;
1145   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1146   if(fontCacheItem != nullptr)
1147   {
1148     fontCacheItem->CreateBitmap(glyphIndex, data, outlineWidth, isItalicRequired, isBoldRequired);
1149   }
1150   else if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count()))
1151   {
1152     // It's an embedded item.
1153     mEmbeddedItemCache[glyphIndex - 1u].CreateBitmap(mPixelBufferCache, data);
1154   }
1155 }
1156
1157 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
1158 {
1159   TextAbstraction::FontClient::GlyphBufferData data;
1160
1161   CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
1162
1163   return PixelData::New(data.buffer,
1164                         data.width * data.height * Pixel::GetBytesPerPixel(data.format),
1165                         data.width,
1166                         data.height,
1167                         data.format,
1168                         PixelData::FREE);
1169 }
1170
1171 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
1172 {
1173   blob       = nullptr;
1174   blobLength = 0;
1175
1176 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1177   if((fontId > 0u) &&
1178      (fontId - 1u < mFontIdCache.Count()))
1179   {
1180     const FontCacheIndex fontFaceId = mFontIdCache[fontId - 1u].index;
1181     FontFaceCacheItem&   font       = mFontFaceCache[fontFaceId];
1182
1183     if(!font.mVectorFontId)
1184     {
1185       font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1186     }
1187
1188     mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1189   }
1190 #endif
1191 }
1192
1193 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
1194 {
1195   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1196   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize);
1197
1198   // First look into the cache if there is an ellipsis glyph for the requested point size.
1199   for(const auto& item : mEllipsisCache)
1200   {
1201     if(item.requestedPointSize == requestedPointSize)
1202     {
1203       // Use the glyph in the cache.
1204       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
1205       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
1206       return item.glyph;
1207     }
1208   }
1209
1210   // No glyph has been found. Create one.
1211   mEllipsisCache.PushBack(EllipsisItem());
1212   EllipsisItem& item = *(mEllipsisCache.End() - 1u);
1213
1214   item.requestedPointSize = requestedPointSize;
1215
1216   // Find a font for the ellipsis glyph.
1217   item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1218                                       requestedPointSize,
1219                                       false);
1220
1221   // Set the character index to access the glyph inside the font.
1222   item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].index].mFreeTypeFace,
1223                                        ELLIPSIS_CHARACTER);
1224
1225   GetBitmapMetrics(&item.glyph, 1u, true);
1226
1227   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
1228   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
1229   return item.glyph;
1230 }
1231
1232 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
1233 {
1234   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1235   return fontCacheItem && fontCacheItem->IsColorGlyph(glyphIndex);
1236 }
1237
1238 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId)
1239 {
1240   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1241   if(fontCacheItem != nullptr)
1242   {
1243     return fontCacheItem->GetTypeface();
1244   }
1245   return nullptr;
1246 }
1247
1248 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId)
1249 {
1250   const FontId index = fontId - 1u;
1251   if((fontId > 0u) &&
1252      (index < mFontIdCache.Count()))
1253   {
1254     return mFontIdCache[index].type;
1255   }
1256   return FontDescription::INVALID;
1257 }
1258
1259 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1260 {
1261   // nullptr as first parameter means the current configuration is used.
1262   return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1263 }
1264
1265 HarfBuzzFontHandle FontClient::Plugin::GetHarfBuzzFont(FontId fontId)
1266 {
1267   FontCacheItemInterface* fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
1268   if(fontCacheItem != nullptr)
1269   {
1270     return fontCacheItem->GetHarfBuzzFont(mDpiHorizontal, mDpiVertical); // May cache
1271   }
1272   return nullptr;
1273 }
1274
1275 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
1276 {
1277   EmbeddedItem embeddedItem;
1278
1279   embeddedItem.pixelBufferId = 0u;
1280   embeddedItem.width         = description.width;
1281   embeddedItem.height        = description.height;
1282
1283   pixelFormat = Pixel::A8;
1284
1285   if(!description.url.empty())
1286   {
1287     // Check if the url is in the cache.
1288     PixelBufferId index = 0u;
1289
1290     for(const auto& cacheItem : mPixelBufferCache)
1291     {
1292       ++index;
1293       if(cacheItem.url == description.url)
1294       {
1295         // The url is in the pixel buffer cache.
1296         // Set the index +1 to the vector.
1297         embeddedItem.pixelBufferId = index;
1298         break;
1299       }
1300     }
1301
1302     Devel::PixelBuffer pixelBuffer;
1303     if(0u == embeddedItem.pixelBufferId)
1304     {
1305       // The pixel buffer is not in the cache. Create one and cache it.
1306
1307       // Load the image from the url.
1308       pixelBuffer = LoadImageFromFile(description.url);
1309
1310       // Create the cache item.
1311       PixelBufferCacheItem pixelBufferCacheItem;
1312       pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1313       pixelBufferCacheItem.url         = description.url;
1314
1315       // Store the cache item in the cache.
1316       mPixelBufferCache.push_back(std::move(pixelBufferCacheItem));
1317
1318       // Set the pixel buffer id to the embedded item.
1319       embeddedItem.pixelBufferId = mPixelBufferCache.size();
1320     }
1321     else
1322     {
1323       // Retrieve the pixel buffer from the cache to set the pixel format.
1324       pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1325     }
1326
1327     if(pixelBuffer)
1328     {
1329       // Set the size of the embedded item if it has not been set.
1330       if(0u == embeddedItem.width)
1331       {
1332         embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1333       }
1334
1335       if(0u == embeddedItem.height)
1336       {
1337         embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1338       }
1339
1340       // Set the pixel format.
1341       pixelFormat = pixelBuffer.GetPixelFormat();
1342     }
1343   }
1344
1345   // Find if the same embeddedItem has already been created.
1346   unsigned int index = 0u;
1347   for(const auto& item : mEmbeddedItemCache)
1348   {
1349     ++index;
1350     if((item.pixelBufferId == embeddedItem.pixelBufferId) &&
1351        (item.width == embeddedItem.width) &&
1352        (item.height == embeddedItem.height))
1353     {
1354       return index;
1355     }
1356   }
1357
1358   // Cache the embedded item.
1359   mEmbeddedItemCache.PushBack(embeddedItem);
1360
1361   return mEmbeddedItemCache.Count();
1362 }
1363
1364 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
1365 {
1366   mIsAtlasLimitationEnabled = enabled;
1367 }
1368
1369 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
1370 {
1371   return mIsAtlasLimitationEnabled;
1372 }
1373
1374 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
1375 {
1376   return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1377 }
1378
1379 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
1380 {
1381   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
1382 }
1383
1384 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
1385 {
1386   return mCurrentMaximumBlockSizeFitInAtlas;
1387 }
1388
1389 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
1390 {
1391   bool            isChanged        = false;
1392   const Size&     maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1393   const uint16_t& padding          = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
1394
1395   if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
1396   {
1397     mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
1398     isChanged                          = true;
1399   }
1400
1401   return isChanged;
1402 }
1403
1404 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
1405 {
1406   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
1407   ;
1408 }
1409
1410 void FontClient::Plugin::InitSystemFonts()
1411 {
1412   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1413
1414   FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1415
1416   if(fontSet)
1417   {
1418     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont);
1419
1420     // Reserve some space to avoid reallocations.
1421     mSystemFonts.reserve(fontSet->nfont);
1422
1423     for(int i = 0u; i < fontSet->nfont; ++i)
1424     {
1425       FcPattern* fontPattern = fontSet->fonts[i];
1426
1427       FontPath path;
1428
1429       // Skip fonts with no path
1430       if(GetFcString(fontPattern, FC_FILE, path))
1431       {
1432         mSystemFonts.push_back(FontDescription());
1433         FontDescription& fontDescription = mSystemFonts.back();
1434
1435         fontDescription.path = std::move(path);
1436
1437         int width  = 0;
1438         int weight = 0;
1439         int slant  = 0;
1440         GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
1441         GetFcInt(fontPattern, FC_WIDTH, width);
1442         GetFcInt(fontPattern, FC_WEIGHT, weight);
1443         GetFcInt(fontPattern, FC_SLANT, slant);
1444         fontDescription.width  = IntToWidthType(width);
1445         fontDescription.weight = IntToWeightType(weight);
1446         fontDescription.slant  = IntToSlantType(slant);
1447
1448         FONT_LOG_DESCRIPTION(fontDescription, "");
1449       }
1450     }
1451
1452     // Destroys the font set created.
1453     FcFontSetDestroy(fontSet);
1454   }
1455 }
1456
1457 bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
1458 {
1459   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1460
1461   FcResult   result = FcResultMatch;
1462   FcPattern* match  = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1463
1464   const bool matched = nullptr != match;
1465   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  pattern matched : %s\n", (matched ? "true" : "false"));
1466
1467   if(matched)
1468   {
1469     int width  = 0;
1470     int weight = 0;
1471     int slant  = 0;
1472     GetFcString(match, FC_FILE, fontDescription.path);
1473     GetFcString(match, FC_FAMILY, fontDescription.family);
1474     GetFcInt(match, FC_WIDTH, width);
1475     GetFcInt(match, FC_WEIGHT, weight);
1476     GetFcInt(match, FC_SLANT, slant);
1477     fontDescription.width  = IntToWidthType(width);
1478     fontDescription.weight = IntToWeightType(weight);
1479     fontDescription.slant  = IntToSlantType(slant);
1480
1481     // Retrieve the character set and increase the reference counter.
1482     FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
1483     *characterSet = FcCharSetCopy(*characterSet);
1484
1485     // destroyed the matched pattern
1486     FcPatternDestroy(match);
1487     FONT_LOG_DESCRIPTION(fontDescription, "");
1488   }
1489   return matched;
1490 }
1491
1492 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1493 {
1494   FcFontSet* fontset = nullptr;
1495
1496   // create a new pattern.
1497   // a pattern holds a set of names, each name refers to a property of the font
1498   FcPattern* pattern = FcPatternCreate();
1499
1500   if(nullptr != pattern)
1501   {
1502     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1503     FcObjectSet* objectSet = FcObjectSetCreate();
1504
1505     if(nullptr != objectSet)
1506     {
1507       // build an object set from a list of property names
1508       FcObjectSetAdd(objectSet, FC_FILE);
1509       FcObjectSetAdd(objectSet, FC_FAMILY);
1510       FcObjectSetAdd(objectSet, FC_WIDTH);
1511       FcObjectSetAdd(objectSet, FC_WEIGHT);
1512       FcObjectSetAdd(objectSet, FC_SLANT);
1513
1514       // get a list of fonts
1515       // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1516       fontset = FcFontList(nullptr /* the default configuration is checked to be up to date, and used */, pattern, objectSet); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1517
1518       // clear up the object set
1519       FcObjectSetDestroy(objectSet);
1520     }
1521
1522     // clear up the pattern
1523     FcPatternDestroy(pattern);
1524   }
1525
1526   return fontset;
1527 }
1528
1529 bool FontClient::Plugin::GetFcString(const FcPattern* const pattern,
1530                                      const char* const      n,
1531                                      std::string&           string)
1532 {
1533   FcChar8*       file   = nullptr;
1534   const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
1535
1536   if(FcResultMatch == retVal)
1537   {
1538     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1539     string.assign(reinterpret_cast<const char*>(file));
1540
1541     return true;
1542   }
1543
1544   return false;
1545 }
1546
1547 bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
1548 {
1549   const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
1550
1551   if(FcResultMatch == retVal)
1552   {
1553     return true;
1554   }
1555
1556   return false;
1557 }
1558
1559 FontId FontClient::Plugin::CreateFont(const FontPath& path,
1560                                       PointSize26Dot6 requestedPointSize,
1561                                       FaceIndex       faceIndex,
1562                                       bool            cacheDescription)
1563 {
1564   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1565   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
1566   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1567
1568   FontId id = 0u;
1569
1570   // Create & cache new font face
1571   FT_Face ftFace;
1572   int     error = FT_New_Face(mFreeTypeLibrary,
1573                           path.c_str(),
1574                           0,
1575                           &ftFace);
1576
1577   if(FT_Err_Ok == error)
1578   {
1579     // Check if a font is scalable.
1580     const bool isScalable           = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
1581     const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
1582     const bool hasColorTables       = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
1583     FontId     fontFaceId           = 0u;
1584
1585     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "            isScalable : [%s]\n", (isScalable ? "true" : "false"));
1586     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
1587     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "        hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
1588
1589     // Check to see if the font contains fixed sizes?
1590     if(!isScalable && hasFixedSizedBitmaps)
1591     {
1592       PointSize26Dot6 actualPointSize = 0u;
1593       int             fixedSizeIndex  = 0;
1594       for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
1595       {
1596         const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
1597         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
1598
1599         if(fixedSize >= requestedPointSize)
1600         {
1601           actualPointSize = fixedSize;
1602           break;
1603         }
1604       }
1605
1606       if(0u == actualPointSize)
1607       {
1608         // The requested point size is bigger than the bigest fixed size.
1609         fixedSizeIndex  = ftFace->num_fixed_sizes - 1;
1610         actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
1611       }
1612
1613       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
1614
1615       // Tell Freetype to use this size
1616       error = FT_Select_Size(ftFace, fixedSizeIndex);
1617       if(FT_Err_Ok != error)
1618       {
1619         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
1620       }
1621       else
1622       {
1623         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1624
1625         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1626                             static_cast<float>(ftMetrics.descender) * FROM_266,
1627                             static_cast<float>(ftMetrics.height) * FROM_266,
1628                             static_cast<float>(ftFace->underline_position) * FROM_266,
1629                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1630
1631         const float fixedWidth  = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
1632         const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
1633
1634         // Create the FreeType font face item to cache.
1635         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
1636
1637         // Set the index to the font's id cache.
1638         fontFaceCacheItem.mFontId = mFontIdCache.Count();
1639
1640         // Create the font id item to cache.
1641         FontIdCacheItem fontIdCacheItem;
1642         fontIdCacheItem.type = FontDescription::FACE_FONT;
1643
1644         // Set the index to the FreeType font face cache.
1645         fontIdCacheItem.index = mFontFaceCache.size();
1646         fontFaceId            = fontIdCacheItem.index + 1u;
1647
1648         // Cache the items.
1649         mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
1650         mFontIdCache.PushBack(fontIdCacheItem);
1651
1652         // Set the font id to be returned.
1653         id = mFontIdCache.Count();
1654       }
1655     }
1656     else
1657     {
1658       if(mIsAtlasLimitationEnabled)
1659       {
1660         //There is limitation on block size to fit in predefined atlas size.
1661         //If the block size cannot fit into atlas size, then the system cannot draw block.
1662         //This is workaround to avoid issue in advance
1663         //Decrementing point-size until arriving to maximum allowed block size.
1664         auto        requestedPointSizeBackup = requestedPointSize;
1665         const Size& maxSizeFitInAtlas        = GetCurrentMaximumBlockSizeFitInAtlas();
1666         error                                = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
1667
1668         if(requestedPointSize != requestedPointSizeBackup)
1669         {
1670           DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
1671         }
1672       }
1673       else
1674       {
1675         error = FT_Set_Char_Size(ftFace,
1676                                  0,
1677                                  requestedPointSize,
1678                                  mDpiHorizontal,
1679                                  mDpiVertical);
1680       }
1681
1682       if(FT_Err_Ok == error)
1683       {
1684         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1685
1686         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1687                             static_cast<float>(ftMetrics.descender) * FROM_266,
1688                             static_cast<float>(ftMetrics.height) * FROM_266,
1689                             static_cast<float>(ftFace->underline_position) * FROM_266,
1690                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1691
1692         // Create the FreeType font face item to cache.
1693         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics);
1694
1695         // Set the index to the font's id cache.
1696         fontFaceCacheItem.mFontId = mFontIdCache.Count();
1697
1698         // Create the font id item to cache.
1699         FontIdCacheItem fontIdCacheItem;
1700         fontIdCacheItem.type = FontDescription::FACE_FONT;
1701
1702         // Set the index to the FreeType font face cache.
1703         fontIdCacheItem.index = mFontFaceCache.size();
1704         fontFaceId            = fontIdCacheItem.index + 1u;
1705
1706         // Cache the items.
1707         mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
1708         mFontIdCache.PushBack(fontIdCacheItem);
1709
1710         // Set the font id to be returned.
1711         id = mFontIdCache.Count();
1712       }
1713       else
1714       {
1715         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
1716       }
1717     }
1718
1719     if(0u != fontFaceId)
1720     {
1721       if(cacheDescription)
1722       {
1723         CacheFontPath(ftFace, fontFaceId, requestedPointSize, path);
1724       }
1725     }
1726   }
1727   else
1728   {
1729     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str());
1730   }
1731
1732   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
1733   return id;
1734 }
1735
1736 bool FontClient::Plugin::FindFont(const FontPath& path,
1737                                   PointSize26Dot6 requestedPointSize,
1738                                   FaceIndex       faceIndex,
1739                                   FontId&         fontId) const
1740 {
1741   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1742   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
1743   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1744   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of fonts in the cache : %d\n", mFontFaceCache.size());
1745
1746   fontId = 0u;
1747   for(const auto& cacheItem : mFontFaceCache)
1748   {
1749     if(cacheItem.mRequestedPointSize == requestedPointSize &&
1750        cacheItem.mFaceIndex == faceIndex &&
1751        cacheItem.mPath == path)
1752     {
1753       fontId = cacheItem.mFontId + 1u;
1754
1755       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, id : %d\n", fontId);
1756       return true;
1757     }
1758   }
1759
1760   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found\n");
1761   return false;
1762 }
1763
1764 bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
1765                                            FontDescriptionId&     fontDescriptionId)
1766 {
1767   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1768   FONT_LOG_DESCRIPTION(fontDescription, "");
1769   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
1770
1771   fontDescriptionId = 0u;
1772
1773   for(const auto& item : mValidatedFontCache)
1774   {
1775     if(!fontDescription.family.empty() &&
1776        (fontDescription.family == item.fontDescription.family) &&
1777        (fontDescription.width == item.fontDescription.width) &&
1778        (fontDescription.weight == item.fontDescription.weight) &&
1779        (fontDescription.slant == item.fontDescription.slant))
1780     {
1781       fontDescriptionId = item.index;
1782
1783       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description found, id : %d\n", fontDescriptionId);
1784       return true;
1785     }
1786   }
1787
1788   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description not found\n");
1789   return false;
1790 }
1791
1792 bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription,
1793                                               FontList*&             fontList,
1794                                               CharacterSetList*&     characterSetList)
1795 {
1796   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1797   FONT_LOG_DESCRIPTION(fontDescription, "");
1798   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of fallback font lists in the cache : %d\n", mFallbackCache.size());
1799
1800   fontList = nullptr;
1801
1802   for(const auto& item : mFallbackCache)
1803   {
1804     if(!fontDescription.family.empty() &&
1805        (fontDescription.family == item.fontDescription.family) &&
1806        (fontDescription.width == item.fontDescription.width) &&
1807        (fontDescription.weight == item.fontDescription.weight) &&
1808        (fontDescription.slant == item.fontDescription.slant))
1809     {
1810       fontList         = item.fallbackFonts;
1811       characterSetList = item.characterSets;
1812
1813       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list found.\n");
1814       return true;
1815     }
1816   }
1817
1818   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list not found.\n");
1819   return false;
1820 }
1821
1822 bool FontClient::Plugin::FindFont(FontDescriptionId fontDescriptionId,
1823                                   PointSize26Dot6   requestedPointSize,
1824                                   FontCacheIndex&   fontCacheIndex)
1825 {
1826   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1827   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "   fontDescriptionId : %d\n", fontDescriptionId);
1828   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1829
1830   fontCacheIndex = 0u;
1831
1832   FontDescriptionSizeCacheKey key(fontDescriptionId, requestedPointSize);
1833
1834   const auto& iter = mFontDescriptionSizeCache.find(key);
1835   if(iter != mFontDescriptionSizeCache.cend())
1836   {
1837     fontCacheIndex = iter->second;
1838
1839     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, index of font cache : %d\n", fontCacheIndex);
1840     return true;
1841   }
1842
1843   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found.\n");
1844   return false;
1845 }
1846
1847 bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const
1848 {
1849   fontId = 0u;
1850
1851   for(const auto& item : mBitmapFontCache)
1852   {
1853     if(bitmapFont == item.font.name)
1854     {
1855       fontId = item.id + 1u;
1856       return true;
1857     }
1858   }
1859
1860   return false;
1861 }
1862
1863 bool FontClient::Plugin::IsScalable(const FontPath& path)
1864 {
1865   bool isScalable = false;
1866
1867   FT_Face ftFace;
1868   int     error = FT_New_Face(mFreeTypeLibrary,
1869                           path.c_str(),
1870                           0,
1871                           &ftFace);
1872   if(FT_Err_Ok != error)
1873   {
1874     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
1875   }
1876   else
1877   {
1878     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
1879   }
1880
1881   return isScalable;
1882 }
1883
1884 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription)
1885 {
1886   // Create a font pattern.
1887   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1888
1889   FcResult result = FcResultMatch;
1890
1891   // match the pattern
1892   FcPattern* match      = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1893   bool       isScalable = false;
1894
1895   if(match)
1896   {
1897     // Get the path to the font file name.
1898     FontPath path;
1899     GetFcString(match, FC_FILE, path);
1900     isScalable = IsScalable(path);
1901   }
1902   else
1903   {
1904     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1905   }
1906
1907   // Destroys the created patterns.
1908   FcPatternDestroy(match);
1909   FcPatternDestroy(fontFamilyPattern);
1910
1911   return isScalable;
1912 }
1913
1914 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes)
1915 {
1916   // Empty the caller container
1917   sizes.Clear();
1918
1919   FT_Face ftFace;
1920   int     error = FT_New_Face(mFreeTypeLibrary,
1921                           path.c_str(),
1922                           0,
1923                           &ftFace);
1924   if(FT_Err_Ok != error)
1925   {
1926     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
1927   }
1928
1929   // Fetch the number of fixed sizes available
1930   if(ftFace->num_fixed_sizes && ftFace->available_sizes)
1931   {
1932     for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
1933     {
1934       sizes.PushBack(ftFace->available_sizes[i].size);
1935     }
1936   }
1937 }
1938
1939 void FontClient::Plugin::GetFixedSizes(const FontDescription&   fontDescription,
1940                                        Vector<PointSize26Dot6>& sizes)
1941 {
1942   // Create a font pattern.
1943   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1944
1945   FcResult result = FcResultMatch;
1946
1947   // match the pattern
1948   FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1949
1950   if(match)
1951   {
1952     // Get the path to the font file name.
1953     FontPath path;
1954     GetFcString(match, FC_FILE, path);
1955     GetFixedSizes(path, sizes);
1956   }
1957   else
1958   {
1959     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1960   }
1961
1962   // Destroys the created patterns.
1963   FcPatternDestroy(match);
1964   FcPatternDestroy(fontFamilyPattern);
1965 }
1966
1967 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
1968 {
1969   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1970   if(fontCacheItem != nullptr)
1971   {
1972     return fontCacheItem->HasItalicStyle();
1973   }
1974   return false;
1975 }
1976
1977 void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path)
1978 {
1979   FontDescription description;
1980   description.path   = path;
1981   description.family = std::move(FontFamily(ftFace->family_name));
1982   description.weight = FontWeight::NONE;
1983   description.width  = FontWidth::NONE;
1984   description.slant  = FontSlant::NONE;
1985
1986   // Note FreeType doesn't give too much info to build a proper font style.
1987   if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
1988   {
1989     description.slant = FontSlant::ITALIC;
1990   }
1991   if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
1992   {
1993     description.weight = FontWeight::BOLD;
1994   }
1995
1996   FontDescriptionId fontDescriptionId = 0u;
1997   if(!FindValidatedFont(description,
1998                         fontDescriptionId))
1999   {
2000     FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2001
2002     FcResult   result = FcResultMatch;
2003     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2004
2005     FcCharSet* characterSet = nullptr;
2006     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2007
2008     const FontId fontFaceId                  = id - 1u;
2009     mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
2010
2011     // Destroys the created patterns.
2012     FcPatternDestroy(match);
2013     FcPatternDestroy(pattern);
2014
2015     // Add the path to the cache.
2016     description.type = FontDescription::FACE_FONT;
2017     mFontDescriptionCache.push_back(description);
2018
2019     // Set the index to the vector of paths to font file names.
2020     fontDescriptionId = mFontDescriptionCache.size();
2021
2022     // Increase the reference counter and add the character set to the cache.
2023     mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
2024
2025     // Cache the index and the font's description.
2026     mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
2027                                                                      fontDescriptionId)));
2028
2029     // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
2030     mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontFaceId);
2031   }
2032 }
2033
2034 void FontClient::Plugin::ClearFallbackCache(std::vector<FallbackCacheItem>& fallbackCache)
2035 {
2036   for(auto& item : fallbackCache)
2037   {
2038     if(nullptr != item.fallbackFonts)
2039     {
2040       delete item.fallbackFonts;
2041     }
2042
2043     if(nullptr != item.characterSets)
2044     {
2045       // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2046       DestroyCharacterSets(*item.characterSets);
2047       delete item.characterSets;
2048     }
2049   }
2050 }
2051
2052 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2053 {
2054   for(auto& item : mFontFaceCache)
2055   {
2056     FcCharSetDestroy(item.mCharacterSet);
2057     item.mCharacterSet = nullptr;
2058   }
2059 }
2060
2061 } // namespace Dali::TextAbstraction::Internal