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