[Tizen] Fix small font size text layout issue
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-face-cache-item.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // EXTERNAL HEADERS
18 #include <dali/integration-api/debug.h>
19
20 // INTERNAL HEADERS
21 #include <dali/devel-api/adaptor-framework/environment-variable.h>
22 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
23 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
24
25 #if defined(DEBUG_ENABLED)
26 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
27 #endif
28
29 namespace Dali::TextAbstraction::Internal
30 {
31 namespace
32 {
33 const float FROM_266        = 1.0f / 64.0f;
34 const float POINTS_PER_INCH = 72.f;
35
36 /**
37  * @brief Maximum rate of bitmap glyph resize.
38  * If scale factor is bigger than this value, we will not cache resized glyph.
39  * Else, resize bitmap glyph itself and cache it.
40  */
41 constexpr float MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE = 1.5f;
42
43 /**
44  * @brief Behavior about cache the rendered glyph cache.
45  */
46 constexpr bool DEFAULT_ENABLE_CACHE_RENDERED_GLYPH = true;
47 constexpr auto ENABLE_CACHE_RENDERED_GLYPH_ENV     = "DALI_ENABLE_CACHE_RENDERED_GLYPH";
48
49 /**
50  * @brief Get whether we allow to cache rendered glyph from environment.
51  * If not settuped, default as true.
52  * @note This value fixed when we call it first time.
53  * @return True if we allow to cache rendered glyph.
54  */
55 inline bool EnableCacheRenderedGlyph()
56 {
57   using Dali::EnvironmentVariable::GetEnvironmentVariable;
58   static auto numberString = GetEnvironmentVariable(ENABLE_CACHE_RENDERED_GLYPH_ENV);
59   static auto number       = numberString ? (std::strtoul(numberString, nullptr, 10) ? true : false) : DEFAULT_ENABLE_CACHE_RENDERED_GLYPH;
60   return number;
61 }
62
63 /**
64  * @brief Policy about compress the cached rendered glyph.
65  * It will be used only if CacheRenderedGlyph is enabled
66  */
67 constexpr auto DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY =
68 #if !(defined(DALI_PROFILE_UBUNTU) || defined(ANDROID) || defined(WIN32) || defined(__APPLE__))
69   GlyphCacheManager::CompressionPolicyType::MEMORY; // If tizen target
70 #else
71   GlyphCacheManager::CompressionPolicyType::SPEED; // If not tizen target
72 #endif
73 constexpr auto RENDERED_GLYPH_COMPRESS_POLICY_ENV = "DALI_RENDERED_GLYPH_COMPRESS_POLICY";
74
75 /**
76  * @brief Get whether we allow to cache rendered glyph from environment.
77  * If not settuped, default value used, as defined above.
78  * @note This value fixed when we call it first time.
79  * @return SPEED if value start with 's' or 'S'. MEMORY if value start with 'm' or 'M'. otherwise, use default
80  */
81 inline GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy()
82 {
83   using Dali::EnvironmentVariable::GetEnvironmentVariable;
84   static auto policyString = GetEnvironmentVariable(RENDERED_GLYPH_COMPRESS_POLICY_ENV);
85
86   static auto policy = policyString ? policyString[0] == 's' || policyString[0] == 'S' ? GlyphCacheManager::CompressionPolicyType::SPEED
87                                                                                        : policyString[0] == 'm' || policyString[0] == 'M' ? GlyphCacheManager::CompressionPolicyType::MEMORY
88                                                                                                                                           : DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY
89                                     : DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY;
90   return policy;
91 }
92 } // namespace
93
94 FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
95                                      FT_Face            ftFace,
96                                      GlyphCacheManager* glyphCacheManager,
97                                      const FontPath&    path,
98                                      PointSize26Dot6    requestedPointSize,
99                                      FaceIndex          face,
100                                      const FontMetrics& metrics)
101 : mFreeTypeLibrary(freeTypeLibrary),
102   mFreeTypeFace(ftFace),
103   mGlyphCacheManager(glyphCacheManager),
104   mHarfBuzzProxyFont(),
105   mPath(path),
106   mRequestedPointSize(requestedPointSize),
107   mFaceIndex(face),
108   mMetrics(metrics),
109   mCharacterSet(nullptr),
110   mFixedSizeIndex(0),
111   mFixedWidthPixels(0.f),
112   mFixedHeightPixels(0.f),
113   mVectorFontId(0u),
114   mFontId(0u),
115   mIsFixedSizeBitmap(false),
116   mHasColorTables(false)
117 {
118 }
119
120 FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
121                                      FT_Face            ftFace,
122                                      GlyphCacheManager* glyphCacheManager,
123                                      const FontPath&    path,
124                                      PointSize26Dot6    requestedPointSize,
125                                      FaceIndex          face,
126                                      const FontMetrics& metrics,
127                                      int                fixedSizeIndex,
128                                      float              fixedWidth,
129                                      float              fixedHeight,
130                                      bool               hasColorTables)
131 : mFreeTypeLibrary(freeTypeLibrary),
132   mFreeTypeFace(ftFace),
133   mGlyphCacheManager(glyphCacheManager),
134   mHarfBuzzProxyFont(),
135   mPath(path),
136   mRequestedPointSize(requestedPointSize),
137   mFaceIndex(face),
138   mMetrics(metrics),
139   mCharacterSet(nullptr),
140   mFixedSizeIndex(fixedSizeIndex),
141   mFixedWidthPixels(fixedWidth),
142   mFixedHeightPixels(fixedHeight),
143   mVectorFontId(0u),
144   mFontId(0u),
145   mIsFixedSizeBitmap(true),
146   mHasColorTables(hasColorTables)
147 {
148 }
149
150 // Move constructor. font client plugin container may call this.
151 // Note that we make nullptr of some reference sensitive values here.
152 FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs)
153 : mFreeTypeLibrary(rhs.mFreeTypeLibrary)
154 {
155   mFreeTypeFace       = rhs.mFreeTypeFace;
156   mGlyphCacheManager  = rhs.mGlyphCacheManager;
157   mHarfBuzzProxyFont  = std::move(rhs.mHarfBuzzProxyFont);
158   mPath               = std::move(rhs.mPath);
159   mRequestedPointSize = rhs.mRequestedPointSize;
160   mFaceIndex          = rhs.mFaceIndex;
161   mMetrics            = rhs.mMetrics;
162   mCharacterSet       = rhs.mCharacterSet;
163   mFixedSizeIndex     = rhs.mFixedSizeIndex;
164   mFixedWidthPixels   = rhs.mFixedWidthPixels;
165   mFixedHeightPixels  = rhs.mFixedWidthPixels;
166   mVectorFontId       = rhs.mVectorFontId;
167   mFontId             = rhs.mFontId;
168   mIsFixedSizeBitmap  = rhs.mIsFixedSizeBitmap;
169   mHasColorTables     = rhs.mHasColorTables;
170
171   rhs.mFreeTypeFace      = nullptr;
172   rhs.mGlyphCacheManager = nullptr;
173 }
174
175 FontFaceCacheItem::~FontFaceCacheItem()
176 {
177   // delete cached glyph informations before free face.
178   if(mGlyphCacheManager)
179   {
180     mGlyphCacheManager->RemoveGlyphFromFace(mFreeTypeFace);
181   }
182
183   // delete harfbuzz proxy font before free face.
184   if(mHarfBuzzProxyFont)
185   {
186     mHarfBuzzProxyFont.reset();
187   }
188
189   // Free face.
190   if(mFreeTypeFace)
191   {
192     FT_Done_Face(mFreeTypeFace);
193   }
194 }
195
196 void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const
197 {
198   metrics = mMetrics;
199
200   // Adjust the metrics if the fixed-size font should be down-scaled
201   if(mIsFixedSizeBitmap)
202   {
203     const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
204
205     if(desiredFixedSize > 0.f)
206     {
207       const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
208
209       metrics.ascender           = round(metrics.ascender * scaleFactor);
210       metrics.descender          = round(metrics.descender * scaleFactor);
211       metrics.height             = round(metrics.height * scaleFactor);
212       metrics.underlinePosition  = metrics.underlinePosition * scaleFactor;
213       metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
214     }
215   }
216 }
217
218 bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVertical, bool horizontal) const
219 {
220   bool success(true);
221
222   GlyphCacheManager::GlyphCacheData glyphData;
223   FT_Error                          error;
224
225 #ifdef FREETYPE_BITMAP_SUPPORT
226   // Check to see if we should be loading a Fixed Size bitmap?
227   if(mIsFixedSizeBitmap)
228   {
229     FT_Select_Size(mFreeTypeFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
230     mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, glyphData, error);
231
232     if(FT_Err_Ok == error)
233     {
234       glyphInfo.width    = mFixedWidthPixels;
235       glyphInfo.height   = mFixedHeightPixels;
236       glyphInfo.advance  = mFixedWidthPixels;
237       glyphInfo.xBearing = 0.0f;
238
239       const auto& metrics = glyphData.mGlyphMetrics;
240
241       if(horizontal)
242       {
243         glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
244       }
245       else
246       {
247         glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
248       }
249
250       // Adjust the metrics if the fixed-size font should be down-scaled
251       const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
252
253       if(desiredFixedSize > 0.f)
254       {
255         const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
256         glyphInfo.width         = round(glyphInfo.width * scaleFactor);
257         glyphInfo.height        = round(glyphInfo.height * scaleFactor);
258         glyphInfo.advance       = round(glyphInfo.advance * scaleFactor);
259         glyphInfo.xBearing      = round(glyphInfo.xBearing * scaleFactor);
260         glyphInfo.yBearing      = round(glyphInfo.yBearing * scaleFactor);
261
262         glyphInfo.scaleFactor = scaleFactor;
263
264         if(scaleFactor < MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE)
265         {
266           // Resize bitmap glyph and cache it due to the performance issue.
267           // If scaleFactor is too big, cached bitmap may hold too big memory.
268           // So, we only hold small enough case.
269
270           // TODO : If dpiVertical value changed, this resize feature will be break down.
271           // Otherwise, this glyph will be resized only one times.
272           mGlyphCacheManager->ResizeBitmapGlyph(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast<uint32_t>(glyphInfo.width), static_cast<uint32_t>(glyphInfo.height));
273         }
274       }
275     }
276     else
277     {
278       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
279       success = false;
280     }
281   }
282   else
283 #endif
284   {
285     // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
286     // i.e. with the SNum-3R font.
287     // @todo: add an option to use the FT_LOAD_DEFAULT if required?
288     mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, error);
289
290     // Keep the width of the glyph before doing the software emboldening.
291     // It will be used to calculate a scale factor to be applied to the
292     // advance as Harfbuzz doesn't apply any SW emboldening to calculate
293     // the advance of the glyph.
294
295     if(FT_Err_Ok == error)
296     {
297       const auto& metrics = glyphData.mGlyphMetrics;
298
299       glyphInfo.width  = static_cast<float>(metrics.width) * FROM_266;
300       glyphInfo.height = static_cast<float>(metrics.height) * FROM_266;
301       if(horizontal)
302       {
303         glyphInfo.xBearing += static_cast<float>(metrics.horiBearingX) * FROM_266;
304         glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
305       }
306       else
307       {
308         glyphInfo.xBearing += static_cast<float>(metrics.vertBearingX) * FROM_266;
309         glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
310       }
311       glyphInfo.advance = round(glyphInfo.advance);
312
313       const bool isEmboldeningRequired = glyphInfo.isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
314       if(isEmboldeningRequired)
315       {
316         // Get dummy glyph data without embolden.
317         GlyphCacheManager::GlyphCacheData dummyData;
318         if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error))
319         {
320           // If the glyph is emboldened by software, the advance is multiplied by a
321           // scale factor to make it slightly bigger.
322           const float width = static_cast<float>(dummyData.mGlyphMetrics.width) * FROM_266;
323           if(!EqualsZero(width))
324           {
325             glyphInfo.advance *= (glyphInfo.width / width);
326           }
327         }
328       }
329
330       // Use the bounding box of the bitmap to correct the metrics.
331       // For some fonts i.e the SNum-3R the metrics need to be corrected,
332       // otherwise the glyphs 'dance' up and down depending on the
333       // font's point size.
334       FT_Glyph glyph = glyphData.mGlyph;
335
336       FT_BBox bbox;
337       FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
338
339       const float descender = glyphInfo.height - glyphInfo.yBearing;
340       glyphInfo.height      = (bbox.yMax - bbox.yMin) * FROM_266;
341       glyphInfo.yBearing    = glyphInfo.height - round(descender);
342     }
343     else
344     {
345       success = false;
346     }
347   }
348   return success;
349 }
350
351 /**
352  * @brief Create a bitmap representation of a glyph from a face font
353  *
354  * @param[in]  glyphIndex        The index of a glyph within the specified font.
355  * @param[out] data              The bitmap data.
356  * @param[in]  outlineWidth      The width of the glyph outline in pixels.
357  * @param[in]  isItalicRequired  Whether the glyph requires italic style.
358  * @param[in]  isBoldRequired    Whether the glyph requires bold style.
359  */
360 void FontFaceCacheItem::CreateBitmap(
361   GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
362 {
363   GlyphCacheManager::GlyphCacheData glyphData;
364   FT_Error                          error;
365   FT_Int32                          loadFlag;
366   // For the software italics.
367   bool isShearRequired = false;
368
369 #ifdef FREETYPE_BITMAP_SUPPORT
370   // Check to see if this is fixed size bitmap
371   if(mIsFixedSizeBitmap)
372   {
373     loadFlag = FT_LOAD_COLOR;
374   }
375   else
376 #endif
377   {
378     // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
379     // i.e. with the SNum-3R font.
380     // @todo: add an option to use the FT_LOAD_DEFAULT if required?
381     loadFlag = FT_LOAD_NO_AUTOHINT;
382   }
383   mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, glyphData, error);
384
385   if(FT_Err_Ok == error)
386   {
387     if(isItalicRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_ITALIC))
388     {
389       // Will do the software italic.
390       isShearRequired = true;
391     }
392
393     if(!glyphData.mIsBitmap)
394     {
395       // Convert to bitmap if necessary
396       FT_Glyph glyph = glyphData.mGlyph;
397
398       DALI_ASSERT_ALWAYS(glyph->format != FT_GLYPH_FORMAT_BITMAP && "Something wrong with cashing. Some bitmap glyph cached failed.");
399
400       int  offsetX = 0, offsetY = 0;
401       bool isOutlineGlyph       = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
402       bool isStrokeGlyphSuccess = false;
403
404       // Create a bitmap for the outline
405       if(isOutlineGlyph)
406       {
407         // Retrieve the horizontal and vertical distance from the current pen position to the
408         // left and top border of the glyph bitmap for a normal glyph before applying the outline.
409         if(FT_Err_Ok == error)
410         {
411           // Copy new glyph, and keep original cached glyph.
412           error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0);
413           if(FT_Err_Ok == error)
414           {
415             FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
416
417             offsetX = bitmapGlyph->left;
418             offsetY = bitmapGlyph->top;
419
420             // Copied FT_Glyph object must be released with FT_Done_Glyph
421             FT_Done_Glyph(glyph);
422           }
423
424           // Replace as original glyph
425           glyph = glyphData.mGlyph;
426         }
427
428         // Now apply the outline
429
430         // Set up a stroker
431         FT_Stroker stroker;
432         error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
433
434         if(FT_Err_Ok == error)
435         {
436           // Copy glyph pointer for release memory.
437           FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
438           error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 0);
439
440           if(FT_Err_Ok == error)
441           {
442             FT_Stroker_Done(stroker);
443             isStrokeGlyphSuccess = true;
444           }
445           else
446           {
447             DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
448           }
449         }
450         else
451         {
452           DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
453         }
454       }
455
456       const bool ableUseCachedRenderedGlyph = EnableCacheRenderedGlyph() && !isOutlineGlyph && !isShearRequired;
457
458       // If we cache rendered glyph, and if we can use it, use cached thing first.
459       if(ableUseCachedRenderedGlyph && glyphData.mRenderedBuffer)
460       {
461         data.buffer          = glyphData.mRenderedBuffer->buffer;
462         data.width           = glyphData.mRenderedBuffer->width;
463         data.height          = glyphData.mRenderedBuffer->height;
464         data.format          = glyphData.mRenderedBuffer->format;
465         data.compressionType = glyphData.mRenderedBuffer->compressionType;
466         data.isBufferOwned   = false;
467       }
468       else
469       {
470         // Copy new glyph, and keep original cached glyph.
471         // If we already copy new glyph by stroke, just re-use that.
472         error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, isStrokeGlyphSuccess);
473         if(FT_Err_Ok == error)
474         {
475           FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
476
477           if(isOutlineGlyph)
478           {
479             // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
480             data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
481             data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
482           }
483
484           // If we can cache this bitmapGlyph, store it.
485           // Note : We will call this API once per each glyph.
486           if(ableUseCachedRenderedGlyph)
487           {
488             mGlyphCacheManager->CacheRenderedGlyphBuffer(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
489
490             GlyphCacheManager::GlyphCacheData dummyData;
491             mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, dummyData, error);
492
493             if(DALI_LIKELY(FT_Err_Ok == error && dummyData.mRenderedBuffer))
494             {
495               data.buffer          = dummyData.mRenderedBuffer->buffer;
496               data.width           = dummyData.mRenderedBuffer->width;
497               data.height          = dummyData.mRenderedBuffer->height;
498               data.format          = dummyData.mRenderedBuffer->format;
499               data.compressionType = dummyData.mRenderedBuffer->compressionType;
500               data.isBufferOwned   = false;
501             }
502             else
503             {
504               // Something problem during cache or get rendered glyph buffer.
505               // Move bitmap buffer into data.buffer
506               ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
507             }
508           }
509           else
510           {
511             // Move bitmap buffer into data.buffer
512             ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
513           }
514
515           // Copied FT_Glyph object must be released with FT_Done_Glyph
516           FT_Done_Glyph(glyph);
517         }
518         else
519         {
520           DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
521         }
522       }
523     }
524     else
525     {
526       ConvertBitmap(data, *glyphData.mBitmap, isShearRequired);
527     }
528
529     data.isColorEmoji = mIsFixedSizeBitmap;
530   }
531   else
532   {
533     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
534   }
535 }
536
537 bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
538 {
539   FT_Error error = -1;
540
541 #ifdef FREETYPE_BITMAP_SUPPORT
542   // Check to see if this is fixed size bitmap
543   if(mHasColorTables)
544   {
545     GlyphCacheManager::GlyphCacheData dummyData;
546     mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR, false, dummyData, error);
547   }
548 #endif
549   return FT_Err_Ok == error;
550 }
551
552 /**
553  * Check if the character is supported by this font
554  * @param[in] character The character to test
555  */
556 bool FontFaceCacheItem::IsCharacterSupported(Character character)
557 {
558   if(nullptr == mCharacterSet)
559   {
560     // Create again the character set.
561     // It can be null if the ResetSystemDefaults() method has been called.
562
563     FontDescription description;
564     description.path   = mPath;
565     description.family = std::move(FontFamily(mFreeTypeFace->family_name));
566     description.weight = FontWeight::NONE;
567     description.width  = FontWidth::NONE;
568     description.slant  = FontSlant::NONE;
569
570     // Note FreeType doesn't give too much info to build a proper font style.
571     if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
572     {
573       description.slant = FontSlant::ITALIC;
574     }
575     if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
576     {
577       description.weight = FontWeight::BOLD;
578     }
579
580     mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
581   }
582
583   return FcCharSetHasChar(mCharacterSet, character);
584 }
585
586 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character) const
587 {
588   return FT_Get_Char_Index(mFreeTypeFace, character);
589 }
590
591 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character, Character variantSelector) const
592 {
593   return FT_Face_GetCharVariantIndex(mFreeTypeFace, character, variantSelector);
594 }
595
596 HarfBuzzFontHandle FontFaceCacheItem::GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi)
597 {
598   // Create new harfbuzz font only first time or DPI changed.
599   if(DALI_UNLIKELY(!mHarfBuzzProxyFont || mHarfBuzzProxyFont->mHorizontalDpi != horizontalDpi || mHarfBuzzProxyFont->mVerticalDpi != verticalDpi))
600   {
601     mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager));
602   }
603   return mHarfBuzzProxyFont->GetHarfBuzzFont();
604 }
605
606 } // namespace Dali::TextAbstraction::Internal