Merge "[AT-SPI] Add API for blocking automatic Bridge initialization" into devel...
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / font-client-plugin-impl.cpp
1 /*
2  * Copyright (c) 2021 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/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/font-client-helper.h>
30 #include <dali/public-api/common/dali-vector.h>
31 #include <dali/public-api/common/vector-wrapper.h>
32
33 // EXTERNAL INCLUDES
34 #include <fontconfig/fontconfig.h>
35 #include <algorithm>
36 #include <iterator>
37
38 namespace
39 {
40 #if defined(DEBUG_ENABLED)
41 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
42 #endif
43
44 /**
45  * Conversion from Fractional26.6 to float
46  */
47 const float FROM_266        = 1.0f / 64.0f;
48 const float POINTS_PER_INCH = 72.f;
49
50 const std::string DEFAULT_FONT_FAMILY_NAME("Tizen");
51 const int         DEFAULT_FONT_WIDTH  = 100; // normal
52 const int         DEFAULT_FONT_WEIGHT = 80;  // normal
53 const int         DEFAULT_FONT_SLANT  = 0;   // normal
54
55 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
56
57 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
58
59 // NONE            -1  --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
60 // ULTRA_CONDENSED 50
61 // EXTRA_CONDENSED 63
62 // CONDENSED       75
63 // SEMI_CONDENSED  87
64 // NORMAL         100
65 // SEMI_EXPANDED  113
66 // EXPANDED       125
67 // EXTRA_EXPANDED 150
68 // ULTRA_EXPANDED 200
69 const int          FONT_WIDTH_TYPE_TO_INT[] = {-1, 50, 63, 75, 87, 100, 113, 125, 150, 200};
70 const unsigned int NUM_FONT_WIDTH_TYPE      = sizeof(FONT_WIDTH_TYPE_TO_INT) / sizeof(int);
71
72 // NONE                       -1  --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
73 // THIN                        0
74 // ULTRA_LIGHT, EXTRA_LIGHT   40
75 // LIGHT                      50
76 // DEMI_LIGHT, SEMI_LIGHT     55
77 // BOOK                       75
78 // NORMAL, REGULAR            80
79 // MEDIUM                    100
80 // DEMI_BOLD, SEMI_BOLD      180
81 // BOLD                      200
82 // ULTRA_BOLD, EXTRA_BOLD    205
83 // BLACK, HEAVY, EXTRA_BLACK 210
84 const int          FONT_WEIGHT_TYPE_TO_INT[] = {-1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210};
85 const unsigned int NUM_FONT_WEIGHT_TYPE      = sizeof(FONT_WEIGHT_TYPE_TO_INT) / sizeof(int);
86
87 // NONE             -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
88 // NORMAL, ROMAN     0
89 // ITALIC          100
90 // OBLIQUE         110
91 const int          FONT_SLANT_TYPE_TO_INT[] = {-1, 0, 100, 110};
92 const unsigned int NUM_FONT_SLANT_TYPE      = sizeof(FONT_SLANT_TYPE_TO_INT) / sizeof(int);
93
94 } // namespace
95
96 using Dali::Vector;
97 using namespace std;
98
99 namespace Dali
100 {
101 namespace TextAbstraction
102 {
103 namespace Internal
104 {
105 /**
106  * @brief Returns the FontWidth's enum index for the given width value.
107  *
108  * @param[in] width The width value.
109  *
110  * @return The FontWidth's enum index.
111  */
112 FontWidth::Type IntToWidthType(int width)
113 {
114   return static_cast<FontWidth::Type>(ValueToIndex(width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u));
115 }
116
117 /**
118  * @brief Returns the FontWeight's enum index for the given weight value.
119  *
120  * @param[in] weight The weight value.
121  *
122  * @return The FontWeight's enum index.
123  */
124 FontWeight::Type IntToWeightType(int weight)
125 {
126   return static_cast<FontWeight::Type>(ValueToIndex(weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u));
127 }
128
129 /**
130  * @brief Returns the FontSlant's enum index for the given slant value.
131  *
132  * @param[in] slant The slant value.
133  *
134  * @return The FontSlant's enum index.
135  */
136 FontSlant::Type IntToSlantType(int slant)
137 {
138   return static_cast<FontSlant::Type>(ValueToIndex(slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u));
139 }
140
141 /**
142  * @brief Free the resources allocated by the FcCharSet objects.
143  *
144  * @param[in] characterSets The vector of character sets.
145  */
146 void DestroyCharacterSets(CharacterSetList& characterSets)
147 {
148   for(auto& item : characterSets)
149   {
150     if(item)
151     {
152       FcCharSetDestroy(item);
153     }
154   }
155 }
156
157 /**
158  * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
159  *
160  * @param[in/out] ftFace Face type object.
161  * @param[in] horizontalDpi The horizontal dpi.
162  * @param[in] verticalDpi The vertical dpi.
163  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
164  * @param[in] requestedPointSize The requested point-size.
165  * @return whether the  ftFace's block can fit into atlas
166  */
167 bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
168 {
169   bool isFit = false;
170
171   error = FT_Set_Char_Size(ftFace,
172                       0,
173                       requestedPointSize,
174                       horizontalDpi,
175                       verticalDpi);
176
177   if( error == FT_Err_Ok)
178   {
179     //Check width and height of block for requestedPointSize
180     //If the width or height is greater than the maximum-size then decrement by one unit of point-size.
181     if( static_cast<float>(ftFace->size->metrics.height) * FROM_266 <= maxSizeFitInAtlas.height
182       && (static_cast<float>(ftFace->size->metrics.ascender)-static_cast<float>(ftFace->size->metrics.descender))* FROM_266 <= maxSizeFitInAtlas.width)
183     {
184       isFit = true;
185     }
186   }
187
188   return isFit;
189 }
190
191 /**
192  * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
193  *
194  * @param[in/out] ftFace Face type object.
195  * @param[in] horizontalDpi The horizontal dpi.
196  * @param[in] verticalDpi The vertical dpi.
197  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
198  * @param[in/out] requestedPointSize The requested point-size.
199  * @return FreeType error code. 0 means success when requesting the nominal size (in points).
200  */
201 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
202 {
203   //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
204   const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
205   bool canFitInAtlas;
206   int error; // FreeType error code.
207
208   canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
209   if(FT_Err_Ok != error)
210   {
211     return error;
212   }
213
214   if(!canFitInAtlas)
215   {
216     //Exponential search
217     uint32_t exponentialDecrement = 1;
218
219     while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit*exponentialDecrement)
220     {
221       requestedPointSize-=(pointSizePerOneUnit*exponentialDecrement);
222       canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
223       if(FT_Err_Ok != error)
224       {
225         return error;
226       }
227
228       exponentialDecrement*=2;
229     }
230
231     //Binary search
232     uint32_t minPointSize;
233     uint32_t maxPointSize;
234
235     if(canFitInAtlas)
236     {
237       exponentialDecrement/=2;
238       minPointSize = requestedPointSize;
239       maxPointSize = requestedPointSize + (pointSizePerOneUnit*exponentialDecrement);
240     }
241     else
242     {
243       minPointSize = 0;
244       maxPointSize = requestedPointSize;
245     }
246
247     while(minPointSize < maxPointSize)
248     {
249       requestedPointSize = ((maxPointSize/pointSizePerOneUnit - minPointSize/pointSizePerOneUnit)/2) * pointSizePerOneUnit + minPointSize;
250       canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
251       if(FT_Err_Ok != error)
252       {
253         return error;
254       }
255
256       if(canFitInAtlas)
257       {
258         if(minPointSize == requestedPointSize)
259         {
260           //Found targeted point-size
261           return error;
262         }
263
264         minPointSize = requestedPointSize;
265       }
266       else
267       {
268         maxPointSize = requestedPointSize;
269       }
270     }
271   }
272
273   return error;
274 }
275
276
277 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
278 : fontDescription{std::move(font)},
279   fallbackFonts{fallbackFonts},
280   characterSets{characterSets}
281 {
282 }
283
284 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
285                                                                        FontDescriptionId      index)
286 : fontDescription{fontDescription},
287   index{index}
288 {
289 }
290
291 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
292                                                                        FontDescriptionId index)
293 : fontDescription{std::move(fontDescription)},
294   index{index}
295 {
296 }
297
298 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem(FontDescriptionId validatedFontId,
299                                                                                PointSize26Dot6   requestedPointSize,
300                                                                                FontId            fontId)
301 : validatedFontId(validatedFontId),
302   requestedPointSize(requestedPointSize),
303   fontId(fontId)
304 {
305 }
306
307 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face            ftFace,
308                                                          const FontPath&    path,
309                                                          PointSize26Dot6    requestedPointSize,
310                                                          FaceIndex          face,
311                                                          const FontMetrics& metrics)
312 : mFreeTypeFace(ftFace),
313   mPath(path),
314   mRequestedPointSize(requestedPointSize),
315   mFaceIndex(face),
316   mMetrics(metrics),
317   mCharacterSet(nullptr),
318   mFixedSizeIndex(0),
319   mFixedWidthPixels(0.f),
320   mFixedHeightPixels(0.f),
321   mVectorFontId(0u),
322   mFontId(0u),
323   mIsFixedSizeBitmap(false),
324   mHasColorTables(false)
325 {
326 }
327
328 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face            ftFace,
329                                                          const FontPath&    path,
330                                                          PointSize26Dot6    requestedPointSize,
331                                                          FaceIndex          face,
332                                                          const FontMetrics& metrics,
333                                                          int                fixedSizeIndex,
334                                                          float              fixedWidth,
335                                                          float              fixedHeight,
336                                                          bool               hasColorTables)
337 : mFreeTypeFace(ftFace),
338   mPath(path),
339   mRequestedPointSize(requestedPointSize),
340   mFaceIndex(face),
341   mMetrics(metrics),
342   mCharacterSet(nullptr),
343   mFixedSizeIndex(fixedSizeIndex),
344   mFixedWidthPixels(fixedWidth),
345   mFixedHeightPixels(fixedHeight),
346   mVectorFontId(0u),
347   mFontId(0u),
348   mIsFixedSizeBitmap(true),
349   mHasColorTables(hasColorTables)
350 {
351 }
352
353 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
354                            unsigned int verticalDpi)
355 : mFreeTypeLibrary(nullptr),
356   mDpiHorizontal(horizontalDpi),
357   mDpiVertical(verticalDpi),
358   mDefaultFontDescription(),
359   mSystemFonts(),
360   mDefaultFonts(),
361   mFontIdCache(),
362   mFontFaceCache(),
363   mValidatedFontCache(),
364   mFontDescriptionCache(),
365   mCharacterSetCache(),
366   mFontDescriptionSizeCache(),
367   mVectorFontCache(nullptr),
368   mEllipsisCache(),
369   mEmbeddedItemCache(),
370   mDefaultFontDescriptionCached(false),
371   mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
372   mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS)
373
374 {
375   int error = FT_Init_FreeType(&mFreeTypeLibrary);
376   if(FT_Err_Ok != error)
377   {
378     DALI_LOG_INFO(gLogFilter, Debug::General, "FreeType Init error: %d\n", error);
379   }
380
381 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
382   mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
383 #endif
384 }
385
386 FontClient::Plugin::~Plugin()
387 {
388   ClearFallbackCache(mFallbackCache);
389
390   // Free the resources allocated by the FcCharSet objects.
391   DestroyCharacterSets(mDefaultFontCharacterSets);
392   DestroyCharacterSets(mCharacterSetCache);
393   ClearCharacterSetFromFontFaceCache();
394
395 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
396   delete mVectorFontCache;
397 #endif
398   FT_Done_FreeType(mFreeTypeLibrary);
399 }
400
401 void FontClient::Plugin::ClearCache()
402 {
403   mDefaultFontDescription = FontDescription();
404
405   mSystemFonts.clear();
406   mDefaultFonts.clear();
407
408   DestroyCharacterSets(mDefaultFontCharacterSets);
409   mDefaultFontCharacterSets.Clear();
410
411   ClearFallbackCache(mFallbackCache);
412   mFallbackCache.clear();
413
414   mFontIdCache.Clear();
415
416   ClearCharacterSetFromFontFaceCache();
417   mFontFaceCache.clear();
418
419   mValidatedFontCache.clear();
420   mFontDescriptionCache.clear();
421
422   DestroyCharacterSets(mCharacterSetCache);
423   mCharacterSetCache.Clear();
424
425   mFontDescriptionSizeCache.clear();
426
427   mEllipsisCache.Clear();
428   mPixelBufferCache.clear();
429   mEmbeddedItemCache.Clear();
430   mBitmapFontCache.clear();
431
432   mDefaultFontDescriptionCached = false;
433 }
434
435 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
436                                 unsigned int verticalDpi)
437 {
438   mDpiHorizontal = horizontalDpi;
439   mDpiVertical   = verticalDpi;
440 }
441
442 void FontClient::Plugin::ResetSystemDefaults()
443 {
444   mDefaultFontDescriptionCached = false;
445 }
446
447 void FontClient::Plugin::SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
448 {
449   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n");
450   DALI_LOG_INFO(gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str());
451   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width]);
452   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
453   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
454
455   fontList.clear();
456
457   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
458
459   FcResult result = FcResultMatch;
460
461   // Match the pattern.
462   FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
463                                   fontFamilyPattern,
464                                   false /* don't trim */,
465                                   nullptr,
466                                   &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
467
468   if(nullptr != fontSet)
469   {
470     DALI_LOG_INFO(gLogFilter, Debug::General, "  number of fonts found : [%d]\n", fontSet->nfont);
471     // Reserve some space to avoid reallocations.
472     fontList.reserve(fontSet->nfont);
473
474     for(int i = 0u; i < fontSet->nfont; ++i)
475     {
476       FcPattern* fontPattern = fontSet->fonts[i];
477
478       FontPath path;
479
480       // Skip fonts with no path
481       if(GetFcString(fontPattern, FC_FILE, path))
482       {
483         // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
484         FcCharSet* characterSet = nullptr;
485         FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
486
487         // Increase the reference counter of the character set.
488         characterSetList.PushBack(FcCharSetCopy(characterSet));
489
490         fontList.push_back(FontDescription());
491         FontDescription& newFontDescription = fontList.back();
492
493         newFontDescription.path = std::move(path);
494
495         int width  = 0;
496         int weight = 0;
497         int slant  = 0;
498         GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
499         GetFcInt(fontPattern, FC_WIDTH, width);
500         GetFcInt(fontPattern, FC_WEIGHT, weight);
501         GetFcInt(fontPattern, FC_SLANT, slant);
502         newFontDescription.width  = IntToWidthType(width);
503         newFontDescription.weight = IntToWeightType(weight);
504         newFontDescription.slant  = IntToSlantType(slant);
505
506         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  description; family : [%s]\n", newFontDescription.family.c_str());
507         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                 path : [%s]\n", newFontDescription.path.c_str());
508         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[newFontDescription.width]);
509         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[newFontDescription.weight]);
510         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant]);
511       }
512     }
513
514     // Destroys the font set created by FcFontSort.
515     FcFontSetDestroy(fontSet);
516   }
517   else
518   {
519     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  No fonts found.\n");
520   }
521
522   // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
523   FcPatternDestroy(fontFamilyPattern);
524
525   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n");
526 }
527
528 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts)
529 {
530   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n");
531
532   if(mDefaultFonts.empty())
533   {
534     FontDescription fontDescription;
535     fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
536     fontDescription.width  = IntToWidthType(DEFAULT_FONT_WIDTH);
537     fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT);
538     fontDescription.slant  = IntToSlantType(DEFAULT_FONT_SLANT);
539     SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
540   }
541
542   defaultFonts = mDefaultFonts;
543
544   DALI_LOG_INFO(gLogFilter, Debug::General, "  number of default fonts : [%d]\n", mDefaultFonts.size());
545   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n");
546 }
547
548 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
549 {
550   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
551
552   if(!mDefaultFontDescriptionCached)
553   {
554     // Clear any font config stored info in the caches.
555
556     // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
557     DestroyCharacterSets(mDefaultFontCharacterSets);
558     DestroyCharacterSets(mCharacterSetCache);
559     mDefaultFontCharacterSets.Clear();
560     mCharacterSetCache.Clear();
561
562     for(auto& item : mFallbackCache)
563     {
564       // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
565       DestroyCharacterSets(*item.characterSets);
566
567       delete item.characterSets;
568       item.characterSets = nullptr;
569     }
570
571     // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
572     ClearCharacterSetFromFontFaceCache();
573
574     // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
575     FcInitReinitialize();
576
577     FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
578
579     if(nullptr != matchPattern)
580     {
581       FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
582       FcDefaultSubstitute(matchPattern);
583
584       FcCharSet* characterSet = nullptr;
585       MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
586       // Decrease the reference counter of the character set as it's not stored.
587       FcCharSetDestroy(characterSet);
588
589       // Destroys the pattern created.
590       FcPatternDestroy(matchPattern);
591     }
592
593     // Create again the character sets as they are not valid after FcInitReinitialize()
594
595     for(const auto& description : mDefaultFonts)
596     {
597       mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
598     }
599
600     for(const auto& description : mFontDescriptionCache)
601     {
602       mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
603     }
604
605     for(auto& item : mFallbackCache)
606     {
607       if(nullptr != item.fallbackFonts)
608       {
609         if(nullptr == item.characterSets)
610         {
611           item.characterSets = new CharacterSetList;
612         }
613
614         for(const auto& description : *(item.fallbackFonts))
615         {
616           item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
617         }
618       }
619     }
620
621     mDefaultFontDescriptionCached = true;
622   }
623
624   fontDescription.path   = mDefaultFontDescription.path;
625   fontDescription.family = mDefaultFontDescription.family;
626   fontDescription.width  = mDefaultFontDescription.width;
627   fontDescription.weight = mDefaultFontDescription.weight;
628   fontDescription.slant  = mDefaultFontDescription.slant;
629
630   DALI_LOG_INFO(gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str());
631   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str());
632   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width]);
633   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
634   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
635   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
636 }
637
638 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts)
639 {
640   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
641
642   if(mSystemFonts.empty())
643   {
644     InitSystemFonts();
645   }
646
647   systemFonts = mSystemFonts;
648   DALI_LOG_INFO(gLogFilter, Debug::General, "  number of system fonts : [%d]\n", mSystemFonts.size());
649   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
650 }
651
652 void FontClient::Plugin::GetDescription(FontId           id,
653                                         FontDescription& fontDescription) const
654 {
655   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
656   DALI_LOG_INFO(gLogFilter, Debug::General, "  font id : %d\n", id);
657   const FontId index = id - 1u;
658
659   if((id > 0u) && (index < mFontIdCache.Count()))
660   {
661     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
662     switch(fontIdCacheItem.type)
663     {
664       case FontDescription::FACE_FONT:
665       {
666         for(const auto& item : mFontDescriptionSizeCache)
667         {
668           if(item.fontId == fontIdCacheItem.id)
669           {
670             fontDescription = *(mFontDescriptionCache.begin() + item.validatedFontId - 1u);
671
672             DALI_LOG_INFO(gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str());
673             DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str());
674             DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width]);
675             DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
676             DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
677             DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
678             return;
679           }
680         }
681         break;
682       }
683       case FontDescription::BITMAP_FONT:
684       {
685         fontDescription.type   = FontDescription::BITMAP_FONT;
686         fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
687         break;
688       }
689       default:
690       {
691         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
692         fontDescription.type = FontDescription::INVALID;
693         fontDescription.family.clear();
694       }
695     }
696   }
697
698   DALI_LOG_INFO(gLogFilter, Debug::General, "  No description found for the font ID %d\n", id);
699   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
700 }
701
702 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id)
703 {
704   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
705   DALI_LOG_INFO(gLogFilter, Debug::General, "  font id : %d\n", id);
706   const FontId index = id - 1u;
707
708   if((id > 0u) &&
709      (index < mFontIdCache.Count()))
710   {
711     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
712
713     switch(fontIdCacheItem.type)
714     {
715       case FontDescription::FACE_FONT:
716       {
717         DALI_LOG_INFO(gLogFilter, Debug::General, "  point size : %d\n", (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize);
718         DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
719         return (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize;
720       }
721       case FontDescription::BITMAP_FONT:
722       {
723         return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
724       }
725       default:
726       {
727         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
728       }
729     }
730   }
731   else
732   {
733     DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid font ID %d\n", id);
734   }
735
736   DALI_LOG_INFO(gLogFilter, Debug::General, "  default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE);
737   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
738   return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
739 }
740
741 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character)
742 {
743   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
744   DALI_LOG_INFO(gLogFilter, Debug::General, "    font id : %d\n", fontId);
745   DALI_LOG_INFO(gLogFilter, Debug::General, "  character : %p\n", character);
746
747   if((fontId < 1u) || (fontId > mFontIdCache.Count()))
748   {
749     DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid font id. Number of items in the cache: %d\n", mFontIdCache.Count());
750     DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
751     return false;
752   }
753
754   --fontId;
755
756   bool isSupported = false;
757
758   const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
759
760   switch(fontIdCacheItem.type)
761   {
762     case FontDescription::FACE_FONT:
763     {
764       if(fontIdCacheItem.id < mFontFaceCache.size())
765       {
766         FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
767
768         if(nullptr == cacheItem.mCharacterSet)
769         {
770           // Create again the character set.
771           // It can be null if the ResetSystemDefaults() method has been called.
772
773           FontDescription description;
774           description.path   = cacheItem.mPath;
775           description.family = std::move(FontFamily(cacheItem.mFreeTypeFace->family_name));
776           description.weight = FontWeight::NONE;
777           description.width  = FontWidth::NONE;
778           description.slant  = FontSlant::NONE;
779
780           // Note FreeType doesn't give too much info to build a proper font style.
781           if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
782           {
783             description.slant = FontSlant::ITALIC;
784           }
785           if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
786           {
787             description.weight = FontWeight::BOLD;
788           }
789
790           cacheItem.mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
791         }
792
793         isSupported = FcCharSetHasChar(cacheItem.mCharacterSet, character);
794       }
795       break;
796     }
797     case FontDescription::BITMAP_FONT:
798     {
799       const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
800
801       for(const auto& glyph : bitmapFont.glyphs)
802       {
803         if(glyph.utf32 == character)
804         {
805           isSupported = true;
806           break;
807         }
808       }
809       break;
810     }
811     default:
812     {
813       DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
814     }
815   }
816
817   DALI_LOG_INFO(gLogFilter, Debug::General, "  is supported : %s\n", (isSupported ? "true" : "false"));
818   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
819   return isSupported;
820 }
821
822 FontId FontClient::Plugin::FindFontForCharacter(const FontList&         fontList,
823                                                 const CharacterSetList& characterSetList,
824                                                 Character               character,
825                                                 PointSize26Dot6         requestedPointSize,
826                                                 bool                    preferColor)
827 {
828   DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
829
830   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n");
831   DALI_LOG_INFO(gLogFilter, Debug::General, "           character : %p\n", character);
832   DALI_LOG_INFO(gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
833   DALI_LOG_INFO(gLogFilter, Debug::General, "         preferColor : %s\n", (preferColor ? "true" : "false"));
834
835   FontId fontId     = 0u;
836   bool   foundColor = false;
837
838   DALI_LOG_INFO(gLogFilter, Debug::General, "  number of fonts : %d\n", fontList.size());
839
840   // Traverse the list of fonts.
841   // Check for each font if supports the character.
842   for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
843   {
844     const FontDescription& description  = fontList[index];
845     const FcCharSet* const characterSet = characterSetList[index];
846
847     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  description; family : [%s]\n", description.family.c_str());
848     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                 path : [%s]\n", description.path.c_str());
849     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[description.width]);
850     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[description.weight]);
851     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[description.slant]);
852
853     bool foundInRanges = false;
854     if(nullptr != characterSet)
855     {
856       foundInRanges = FcCharSetHasChar(characterSet, character);
857     }
858
859     if(foundInRanges)
860     {
861       fontId = GetFontId(description,
862                          requestedPointSize,
863                          0u);
864
865       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "     font id : %d\n", fontId);
866
867       if(preferColor)
868       {
869         if((fontId > 0) &&
870            (fontId - 1u < mFontIdCache.Count()))
871         {
872           const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
873
874           foundColor = item.mHasColorTables;
875         }
876
877         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  foundColor : %s\n", (foundColor ? "true" : "false"));
878       }
879
880       // Keep going unless we prefer a different (color) font.
881       if(!preferColor || foundColor)
882       {
883         break;
884       }
885     }
886   }
887
888   DALI_LOG_INFO(gLogFilter, Debug::General, "  font id : %d\n", fontId);
889   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n");
890   return fontId;
891 }
892
893 FontId FontClient::Plugin::FindDefaultFont(Character       charcode,
894                                            PointSize26Dot6 requestedPointSize,
895                                            bool            preferColor)
896 {
897   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n");
898   DALI_LOG_INFO(gLogFilter, Debug::General, "           character : %p\n", charcode);
899   DALI_LOG_INFO(gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
900   DALI_LOG_INFO(gLogFilter, Debug::General, "         preferColor : %s\n", (preferColor ? "true" : "false"));
901
902   FontId fontId(0);
903
904   // Create the list of default fonts if it has not been created.
905   if(mDefaultFonts.empty())
906   {
907     FontDescription fontDescription;
908     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
909     fontDescription.width  = IntToWidthType(DEFAULT_FONT_WIDTH);
910     fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT);
911     fontDescription.slant  = IntToSlantType(DEFAULT_FONT_SLANT);
912
913     SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
914   }
915   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  number of default fonts : %d\n", mDefaultFonts.size());
916
917   // Traverse the list of default fonts.
918   // Check for each default font if supports the character.
919   fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
920
921   DALI_LOG_INFO(gLogFilter, Debug::General, "  font id : %d\n", fontId);
922   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n");
923
924   return fontId;
925 }
926
927 FontId FontClient::Plugin::FindFallbackFont(Character              charcode,
928                                             const FontDescription& preferredFontDescription,
929                                             PointSize26Dot6        requestedPointSize,
930                                             bool                   preferColor)
931 {
932   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n");
933   DALI_LOG_INFO(gLogFilter, Debug::General, "           character : %p\n", charcode);
934   DALI_LOG_INFO(gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
935   DALI_LOG_INFO(gLogFilter, Debug::General, "         preferColor : %s\n", (preferColor ? "true" : "false"));
936
937   // The font id to be returned.
938   FontId fontId = 0u;
939
940   FontDescription fontDescription;
941
942   // Fill the font description with the preferred font description and complete with the defaults.
943   fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
944   fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? IntToWeightType(DEFAULT_FONT_WEIGHT) : preferredFontDescription.weight);
945   fontDescription.width  = ((FontWidth::NONE == preferredFontDescription.width) ? IntToWidthType(DEFAULT_FONT_WIDTH) : preferredFontDescription.width);
946   fontDescription.slant  = ((FontSlant::NONE == preferredFontDescription.slant) ? IntToSlantType(DEFAULT_FONT_SLANT) : preferredFontDescription.slant);
947
948   DALI_LOG_INFO(gLogFilter, Debug::General, "  preferredFontDescription --> fontDescription\n");
949   DALI_LOG_INFO(gLogFilter, Debug::General, "  [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
950   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
951   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
952   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
953
954   // Check first if the font's description has been queried before.
955   FontList*         fontList         = nullptr;
956   CharacterSetList* characterSetList = nullptr;
957
958   if(!FindFallbackFontList(fontDescription, fontList, characterSetList))
959   {
960     fontList         = new FontList;
961     characterSetList = new CharacterSetList;
962
963     SetFontList(fontDescription, *fontList, *characterSetList);
964 #ifdef __APPLE__
965     FontDescription appleColorEmoji;
966     appleColorEmoji.family = "Apple Color Emoji";
967     appleColorEmoji.width  = fontDescription.width;
968     appleColorEmoji.weight = fontDescription.weight;
969     appleColorEmoji.slant  = fontDescription.slant;
970     FontList         emojiFontList;
971     CharacterSetList emojiCharSetList;
972     SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
973
974     std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
975     emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
976     *fontList         = std::move(emojiFontList);
977     *characterSetList = std::move(emojiCharSetList);
978 #endif
979
980     // Add the font-list to the cache.
981     mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
982   }
983
984   if(fontList && characterSetList)
985   {
986     fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
987   }
988
989   DALI_LOG_INFO(gLogFilter, Debug::General, "  font id : %d\n", fontId);
990   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
991   return fontId;
992 }
993
994 FontId FontClient::Plugin::GetFontId(const FontPath& path,
995                                      PointSize26Dot6 requestedPointSize,
996                                      FaceIndex       faceIndex,
997                                      bool            cacheDescription)
998 {
999   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n");
1000   DALI_LOG_INFO(gLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
1001   DALI_LOG_INFO(gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1002
1003   FontId id = 0u;
1004
1005   if(nullptr != mFreeTypeLibrary)
1006   {
1007     FontId foundId = 0u;
1008     if(FindFont(path, requestedPointSize, faceIndex, foundId))
1009     {
1010       id = foundId;
1011     }
1012     else
1013     {
1014       id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
1015     }
1016   }
1017
1018   DALI_LOG_INFO(gLogFilter, Debug::General, "  font id : %d\n", id);
1019   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n");
1020
1021   return id;
1022 }
1023
1024 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
1025                                      PointSize26Dot6        requestedPointSize,
1026                                      FaceIndex              faceIndex)
1027 {
1028   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n");
1029   DALI_LOG_INFO(gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str());
1030   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str());
1031   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width]);
1032   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
1033   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
1034   DALI_LOG_INFO(gLogFilter, Debug::General, "   requestedPointSize : %d\n", requestedPointSize);
1035
1036   // This method uses three vectors which caches:
1037   // * The bitmap font cache
1038   // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
1039   // * The path to font file names.
1040   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
1041
1042   // 1) Checks if the font description matches with a previously loaded bitmap font.
1043   //    Returns if a font is found.
1044   // 2) Checks in the cache if the font's description has been validated before.
1045   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
1046   //    retrieves using font config a path to a font file name which matches with the
1047   //    font's description. The path is stored in the cache.
1048   //
1049   // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
1050   //    font file names' exists. If exists, it gets the font id. If it doesn't it calls
1051   //    the GetFontId() method with the path to the font file name and the point size to
1052   //    get the font id.
1053
1054   // The font id to be returned.
1055   FontId fontId = 0u;
1056
1057   // Check first if the font description matches with a previously loaded bitmap font.
1058   if(FindBitmapFont(fontDescription.family, fontId))
1059   {
1060     return fontId;
1061   }
1062
1063   // Check if the font's description have been validated before.
1064   FontDescriptionId validatedFontId = 0u;
1065
1066   if(!FindValidatedFont(fontDescription,
1067                         validatedFontId))
1068   {
1069     // Use font config to validate the font's description.
1070     ValidateFont(fontDescription,
1071                  validatedFontId);
1072   }
1073
1074   FontId fontFaceId = 0u;
1075   // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
1076   if(!FindFont(validatedFontId, requestedPointSize, fontFaceId))
1077   {
1078     // Retrieve the font file name path.
1079     const FontDescription& description = *(mFontDescriptionCache.begin() + validatedFontId - 1u);
1080
1081     // Retrieve the font id. Do not cache the description as it has been already cached.
1082     fontId = GetFontId(description.path,
1083                        requestedPointSize,
1084                        faceIndex,
1085                        false);
1086
1087     fontFaceId                               = mFontIdCache[fontId - 1u].id;
1088     mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(mCharacterSetCache[validatedFontId - 1u]);
1089
1090     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
1091     mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
1092                                                                      requestedPointSize,
1093                                                                      fontFaceId));
1094   }
1095   else
1096   {
1097     fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
1098   }
1099
1100   DALI_LOG_INFO(gLogFilter, Debug::General, "  font id : %d\n", fontId);
1101   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n");
1102
1103   return fontId;
1104 }
1105
1106 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont)
1107 {
1108   for(const auto& item : mBitmapFontCache)
1109   {
1110     if(bitmapFont.name == item.font.name)
1111     {
1112       return item.id + 1u;
1113     }
1114   }
1115
1116   BitmapFontCacheItem bitmapFontCacheItem;
1117   bitmapFontCacheItem.font = bitmapFont;
1118   bitmapFontCacheItem.id   = mFontIdCache.Count();
1119
1120   // Resize the vector with the pixel buffers.
1121   bitmapFontCacheItem.pixelBuffers.resize(bitmapFont.glyphs.size());
1122
1123   // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
1124   unsigned int index = 0u;
1125   for(auto& glyph : bitmapFontCacheItem.font.glyphs)
1126   {
1127     Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1128
1129     if(EqualsZero(glyph.ascender) && EqualsZero(glyph.descender))
1130     {
1131       // Load the glyph.
1132       pixelBuffer = LoadImageFromFile(glyph.url);
1133
1134       if(pixelBuffer)
1135       {
1136         glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1137       }
1138     }
1139
1140     bitmapFontCacheItem.font.ascender  = std::max(glyph.ascender, bitmapFontCacheItem.font.ascender);
1141     bitmapFontCacheItem.font.descender = std::min(glyph.descender, bitmapFontCacheItem.font.descender);
1142
1143     ++index;
1144   }
1145
1146   FontIdCacheItem fontIdCacheItem;
1147   fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1148   fontIdCacheItem.id   = mBitmapFontCache.size();
1149
1150   mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
1151   mFontIdCache.PushBack(fontIdCacheItem);
1152
1153   return bitmapFontCacheItem.id + 1u;
1154 }
1155
1156 void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
1157                                       FontDescriptionId&     validatedFontId)
1158 {
1159   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n");
1160   DALI_LOG_INFO(gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str());
1161   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str());
1162   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width]);
1163   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
1164   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
1165
1166   // Create a font pattern.
1167   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
1168
1169   FontDescription description;
1170
1171   FcCharSet* characterSet = nullptr;
1172   bool       matched      = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
1173   FcPatternDestroy(fontFamilyPattern);
1174
1175   if(matched && (nullptr != characterSet))
1176   {
1177     // Add the path to the cache.
1178     description.type = FontDescription::FACE_FONT;
1179     mFontDescriptionCache.push_back(description);
1180
1181     // Set the index to the vector of paths to font file names.
1182     validatedFontId = mFontDescriptionCache.size();
1183
1184     DALI_LOG_INFO(gLogFilter, Debug::General, "  matched description; family : [%s]\n", description.family.c_str());
1185     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                         path : [%s]\n", description.path.c_str());
1186     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                        width : [%s]\n", FontWidth::Name[description.width]);
1187     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                       weight : [%s]\n", FontWeight::Name[description.weight]);
1188     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                        slant : [%s]\n\n", FontSlant::Name[description.slant]);
1189     DALI_LOG_INFO(gLogFilter, Debug::General, "  validatedFontId : %d\n", validatedFontId);
1190
1191     // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1192     mCharacterSetCache.PushBack(characterSet);
1193
1194     // Cache the index and the matched font's description.
1195     FontDescriptionCacheItem item(description,
1196                                   validatedFontId);
1197
1198     mValidatedFontCache.push_back(std::move(item));
1199
1200     if((fontDescription.family != description.family) ||
1201        (fontDescription.width != description.width) ||
1202        (fontDescription.weight != description.weight) ||
1203        (fontDescription.slant != description.slant))
1204     {
1205       // Cache the given font's description if it's different than the matched.
1206       FontDescriptionCacheItem item(fontDescription,
1207                                     validatedFontId);
1208
1209       mValidatedFontCache.push_back(std::move(item));
1210     }
1211   }
1212   else
1213   {
1214     DALI_LOG_INFO(gLogFilter, Debug::General, "  font validation failed for font [%s]\n", fontDescription.family.c_str());
1215   }
1216
1217   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n");
1218 }
1219
1220 void FontClient::Plugin::GetFontMetrics(FontId       fontId,
1221                                         FontMetrics& metrics)
1222 {
1223   const FontId index = fontId - 1u;
1224
1225   if((fontId > 0) &&
1226      (index < mFontIdCache.Count()))
1227   {
1228     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1229
1230     switch(fontIdCacheItem.type)
1231     {
1232       case FontDescription::FACE_FONT:
1233       {
1234         const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1235
1236         metrics = font.mMetrics;
1237
1238         // Adjust the metrics if the fixed-size font should be down-scaled
1239         if(font.mIsFixedSizeBitmap)
1240         {
1241           const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1242
1243           if(desiredFixedSize > 0.f)
1244           {
1245             const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1246
1247             metrics.ascender           = metrics.ascender * scaleFactor;
1248             metrics.descender          = metrics.descender * scaleFactor;
1249             metrics.height             = metrics.height * scaleFactor;
1250             metrics.underlinePosition  = metrics.underlinePosition * scaleFactor;
1251             metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1252           }
1253         }
1254         break;
1255       }
1256       case FontDescription::BITMAP_FONT:
1257       {
1258         const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1259
1260         metrics.ascender           = bitmapFontCacheItem.font.ascender;
1261         metrics.descender          = bitmapFontCacheItem.font.descender;
1262         metrics.height             = metrics.ascender - metrics.descender;
1263         metrics.underlinePosition  = bitmapFontCacheItem.font.underlinePosition;
1264         metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1265         break;
1266       }
1267       default:
1268       {
1269         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1270       }
1271     }
1272   }
1273   else
1274   {
1275     DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
1276   }
1277 }
1278
1279 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
1280                                              Character charcode)
1281 {
1282   GlyphIndex   glyphIndex = 0u;
1283   const FontId index      = fontId - 1u;
1284
1285   if((fontId > 0u) &&
1286      (index < mFontIdCache.Count()))
1287   {
1288     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1289
1290     if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1291     {
1292       FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1293
1294       glyphIndex = FT_Get_Char_Index(ftFace, charcode);
1295     }
1296   }
1297
1298   return glyphIndex;
1299 }
1300
1301 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
1302                                          uint32_t   size,
1303                                          GlyphType  type,
1304                                          bool       horizontal)
1305 {
1306   if(VECTOR_GLYPH == type)
1307   {
1308     return GetVectorMetrics(array, size, horizontal);
1309   }
1310
1311   return GetBitmapMetrics(array, size, horizontal);
1312 }
1313
1314 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
1315                                           uint32_t   size,
1316                                           bool       horizontal)
1317 {
1318   bool success(true);
1319
1320   for(unsigned int i = 0; i < size; ++i)
1321   {
1322     GlyphInfo& glyph = array[i];
1323
1324     FontId index = glyph.fontId - 1u;
1325
1326     if((glyph.fontId > 0u) &&
1327        (index < mFontIdCache.Count()))
1328     {
1329       const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1330
1331       switch(fontIdCacheItem.type)
1332       {
1333         case FontDescription::FACE_FONT:
1334         {
1335           const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1336
1337           FT_Face ftFace = font.mFreeTypeFace;
1338
1339 #ifdef FREETYPE_BITMAP_SUPPORT
1340           // Check to see if we should be loading a Fixed Size bitmap?
1341           if(font.mIsFixedSizeBitmap)
1342           {
1343             FT_Select_Size(ftFace, font.mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
1344             int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
1345             if(FT_Err_Ok == error)
1346             {
1347               glyph.width    = font.mFixedWidthPixels;
1348               glyph.height   = font.mFixedHeightPixels;
1349               glyph.advance  = font.mFixedWidthPixels;
1350               glyph.xBearing = 0.0f;
1351               glyph.yBearing = font.mFixedHeightPixels;
1352
1353               // Adjust the metrics if the fixed-size font should be down-scaled
1354               const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1355
1356               if(desiredFixedSize > 0.f)
1357               {
1358                 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1359
1360                 glyph.width    = glyph.width * scaleFactor;
1361                 glyph.height   = glyph.height * scaleFactor;
1362                 glyph.advance  = glyph.advance * scaleFactor;
1363                 glyph.xBearing = glyph.xBearing * scaleFactor;
1364                 glyph.yBearing = glyph.yBearing * scaleFactor;
1365
1366                 glyph.scaleFactor = scaleFactor;
1367               }
1368             }
1369             else
1370             {
1371               DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
1372               success = false;
1373             }
1374           }
1375           else
1376 #endif
1377           {
1378             // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1379             // i.e. with the SNum-3R font.
1380             // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1381             int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);
1382
1383             // Keep the width of the glyph before doing the software emboldening.
1384             // It will be used to calculate a scale factor to be applied to the
1385             // advance as Harfbuzz doesn't apply any SW emboldening to calculate
1386             // the advance of the glyph.
1387             const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1388
1389             if(FT_Err_Ok == error)
1390             {
1391               const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
1392               if(isEmboldeningRequired)
1393               {
1394                 // Does the software bold.
1395                 FT_GlyphSlot_Embolden(ftFace->glyph);
1396               }
1397
1398               glyph.width  = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1399               glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
1400               if(horizontal)
1401               {
1402                 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
1403                 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
1404               }
1405               else
1406               {
1407                 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
1408                 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
1409               }
1410
1411               if(isEmboldeningRequired && !Dali::EqualsZero(width))
1412               {
1413                 // If the glyph is emboldened by software, the advance is multiplied by a
1414                 // scale factor to make it slightly bigger.
1415                 glyph.advance *= (glyph.width / width);
1416               }
1417
1418               // Use the bounding box of the bitmap to correct the metrics.
1419               // For some fonts i.e the SNum-3R the metrics need to be corrected,
1420               // otherwise the glyphs 'dance' up and down depending on the
1421               // font's point size.
1422
1423               FT_Glyph ftGlyph;
1424               error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);
1425
1426               FT_BBox bbox;
1427               FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
1428
1429               const float descender = glyph.height - glyph.yBearing;
1430               glyph.height          = (bbox.yMax - bbox.yMin) * FROM_266;
1431               glyph.yBearing        = glyph.height - round(descender);
1432
1433               // Created FT_Glyph object must be released with FT_Done_Glyph
1434               FT_Done_Glyph(ftGlyph);
1435             }
1436             else
1437             {
1438               success = false;
1439             }
1440           }
1441           break;
1442         }
1443         case FontDescription::BITMAP_FONT:
1444         {
1445           BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1446
1447           unsigned int index = 0u;
1448           for(auto& item : bitmapFontCacheItem.font.glyphs)
1449           {
1450             if(item.utf32 == glyph.index)
1451             {
1452               Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1453               if(!pixelBuffer)
1454               {
1455                 pixelBuffer = LoadImageFromFile(item.url);
1456               }
1457
1458               glyph.width       = static_cast<float>(pixelBuffer.GetWidth());
1459               glyph.height      = static_cast<float>(pixelBuffer.GetHeight());
1460               glyph.xBearing    = 0.f;
1461               glyph.yBearing    = glyph.height + item.descender;
1462               glyph.advance     = glyph.width;
1463               glyph.scaleFactor = 1.f;
1464               break;
1465             }
1466             ++index;
1467           }
1468
1469           success = true;
1470           break;
1471         }
1472         default:
1473         {
1474           DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1475         }
1476       }
1477     }
1478     else
1479     {
1480       // Check if it's an embedded image.
1481       if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count()))
1482       {
1483         const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1484
1485         glyph.width       = static_cast<float>(item.width);
1486         glyph.height      = static_cast<float>(item.height);
1487         glyph.xBearing    = 0.f;
1488         glyph.yBearing    = glyph.height;
1489         glyph.advance     = glyph.width;
1490         glyph.scaleFactor = 1.f;
1491       }
1492       else
1493       {
1494         success = false;
1495       }
1496     }
1497   }
1498
1499   return success;
1500 }
1501
1502 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
1503                                           uint32_t   size,
1504                                           bool       horizontal)
1505 {
1506 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1507   bool success(true);
1508
1509   for(unsigned int i = 0u; i < size; ++i)
1510   {
1511     FontId fontId = array[i].fontId;
1512
1513     if((fontId > 0u) &&
1514        (fontId - 1u) < mFontIdCache.Count())
1515     {
1516       FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1517
1518       if(!font.mVectorFontId)
1519       {
1520         font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1521       }
1522
1523       mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
1524
1525       // Vector metrics are in EMs, convert to pixels
1526       const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
1527       array[i].width *= scale;
1528       array[i].height *= scale;
1529       array[i].xBearing *= scale;
1530       array[i].yBearing *= scale;
1531       array[i].advance *= scale;
1532     }
1533     else
1534     {
1535       success = false;
1536     }
1537   }
1538
1539   return success;
1540 #else
1541   return false;
1542 #endif
1543 }
1544
1545 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
1546 {
1547   const FontId index = fontId - 1u;
1548
1549   if((fontId > 0u) &&
1550      (index < mFontIdCache.Count()))
1551   {
1552     data.isColorBitmap = false;
1553     data.isColorEmoji  = false;
1554
1555     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1556
1557     switch(fontIdCacheItem.type)
1558     {
1559       case FontDescription::FACE_FONT:
1560       {
1561         // For the software italics.
1562         bool isShearRequired = false;
1563
1564         const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1565         FT_Face                  ftFace            = fontFaceCacheItem.mFreeTypeFace;
1566
1567         FT_Error error;
1568
1569 #ifdef FREETYPE_BITMAP_SUPPORT
1570         // Check to see if this is fixed size bitmap
1571         if(fontFaceCacheItem.mIsFixedSizeBitmap)
1572         {
1573           error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1574         }
1575         else
1576 #endif
1577         {
1578           // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1579           // i.e. with the SNum-3R font.
1580           // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1581           error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
1582         }
1583         if(FT_Err_Ok == error)
1584         {
1585           if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
1586           {
1587             // Does the software bold.
1588             FT_GlyphSlot_Embolden(ftFace->glyph);
1589           }
1590
1591           if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
1592           {
1593             // Will do the software italic.
1594             isShearRequired = true;
1595           }
1596
1597           FT_Glyph glyph;
1598           error = FT_Get_Glyph(ftFace->glyph, &glyph);
1599
1600           // Convert to bitmap if necessary
1601           if(FT_Err_Ok == error)
1602           {
1603             if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
1604             {
1605               int  offsetX = 0, offsetY = 0;
1606               bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
1607
1608               // Create a bitmap for the outline
1609               if(isOutlineGlyph)
1610               {
1611                 // Retrieve the horizontal and vertical distance from the current pen position to the
1612                 // left and top border of the glyph bitmap for a normal glyph before applying the outline.
1613                 if(FT_Err_Ok == error)
1614                 {
1615                   FT_Glyph normalGlyph;
1616                   error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);
1617
1618                   error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
1619                   if(FT_Err_Ok == error)
1620                   {
1621                     FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);
1622
1623                     offsetX = bitmapGlyph->left;
1624                     offsetY = bitmapGlyph->top;
1625                   }
1626
1627                   // Created FT_Glyph object must be released with FT_Done_Glyph
1628                   FT_Done_Glyph(normalGlyph);
1629                 }
1630
1631                 // Now apply the outline
1632
1633                 // Set up a stroker
1634                 FT_Stroker stroker;
1635                 error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
1636
1637                 if(FT_Err_Ok == error)
1638                 {
1639                   FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
1640                   error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
1641
1642                   if(FT_Err_Ok == error)
1643                   {
1644                     FT_Stroker_Done(stroker);
1645                   }
1646                   else
1647                   {
1648                     DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
1649                   }
1650                 }
1651                 else
1652                 {
1653                   DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
1654                 }
1655               }
1656
1657               error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1658               if(FT_Err_Ok == error)
1659               {
1660                 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
1661
1662                 if(isOutlineGlyph)
1663                 {
1664                   // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
1665                   data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
1666                   data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
1667                 }
1668
1669                 ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
1670               }
1671               else
1672               {
1673                 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
1674               }
1675             }
1676             else
1677             {
1678               ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
1679             }
1680
1681             data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1682
1683             // Created FT_Glyph object must be released with FT_Done_Glyph
1684             FT_Done_Glyph(glyph);
1685           }
1686         }
1687         else
1688         {
1689           DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
1690         }
1691         break;
1692       }
1693       case FontDescription::BITMAP_FONT:
1694       {
1695         BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1696
1697         unsigned int index = 0u;
1698         for(auto& item : bitmapFontCacheItem.font.glyphs)
1699         {
1700           if(item.utf32 == glyphIndex)
1701           {
1702             Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1703             if(!pixelBuffer)
1704             {
1705               pixelBuffer = LoadImageFromFile(item.url);
1706             }
1707
1708             data.width  = pixelBuffer.GetWidth();
1709             data.height = pixelBuffer.GetHeight();
1710
1711             data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1712
1713             ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer());
1714
1715             // Sets the pixel format.
1716             data.format = pixelBuffer.GetPixelFormat();
1717             break;
1718           }
1719           ++index;
1720         }
1721         break;
1722       }
1723       default:
1724       {
1725         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1726       }
1727     }
1728   }
1729   else
1730   {
1731     if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count()))
1732     {
1733       // It's an embedded item.
1734       const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1735
1736       data.width  = item.width;
1737       data.height = item.height;
1738       if(0u != item.pixelBufferId)
1739       {
1740         Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId - 1u].pixelBuffer;
1741         if(pixelBuffer)
1742         {
1743           ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer());
1744
1745           // Sets the pixel format.
1746           data.format = pixelBuffer.GetPixelFormat();
1747         }
1748       }
1749       else
1750       {
1751         // Creates the output buffer
1752         const unsigned int bufferSize = data.width * data.height * 4u;
1753         data.buffer                   = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1754
1755         memset(data.buffer, 0u, bufferSize);
1756
1757         // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1758       }
1759     }
1760   }
1761 }
1762
1763 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
1764 {
1765   TextAbstraction::FontClient::GlyphBufferData data;
1766
1767   CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
1768
1769   return PixelData::New(data.buffer,
1770                         data.width * data.height * Pixel::GetBytesPerPixel(data.format),
1771                         data.width,
1772                         data.height,
1773                         data.format,
1774                         PixelData::DELETE_ARRAY);
1775 }
1776
1777 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
1778 {
1779   blob       = nullptr;
1780   blobLength = 0;
1781
1782 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1783   if((fontId > 0u) &&
1784      (fontId - 1u < mFontIdCache.Count()))
1785   {
1786     const FontId       fontFaceId = mFontIdCache[fontId - 1u].id;
1787     FontFaceCacheItem& font       = mFontFaceCache[fontFaceId];
1788
1789     if(!font.mVectorFontId)
1790     {
1791       font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1792     }
1793
1794     mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1795   }
1796 #endif
1797 }
1798
1799 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
1800 {
1801   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n");
1802   DALI_LOG_INFO(gLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize);
1803
1804   // First look into the cache if there is an ellipsis glyph for the requested point size.
1805   for(const auto& item : mEllipsisCache)
1806   {
1807     if(item.requestedPointSize == requestedPointSize)
1808     {
1809       // Use the glyph in the cache.
1810
1811       DALI_LOG_INFO(gLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
1812       DALI_LOG_INFO(gLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
1813       DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n");
1814
1815       return item.glyph;
1816     }
1817   }
1818
1819   // No glyph has been found. Create one.
1820   mEllipsisCache.PushBack(EllipsisItem());
1821   EllipsisItem& item = *(mEllipsisCache.End() - 1u);
1822
1823   item.requestedPointSize = requestedPointSize;
1824
1825   // Find a font for the ellipsis glyph.
1826   item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1827                                       requestedPointSize,
1828                                       false);
1829
1830   // Set the character index to access the glyph inside the font.
1831   item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].id].mFreeTypeFace,
1832                                        ELLIPSIS_CHARACTER);
1833
1834   GetBitmapMetrics(&item.glyph, 1u, true);
1835
1836   DALI_LOG_INFO(gLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
1837   DALI_LOG_INFO(gLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
1838   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n");
1839
1840   return item.glyph;
1841 }
1842
1843 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
1844 {
1845   FT_Error error = -1;
1846
1847   const FontId index = fontId - 1u;
1848
1849   if((fontId > 0u) &&
1850      (index < mFontIdCache.Count()))
1851   {
1852     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1853
1854     switch(fontIdCacheItem.type)
1855     {
1856       case FontDescription::FACE_FONT:
1857       {
1858 #ifdef FREETYPE_BITMAP_SUPPORT
1859         const FontFaceCacheItem& item   = mFontFaceCache[fontIdCacheItem.id];
1860         FT_Face                  ftFace = item.mFreeTypeFace;
1861
1862         // Check to see if this is fixed size bitmap
1863         if(item.mHasColorTables)
1864         {
1865           error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1866         }
1867 #endif
1868         break;
1869       }
1870       case FontDescription::BITMAP_FONT:
1871       {
1872         error = FT_Err_Ok; // Will return true;
1873         break;
1874       }
1875       default:
1876       {
1877         DALI_LOG_INFO(gLogFilter, Debug::General, "  Invalid type of font\n");
1878       }
1879     }
1880   }
1881
1882   return FT_Err_Ok == error;
1883 }
1884
1885 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId)
1886 {
1887   FT_Face fontFace = nullptr;
1888
1889   const FontId index = fontId - 1u;
1890   if((fontId > 0u) &&
1891      (index < mFontIdCache.Count()))
1892   {
1893     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1894
1895     if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1896     {
1897       fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1898     }
1899   }
1900   return fontFace;
1901 }
1902
1903 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId)
1904 {
1905   const FontId index = fontId - 1u;
1906   if((fontId > 0u) &&
1907      (index < mFontIdCache.Count()))
1908   {
1909     return mFontIdCache[index].type;
1910   }
1911   return FontDescription::INVALID;
1912 }
1913
1914 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1915 {
1916   // nullptr as first parameter means the current configuration is used.
1917   return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1918 }
1919
1920 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
1921 {
1922   EmbeddedItem embeddedItem;
1923
1924   embeddedItem.pixelBufferId = 0u;
1925   embeddedItem.width         = description.width;
1926   embeddedItem.height        = description.height;
1927
1928   pixelFormat = Pixel::A8;
1929
1930   if(!description.url.empty())
1931   {
1932     // Check if the url is in the cache.
1933     PixelBufferId index = 0u;
1934
1935     for(const auto& cacheItem : mPixelBufferCache)
1936     {
1937       ++index;
1938       if(cacheItem.url == description.url)
1939       {
1940         // The url is in the pixel buffer cache.
1941         // Set the index +1 to the vector.
1942         embeddedItem.pixelBufferId = index;
1943         break;
1944       }
1945     }
1946
1947     Devel::PixelBuffer pixelBuffer;
1948     if(0u == embeddedItem.pixelBufferId)
1949     {
1950       // The pixel buffer is not in the cache. Create one and cache it.
1951
1952       // Load the image from the url.
1953       pixelBuffer = LoadImageFromFile(description.url);
1954
1955       // Create the cache item.
1956       PixelBufferCacheItem pixelBufferCacheItem;
1957       pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1958       pixelBufferCacheItem.url         = description.url;
1959
1960       // Store the cache item in the cache.
1961       mPixelBufferCache.push_back(std::move(pixelBufferCacheItem));
1962
1963       // Set the pixel buffer id to the embedded item.
1964       embeddedItem.pixelBufferId = mPixelBufferCache.size();
1965     }
1966     else
1967     {
1968       // Retrieve the pixel buffer from the cache to set the pixel format.
1969       pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1970     }
1971
1972     if(pixelBuffer)
1973     {
1974       // Set the size of the embedded item if it has not been set.
1975       if(0u == embeddedItem.width)
1976       {
1977         embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1978       }
1979
1980       if(0u == embeddedItem.height)
1981       {
1982         embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1983       }
1984
1985       // Set the pixel format.
1986       pixelFormat = pixelBuffer.GetPixelFormat();
1987     }
1988   }
1989
1990   // Find if the same embeddedItem has already been created.
1991   unsigned int index = 0u;
1992   for(const auto& item : mEmbeddedItemCache)
1993   {
1994     ++index;
1995     if((item.pixelBufferId == embeddedItem.pixelBufferId) &&
1996        (item.width == embeddedItem.width) &&
1997        (item.height == embeddedItem.height))
1998     {
1999       return index;
2000     }
2001   }
2002
2003   // Cache the embedded item.
2004   mEmbeddedItemCache.PushBack(embeddedItem);
2005
2006   return mEmbeddedItemCache.Count();
2007 }
2008 //SHS
2009
2010 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
2011 {
2012   mIsAtlasLimitationEnabled = enabled;
2013 }
2014
2015 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
2016 {
2017   return mIsAtlasLimitationEnabled;
2018 }
2019
2020 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
2021 {
2022   return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
2023 }
2024
2025 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
2026 {
2027   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
2028 }
2029
2030 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
2031 {
2032   return mCurrentMaximumBlockSizeFitInAtlas;
2033 }
2034
2035 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
2036 {
2037   bool            isChanged        = false;
2038   const Size&     maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
2039   const uint16_t& padding          = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
2040
2041   if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding
2042       && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
2043   {
2044     mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
2045     isChanged = true;
2046   }
2047
2048   return isChanged;
2049 }
2050
2051 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
2052 {
2053   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;;
2054 }
2055
2056
2057 void FontClient::Plugin::InitSystemFonts()
2058 {
2059   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n");
2060
2061   FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
2062
2063   if(fontSet)
2064   {
2065     DALI_LOG_INFO(gLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont);
2066
2067     // Reserve some space to avoid reallocations.
2068     mSystemFonts.reserve(fontSet->nfont);
2069
2070     for(int i = 0u; i < fontSet->nfont; ++i)
2071     {
2072       FcPattern* fontPattern = fontSet->fonts[i];
2073
2074       FontPath path;
2075
2076       // Skip fonts with no path
2077       if(GetFcString(fontPattern, FC_FILE, path))
2078       {
2079         mSystemFonts.push_back(FontDescription());
2080         FontDescription& fontDescription = mSystemFonts.back();
2081
2082         fontDescription.path = std::move(path);
2083
2084         int width  = 0;
2085         int weight = 0;
2086         int slant  = 0;
2087         GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
2088         GetFcInt(fontPattern, FC_WIDTH, width);
2089         GetFcInt(fontPattern, FC_WEIGHT, weight);
2090         GetFcInt(fontPattern, FC_SLANT, slant);
2091         fontDescription.width  = IntToWidthType(width);
2092         fontDescription.weight = IntToWeightType(weight);
2093         fontDescription.slant  = IntToSlantType(slant);
2094
2095         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  description; family : [%s]\n", fontDescription.family.c_str());
2096         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str());
2097         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width]);
2098         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2099         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2100       }
2101     }
2102
2103     // Destroys the font set created.
2104     FcFontSetDestroy(fontSet);
2105   }
2106   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n");
2107 }
2108
2109 bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
2110 {
2111   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n");
2112
2113   FcResult   result = FcResultMatch;
2114   FcPattern* match  = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
2115
2116   const bool matched = nullptr != match;
2117   DALI_LOG_INFO(gLogFilter, Debug::General, "  pattern matched : %s\n", (matched ? "true" : "false"));
2118
2119   if(matched)
2120   {
2121     int width  = 0;
2122     int weight = 0;
2123     int slant  = 0;
2124     GetFcString(match, FC_FILE, fontDescription.path);
2125     GetFcString(match, FC_FAMILY, fontDescription.family);
2126     GetFcInt(match, FC_WIDTH, width);
2127     GetFcInt(match, FC_WEIGHT, weight);
2128     GetFcInt(match, FC_SLANT, slant);
2129     fontDescription.width  = IntToWidthType(width);
2130     fontDescription.weight = IntToWeightType(weight);
2131     fontDescription.slant  = IntToSlantType(slant);
2132
2133     // Retrieve the character set and increase the reference counter.
2134     FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
2135     *characterSet = FcCharSetCopy(*characterSet);
2136
2137     // destroyed the matched pattern
2138     FcPatternDestroy(match);
2139
2140     DALI_LOG_INFO(gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str());
2141     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str());
2142     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width]);
2143     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2144     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2145   }
2146
2147   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n");
2148   return matched;
2149 }
2150
2151 FcPattern* FontClient::Plugin::CreateFontFamilyPattern(const FontDescription& fontDescription) const
2152 {
2153   // create the cached font family lookup pattern
2154   // a pattern holds a set of names, each name refers to a property of the font
2155   FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2156
2157   if(!fontFamilyPattern)
2158   {
2159     return nullptr;
2160   }
2161
2162   // add a property to the pattern for the font family
2163   FcPatternAddString(fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fontDescription.family.c_str()));
2164
2165   // add a property to the pattern for local setting.
2166   const char* locale = setlocale(LC_MESSAGES, nullptr);
2167   if(locale != nullptr)
2168   {
2169     FcPatternAddString(fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale));
2170   }
2171
2172   int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
2173   if(width < 0)
2174   {
2175     // Use default.
2176     width = DEFAULT_FONT_WIDTH;
2177   }
2178
2179   int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
2180   if(weight < 0)
2181   {
2182     // Use default.
2183     weight = DEFAULT_FONT_WEIGHT;
2184   }
2185
2186   int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
2187   if(slant < 0)
2188   {
2189     // Use default.
2190     slant = DEFAULT_FONT_SLANT;
2191   }
2192
2193   FcPatternAddInteger(fontFamilyPattern, FC_WIDTH, width);
2194   FcPatternAddInteger(fontFamilyPattern, FC_WEIGHT, weight);
2195   FcPatternAddInteger(fontFamilyPattern, FC_SLANT, slant);
2196
2197   // modify the config, with the mFontFamilyPatterm
2198   FcConfigSubstitute(nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern);
2199
2200   // provide default values for unspecified properties in the font pattern
2201   // e.g. patterns without a specified style or weight are set to Medium
2202   FcDefaultSubstitute(fontFamilyPattern);
2203
2204   return fontFamilyPattern;
2205 }
2206
2207 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
2208 {
2209   FcFontSet* fontset = nullptr;
2210
2211   // create a new pattern.
2212   // a pattern holds a set of names, each name refers to a property of the font
2213   FcPattern* pattern = FcPatternCreate();
2214
2215   if(nullptr != pattern)
2216   {
2217     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
2218     FcObjectSet* objectSet = FcObjectSetCreate();
2219
2220     if(nullptr != objectSet)
2221     {
2222       // build an object set from a list of property names
2223       FcObjectSetAdd(objectSet, FC_FILE);
2224       FcObjectSetAdd(objectSet, FC_FAMILY);
2225       FcObjectSetAdd(objectSet, FC_WIDTH);
2226       FcObjectSetAdd(objectSet, FC_WEIGHT);
2227       FcObjectSetAdd(objectSet, FC_SLANT);
2228
2229       // get a list of fonts
2230       // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
2231       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.
2232
2233       // clear up the object set
2234       FcObjectSetDestroy(objectSet);
2235     }
2236
2237     // clear up the pattern
2238     FcPatternDestroy(pattern);
2239   }
2240
2241   return fontset;
2242 }
2243
2244 bool FontClient::Plugin::GetFcString(const FcPattern* const pattern,
2245                                      const char* const      n,
2246                                      std::string&           string)
2247 {
2248   FcChar8*       file   = nullptr;
2249   const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
2250
2251   if(FcResultMatch == retVal)
2252   {
2253     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
2254     string.assign(reinterpret_cast<const char*>(file));
2255
2256     return true;
2257   }
2258
2259   return false;
2260 }
2261
2262 bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
2263 {
2264   const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
2265
2266   if(FcResultMatch == retVal)
2267   {
2268     return true;
2269   }
2270
2271   return false;
2272 }
2273
2274 FontId FontClient::Plugin::CreateFont(const FontPath& path,
2275                                       PointSize26Dot6 requestedPointSize,
2276                                       FaceIndex       faceIndex,
2277                                       bool            cacheDescription)
2278 {
2279   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n");
2280   DALI_LOG_INFO(gLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
2281   DALI_LOG_INFO(gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
2282
2283   FontId id = 0u;
2284
2285   // Create & cache new font face
2286   FT_Face ftFace;
2287   int     error = FT_New_Face(mFreeTypeLibrary,
2288                           path.c_str(),
2289                           0,
2290                           &ftFace);
2291
2292   if(FT_Err_Ok == error)
2293   {
2294     // Check if a font is scalable.
2295     const bool isScalable           = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
2296     const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
2297     const bool hasColorTables       = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
2298     FontId     fontFaceId           = 0u;
2299
2300     DALI_LOG_INFO(gLogFilter, Debug::General, "            isScalable : [%s]\n", (isScalable ? "true" : "false"));
2301     DALI_LOG_INFO(gLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
2302     DALI_LOG_INFO(gLogFilter, Debug::General, "        hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
2303
2304     // Check to see if the font contains fixed sizes?
2305     if(!isScalable && hasFixedSizedBitmaps)
2306     {
2307       PointSize26Dot6 actualPointSize = 0u;
2308       int             fixedSizeIndex  = 0;
2309       for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
2310       {
2311         const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2312         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
2313
2314         if(fixedSize >= requestedPointSize)
2315         {
2316           actualPointSize = fixedSize;
2317           break;
2318         }
2319       }
2320
2321       if(0u == actualPointSize)
2322       {
2323         // The requested point size is bigger than the bigest fixed size.
2324         fixedSizeIndex  = ftFace->num_fixed_sizes - 1;
2325         actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2326       }
2327
2328       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
2329
2330       // Tell Freetype to use this size
2331       error = FT_Select_Size(ftFace, fixedSizeIndex);
2332       if(FT_Err_Ok != error)
2333       {
2334         DALI_LOG_INFO(gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
2335       }
2336       else
2337       {
2338         const float fixedWidth  = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
2339         const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
2340
2341         // Indicate that the font is a fixed sized bitmap
2342         FontMetrics metrics(fixedHeight, // The ascender in pixels.
2343                             0.0f,
2344                             fixedHeight, // The height in pixels.
2345                             0.0f,
2346                             0.0f);
2347
2348         // Create the FreeType font face item to cache.
2349         FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
2350
2351         // Set the index to the font's id cache.
2352         fontFaceCacheItem.mFontId = mFontIdCache.Count();
2353
2354         // Create the font id item to cache.
2355         FontIdCacheItem fontIdCacheItem;
2356         fontIdCacheItem.type = FontDescription::FACE_FONT;
2357
2358         // Set the index to the FreeType font face cache.
2359         fontIdCacheItem.id = mFontFaceCache.size();
2360         fontFaceId         = fontIdCacheItem.id + 1u;
2361
2362         // Cache the items.
2363         mFontFaceCache.push_back(fontFaceCacheItem);
2364         mFontIdCache.PushBack(fontIdCacheItem);
2365
2366         // Set the font id to be returned.
2367         id = mFontIdCache.Count();
2368       }
2369     }
2370     else
2371     {
2372       if(mIsAtlasLimitationEnabled)
2373       {
2374         //There is limitation on block size to fit in predefined atlas size.
2375         //If the block size cannot fit into atlas size, then the system cannot draw block.
2376         //This is workaround to avoid issue in advance
2377         //Decrementing point-size until arriving to maximum allowed block size.
2378         auto requestedPointSizeBackup= requestedPointSize;
2379         const Size& maxSizeFitInAtlas = GetCurrentMaximumBlockSizeFitInAtlas();
2380         error = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
2381
2382         if(requestedPointSize != requestedPointSizeBackup)
2383         {
2384           DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
2385         }
2386       }
2387       else
2388       {
2389          error = FT_Set_Char_Size(ftFace,
2390                                0,
2391                                requestedPointSize,
2392                                mDpiHorizontal,
2393                                mDpiVertical);
2394       }
2395
2396       if(FT_Err_Ok == error)
2397       {
2398         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2399
2400         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
2401                             static_cast<float>(ftMetrics.descender) * FROM_266,
2402                             static_cast<float>(ftMetrics.height) * FROM_266,
2403                             static_cast<float>(ftFace->underline_position) * FROM_266,
2404                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
2405
2406         // Create the FreeType font face item to cache.
2407         FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics);
2408
2409         // Set the index to the font's id cache.
2410         fontFaceCacheItem.mFontId = mFontIdCache.Count();
2411
2412         // Create the font id item to cache.
2413         FontIdCacheItem fontIdCacheItem;
2414         fontIdCacheItem.type = FontDescription::FACE_FONT;
2415
2416         // Set the index to the FreeType font face cache.
2417         fontIdCacheItem.id = mFontFaceCache.size();
2418         fontFaceId         = fontIdCacheItem.id + 1u;
2419
2420         // Cache the items.
2421         mFontFaceCache.push_back(fontFaceCacheItem);
2422         mFontIdCache.PushBack(fontIdCacheItem);
2423
2424         // Set the font id to be returned.
2425         id = mFontIdCache.Count();
2426       }
2427       else
2428       {
2429         DALI_LOG_INFO(gLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
2430       }
2431     }
2432
2433     if(0u != fontFaceId)
2434     {
2435       if(cacheDescription)
2436       {
2437         CacheFontPath(ftFace, fontFaceId, requestedPointSize, path);
2438       }
2439     }
2440   }
2441   else
2442   {
2443     DALI_LOG_INFO(gLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str());
2444   }
2445
2446   DALI_LOG_INFO(gLogFilter, Debug::General, "  font id : %d\n", id);
2447   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n");
2448
2449   return id;
2450 }
2451
2452 void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer)
2453 {
2454   // Set the input dimensions.
2455   const ImageDimensions inputDimensions(srcWidth, srcHeight);
2456
2457   // Set the output dimensions.
2458   // If the output dimension is not given, the input dimension is set
2459   // and won't be downscaling.
2460   data.width  = (data.width == 0) ? srcWidth : data.width;
2461   data.height = (data.height == 0) ? srcHeight : data.height;
2462   const ImageDimensions desiredDimensions(data.width, data.height);
2463
2464   // Creates the output buffer
2465   const unsigned int bufferSize = data.width * data.height * 4u;
2466   data.buffer                   = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2467
2468   if(inputDimensions == desiredDimensions)
2469   {
2470     // There isn't downscaling.
2471     memcpy(data.buffer, srcBuffer, bufferSize);
2472   }
2473   else
2474   {
2475     Dali::Internal::Platform::LanczosSample4BPP(srcBuffer,
2476                                                 inputDimensions,
2477                                                 data.buffer,
2478                                                 desiredDimensions);
2479   }
2480 }
2481
2482 void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired)
2483 {
2484   if(srcBitmap.width * srcBitmap.rows > 0)
2485   {
2486     switch(srcBitmap.pixel_mode)
2487     {
2488       case FT_PIXEL_MODE_GRAY:
2489       {
2490         if(srcBitmap.pitch == static_cast<int>(srcBitmap.width))
2491         {
2492           uint8_t*     pixelsIn = srcBitmap.buffer;
2493           unsigned int width    = srcBitmap.width;
2494           unsigned     height   = srcBitmap.rows;
2495
2496           std::unique_ptr<uint8_t, void (*)(void*)> pixelsOutPtr(nullptr, free);
2497
2498           if(isShearRequired)
2499           {
2500             /**
2501              * Glyphs' bitmaps with no slant retrieved from FreeType:
2502              * __________     ____
2503              * |XXXXXXXX|     |XX|
2504              * |   XX   |     |XX|
2505              * |   XX   |     |XX|
2506              * |   XX   |     |XX|
2507              * |   XX   |     |XX|
2508              * |   XX   |     |XX|
2509              * ----------     ----
2510              *
2511              * Expected glyphs' bitmaps with italic slant:
2512              * ____________   ______
2513              * |  XXXXXXXX|   |  XX|
2514              * |     XX   |   |  XX|
2515              * |    XX    |   | XX |
2516              * |    XX    |   | XX |
2517              * |   XX     |   |XX  |
2518              * |   XX     |   |XX  |
2519              * ------------   ------
2520              *
2521              * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2522              * __________     ______
2523              * |XXXXXXXX|     |  XX|
2524              * |   XX   |     |  XX|
2525              * |  XX    |     | XX |
2526              * |  XX    |     | XX |
2527              * | XX     |     |XX  |
2528              * | XX     |     |XX  |
2529              * ----------     ------
2530              *
2531              * This difference in some bitmaps' width causes an overlap of some glyphs. This is the reason why a shear operation is done here instead of relying on the experimental FT_GlyphSlot_Oblique() implementation.
2532              */
2533             unsigned int widthOut  = 0u;
2534             unsigned int heightOut = 0u;
2535             uint8_t*     pixelsOut = nullptr;
2536
2537             Dali::Internal::Platform::HorizontalShear(pixelsIn,
2538                                                       width,
2539                                                       height,
2540                                                       1u,
2541                                                       -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2542                                                       pixelsOut,
2543                                                       widthOut,
2544                                                       heightOut);
2545
2546             width    = widthOut;
2547             height   = heightOut;
2548             pixelsIn = pixelsOut;
2549             pixelsOutPtr.reset(pixelsOut);
2550           }
2551
2552           const unsigned int bufferSize = width * height;
2553           data.buffer                   = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2554           data.width                    = width;
2555           data.height                   = height;
2556           data.format                   = Pixel::L8; // Sets the pixel format.
2557           memcpy(data.buffer, pixelsIn, bufferSize);
2558         }
2559         break;
2560       }
2561
2562 #ifdef FREETYPE_BITMAP_SUPPORT
2563       case FT_PIXEL_MODE_BGRA:
2564       {
2565         if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
2566         {
2567           ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer);
2568
2569           // Sets the pixel format.
2570           data.format = Pixel::BGRA8888;
2571         }
2572         break;
2573       }
2574 #endif
2575       default:
2576       {
2577         DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n");
2578         break;
2579       }
2580     }
2581   }
2582 }
2583
2584 bool FontClient::Plugin::FindFont(const FontPath& path,
2585                                   PointSize26Dot6 requestedPointSize,
2586                                   FaceIndex       faceIndex,
2587                                   FontId&         fontId) const
2588 {
2589   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n");
2590   DALI_LOG_INFO(gLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
2591   DALI_LOG_INFO(gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
2592   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  number of fonts in the cache : %d\n", mFontFaceCache.size());
2593
2594   fontId = 0u;
2595   for(const auto& cacheItem : mFontFaceCache)
2596   {
2597     if(cacheItem.mRequestedPointSize == requestedPointSize &&
2598        cacheItem.mFaceIndex == faceIndex &&
2599        cacheItem.mPath == path)
2600     {
2601       fontId = cacheItem.mFontId + 1u;
2602
2603       DALI_LOG_INFO(gLogFilter, Debug::General, "  font found, id : %d\n", fontId);
2604       DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2605
2606       return true;
2607     }
2608   }
2609
2610   DALI_LOG_INFO(gLogFilter, Debug::General, "  font not found\n");
2611   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2612
2613   return false;
2614 }
2615
2616 bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
2617                                            FontDescriptionId&     validatedFontId)
2618 {
2619   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n");
2620   DALI_LOG_INFO(gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str());
2621   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str());
2622   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width]);
2623   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2624   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2625   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
2626
2627   validatedFontId = 0u;
2628
2629   for(const auto& item : mValidatedFontCache)
2630   {
2631     if(!fontDescription.family.empty() &&
2632        (fontDescription.family == item.fontDescription.family) &&
2633        (fontDescription.width == item.fontDescription.width) &&
2634        (fontDescription.weight == item.fontDescription.weight) &&
2635        (fontDescription.slant == item.fontDescription.slant))
2636     {
2637       validatedFontId = item.index;
2638
2639       DALI_LOG_INFO(gLogFilter, Debug::General, "  validated font found, id : %d\n", validatedFontId);
2640       DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n");
2641       return true;
2642     }
2643   }
2644
2645   DALI_LOG_INFO(gLogFilter, Debug::General, "  validated font not found\n");
2646   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n");
2647   return false;
2648 }
2649
2650 bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription,
2651                                               FontList*&             fontList,
2652                                               CharacterSetList*&     characterSetList)
2653 {
2654   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n");
2655   DALI_LOG_INFO(gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str());
2656   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str());
2657   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width]);
2658   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2659   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2660   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  number of fallback font lists in the cache : %d\n", mFallbackCache.size());
2661
2662   fontList = nullptr;
2663
2664   for(const auto& item : mFallbackCache)
2665   {
2666     if(!fontDescription.family.empty() &&
2667        (fontDescription.family == item.fontDescription.family) &&
2668        (fontDescription.width == item.fontDescription.width) &&
2669        (fontDescription.weight == item.fontDescription.weight) &&
2670        (fontDescription.slant == item.fontDescription.slant))
2671     {
2672       fontList         = item.fallbackFonts;
2673       characterSetList = item.characterSets;
2674
2675       DALI_LOG_INFO(gLogFilter, Debug::General, "  fallback font list found.\n");
2676       DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n");
2677       return true;
2678     }
2679   }
2680
2681   DALI_LOG_INFO(gLogFilter, Debug::General, "  fallback font list not found.\n");
2682   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n");
2683   return false;
2684 }
2685
2686 bool FontClient::Plugin::FindFont(FontDescriptionId validatedFontId,
2687                                   PointSize26Dot6   requestedPointSize,
2688                                   FontId&           fontId)
2689 {
2690   DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n");
2691   DALI_LOG_INFO(gLogFilter, Debug::General, "    validatedFontId  : %d\n", validatedFontId);
2692   DALI_LOG_INFO(gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
2693
2694   fontId = 0u;
2695
2696   for(const auto& item : mFontDescriptionSizeCache)
2697   {
2698     if((validatedFontId == item.validatedFontId) &&
2699        (requestedPointSize == item.requestedPointSize))
2700     {
2701       fontId = item.fontId;
2702
2703       DALI_LOG_INFO(gLogFilter, Debug::General, "  font found, id : %d\n", fontId);
2704       DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2705       return true;
2706     }
2707   }
2708
2709   DALI_LOG_INFO(gLogFilter, Debug::General, "  font not found.\n");
2710   DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2711   return false;
2712 }
2713
2714 bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const
2715 {
2716   fontId = 0u;
2717
2718   for(const auto& item : mBitmapFontCache)
2719   {
2720     if(bitmapFont == item.font.name)
2721     {
2722       fontId = item.id + 1u;
2723       return true;
2724     }
2725   }
2726
2727   return false;
2728 }
2729
2730 bool FontClient::Plugin::IsScalable(const FontPath& path)
2731 {
2732   bool isScalable = false;
2733
2734   FT_Face ftFace;
2735   int     error = FT_New_Face(mFreeTypeLibrary,
2736                           path.c_str(),
2737                           0,
2738                           &ftFace);
2739   if(FT_Err_Ok != error)
2740   {
2741     DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
2742   }
2743   else
2744   {
2745     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2746   }
2747
2748   return isScalable;
2749 }
2750
2751 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription)
2752 {
2753   // Create a font pattern.
2754   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2755
2756   FcResult result = FcResultMatch;
2757
2758   // match the pattern
2759   FcPattern* match      = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2760   bool       isScalable = false;
2761
2762   if(match)
2763   {
2764     // Get the path to the font file name.
2765     FontPath path;
2766     GetFcString(match, FC_FILE, path);
2767     isScalable = IsScalable(path);
2768   }
2769   else
2770   {
2771     DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2772   }
2773
2774   // Destroys the created patterns.
2775   FcPatternDestroy(match);
2776   FcPatternDestroy(fontFamilyPattern);
2777
2778   return isScalable;
2779 }
2780
2781 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes)
2782 {
2783   // Empty the caller container
2784   sizes.Clear();
2785
2786   FT_Face ftFace;
2787   int     error = FT_New_Face(mFreeTypeLibrary,
2788                           path.c_str(),
2789                           0,
2790                           &ftFace);
2791   if(FT_Err_Ok != error)
2792   {
2793     DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
2794   }
2795
2796   // Fetch the number of fixed sizes available
2797   if(ftFace->num_fixed_sizes && ftFace->available_sizes)
2798   {
2799     for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
2800     {
2801       sizes.PushBack(ftFace->available_sizes[i].size);
2802     }
2803   }
2804 }
2805
2806 void FontClient::Plugin::GetFixedSizes(const FontDescription&   fontDescription,
2807                                        Vector<PointSize26Dot6>& sizes)
2808 {
2809   // Create a font pattern.
2810   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2811
2812   FcResult result = FcResultMatch;
2813
2814   // match the pattern
2815   FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2816
2817   if(match)
2818   {
2819     // Get the path to the font file name.
2820     FontPath path;
2821     GetFcString(match, FC_FILE, path);
2822     GetFixedSizes(path, sizes);
2823   }
2824   else
2825   {
2826     DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2827   }
2828
2829   // Destroys the created patterns.
2830   FcPatternDestroy(match);
2831   FcPatternDestroy(fontFamilyPattern);
2832 }
2833
2834 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
2835 {
2836   bool hasItalicStyle = false;
2837
2838   const FontId index = fontId - 1u;
2839
2840   if((fontId > 0) &&
2841      (index < mFontIdCache.Count()))
2842   {
2843     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2844
2845     if(FontDescription::FACE_FONT == fontIdCacheItem.type)
2846     {
2847       const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2848
2849       hasItalicStyle = 0u != (font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC);
2850     }
2851   }
2852   else
2853   {
2854     DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
2855   }
2856
2857   return hasItalicStyle;
2858 }
2859
2860 void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path)
2861 {
2862   FontDescription description;
2863   description.path   = path;
2864   description.family = std::move(FontFamily(ftFace->family_name));
2865   description.weight = FontWeight::NONE;
2866   description.width  = FontWidth::NONE;
2867   description.slant  = FontSlant::NONE;
2868
2869   // Note FreeType doesn't give too much info to build a proper font style.
2870   if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
2871   {
2872     description.slant = FontSlant::ITALIC;
2873   }
2874   if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
2875   {
2876     description.weight = FontWeight::BOLD;
2877   }
2878
2879   FontDescriptionId validatedFontId = 0u;
2880   if(!FindValidatedFont(description,
2881                         validatedFontId))
2882   {
2883     FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2884
2885     FcResult   result = FcResultMatch;
2886     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2887
2888     FcCharSet* characterSet = nullptr;
2889     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2890
2891     const FontId fontFaceId                  = id - 1u;
2892     mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
2893
2894     // Destroys the created patterns.
2895     FcPatternDestroy(match);
2896     FcPatternDestroy(pattern);
2897
2898     // Add the path to the cache.
2899     description.type = FontDescription::FACE_FONT;
2900     mFontDescriptionCache.push_back(description);
2901
2902     // Set the index to the vector of paths to font file names.
2903     validatedFontId = mFontDescriptionCache.size();
2904
2905     // Increase the reference counter and add the character set to the cache.
2906     mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
2907
2908     // Cache the index and the font's description.
2909     mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
2910                                                                      validatedFontId)));
2911
2912     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2913     mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
2914                                                                      requestedPointSize,
2915                                                                      fontFaceId));
2916   }
2917 }
2918
2919 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription(const FontDescription& description)
2920 {
2921   FcCharSet* characterSet = nullptr;
2922
2923   FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2924
2925   if(nullptr != pattern)
2926   {
2927     FcResult   result = FcResultMatch;
2928     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2929
2930     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2931
2932     // Destroys the created patterns.
2933     FcPatternDestroy(match);
2934     FcPatternDestroy(pattern);
2935   }
2936
2937   return characterSet;
2938 }
2939
2940 void FontClient::Plugin::ClearFallbackCache(std::vector<FallbackCacheItem>& fallbackCache)
2941 {
2942   for(auto& item : fallbackCache)
2943   {
2944     if(nullptr != item.fallbackFonts)
2945     {
2946       delete item.fallbackFonts;
2947     }
2948
2949     if(nullptr != item.characterSets)
2950     {
2951       // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2952       DestroyCharacterSets(*item.characterSets);
2953       delete item.characterSets;
2954     }
2955   }
2956 }
2957
2958 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2959 {
2960   for(auto& item : mFontFaceCache)
2961   {
2962     FcCharSetDestroy(item.mCharacterSet);
2963     item.mCharacterSet = nullptr;
2964   }
2965 }
2966
2967 } // namespace Internal
2968
2969 } // namespace TextAbstraction
2970
2971 } // namespace Dali