DALi Version 2.3.21
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-face-cache-item.cpp
1 /*
2  * Copyright (c) 2023 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::GlyphCacheDataPtr glyphDataPtr;
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, glyphDataPtr, error);
231
232     if(FT_Err_Ok == error)
233     {
234       GlyphCacheManager::GlyphCacheData& glyphData = *glyphDataPtr.get();
235
236       glyphInfo.width    = mFixedWidthPixels;
237       glyphInfo.height   = mFixedHeightPixels;
238       glyphInfo.advance  = mFixedWidthPixels;
239       glyphInfo.xBearing = 0.0f;
240
241       const auto& metrics = glyphData.mGlyphMetrics;
242
243       if(horizontal)
244       {
245         glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
246       }
247       else
248       {
249         glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
250       }
251
252       // Adjust the metrics if the fixed-size font should be down-scaled
253       const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
254
255       if(desiredFixedSize > 0.f)
256       {
257         const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
258         glyphInfo.width         = round(glyphInfo.width * scaleFactor);
259         glyphInfo.height        = round(glyphInfo.height * scaleFactor);
260         glyphInfo.advance       = round(glyphInfo.advance * scaleFactor);
261         glyphInfo.xBearing      = round(glyphInfo.xBearing * scaleFactor);
262         glyphInfo.yBearing      = round(glyphInfo.yBearing * scaleFactor);
263
264         glyphInfo.scaleFactor = scaleFactor;
265
266         if(scaleFactor < MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE)
267         {
268           // Resize bitmap glyph and cache it due to the performance issue.
269           // If scaleFactor is too big, cached bitmap may hold too big memory.
270           // So, we only hold small enough case.
271
272           // TODO : If dpiVertical value changed, this resize feature will be break down.
273           // Otherwise, this glyph will be resized only one times.
274           mGlyphCacheManager->ResizeBitmapGlyph(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast<uint32_t>(glyphInfo.width), static_cast<uint32_t>(glyphInfo.height));
275         }
276       }
277     }
278     else
279     {
280       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
281       success = false;
282     }
283   }
284   else
285 #endif
286   {
287     // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
288     // i.e. with the SNum-3R font.
289     // @todo: add an option to use the FT_LOAD_DEFAULT if required?
290     mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphDataPtr, error);
291
292     // Keep the width of the glyph before doing the software emboldening.
293     // It will be used to calculate a scale factor to be applied to the
294     // advance as Harfbuzz doesn't apply any SW emboldening to calculate
295     // the advance of the glyph.
296
297     if(FT_Err_Ok == error)
298     {
299       GlyphCacheManager::GlyphCacheData& glyphData = *glyphDataPtr.get();
300
301       const auto& metrics = glyphData.mGlyphMetrics;
302
303       glyphInfo.width  = static_cast<float>(metrics.width) * FROM_266;
304       glyphInfo.height = static_cast<float>(metrics.height) * FROM_266;
305       if(horizontal)
306       {
307         glyphInfo.xBearing += static_cast<float>(metrics.horiBearingX) * FROM_266;
308         glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
309       }
310       else
311       {
312         glyphInfo.xBearing += static_cast<float>(metrics.vertBearingX) * FROM_266;
313         glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
314       }
315       glyphInfo.advance = round(glyphInfo.advance);
316
317       const bool isEmboldeningRequired = glyphInfo.isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
318       if(isEmboldeningRequired)
319       {
320         // Get dummy glyph data without embolden.
321         GlyphCacheManager::GlyphCacheDataPtr dummyDataPtr;
322         if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyDataPtr, error))
323         {
324           // If the glyph is emboldened by software, the advance is multiplied by a
325           // scale factor to make it slightly bigger.
326           const float width = static_cast<float>(dummyDataPtr->mGlyphMetrics.width) * FROM_266;
327           if(!EqualsZero(width))
328           {
329             glyphInfo.advance *= (glyphInfo.width / width);
330           }
331         }
332       }
333
334       // Use the bounding box of the bitmap to correct the metrics.
335       // For some fonts i.e the SNum-3R the metrics need to be corrected,
336       // otherwise the glyphs 'dance' up and down depending on the
337       // font's point size.
338       FT_Glyph glyph = glyphData.mGlyph;
339
340       FT_BBox bbox;
341       FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
342
343       const float descender = glyphInfo.height - glyphInfo.yBearing;
344       glyphInfo.height      = (bbox.yMax - bbox.yMin) * FROM_266;
345       glyphInfo.yBearing    = glyphInfo.height - round(descender);
346     }
347     else
348     {
349       success = false;
350     }
351   }
352   return success;
353 }
354
355 /**
356  * @brief Create a bitmap representation of a glyph from a face font
357  *
358  * @param[in]  glyphIndex        The index of a glyph within the specified font.
359  * @param[out] data              The bitmap data.
360  * @param[in]  outlineWidth      The width of the glyph outline in pixels.
361  * @param[in]  isItalicRequired  Whether the glyph requires italic style.
362  * @param[in]  isBoldRequired    Whether the glyph requires bold style.
363  */
364 void FontFaceCacheItem::CreateBitmap(
365   GlyphIndex glyphIndex, Dali::TextAbstraction::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
366 {
367   GlyphCacheManager::GlyphCacheDataPtr glyphDataPtr;
368   FT_Error                             error;
369   FT_Int32                             loadFlag;
370   // For the software italics.
371   bool isShearRequired = false;
372
373 #ifdef FREETYPE_BITMAP_SUPPORT
374   // Check to see if this is fixed size bitmap
375   if(mIsFixedSizeBitmap)
376   {
377     loadFlag = FT_LOAD_COLOR;
378   }
379   else
380 #endif
381   {
382     // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
383     // i.e. with the SNum-3R font.
384     // @todo: add an option to use the FT_LOAD_DEFAULT if required?
385     loadFlag = FT_LOAD_NO_AUTOHINT;
386   }
387   mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, glyphDataPtr, error);
388
389   if(FT_Err_Ok == error)
390   {
391     GlyphCacheManager::GlyphCacheData& glyphData = *glyphDataPtr.get();
392
393     if(isItalicRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_ITALIC))
394     {
395       // Will do the software italic.
396       isShearRequired = true;
397     }
398
399     if(!glyphData.mIsBitmap)
400     {
401       // Convert to bitmap if necessary
402       FT_Glyph glyph = glyphData.mGlyph;
403
404       DALI_ASSERT_ALWAYS(glyph->format != FT_GLYPH_FORMAT_BITMAP && "Something wrong with cashing. Some bitmap glyph cached failed.");
405
406       int  offsetX = 0, offsetY = 0;
407       bool isOutlineGlyph       = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
408       bool isStrokeGlyphSuccess = false;
409
410       // Create a bitmap for the outline
411       if(isOutlineGlyph)
412       {
413         // Retrieve the horizontal and vertical distance from the current pen position to the
414         // left and top border of the glyph bitmap for a normal glyph before applying the outline.
415         if(FT_Err_Ok == error)
416         {
417           // Copy new glyph, and keep original cached glyph.
418           error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0);
419           if(FT_Err_Ok == error)
420           {
421             FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
422
423             offsetX = bitmapGlyph->left;
424             offsetY = bitmapGlyph->top;
425
426             // Copied FT_Glyph object must be released with FT_Done_Glyph
427             FT_Done_Glyph(glyph);
428           }
429
430           // Replace as original glyph
431           glyph = glyphData.mGlyph;
432         }
433
434         // Now apply the outline
435
436         // Set up a stroker
437         FT_Stroker stroker;
438         error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
439
440         if(FT_Err_Ok == error)
441         {
442           // Copy glyph pointer for release memory.
443           FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
444           error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 0);
445
446           if(FT_Err_Ok == error)
447           {
448             FT_Stroker_Done(stroker);
449             isStrokeGlyphSuccess = true;
450           }
451           else
452           {
453             DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
454           }
455         }
456         else
457         {
458           DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
459         }
460       }
461
462       const bool ableUseCachedRenderedGlyph = EnableCacheRenderedGlyph() && !isOutlineGlyph && !isShearRequired;
463
464       // If we cache rendered glyph, and if we can use it, use cached thing first.
465       if(ableUseCachedRenderedGlyph && glyphData.mRenderedBuffer)
466       {
467         data.buffer          = glyphData.mRenderedBuffer->buffer;
468         data.width           = glyphData.mRenderedBuffer->width;
469         data.height          = glyphData.mRenderedBuffer->height;
470         data.format          = glyphData.mRenderedBuffer->format;
471         data.compressionType = glyphData.mRenderedBuffer->compressionType;
472         data.isBufferOwned   = false;
473       }
474       else
475       {
476         // Copy new glyph, and keep original cached glyph.
477         // If we already copy new glyph by stroke, just re-use that.
478         error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, isStrokeGlyphSuccess);
479         if(FT_Err_Ok == error)
480         {
481           FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
482
483           if(isOutlineGlyph)
484           {
485             // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
486             data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
487             data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
488           }
489
490           // If we can cache this bitmapGlyph, store it.
491           // Note : We will call this API once per each glyph.
492           if(ableUseCachedRenderedGlyph)
493           {
494             mGlyphCacheManager->CacheRenderedGlyphBuffer(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
495
496             GlyphCacheManager::GlyphCacheDataPtr dummyDataPtr;
497             mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, dummyDataPtr, error);
498
499             if(DALI_LIKELY(FT_Err_Ok == error && dummyDataPtr->mRenderedBuffer))
500             {
501               data.buffer          = dummyDataPtr->mRenderedBuffer->buffer;
502               data.width           = dummyDataPtr->mRenderedBuffer->width;
503               data.height          = dummyDataPtr->mRenderedBuffer->height;
504               data.format          = dummyDataPtr->mRenderedBuffer->format;
505               data.compressionType = dummyDataPtr->mRenderedBuffer->compressionType;
506               data.isBufferOwned   = false;
507             }
508             else
509             {
510               // Something problem during cache or get rendered glyph buffer.
511               // Move bitmap buffer into data.buffer
512               ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
513             }
514           }
515           else
516           {
517             // Move bitmap buffer into data.buffer
518             ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
519           }
520
521           // Copied FT_Glyph object must be released with FT_Done_Glyph
522           FT_Done_Glyph(glyph);
523         }
524         else
525         {
526           DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
527         }
528       }
529     }
530     else
531     {
532       ConvertBitmap(data, *glyphData.mBitmap, isShearRequired);
533     }
534
535     data.isColorEmoji = mIsFixedSizeBitmap;
536   }
537   else
538   {
539     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
540   }
541 }
542
543 bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
544 {
545   FT_Error error = -1;
546
547 #ifdef FREETYPE_BITMAP_SUPPORT
548   // Check to see if this is fixed size bitmap
549   if(mHasColorTables)
550   {
551     GlyphCacheManager::GlyphCacheDataPtr dummyDataPtr;
552     mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR, false, dummyDataPtr, error);
553   }
554 #endif
555   return FT_Err_Ok == error;
556 }
557
558 /**
559  * Check if the character is supported by this font
560  * @param[in] character The character to test
561  */
562 bool FontFaceCacheItem::IsCharacterSupported(Character character)
563 {
564   if(nullptr == mCharacterSet)
565   {
566     // Create again the character set.
567     // It can be null if the ResetSystemDefaults() method has been called.
568
569     FontDescription description;
570     description.path   = mPath;
571     description.family = std::move(FontFamily(mFreeTypeFace->family_name));
572     description.weight = FontWeight::NONE;
573     description.width  = FontWidth::NONE;
574     description.slant  = FontSlant::NONE;
575
576     // Note FreeType doesn't give too much info to build a proper font style.
577     if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
578     {
579       description.slant = FontSlant::ITALIC;
580     }
581     if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
582     {
583       description.weight = FontWeight::BOLD;
584     }
585
586     mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
587   }
588
589   return FcCharSetHasChar(mCharacterSet, character);
590 }
591
592 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character) const
593 {
594   return FT_Get_Char_Index(mFreeTypeFace, character);
595 }
596
597 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character, Character variantSelector) const
598 {
599   return FT_Face_GetCharVariantIndex(mFreeTypeFace, character, variantSelector);
600 }
601
602 HarfBuzzFontHandle FontFaceCacheItem::GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi)
603 {
604   // Create new harfbuzz font only first time or DPI changed.
605   if(DALI_UNLIKELY(!mHarfBuzzProxyFont || mHarfBuzzProxyFont->mHorizontalDpi != horizontalDpi || mHarfBuzzProxyFont->mVerticalDpi != verticalDpi))
606   {
607     mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager));
608   }
609   return mHarfBuzzProxyFont->GetHarfBuzzFont();
610 }
611
612 } // namespace Dali::TextAbstraction::Internal