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