2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
22 #include <dali/devel-api/text-abstraction/font-list.h>
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/plugin/bitmap-font-cache-item.h>
30 #include <dali/internal/text/text-abstraction/plugin/embedded-item.h>
31 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
32 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
33 #include <dali/public-api/common/dali-vector.h>
34 #include <dali/public-api/common/vector-wrapper.h>
37 #include <fontconfig/fontconfig.h>
41 #if defined(DEBUG_ENABLED)
43 // Note, to turn on trace and verbose logging, use "export LOG_FONT_CLIENT=3,true"
44 // Or re-define the following filter using Verbose,true instead of NoLogging,false,
45 // Or, add DALI_LOG_FILTER_ENABLE_TRACE(gFontClientLogFilter) in the code below.
47 Dali::Integration::Log::Filter* gFontClientLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
49 #define FONT_LOG_DESCRIPTION(fontDescription, prefix) \
50 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix " description; family : [%s]\n", fontDescription.family.c_str()); \
51 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, \
55 " slant : [%s]\n\n", \
56 fontDescription.path.c_str(), \
57 FontWidth::Name[fontDescription.width], \
58 FontWeight::Name[fontDescription.weight], \
59 FontSlant::Name[fontDescription.slant])
61 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
62 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, \
64 " requestedPointSize : %d\n" \
65 " preferColor : %s\n", \
68 (preferColor ? "true" : "false"))
72 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
73 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
80 * Conversion from Fractional26.6 to float
82 const float FROM_266 = 1.0f / 64.0f;
83 const float POINTS_PER_INCH = 72.f;
85 const std::string DEFAULT_FONT_FAMILY_NAME("Tizen");
87 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
94 namespace Dali::TextAbstraction::Internal
97 * @brief Free the resources allocated by the FcCharSet objects.
99 * @param[in] characterSets The vector of character sets.
101 void DestroyCharacterSets(CharacterSetList& characterSets)
103 for(auto& item : characterSets)
107 FcCharSetDestroy(item);
113 * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
115 * @param[in/out] ftFace Face type object.
116 * @param[in] horizontalDpi The horizontal dpi.
117 * @param[in] verticalDpi The vertical dpi.
118 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
119 * @param[in] requestedPointSize The requested point-size.
120 * @return whether the ftFace's block can fit into atlas
122 bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
126 error = FT_Set_Char_Size(ftFace,
132 if(error == FT_Err_Ok)
134 //Check width and height of block for requestedPointSize
135 //If the width or height is greater than the maximum-size then decrement by one unit of point-size.
136 if(static_cast<float>(ftFace->size->metrics.height) * FROM_266 <= maxSizeFitInAtlas.height && (static_cast<float>(ftFace->size->metrics.ascender) - static_cast<float>(ftFace->size->metrics.descender)) * FROM_266 <= maxSizeFitInAtlas.width)
146 * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
148 * @param[in/out] ftFace Face type object.
149 * @param[in] horizontalDpi The horizontal dpi.
150 * @param[in] verticalDpi The vertical dpi.
151 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
152 * @param[in/out] requestedPointSize The requested point-size.
153 * @return FreeType error code. 0 means success when requesting the nominal size (in points).
155 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
157 //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
158 const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
160 int error; // FreeType error code.
162 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
163 if(FT_Err_Ok != error)
171 uint32_t exponentialDecrement = 1;
173 while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit * exponentialDecrement)
175 requestedPointSize -= (pointSizePerOneUnit * exponentialDecrement);
176 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
177 if(FT_Err_Ok != error)
182 exponentialDecrement *= 2;
186 uint32_t minPointSize;
187 uint32_t maxPointSize;
191 exponentialDecrement /= 2;
192 minPointSize = requestedPointSize;
193 maxPointSize = requestedPointSize + (pointSizePerOneUnit * exponentialDecrement);
198 maxPointSize = requestedPointSize;
201 while(minPointSize < maxPointSize)
203 requestedPointSize = ((maxPointSize / pointSizePerOneUnit - minPointSize / pointSizePerOneUnit) / 2) * pointSizePerOneUnit + minPointSize;
204 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
205 if(FT_Err_Ok != error)
212 if(minPointSize == requestedPointSize)
214 //Found targeted point-size
218 minPointSize = requestedPointSize;
222 maxPointSize = requestedPointSize;
230 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
231 : fontDescription{std::move(font)},
232 fallbackFonts{fallbackFonts},
233 characterSets{characterSets}
237 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
238 FontDescriptionId index)
239 : fontDescription{fontDescription},
244 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
245 FontDescriptionId index)
246 : fontDescription{std::move(fontDescription)},
251 FontClient::Plugin::FontDescriptionSizeCacheKey::FontDescriptionSizeCacheKey(FontDescriptionId fontDescriptionId,
252 PointSize26Dot6 requestedPointSize)
253 : fontDescriptionId(fontDescriptionId),
254 requestedPointSize(requestedPointSize)
258 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
259 unsigned int verticalDpi)
260 : mFreeTypeLibrary(nullptr),
261 mDpiHorizontal(horizontalDpi),
262 mDpiVertical(verticalDpi),
263 mDefaultFontDescription(),
268 mValidatedFontCache(),
269 mFontDescriptionCache(),
270 mCharacterSetCache(),
271 mFontDescriptionSizeCache(),
272 mVectorFontCache(nullptr),
274 mEmbeddedItemCache(),
275 mLatestFoundFontDescription(),
276 mLatestFoundCacheKey(0, 0),
277 mDefaultFontDescriptionCached(false),
278 mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
279 mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS)
282 int error = FT_Init_FreeType(&mFreeTypeLibrary);
283 if(FT_Err_Ok != error)
285 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Init error: %d\n", error);
288 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
289 mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
293 FontClient::Plugin::~Plugin()
295 ClearFallbackCache(mFallbackCache);
297 // Free the resources allocated by the FcCharSet objects.
298 DestroyCharacterSets(mDefaultFontCharacterSets);
299 DestroyCharacterSets(mCharacterSetCache);
300 ClearCharacterSetFromFontFaceCache();
302 // Clear FontFaceCache here. Due to we sould deallocate FT_Faces before done freetype library
303 mFontFaceCache.clear();
305 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
306 delete mVectorFontCache;
308 FT_Done_FreeType(mFreeTypeLibrary);
311 void FontClient::Plugin::ClearCache()
313 mDefaultFontDescription = FontDescription();
315 mSystemFonts.clear();
316 mDefaultFonts.clear();
318 DestroyCharacterSets(mDefaultFontCharacterSets);
319 mDefaultFontCharacterSets.Clear();
321 ClearFallbackCache(mFallbackCache);
322 mFallbackCache.clear();
324 mFontIdCache.Clear();
326 ClearCharacterSetFromFontFaceCache();
327 mFontFaceCache.clear();
329 mValidatedFontCache.clear();
330 mFontDescriptionCache.clear();
332 DestroyCharacterSets(mCharacterSetCache);
333 mCharacterSetCache.Clear();
335 mFontDescriptionSizeCache.clear();
336 mFontDescriptionSizeCache.rehash(0); // Note : unordered_map.clear() didn't deallocate memory
338 mEllipsisCache.Clear();
339 mPixelBufferCache.clear();
340 mEmbeddedItemCache.Clear();
341 mBitmapFontCache.clear();
343 mLatestFoundFontDescription.family.clear();
344 mLatestFoundCacheKey = FontDescriptionSizeCacheKey(0, 0);
346 mDefaultFontDescriptionCached = false;
349 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
350 unsigned int verticalDpi)
352 mDpiHorizontal = horizontalDpi;
353 mDpiVertical = verticalDpi;
356 void FontClient::Plugin::ResetSystemDefaults()
358 mDefaultFontDescriptionCached = false;
361 void FontClient::Plugin::SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
363 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
364 FONT_LOG_DESCRIPTION(fontDescription, "");
367 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
369 FcResult result = FcResultMatch;
371 // Match the pattern.
372 FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
374 false /* don't trim */,
376 &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
378 if(nullptr != fontSet)
380 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont);
381 // Reserve some space to avoid reallocations.
382 fontList.reserve(fontSet->nfont);
384 for(int i = 0u; i < fontSet->nfont; ++i)
386 FcPattern* fontPattern = fontSet->fonts[i];
390 // Skip fonts with no path
391 if(GetFcString(fontPattern, FC_FILE, path))
393 // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
394 FcCharSet* characterSet = nullptr;
395 FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
397 // Increase the reference counter of the character set.
398 characterSetList.PushBack(FcCharSetCopy(characterSet));
400 fontList.push_back(FontDescription());
401 FontDescription& newFontDescription = fontList.back();
403 newFontDescription.path = std::move(path);
408 GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
409 GetFcInt(fontPattern, FC_WIDTH, width);
410 GetFcInt(fontPattern, FC_WEIGHT, weight);
411 GetFcInt(fontPattern, FC_SLANT, slant);
412 newFontDescription.width = IntToWidthType(width);
413 newFontDescription.weight = IntToWeightType(weight);
414 newFontDescription.slant = IntToSlantType(slant);
416 FONT_LOG_DESCRIPTION(newFontDescription, "");
420 // Destroys the font set created by FcFontSort.
421 FcFontSetDestroy(fontSet);
425 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " No fonts found.\n");
428 // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
429 FcPatternDestroy(fontFamilyPattern);
432 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts)
434 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
436 if(mDefaultFonts.empty())
438 FontDescription fontDescription;
439 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
440 fontDescription.width = DefaultFontWidth();
441 fontDescription.weight = DefaultFontWeight();
442 fontDescription.slant = DefaultFontSlant();
443 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
446 defaultFonts = mDefaultFonts;
448 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size());
451 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
453 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
455 if(!mDefaultFontDescriptionCached)
457 // Clear any font config stored info in the caches.
459 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
460 DestroyCharacterSets(mDefaultFontCharacterSets);
461 DestroyCharacterSets(mCharacterSetCache);
462 mDefaultFontCharacterSets.Clear();
463 mCharacterSetCache.Clear();
465 for(auto& item : mFallbackCache)
467 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
468 DestroyCharacterSets(*item.characterSets);
470 delete item.characterSets;
471 item.characterSets = nullptr;
474 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
475 ClearCharacterSetFromFontFaceCache();
477 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
478 FcInitReinitialize();
480 FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
482 if(nullptr != matchPattern)
484 FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
485 FcDefaultSubstitute(matchPattern);
487 FcCharSet* characterSet = nullptr;
488 bool matched = MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
490 // Caching the default font description
493 // Copy default font description info.
494 // Due to the type changed, we need to make some temperal font description.
495 FontDescription tempFontDescription = mDefaultFontDescription;
497 // Add the path to the cache.
498 tempFontDescription.type = FontDescription::FACE_FONT;
499 mFontDescriptionCache.push_back(tempFontDescription);
501 // Set the index to the vector of paths to font file names.
502 const FontDescriptionId fontDescriptionId = mFontDescriptionCache.size();
504 FONT_LOG_DESCRIPTION(tempFontDescription, "default platform font");
505 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " default font fontDescriptionId : %d\n", fontDescriptionId);
507 // Cache the index and the matched font's description.
508 FontDescriptionCacheItem item(tempFontDescription,
511 mValidatedFontCache.push_back(std::move(item));
515 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " default font validation failed for font [%s]\n", mDefaultFontDescription.family.c_str());
518 // Decrease the reference counter of the character set as it's not stored.
519 // Note. the cached default font description will increase reference counter by
520 // mFontDescriptionCache. So we can decrease reference counter here.
521 FcCharSetDestroy(characterSet);
523 // Destroys the pattern created.
524 FcPatternDestroy(matchPattern);
527 // Create again the character sets as they are not valid after FcInitReinitialize()
529 for(const auto& description : mDefaultFonts)
531 mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
534 for(const auto& description : mFontDescriptionCache)
536 mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
539 for(auto& item : mFallbackCache)
541 if(nullptr != item.fallbackFonts)
543 if(nullptr == item.characterSets)
545 item.characterSets = new CharacterSetList;
548 for(const auto& description : *(item.fallbackFonts))
550 item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
555 mDefaultFontDescriptionCached = true;
558 fontDescription.path = mDefaultFontDescription.path;
559 fontDescription.family = mDefaultFontDescription.family;
560 fontDescription.width = mDefaultFontDescription.width;
561 fontDescription.weight = mDefaultFontDescription.weight;
562 fontDescription.slant = mDefaultFontDescription.slant;
564 FONT_LOG_DESCRIPTION(fontDescription, "");
567 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts)
569 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
571 if(mSystemFonts.empty())
576 systemFonts = mSystemFonts;
577 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size());
580 void FontClient::Plugin::GetDescription(FontId id,
581 FontDescription& fontDescription) const
583 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
584 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
585 const FontId index = id - 1u;
587 if((id > 0u) && (index < mFontIdCache.Count()))
589 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
590 switch(fontIdCacheItem.type)
592 case FontDescription::FACE_FONT:
594 for(const auto& item : mFontDescriptionSizeCache)
596 if(item.second == fontIdCacheItem.index)
598 fontDescription = *(mFontDescriptionCache.begin() + item.first.fontDescriptionId - 1u);
600 FONT_LOG_DESCRIPTION(fontDescription, "");
606 case FontDescription::BITMAP_FONT:
608 fontDescription.type = FontDescription::BITMAP_FONT;
609 fontDescription.family = mBitmapFontCache[fontIdCacheItem.index].font.name;
614 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
615 fontDescription.type = FontDescription::INVALID;
616 fontDescription.family.clear();
621 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " No description found for the font ID %d\n", id);
624 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id)
626 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
627 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
629 PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
630 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(id);
631 if(fontCacheItem != nullptr)
633 pointSize = fontCacheItem->GetPointSize();
635 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " point size : %d\n", pointSize);
640 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character)
642 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
643 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
644 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " character : %p\n", character);
646 bool isSupported = false;
647 auto fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
648 if(fontCacheItem != nullptr)
650 isSupported = fontCacheItem->IsCharacterSupported(character); // May cache
653 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false"));
657 const FontCacheItemInterface* FontClient::Plugin::GetCachedFontItem(FontId id) const
659 const FontCacheIndex index = id - 1u;
660 if((id > 0u) && (index < mFontIdCache.Count()))
662 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
663 switch(fontIdCacheItem.type)
665 case FontDescription::FACE_FONT:
667 return &mFontFaceCache[fontIdCacheItem.index];
669 case FontDescription::BITMAP_FONT:
671 return &mBitmapFontCache[fontIdCacheItem.index];
675 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
682 FontId FontClient::Plugin::FindFontForCharacter(const FontList& fontList,
683 const CharacterSetList& characterSetList,
685 PointSize26Dot6 requestedPointSize,
688 DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
689 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
690 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " character : %p\n", character);
691 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
692 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
695 bool foundColor = false;
697 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of fonts : %d\n", fontList.size());
699 // Traverse the list of fonts.
700 // Check for each font if supports the character.
701 for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
703 const FontDescription& description = fontList[index];
704 const FcCharSet* const characterSet = characterSetList[index];
706 FONT_LOG_DESCRIPTION(description, "");
708 bool foundInRanges = false;
709 if(nullptr != characterSet)
711 foundInRanges = FcCharSetHasChar(characterSet, character);
716 fontId = GetFontId(description, requestedPointSize, 0u);
718 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " font id : %d\n", fontId);
723 (fontId - 1u < mFontIdCache.Count()))
725 const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].index];
727 foundColor = item.mHasColorTables;
730 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " foundColor : %s\n", (foundColor ? "true" : "false"));
733 // Keep going unless we prefer a different (color) font.
734 if(!preferColor || foundColor)
741 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
745 FontId FontClient::Plugin::FindDefaultFont(Character charcode,
746 PointSize26Dot6 requestedPointSize,
749 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
750 FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
754 // Create the list of default fonts if it has not been created.
755 if(mDefaultFonts.empty())
757 FontDescription fontDescription;
758 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
759 fontDescription.width = DefaultFontWidth();
760 fontDescription.weight = DefaultFontWeight();
761 fontDescription.slant = DefaultFontSlant();
763 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
765 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size());
767 // Traverse the list of default fonts.
768 // Check for each default font if supports the character.
769 fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
771 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
775 FontId FontClient::Plugin::FindFallbackFont(Character charcode,
776 const FontDescription& preferredFontDescription,
777 PointSize26Dot6 requestedPointSize,
780 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
781 FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
783 // The font id to be returned.
786 FontDescription fontDescription;
788 // Fill the font description with the preferred font description and complete with the defaults.
789 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
790 fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? DefaultFontWeight() : preferredFontDescription.weight);
791 fontDescription.width = ((FontWidth::NONE == preferredFontDescription.width) ? DefaultFontWidth() : preferredFontDescription.width);
792 fontDescription.slant = ((FontSlant::NONE == preferredFontDescription.slant) ? DefaultFontSlant() : preferredFontDescription.slant);
794 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n");
795 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
796 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
797 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
798 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
800 // Check first if the font's description has been queried before.
801 FontList* fontList = nullptr;
802 CharacterSetList* characterSetList = nullptr;
804 if(!FindFallbackFontList(fontDescription, fontList, characterSetList))
806 fontList = new FontList;
807 characterSetList = new CharacterSetList;
809 SetFontList(fontDescription, *fontList, *characterSetList);
811 FontDescription appleColorEmoji;
812 appleColorEmoji.family = "Apple Color Emoji";
813 appleColorEmoji.width = fontDescription.width;
814 appleColorEmoji.weight = fontDescription.weight;
815 appleColorEmoji.slant = fontDescription.slant;
816 FontList emojiFontList;
817 CharacterSetList emojiCharSetList;
818 SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
820 std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
821 emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
822 *fontList = std::move(emojiFontList);
823 *characterSetList = std::move(emojiCharSetList);
826 // Add the font-list to the cache.
827 mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
830 if(fontList && characterSetList)
832 fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
835 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
839 FontId FontClient::Plugin::GetFontId(const FontPath& path,
840 PointSize26Dot6 requestedPointSize,
842 bool cacheDescription)
844 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
845 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
846 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
850 if(nullptr != mFreeTypeLibrary)
853 if(FindFont(path, requestedPointSize, faceIndex, foundId))
859 id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
863 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
867 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
868 PointSize26Dot6 requestedPointSize,
871 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
872 FONT_LOG_DESCRIPTION(fontDescription, "");
874 // Special case when font Description don't have family information.
875 // In this case, we just use default description family and path.
876 const FontDescription& realFontDescription = fontDescription.family.empty() ? FontDescription(mDefaultFontDescription.path,
877 mDefaultFontDescription.family,
878 fontDescription.width,
879 fontDescription.weight,
880 fontDescription.slant,
881 fontDescription.type)
884 FONT_LOG_DESCRIPTION(realFontDescription, "");
885 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
887 // This method uses three vectors which caches:
888 // * The bitmap font cache
889 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
890 // * The path to font file names.
891 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
893 // 1) Checks if the font description matches with a previously loaded bitmap font.
894 // Returns if a font is found.
895 // 2) Checks in the cache if the font's description has been validated before.
896 // If it was it gets an index to the vector with paths to font file names. Otherwise,
897 // retrieves using font config a path to a font file name which matches with the
898 // font's description. The path is stored in the cache.
900 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
901 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
902 // the GetFontId() method with the path to the font file name and the point size to
905 // The font id to be returned.
908 // Check first if the font description matches with a previously loaded bitmap font.
909 if(FindBitmapFont(realFontDescription.family, fontId))
914 // Check if the font's description have been validated before.
915 FontDescriptionId fontDescriptionId = 0u;
917 if(!FindValidatedFont(realFontDescription,
920 // Use font config to validate the font's description.
921 ValidateFont(realFontDescription,
925 FontCacheIndex fontCacheIndex = 0u;
926 // Check if exists a pair 'fontDescriptionId, requestedPointSize' in the cache.
927 if(!FindFont(fontDescriptionId, requestedPointSize, fontCacheIndex))
929 // Retrieve the font file name path.
930 const FontDescription& description = *(mFontDescriptionCache.begin() + fontDescriptionId - 1u);
932 // Retrieve the font id. Do not cache the description as it has been already cached.
933 fontId = GetFontId(description.path,
938 fontCacheIndex = mFontIdCache[fontId - 1u].index;
939 mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCharacterSetCache[fontDescriptionId - 1u]);
941 // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
942 mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
946 fontId = mFontFaceCache[fontCacheIndex].mFontId + 1u;
949 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
953 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont)
955 for(const auto& item : mBitmapFontCache)
957 if(bitmapFont.name == item.font.name)
963 BitmapFontCacheItem bitmapFontCacheItem(bitmapFont, mFontIdCache.Count());
965 FontIdCacheItem fontIdCacheItem;
966 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
967 fontIdCacheItem.index = mBitmapFontCache.size();
969 mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
970 mFontIdCache.PushBack(fontIdCacheItem);
972 return bitmapFontCacheItem.id + 1u;
975 void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
976 FontDescriptionId& fontDescriptionId)
978 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
979 FONT_LOG_DESCRIPTION(fontDescription, "");
981 // Create a font pattern.
982 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
984 FontDescription description;
986 FcCharSet* characterSet = nullptr;
987 bool matched = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
988 FcPatternDestroy(fontFamilyPattern);
990 if(matched && (nullptr != characterSet))
992 // Add the path to the cache.
993 description.type = FontDescription::FACE_FONT;
994 mFontDescriptionCache.push_back(description);
996 // Set the index to the vector of paths to font file names.
997 fontDescriptionId = mFontDescriptionCache.size();
999 FONT_LOG_DESCRIPTION(description, "matched");
1000 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fontDescriptionId : %d\n", fontDescriptionId);
1002 // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1003 mCharacterSetCache.PushBack(characterSet);
1005 // Cache the index and the matched font's description.
1006 FontDescriptionCacheItem item(description,
1009 mValidatedFontCache.push_back(std::move(item));
1011 if((fontDescription.family != description.family) ||
1012 (fontDescription.width != description.width) ||
1013 (fontDescription.weight != description.weight) ||
1014 (fontDescription.slant != description.slant))
1016 // Cache the given font's description if it's different than the matched.
1017 FontDescriptionCacheItem item(fontDescription,
1020 mValidatedFontCache.push_back(std::move(item));
1025 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str());
1029 void FontClient::Plugin::GetFontMetrics(FontId fontId,
1030 FontMetrics& metrics)
1032 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1033 if(fontCacheItem != nullptr)
1035 fontCacheItem->GetFontMetrics(metrics, mDpiVertical);
1039 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId fontId,
1042 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1043 if(fontCacheItem != nullptr)
1045 return fontCacheItem->GetGlyphIndex(charcode);
1051 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId fontId,
1053 Character variantSelector)
1055 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1056 if(fontCacheItem != nullptr)
1058 return fontCacheItem->GetGlyphIndex(charcode, variantSelector);
1064 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
1069 if(VECTOR_GLYPH == type)
1071 return GetVectorMetrics(array, size, horizontal);
1074 return GetBitmapMetrics(array, size, horizontal);
1077 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
1081 bool success(false);
1083 for(unsigned int i = 0; i < size; ++i)
1085 GlyphInfo& glyph = array[i];
1087 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(glyph.fontId);
1088 if(fontCacheItem != nullptr)
1090 success = fontCacheItem->GetGlyphMetrics(glyph, mDpiVertical, horizontal);
1092 // Check if it's an embedded image.
1093 else if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count()))
1095 mEmbeddedItemCache[glyph.index - 1u].GetGlyphMetrics(glyph);
1103 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
1107 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1110 for(unsigned int i = 0u; i < size; ++i)
1112 FontId fontId = array[i].fontId;
1115 (fontId - 1u) < mFontIdCache.Count())
1117 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].index];
1119 if(!font.mVectorFontId)
1121 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1124 mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
1126 // Vector metrics are in EMs, convert to pixels
1127 const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
1128 array[i].width *= scale;
1129 array[i].height *= scale;
1130 array[i].xBearing *= scale;
1131 array[i].yBearing *= scale;
1132 array[i].advance *= scale;
1146 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
1148 data.isColorBitmap = false;
1149 data.isColorEmoji = false;
1150 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1151 if(fontCacheItem != nullptr)
1153 fontCacheItem->CreateBitmap(glyphIndex, data, outlineWidth, isItalicRequired, isBoldRequired);
1155 else if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count()))
1157 // It's an embedded item.
1158 mEmbeddedItemCache[glyphIndex - 1u].CreateBitmap(mPixelBufferCache, data);
1162 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
1164 TextAbstraction::FontClient::GlyphBufferData data;
1166 CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
1168 return PixelData::New(data.buffer,
1169 data.width * data.height * Pixel::GetBytesPerPixel(data.format),
1176 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
1181 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1183 (fontId - 1u < mFontIdCache.Count()))
1185 const FontCacheIndex fontFaceId = mFontIdCache[fontId - 1u].index;
1186 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1188 if(!font.mVectorFontId)
1190 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1193 mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1198 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
1200 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1201 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize);
1203 // First look into the cache if there is an ellipsis glyph for the requested point size.
1204 for(const auto& item : mEllipsisCache)
1206 if(item.requestedPointSize == requestedPointSize)
1208 // Use the glyph in the cache.
1209 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1210 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1215 // No glyph has been found. Create one.
1216 mEllipsisCache.PushBack(EllipsisItem());
1217 EllipsisItem& item = *(mEllipsisCache.End() - 1u);
1219 item.requestedPointSize = requestedPointSize;
1221 // Find a font for the ellipsis glyph.
1222 item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1226 // Set the character index to access the glyph inside the font.
1227 item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].index].mFreeTypeFace,
1228 ELLIPSIS_CHARACTER);
1230 GetBitmapMetrics(&item.glyph, 1u, true);
1232 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1233 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1237 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
1239 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1240 return fontCacheItem && fontCacheItem->IsColorGlyph(glyphIndex);
1243 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId)
1245 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1246 if(fontCacheItem != nullptr)
1248 return fontCacheItem->GetTypeface();
1253 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId)
1255 const FontId index = fontId - 1u;
1257 (index < mFontIdCache.Count()))
1259 return mFontIdCache[index].type;
1261 return FontDescription::INVALID;
1264 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1266 // nullptr as first parameter means the current configuration is used.
1267 return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1270 HarfBuzzFontHandle FontClient::Plugin::GetHarfBuzzFont(FontId fontId)
1272 FontCacheItemInterface* fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
1273 if(fontCacheItem != nullptr)
1275 return fontCacheItem->GetHarfBuzzFont(mDpiHorizontal, mDpiVertical); // May cache
1280 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
1282 EmbeddedItem embeddedItem;
1284 embeddedItem.pixelBufferId = 0u;
1285 embeddedItem.width = description.width;
1286 embeddedItem.height = description.height;
1288 pixelFormat = Pixel::A8;
1290 if(!description.url.empty())
1292 // Check if the url is in the cache.
1293 PixelBufferId index = 0u;
1295 for(const auto& cacheItem : mPixelBufferCache)
1298 if(cacheItem.url == description.url)
1300 // The url is in the pixel buffer cache.
1301 // Set the index +1 to the vector.
1302 embeddedItem.pixelBufferId = index;
1307 Devel::PixelBuffer pixelBuffer;
1308 if(0u == embeddedItem.pixelBufferId)
1310 // The pixel buffer is not in the cache. Create one and cache it.
1312 // Load the image from the url.
1313 pixelBuffer = LoadImageFromFile(description.url);
1315 // Create the cache item.
1316 PixelBufferCacheItem pixelBufferCacheItem;
1317 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1318 pixelBufferCacheItem.url = description.url;
1320 // Store the cache item in the cache.
1321 mPixelBufferCache.push_back(std::move(pixelBufferCacheItem));
1323 // Set the pixel buffer id to the embedded item.
1324 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1328 // Retrieve the pixel buffer from the cache to set the pixel format.
1329 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1334 // Set the size of the embedded item if it has not been set.
1335 if(0u == embeddedItem.width)
1337 embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1340 if(0u == embeddedItem.height)
1342 embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1345 // Set the pixel format.
1346 pixelFormat = pixelBuffer.GetPixelFormat();
1350 // Find if the same embeddedItem has already been created.
1351 unsigned int index = 0u;
1352 for(const auto& item : mEmbeddedItemCache)
1355 if((item.pixelBufferId == embeddedItem.pixelBufferId) &&
1356 (item.width == embeddedItem.width) &&
1357 (item.height == embeddedItem.height))
1363 // Cache the embedded item.
1364 mEmbeddedItemCache.PushBack(embeddedItem);
1366 return mEmbeddedItemCache.Count();
1369 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
1371 mIsAtlasLimitationEnabled = enabled;
1374 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
1376 return mIsAtlasLimitationEnabled;
1379 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
1381 return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1384 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
1386 return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
1389 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
1391 return mCurrentMaximumBlockSizeFitInAtlas;
1394 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
1396 bool isChanged = false;
1397 const Size& maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1398 const uint16_t& padding = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
1400 if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
1402 mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
1409 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
1411 return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
1415 void FontClient::Plugin::InitSystemFonts()
1417 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1419 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1423 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont);
1425 // Reserve some space to avoid reallocations.
1426 mSystemFonts.reserve(fontSet->nfont);
1428 for(int i = 0u; i < fontSet->nfont; ++i)
1430 FcPattern* fontPattern = fontSet->fonts[i];
1434 // Skip fonts with no path
1435 if(GetFcString(fontPattern, FC_FILE, path))
1437 mSystemFonts.push_back(FontDescription());
1438 FontDescription& fontDescription = mSystemFonts.back();
1440 fontDescription.path = std::move(path);
1445 GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
1446 GetFcInt(fontPattern, FC_WIDTH, width);
1447 GetFcInt(fontPattern, FC_WEIGHT, weight);
1448 GetFcInt(fontPattern, FC_SLANT, slant);
1449 fontDescription.width = IntToWidthType(width);
1450 fontDescription.weight = IntToWeightType(weight);
1451 fontDescription.slant = IntToSlantType(slant);
1453 FONT_LOG_DESCRIPTION(fontDescription, "");
1457 // Destroys the font set created.
1458 FcFontSetDestroy(fontSet);
1462 bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
1464 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1466 FcResult result = FcResultMatch;
1467 FcPattern* match = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1469 const bool matched = nullptr != match;
1470 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " pattern matched : %s\n", (matched ? "true" : "false"));
1477 GetFcString(match, FC_FILE, fontDescription.path);
1478 GetFcString(match, FC_FAMILY, fontDescription.family);
1479 GetFcInt(match, FC_WIDTH, width);
1480 GetFcInt(match, FC_WEIGHT, weight);
1481 GetFcInt(match, FC_SLANT, slant);
1482 fontDescription.width = IntToWidthType(width);
1483 fontDescription.weight = IntToWeightType(weight);
1484 fontDescription.slant = IntToSlantType(slant);
1486 // Retrieve the character set and increase the reference counter.
1487 FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
1488 *characterSet = FcCharSetCopy(*characterSet);
1490 // destroyed the matched pattern
1491 FcPatternDestroy(match);
1492 FONT_LOG_DESCRIPTION(fontDescription, "");
1497 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1499 FcFontSet* fontset = nullptr;
1501 // create a new pattern.
1502 // a pattern holds a set of names, each name refers to a property of the font
1503 FcPattern* pattern = FcPatternCreate();
1505 if(nullptr != pattern)
1507 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1508 FcObjectSet* objectSet = FcObjectSetCreate();
1510 if(nullptr != objectSet)
1512 // build an object set from a list of property names
1513 FcObjectSetAdd(objectSet, FC_FILE);
1514 FcObjectSetAdd(objectSet, FC_FAMILY);
1515 FcObjectSetAdd(objectSet, FC_WIDTH);
1516 FcObjectSetAdd(objectSet, FC_WEIGHT);
1517 FcObjectSetAdd(objectSet, FC_SLANT);
1519 // get a list of fonts
1520 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1521 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.
1523 // clear up the object set
1524 FcObjectSetDestroy(objectSet);
1527 // clear up the pattern
1528 FcPatternDestroy(pattern);
1534 bool FontClient::Plugin::GetFcString(const FcPattern* const pattern,
1535 const char* const n,
1536 std::string& string)
1538 FcChar8* file = nullptr;
1539 const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
1541 if(FcResultMatch == retVal)
1543 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1544 string.assign(reinterpret_cast<const char*>(file));
1552 bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
1554 const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
1556 if(FcResultMatch == retVal)
1564 FontId FontClient::Plugin::CreateFont(const FontPath& path,
1565 PointSize26Dot6 requestedPointSize,
1566 FaceIndex faceIndex,
1567 bool cacheDescription)
1569 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1570 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
1571 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
1575 // Create & cache new font face
1577 int error = FT_New_Face(mFreeTypeLibrary,
1582 if(FT_Err_Ok == error)
1584 // Check if a font is scalable.
1585 const bool isScalable = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
1586 const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
1587 const bool hasColorTables = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
1588 FontId fontFaceId = 0u;
1590 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " isScalable : [%s]\n", (isScalable ? "true" : "false"));
1591 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
1592 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
1594 // Check to see if the font contains fixed sizes?
1595 if(!isScalable && hasFixedSizedBitmaps)
1597 PointSize26Dot6 actualPointSize = 0u;
1598 int fixedSizeIndex = 0;
1599 for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
1601 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
1602 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
1604 if(fixedSize >= requestedPointSize)
1606 actualPointSize = fixedSize;
1611 if(0u == actualPointSize)
1613 // The requested point size is bigger than the bigest fixed size.
1614 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
1615 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
1618 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
1620 // Tell Freetype to use this size
1621 error = FT_Select_Size(ftFace, fixedSizeIndex);
1622 if(FT_Err_Ok != error)
1624 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
1628 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1630 FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1631 static_cast<float>(ftMetrics.descender) * FROM_266,
1632 static_cast<float>(ftMetrics.height) * FROM_266,
1633 static_cast<float>(ftFace->underline_position) * FROM_266,
1634 static_cast<float>(ftFace->underline_thickness) * FROM_266);
1636 const float fixedWidth = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
1637 const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
1639 // Create the FreeType font face item to cache.
1640 FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
1642 // Set the index to the font's id cache.
1643 fontFaceCacheItem.mFontId = mFontIdCache.Count();
1645 // Create the font id item to cache.
1646 FontIdCacheItem fontIdCacheItem;
1647 fontIdCacheItem.type = FontDescription::FACE_FONT;
1649 // Set the index to the FreeType font face cache.
1650 fontIdCacheItem.index = mFontFaceCache.size();
1651 fontFaceId = fontIdCacheItem.index + 1u;
1654 mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
1655 mFontIdCache.PushBack(fontIdCacheItem);
1657 // Set the font id to be returned.
1658 id = mFontIdCache.Count();
1663 if(mIsAtlasLimitationEnabled)
1665 //There is limitation on block size to fit in predefined atlas size.
1666 //If the block size cannot fit into atlas size, then the system cannot draw block.
1667 //This is workaround to avoid issue in advance
1668 //Decrementing point-size until arriving to maximum allowed block size.
1669 auto requestedPointSizeBackup = requestedPointSize;
1670 const Size& maxSizeFitInAtlas = GetCurrentMaximumBlockSizeFitInAtlas();
1671 error = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
1673 if(requestedPointSize != requestedPointSizeBackup)
1675 DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
1680 error = FT_Set_Char_Size(ftFace,
1687 if(FT_Err_Ok == error)
1689 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1691 FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1692 static_cast<float>(ftMetrics.descender) * FROM_266,
1693 static_cast<float>(ftMetrics.height) * FROM_266,
1694 static_cast<float>(ftFace->underline_position) * FROM_266,
1695 static_cast<float>(ftFace->underline_thickness) * FROM_266);
1697 // Create the FreeType font face item to cache.
1698 FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics);
1700 // Set the index to the font's id cache.
1701 fontFaceCacheItem.mFontId = mFontIdCache.Count();
1703 // Create the font id item to cache.
1704 FontIdCacheItem fontIdCacheItem;
1705 fontIdCacheItem.type = FontDescription::FACE_FONT;
1707 // Set the index to the FreeType font face cache.
1708 fontIdCacheItem.index = mFontFaceCache.size();
1709 fontFaceId = fontIdCacheItem.index + 1u;
1712 mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
1713 mFontIdCache.PushBack(fontIdCacheItem);
1715 // Set the font id to be returned.
1716 id = mFontIdCache.Count();
1720 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
1724 if(0u != fontFaceId)
1726 if(cacheDescription)
1728 CacheFontPath(ftFace, fontFaceId, requestedPointSize, path);
1734 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str());
1737 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
1741 bool FontClient::Plugin::FindFont(const FontPath& path,
1742 PointSize26Dot6 requestedPointSize,
1743 FaceIndex faceIndex,
1744 FontId& fontId) const
1746 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1747 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
1748 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
1749 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size());
1752 for(const auto& cacheItem : mFontFaceCache)
1754 if(cacheItem.mRequestedPointSize == requestedPointSize &&
1755 cacheItem.mFaceIndex == faceIndex &&
1756 cacheItem.mPath == path)
1758 fontId = cacheItem.mFontId + 1u;
1760 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font found, id : %d\n", fontId);
1765 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font not found\n");
1769 bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
1770 FontDescriptionId& fontDescriptionId)
1772 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1773 FONT_LOG_DESCRIPTION(fontDescription, "");
1774 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
1776 fontDescriptionId = 0u;
1778 // Fast cut if inputed family is empty.
1779 if(DALI_UNLIKELY(fontDescription.family.empty()))
1781 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description not found / fontDescription.family is empty!\n");
1785 // Heuristic optimize code : Compare with latest found item.
1786 if((fontDescription.width == mLatestFoundFontDescription.width) &&
1787 (fontDescription.weight == mLatestFoundFontDescription.weight) &&
1788 (fontDescription.slant == mLatestFoundFontDescription.slant) &&
1789 (fontDescription.family == mLatestFoundFontDescription.family))
1791 fontDescriptionId = mLatestFoundFontDescriptionId;
1793 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description same as latest, id : %d\n", fontDescriptionId);
1797 for(const auto& item : mValidatedFontCache)
1799 if((fontDescription.width == item.fontDescription.width) &&
1800 (fontDescription.weight == item.fontDescription.weight) &&
1801 (fontDescription.slant == item.fontDescription.slant) &&
1802 (fontDescription.family == item.fontDescription.family))
1804 fontDescriptionId = item.index;
1806 mLatestFoundFontDescription = fontDescription;
1807 mLatestFoundFontDescriptionId = fontDescriptionId;
1809 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description found, id : %d\n", fontDescriptionId);
1814 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description not found\n");
1818 bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription,
1819 FontList*& fontList,
1820 CharacterSetList*& characterSetList)
1822 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1823 FONT_LOG_DESCRIPTION(fontDescription, "");
1824 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size());
1828 for(const auto& item : mFallbackCache)
1830 if(!fontDescription.family.empty() &&
1831 (fontDescription.family == item.fontDescription.family) &&
1832 (fontDescription.width == item.fontDescription.width) &&
1833 (fontDescription.weight == item.fontDescription.weight) &&
1834 (fontDescription.slant == item.fontDescription.slant))
1836 fontList = item.fallbackFonts;
1837 characterSetList = item.characterSets;
1839 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fallback font list found.\n");
1844 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fallback font list not found.\n");
1848 bool FontClient::Plugin::FindFont(FontDescriptionId fontDescriptionId,
1849 PointSize26Dot6 requestedPointSize,
1850 FontCacheIndex& fontCacheIndex)
1852 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1853 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fontDescriptionId : %d\n", fontDescriptionId);
1854 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
1856 fontCacheIndex = 0u;
1858 const FontDescriptionSizeCacheKey key(fontDescriptionId, requestedPointSize);
1860 // Heuristic optimize code : Compare with latest found item.
1861 if(key == mLatestFoundCacheKey)
1863 fontCacheIndex = mLatestFoundCacheIndex;
1865 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font same as latest, index of font cache : %d\n", fontCacheIndex);
1869 const auto& iter = mFontDescriptionSizeCache.find(key);
1870 if(iter != mFontDescriptionSizeCache.cend())
1872 fontCacheIndex = iter->second;
1874 mLatestFoundCacheKey = key;
1875 mLatestFoundCacheIndex = fontCacheIndex;
1877 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font found, index of font cache : %d\n", fontCacheIndex);
1881 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font not found.\n");
1885 bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const
1889 for(const auto& item : mBitmapFontCache)
1891 if(bitmapFont == item.font.name)
1893 fontId = item.id + 1u;
1901 bool FontClient::Plugin::IsScalable(const FontPath& path)
1903 bool isScalable = false;
1906 int error = FT_New_Face(mFreeTypeLibrary,
1910 if(FT_Err_Ok != error)
1912 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
1916 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
1922 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription)
1924 // Create a font pattern.
1925 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1927 FcResult result = FcResultMatch;
1929 // match the pattern
1930 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1931 bool isScalable = false;
1935 // Get the path to the font file name.
1937 GetFcString(match, FC_FILE, path);
1938 isScalable = IsScalable(path);
1942 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1945 // Destroys the created patterns.
1946 FcPatternDestroy(match);
1947 FcPatternDestroy(fontFamilyPattern);
1952 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes)
1954 // Empty the caller container
1958 int error = FT_New_Face(mFreeTypeLibrary,
1962 if(FT_Err_Ok != error)
1964 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
1967 // Fetch the number of fixed sizes available
1968 if(ftFace->num_fixed_sizes && ftFace->available_sizes)
1970 for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
1972 sizes.PushBack(ftFace->available_sizes[i].size);
1977 void FontClient::Plugin::GetFixedSizes(const FontDescription& fontDescription,
1978 Vector<PointSize26Dot6>& sizes)
1980 // Create a font pattern.
1981 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1983 FcResult result = FcResultMatch;
1985 // match the pattern
1986 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1990 // Get the path to the font file name.
1992 GetFcString(match, FC_FILE, path);
1993 GetFixedSizes(path, sizes);
1997 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2000 // Destroys the created patterns.
2001 FcPatternDestroy(match);
2002 FcPatternDestroy(fontFamilyPattern);
2005 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
2007 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
2008 if(fontCacheItem != nullptr)
2010 return fontCacheItem->HasItalicStyle();
2015 void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path)
2017 FontDescription description;
2018 description.path = path;
2019 description.family = std::move(FontFamily(ftFace->family_name));
2020 description.weight = FontWeight::NONE;
2021 description.width = FontWidth::NONE;
2022 description.slant = FontSlant::NONE;
2024 // Note FreeType doesn't give too much info to build a proper font style.
2025 if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
2027 description.slant = FontSlant::ITALIC;
2029 if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
2031 description.weight = FontWeight::BOLD;
2034 FontDescriptionId fontDescriptionId = 0u;
2035 if(!FindValidatedFont(description,
2038 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2040 FcResult result = FcResultMatch;
2041 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2043 FcCharSet* characterSet = nullptr;
2044 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2046 const FontId fontFaceId = id - 1u;
2047 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
2049 // Destroys the created patterns.
2050 FcPatternDestroy(match);
2051 FcPatternDestroy(pattern);
2053 // Add the path to the cache.
2054 description.type = FontDescription::FACE_FONT;
2055 mFontDescriptionCache.push_back(description);
2057 // Set the index to the vector of paths to font file names.
2058 fontDescriptionId = mFontDescriptionCache.size();
2060 // Increase the reference counter and add the character set to the cache.
2061 mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
2063 // Cache the index and the font's description.
2064 mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
2065 fontDescriptionId)));
2067 // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
2068 mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontFaceId);
2072 void FontClient::Plugin::ClearFallbackCache(std::vector<FallbackCacheItem>& fallbackCache)
2074 for(auto& item : fallbackCache)
2076 if(nullptr != item.fallbackFonts)
2078 delete item.fallbackFonts;
2081 if(nullptr != item.characterSets)
2083 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2084 DestroyCharacterSets(*item.characterSets);
2085 delete item.characterSets;
2090 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2092 for(auto& item : mFontFaceCache)
2094 FcCharSetDestroy(item.mCharacterSet);
2095 item.mCharacterSet = nullptr;
2099 } // namespace Dali::TextAbstraction::Internal