2 * Copyright (c) 2021 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/font-client-utils.h>
30 #include <dali/public-api/common/dali-vector.h>
31 #include <dali/public-api/common/vector-wrapper.h>
34 #include <fontconfig/fontconfig.h>
38 #if defined(DEBUG_ENABLED)
40 // Note, to turn on trace and verbose logging, use "export LOG_FONT_CLIENT=3,true"
41 // Or re-define the following filter using Verbose,true instead of NoLogging,false,
42 // Or, add DALI_LOG_FILTER_ENABLE_TRACE(gFontClientLogFilter) in the code below.
44 Dali::Integration::Log::Filter* gFontClientLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
46 #define FONT_LOG_DESCRIPTION(fontDescription, prefix) \
47 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix " description; family : [%s]\n", fontDescription.family.c_str()); \
48 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, \
52 " slant : [%s]\n\n", \
53 fontDescription.path.c_str(), \
54 FontWidth::Name[fontDescription.width], \
55 FontWeight::Name[fontDescription.weight], \
56 FontSlant::Name[fontDescription.slant])
58 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
59 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, \
61 " requestedPointSize : %d\n" \
62 " preferColor : %s\n", \
65 (preferColor ? "true" : "false"))
69 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
70 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
77 * Conversion from Fractional26.6 to float
79 const float FROM_266 = 1.0f / 64.0f;
80 const float POINTS_PER_INCH = 72.f;
82 const std::string DEFAULT_FONT_FAMILY_NAME("Tizen");
84 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
93 namespace TextAbstraction
98 * @brief Free the resources allocated by the FcCharSet objects.
100 * @param[in] characterSets The vector of character sets.
102 void DestroyCharacterSets(CharacterSetList& characterSets)
104 for(auto& item : characterSets)
108 FcCharSetDestroy(item);
114 * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
116 * @param[in/out] ftFace Face type object.
117 * @param[in] horizontalDpi The horizontal dpi.
118 * @param[in] verticalDpi The vertical dpi.
119 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
120 * @param[in] requestedPointSize The requested point-size.
121 * @return whether the ftFace's block can fit into atlas
123 bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
127 error = FT_Set_Char_Size(ftFace,
133 if(error == FT_Err_Ok)
135 //Check width and height of block for requestedPointSize
136 //If the width or height is greater than the maximum-size then decrement by one unit of point-size.
137 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)
147 * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
149 * @param[in/out] ftFace Face type object.
150 * @param[in] horizontalDpi The horizontal dpi.
151 * @param[in] verticalDpi The vertical dpi.
152 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
153 * @param[in/out] requestedPointSize The requested point-size.
154 * @return FreeType error code. 0 means success when requesting the nominal size (in points).
156 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
158 //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
159 const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
161 int error; // FreeType error code.
163 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
164 if(FT_Err_Ok != error)
172 uint32_t exponentialDecrement = 1;
174 while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit * exponentialDecrement)
176 requestedPointSize -= (pointSizePerOneUnit * exponentialDecrement);
177 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
178 if(FT_Err_Ok != error)
183 exponentialDecrement *= 2;
187 uint32_t minPointSize;
188 uint32_t maxPointSize;
192 exponentialDecrement /= 2;
193 minPointSize = requestedPointSize;
194 maxPointSize = requestedPointSize + (pointSizePerOneUnit * exponentialDecrement);
199 maxPointSize = requestedPointSize;
202 while(minPointSize < maxPointSize)
204 requestedPointSize = ((maxPointSize / pointSizePerOneUnit - minPointSize / pointSizePerOneUnit) / 2) * pointSizePerOneUnit + minPointSize;
205 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
206 if(FT_Err_Ok != error)
213 if(minPointSize == requestedPointSize)
215 //Found targeted point-size
219 minPointSize = requestedPointSize;
223 maxPointSize = requestedPointSize;
231 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
232 : fontDescription{std::move(font)},
233 fallbackFonts{fallbackFonts},
234 characterSets{characterSets}
238 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
239 FontDescriptionId index)
240 : fontDescription{fontDescription},
245 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
246 FontDescriptionId index)
247 : fontDescription{std::move(fontDescription)},
252 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem(FontDescriptionId validatedFontId,
253 PointSize26Dot6 requestedPointSize,
255 : validatedFontId(validatedFontId),
256 requestedPointSize(requestedPointSize),
261 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace,
262 const FontPath& path,
263 PointSize26Dot6 requestedPointSize,
265 const FontMetrics& metrics)
266 : mFreeTypeFace(ftFace),
268 mRequestedPointSize(requestedPointSize),
271 mCharacterSet(nullptr),
273 mFixedWidthPixels(0.f),
274 mFixedHeightPixels(0.f),
277 mIsFixedSizeBitmap(false),
278 mHasColorTables(false)
282 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace,
283 const FontPath& path,
284 PointSize26Dot6 requestedPointSize,
286 const FontMetrics& metrics,
291 : mFreeTypeFace(ftFace),
293 mRequestedPointSize(requestedPointSize),
296 mCharacterSet(nullptr),
297 mFixedSizeIndex(fixedSizeIndex),
298 mFixedWidthPixels(fixedWidth),
299 mFixedHeightPixels(fixedHeight),
302 mIsFixedSizeBitmap(true),
303 mHasColorTables(hasColorTables)
307 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
308 unsigned int verticalDpi)
309 : mFreeTypeLibrary(nullptr),
310 mDpiHorizontal(horizontalDpi),
311 mDpiVertical(verticalDpi),
312 mDefaultFontDescription(),
317 mValidatedFontCache(),
318 mFontDescriptionCache(),
319 mCharacterSetCache(),
320 mFontDescriptionSizeCache(),
321 mVectorFontCache(nullptr),
323 mEmbeddedItemCache(),
324 mDefaultFontDescriptionCached(false),
325 mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
326 mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS)
329 int error = FT_Init_FreeType(&mFreeTypeLibrary);
330 if(FT_Err_Ok != error)
332 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Init error: %d\n", error);
335 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
336 mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
340 FontClient::Plugin::~Plugin()
342 ClearFallbackCache(mFallbackCache);
344 // Free the resources allocated by the FcCharSet objects.
345 DestroyCharacterSets(mDefaultFontCharacterSets);
346 DestroyCharacterSets(mCharacterSetCache);
347 ClearCharacterSetFromFontFaceCache();
349 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
350 delete mVectorFontCache;
352 FT_Done_FreeType(mFreeTypeLibrary);
355 void FontClient::Plugin::ClearCache()
357 mDefaultFontDescription = FontDescription();
359 mSystemFonts.clear();
360 mDefaultFonts.clear();
362 DestroyCharacterSets(mDefaultFontCharacterSets);
363 mDefaultFontCharacterSets.Clear();
365 ClearFallbackCache(mFallbackCache);
366 mFallbackCache.clear();
368 mFontIdCache.Clear();
370 ClearCharacterSetFromFontFaceCache();
371 mFontFaceCache.clear();
373 mValidatedFontCache.clear();
374 mFontDescriptionCache.clear();
376 DestroyCharacterSets(mCharacterSetCache);
377 mCharacterSetCache.Clear();
379 mFontDescriptionSizeCache.clear();
381 mEllipsisCache.Clear();
382 mPixelBufferCache.clear();
383 mEmbeddedItemCache.Clear();
384 mBitmapFontCache.clear();
386 mDefaultFontDescriptionCached = false;
389 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
390 unsigned int verticalDpi)
392 mDpiHorizontal = horizontalDpi;
393 mDpiVertical = verticalDpi;
396 void FontClient::Plugin::ResetSystemDefaults()
398 mDefaultFontDescriptionCached = false;
401 void FontClient::Plugin::SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
403 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
404 FONT_LOG_DESCRIPTION(fontDescription, "");
407 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
409 FcResult result = FcResultMatch;
411 // Match the pattern.
412 FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
414 false /* don't trim */,
416 &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
418 if(nullptr != fontSet)
420 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont);
421 // Reserve some space to avoid reallocations.
422 fontList.reserve(fontSet->nfont);
424 for(int i = 0u; i < fontSet->nfont; ++i)
426 FcPattern* fontPattern = fontSet->fonts[i];
430 // Skip fonts with no path
431 if(GetFcString(fontPattern, FC_FILE, path))
433 // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
434 FcCharSet* characterSet = nullptr;
435 FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
437 // Increase the reference counter of the character set.
438 characterSetList.PushBack(FcCharSetCopy(characterSet));
440 fontList.push_back(FontDescription());
441 FontDescription& newFontDescription = fontList.back();
443 newFontDescription.path = std::move(path);
448 GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
449 GetFcInt(fontPattern, FC_WIDTH, width);
450 GetFcInt(fontPattern, FC_WEIGHT, weight);
451 GetFcInt(fontPattern, FC_SLANT, slant);
452 newFontDescription.width = IntToWidthType(width);
453 newFontDescription.weight = IntToWeightType(weight);
454 newFontDescription.slant = IntToSlantType(slant);
456 FONT_LOG_DESCRIPTION(newFontDescription, "");
460 // Destroys the font set created by FcFontSort.
461 FcFontSetDestroy(fontSet);
465 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " No fonts found.\n");
468 // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
469 FcPatternDestroy(fontFamilyPattern);
472 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts)
474 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
476 if(mDefaultFonts.empty())
478 FontDescription fontDescription;
479 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
480 fontDescription.width = DefaultFontWidth();
481 fontDescription.weight = DefaultFontWeight();
482 fontDescription.slant = DefaultFontSlant();
483 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
486 defaultFonts = mDefaultFonts;
488 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size());
491 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
493 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
495 if(!mDefaultFontDescriptionCached)
497 // Clear any font config stored info in the caches.
499 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
500 DestroyCharacterSets(mDefaultFontCharacterSets);
501 DestroyCharacterSets(mCharacterSetCache);
502 mDefaultFontCharacterSets.Clear();
503 mCharacterSetCache.Clear();
505 for(auto& item : mFallbackCache)
507 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
508 DestroyCharacterSets(*item.characterSets);
510 delete item.characterSets;
511 item.characterSets = nullptr;
514 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
515 ClearCharacterSetFromFontFaceCache();
517 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
518 FcInitReinitialize();
520 FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
522 if(nullptr != matchPattern)
524 FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
525 FcDefaultSubstitute(matchPattern);
527 FcCharSet* characterSet = nullptr;
528 MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
529 // Decrease the reference counter of the character set as it's not stored.
530 FcCharSetDestroy(characterSet);
532 // Destroys the pattern created.
533 FcPatternDestroy(matchPattern);
536 // Create again the character sets as they are not valid after FcInitReinitialize()
538 for(const auto& description : mDefaultFonts)
540 mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
543 for(const auto& description : mFontDescriptionCache)
545 mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
548 for(auto& item : mFallbackCache)
550 if(nullptr != item.fallbackFonts)
552 if(nullptr == item.characterSets)
554 item.characterSets = new CharacterSetList;
557 for(const auto& description : *(item.fallbackFonts))
559 item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
564 mDefaultFontDescriptionCached = true;
567 fontDescription.path = mDefaultFontDescription.path;
568 fontDescription.family = mDefaultFontDescription.family;
569 fontDescription.width = mDefaultFontDescription.width;
570 fontDescription.weight = mDefaultFontDescription.weight;
571 fontDescription.slant = mDefaultFontDescription.slant;
573 FONT_LOG_DESCRIPTION(fontDescription, "");
576 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts)
578 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
580 if(mSystemFonts.empty())
585 systemFonts = mSystemFonts;
586 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size());
589 void FontClient::Plugin::GetDescription(FontId id,
590 FontDescription& fontDescription) const
592 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
593 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
594 const FontId index = id - 1u;
596 if((id > 0u) && (index < mFontIdCache.Count()))
598 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
599 switch(fontIdCacheItem.type)
601 case FontDescription::FACE_FONT:
603 for(const auto& item : mFontDescriptionSizeCache)
605 if(item.fontId == fontIdCacheItem.id)
607 fontDescription = *(mFontDescriptionCache.begin() + item.validatedFontId - 1u);
609 FONT_LOG_DESCRIPTION(fontDescription, "");
615 case FontDescription::BITMAP_FONT:
617 fontDescription.type = FontDescription::BITMAP_FONT;
618 fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
623 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
624 fontDescription.type = FontDescription::INVALID;
625 fontDescription.family.clear();
630 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " No description found for the font ID %d\n", id);
633 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id)
635 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
636 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
637 const FontId index = id - 1u;
640 (index < mFontIdCache.Count()))
642 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
644 switch(fontIdCacheItem.type)
646 case FontDescription::FACE_FONT:
648 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " point size : %d\n", (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize);
649 return (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize;
651 case FontDescription::BITMAP_FONT:
653 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
657 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
663 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid font ID %d\n", id);
666 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE);
667 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
670 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character)
672 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
673 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
674 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " character : %p\n", character);
676 if((fontId < 1u) || (fontId > mFontIdCache.Count()))
678 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n", mFontIdCache.Count());
684 bool isSupported = false;
686 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
688 switch(fontIdCacheItem.type)
690 case FontDescription::FACE_FONT:
692 if(fontIdCacheItem.id < mFontFaceCache.size())
694 FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
696 if(nullptr == cacheItem.mCharacterSet)
698 // Create again the character set.
699 // It can be null if the ResetSystemDefaults() method has been called.
701 FontDescription description;
702 description.path = cacheItem.mPath;
703 description.family = std::move(FontFamily(cacheItem.mFreeTypeFace->family_name));
704 description.weight = FontWeight::NONE;
705 description.width = FontWidth::NONE;
706 description.slant = FontSlant::NONE;
708 // Note FreeType doesn't give too much info to build a proper font style.
709 if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
711 description.slant = FontSlant::ITALIC;
713 if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
715 description.weight = FontWeight::BOLD;
718 cacheItem.mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
721 isSupported = FcCharSetHasChar(cacheItem.mCharacterSet, character);
725 case FontDescription::BITMAP_FONT:
727 const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
729 for(const auto& glyph : bitmapFont.glyphs)
731 if(glyph.utf32 == character)
741 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
745 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false"));
749 FontId FontClient::Plugin::FindFontForCharacter(const FontList& fontList,
750 const CharacterSetList& characterSetList,
752 PointSize26Dot6 requestedPointSize,
755 DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
756 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
757 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " character : %p\n", character);
758 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
759 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
762 bool foundColor = false;
764 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of fonts : %d\n", fontList.size());
766 // Traverse the list of fonts.
767 // Check for each font if supports the character.
768 for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
770 const FontDescription& description = fontList[index];
771 const FcCharSet* const characterSet = characterSetList[index];
773 FONT_LOG_DESCRIPTION(description, "");
775 bool foundInRanges = false;
776 if(nullptr != characterSet)
778 foundInRanges = FcCharSetHasChar(characterSet, character);
783 fontId = GetFontId(description, requestedPointSize, 0u);
785 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " font id : %d\n", fontId);
790 (fontId - 1u < mFontIdCache.Count()))
792 const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
794 foundColor = item.mHasColorTables;
797 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " foundColor : %s\n", (foundColor ? "true" : "false"));
800 // Keep going unless we prefer a different (color) font.
801 if(!preferColor || foundColor)
808 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
812 FontId FontClient::Plugin::FindDefaultFont(Character charcode,
813 PointSize26Dot6 requestedPointSize,
816 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
817 FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
821 // Create the list of default fonts if it has not been created.
822 if(mDefaultFonts.empty())
824 FontDescription fontDescription;
825 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
826 fontDescription.width = DefaultFontWidth();
827 fontDescription.weight = DefaultFontWeight();
828 fontDescription.slant = DefaultFontSlant();
830 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
832 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size());
834 // Traverse the list of default fonts.
835 // Check for each default font if supports the character.
836 fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
838 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
842 FontId FontClient::Plugin::FindFallbackFont(Character charcode,
843 const FontDescription& preferredFontDescription,
844 PointSize26Dot6 requestedPointSize,
847 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
848 FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
850 // The font id to be returned.
853 FontDescription fontDescription;
855 // Fill the font description with the preferred font description and complete with the defaults.
856 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
857 fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? DefaultFontWeight() : preferredFontDescription.weight);
858 fontDescription.width = ((FontWidth::NONE == preferredFontDescription.width) ? DefaultFontWidth() : preferredFontDescription.width);
859 fontDescription.slant = ((FontSlant::NONE == preferredFontDescription.slant) ? DefaultFontSlant() : preferredFontDescription.slant);
861 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n");
862 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
863 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
864 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
865 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
867 // Check first if the font's description has been queried before.
868 FontList* fontList = nullptr;
869 CharacterSetList* characterSetList = nullptr;
871 if(!FindFallbackFontList(fontDescription, fontList, characterSetList))
873 fontList = new FontList;
874 characterSetList = new CharacterSetList;
876 SetFontList(fontDescription, *fontList, *characterSetList);
878 FontDescription appleColorEmoji;
879 appleColorEmoji.family = "Apple Color Emoji";
880 appleColorEmoji.width = fontDescription.width;
881 appleColorEmoji.weight = fontDescription.weight;
882 appleColorEmoji.slant = fontDescription.slant;
883 FontList emojiFontList;
884 CharacterSetList emojiCharSetList;
885 SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
887 std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
888 emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
889 *fontList = std::move(emojiFontList);
890 *characterSetList = std::move(emojiCharSetList);
893 // Add the font-list to the cache.
894 mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
897 if(fontList && characterSetList)
899 fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
902 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
906 FontId FontClient::Plugin::GetFontId(const FontPath& path,
907 PointSize26Dot6 requestedPointSize,
909 bool cacheDescription)
911 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
912 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
913 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
917 if(nullptr != mFreeTypeLibrary)
920 if(FindFont(path, requestedPointSize, faceIndex, foundId))
926 id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
930 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
934 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
935 PointSize26Dot6 requestedPointSize,
938 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
939 FONT_LOG_DESCRIPTION(fontDescription, "");
940 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
942 // This method uses three vectors which caches:
943 // * The bitmap font cache
944 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
945 // * The path to font file names.
946 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
948 // 1) Checks if the font description matches with a previously loaded bitmap font.
949 // Returns if a font is found.
950 // 2) Checks in the cache if the font's description has been validated before.
951 // If it was it gets an index to the vector with paths to font file names. Otherwise,
952 // retrieves using font config a path to a font file name which matches with the
953 // font's description. The path is stored in the cache.
955 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
956 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
957 // the GetFontId() method with the path to the font file name and the point size to
960 // The font id to be returned.
963 // Check first if the font description matches with a previously loaded bitmap font.
964 if(FindBitmapFont(fontDescription.family, fontId))
969 // Check if the font's description have been validated before.
970 FontDescriptionId validatedFontId = 0u;
972 if(!FindValidatedFont(fontDescription,
975 // Use font config to validate the font's description.
976 ValidateFont(fontDescription,
980 FontId fontFaceId = 0u;
981 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
982 if(!FindFont(validatedFontId, requestedPointSize, fontFaceId))
984 // Retrieve the font file name path.
985 const FontDescription& description = *(mFontDescriptionCache.begin() + validatedFontId - 1u);
987 // Retrieve the font id. Do not cache the description as it has been already cached.
988 fontId = GetFontId(description.path,
993 fontFaceId = mFontIdCache[fontId - 1u].id;
994 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(mCharacterSetCache[validatedFontId - 1u]);
996 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
997 mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
1003 fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
1006 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
1010 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont)
1012 for(const auto& item : mBitmapFontCache)
1014 if(bitmapFont.name == item.font.name)
1016 return item.id + 1u;
1020 BitmapFontCacheItem bitmapFontCacheItem;
1021 bitmapFontCacheItem.font = bitmapFont;
1022 bitmapFontCacheItem.id = mFontIdCache.Count();
1024 // Resize the vector with the pixel buffers.
1025 bitmapFontCacheItem.pixelBuffers.resize(bitmapFont.glyphs.size());
1027 // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
1028 unsigned int index = 0u;
1029 for(auto& glyph : bitmapFontCacheItem.font.glyphs)
1031 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1033 if(EqualsZero(glyph.ascender) && EqualsZero(glyph.descender))
1036 pixelBuffer = LoadImageFromFile(glyph.url);
1040 glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1044 bitmapFontCacheItem.font.ascender = std::max(glyph.ascender, bitmapFontCacheItem.font.ascender);
1045 bitmapFontCacheItem.font.descender = std::min(glyph.descender, bitmapFontCacheItem.font.descender);
1050 FontIdCacheItem fontIdCacheItem;
1051 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1052 fontIdCacheItem.id = mBitmapFontCache.size();
1054 mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
1055 mFontIdCache.PushBack(fontIdCacheItem);
1057 return bitmapFontCacheItem.id + 1u;
1060 void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
1061 FontDescriptionId& validatedFontId)
1063 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1064 FONT_LOG_DESCRIPTION(fontDescription, "");
1066 // Create a font pattern.
1067 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
1069 FontDescription description;
1071 FcCharSet* characterSet = nullptr;
1072 bool matched = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
1073 FcPatternDestroy(fontFamilyPattern);
1075 if(matched && (nullptr != characterSet))
1077 // Add the path to the cache.
1078 description.type = FontDescription::FACE_FONT;
1079 mFontDescriptionCache.push_back(description);
1081 // Set the index to the vector of paths to font file names.
1082 validatedFontId = mFontDescriptionCache.size();
1084 FONT_LOG_DESCRIPTION(description, "matched");
1085 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
1087 // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1088 mCharacterSetCache.PushBack(characterSet);
1090 // Cache the index and the matched font's description.
1091 FontDescriptionCacheItem item(description,
1094 mValidatedFontCache.push_back(std::move(item));
1096 if((fontDescription.family != description.family) ||
1097 (fontDescription.width != description.width) ||
1098 (fontDescription.weight != description.weight) ||
1099 (fontDescription.slant != description.slant))
1101 // Cache the given font's description if it's different than the matched.
1102 FontDescriptionCacheItem item(fontDescription,
1105 mValidatedFontCache.push_back(std::move(item));
1110 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str());
1114 void FontClient::Plugin::GetFontMetrics(FontId fontId,
1115 FontMetrics& metrics)
1117 const FontId index = fontId - 1u;
1120 (index < mFontIdCache.Count()))
1122 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1124 switch(fontIdCacheItem.type)
1126 case FontDescription::FACE_FONT:
1128 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1130 metrics = font.mMetrics;
1132 // Adjust the metrics if the fixed-size font should be down-scaled
1133 if(font.mIsFixedSizeBitmap)
1135 const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1137 if(desiredFixedSize > 0.f)
1139 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1141 metrics.ascender = metrics.ascender * scaleFactor;
1142 metrics.descender = metrics.descender * scaleFactor;
1143 metrics.height = metrics.height * scaleFactor;
1144 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1145 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1150 case FontDescription::BITMAP_FONT:
1152 const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1154 metrics.ascender = bitmapFontCacheItem.font.ascender;
1155 metrics.descender = bitmapFontCacheItem.font.descender;
1156 metrics.height = metrics.ascender - metrics.descender;
1157 metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1158 metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1163 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
1169 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
1173 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId fontId,
1176 GlyphIndex glyphIndex = 0u;
1177 const FontId index = fontId - 1u;
1180 (index < mFontIdCache.Count()))
1182 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1184 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1186 FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1188 glyphIndex = FT_Get_Char_Index(ftFace, charcode);
1195 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
1200 if(VECTOR_GLYPH == type)
1202 return GetVectorMetrics(array, size, horizontal);
1205 return GetBitmapMetrics(array, size, horizontal);
1208 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
1214 for(unsigned int i = 0; i < size; ++i)
1216 GlyphInfo& glyph = array[i];
1218 FontId index = glyph.fontId - 1u;
1220 if((glyph.fontId > 0u) &&
1221 (index < mFontIdCache.Count()))
1223 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1225 switch(fontIdCacheItem.type)
1227 case FontDescription::FACE_FONT:
1229 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1231 FT_Face ftFace = font.mFreeTypeFace;
1233 #ifdef FREETYPE_BITMAP_SUPPORT
1234 // Check to see if we should be loading a Fixed Size bitmap?
1235 if(font.mIsFixedSizeBitmap)
1237 FT_Select_Size(ftFace, font.mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
1238 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
1239 if(FT_Err_Ok == error)
1241 glyph.width = font.mFixedWidthPixels;
1242 glyph.height = font.mFixedHeightPixels;
1243 glyph.advance = font.mFixedWidthPixels;
1244 glyph.xBearing = 0.0f;
1245 glyph.yBearing = font.mFixedHeightPixels;
1247 // Adjust the metrics if the fixed-size font should be down-scaled
1248 const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1250 if(desiredFixedSize > 0.f)
1252 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1254 glyph.width = glyph.width * scaleFactor;
1255 glyph.height = glyph.height * scaleFactor;
1256 glyph.advance = glyph.advance * scaleFactor;
1257 glyph.xBearing = glyph.xBearing * scaleFactor;
1258 glyph.yBearing = glyph.yBearing * scaleFactor;
1260 glyph.scaleFactor = scaleFactor;
1265 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
1272 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1273 // i.e. with the SNum-3R font.
1274 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1275 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);
1277 // Keep the width of the glyph before doing the software emboldening.
1278 // It will be used to calculate a scale factor to be applied to the
1279 // advance as Harfbuzz doesn't apply any SW emboldening to calculate
1280 // the advance of the glyph.
1281 const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1283 if(FT_Err_Ok == error)
1285 const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
1286 if(isEmboldeningRequired)
1288 // Does the software bold.
1289 FT_GlyphSlot_Embolden(ftFace->glyph);
1292 glyph.width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1293 glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
1296 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
1297 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
1301 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
1302 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
1305 if(isEmboldeningRequired && !Dali::EqualsZero(width))
1307 // If the glyph is emboldened by software, the advance is multiplied by a
1308 // scale factor to make it slightly bigger.
1309 glyph.advance *= (glyph.width / width);
1312 // Use the bounding box of the bitmap to correct the metrics.
1313 // For some fonts i.e the SNum-3R the metrics need to be corrected,
1314 // otherwise the glyphs 'dance' up and down depending on the
1315 // font's point size.
1318 error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);
1321 FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
1323 const float descender = glyph.height - glyph.yBearing;
1324 glyph.height = (bbox.yMax - bbox.yMin) * FROM_266;
1325 glyph.yBearing = glyph.height - round(descender);
1327 // Created FT_Glyph object must be released with FT_Done_Glyph
1328 FT_Done_Glyph(ftGlyph);
1337 case FontDescription::BITMAP_FONT:
1339 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1341 unsigned int index = 0u;
1342 for(auto& item : bitmapFontCacheItem.font.glyphs)
1344 if(item.utf32 == glyph.index)
1346 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1349 pixelBuffer = LoadImageFromFile(item.url);
1352 glyph.width = static_cast<float>(pixelBuffer.GetWidth());
1353 glyph.height = static_cast<float>(pixelBuffer.GetHeight());
1354 glyph.xBearing = 0.f;
1355 glyph.yBearing = glyph.height + item.descender;
1356 glyph.advance = glyph.width;
1357 glyph.scaleFactor = 1.f;
1368 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
1374 // Check if it's an embedded image.
1375 if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count()))
1377 const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1379 glyph.width = static_cast<float>(item.width);
1380 glyph.height = static_cast<float>(item.height);
1381 glyph.xBearing = 0.f;
1382 glyph.yBearing = glyph.height;
1383 glyph.advance = glyph.width;
1384 glyph.scaleFactor = 1.f;
1396 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
1400 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1403 for(unsigned int i = 0u; i < size; ++i)
1405 FontId fontId = array[i].fontId;
1408 (fontId - 1u) < mFontIdCache.Count())
1410 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1412 if(!font.mVectorFontId)
1414 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1417 mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
1419 // Vector metrics are in EMs, convert to pixels
1420 const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
1421 array[i].width *= scale;
1422 array[i].height *= scale;
1423 array[i].xBearing *= scale;
1424 array[i].yBearing *= scale;
1425 array[i].advance *= scale;
1439 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
1441 const FontId index = fontId - 1u;
1444 (index < mFontIdCache.Count()))
1446 data.isColorBitmap = false;
1447 data.isColorEmoji = false;
1449 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1451 switch(fontIdCacheItem.type)
1453 case FontDescription::FACE_FONT:
1455 // For the software italics.
1456 bool isShearRequired = false;
1458 const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1459 FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1463 #ifdef FREETYPE_BITMAP_SUPPORT
1464 // Check to see if this is fixed size bitmap
1465 if(fontFaceCacheItem.mIsFixedSizeBitmap)
1467 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1472 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1473 // i.e. with the SNum-3R font.
1474 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1475 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
1477 if(FT_Err_Ok == error)
1479 if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
1481 // Does the software bold.
1482 FT_GlyphSlot_Embolden(ftFace->glyph);
1485 if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
1487 // Will do the software italic.
1488 isShearRequired = true;
1492 error = FT_Get_Glyph(ftFace->glyph, &glyph);
1494 // Convert to bitmap if necessary
1495 if(FT_Err_Ok == error)
1497 if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
1499 int offsetX = 0, offsetY = 0;
1500 bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
1502 // Create a bitmap for the outline
1505 // Retrieve the horizontal and vertical distance from the current pen position to the
1506 // left and top border of the glyph bitmap for a normal glyph before applying the outline.
1507 if(FT_Err_Ok == error)
1509 FT_Glyph normalGlyph;
1510 error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);
1512 error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
1513 if(FT_Err_Ok == error)
1515 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);
1517 offsetX = bitmapGlyph->left;
1518 offsetY = bitmapGlyph->top;
1521 // Created FT_Glyph object must be released with FT_Done_Glyph
1522 FT_Done_Glyph(normalGlyph);
1525 // Now apply the outline
1529 error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
1531 if(FT_Err_Ok == error)
1533 FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
1534 error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
1536 if(FT_Err_Ok == error)
1538 FT_Stroker_Done(stroker);
1542 DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
1547 DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
1551 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1552 if(FT_Err_Ok == error)
1554 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
1558 // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
1559 data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
1560 data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
1563 ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
1567 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
1572 ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
1575 data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1577 // Created FT_Glyph object must be released with FT_Done_Glyph
1578 FT_Done_Glyph(glyph);
1583 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
1587 case FontDescription::BITMAP_FONT:
1589 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1591 unsigned int index = 0u;
1592 for(auto& item : bitmapFontCacheItem.font.glyphs)
1594 if(item.utf32 == glyphIndex)
1596 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1599 pixelBuffer = LoadImageFromFile(item.url);
1602 data.width = pixelBuffer.GetWidth();
1603 data.height = pixelBuffer.GetHeight();
1605 data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1607 ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer());
1609 // Sets the pixel format.
1610 data.format = pixelBuffer.GetPixelFormat();
1619 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
1625 if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count()))
1627 // It's an embedded item.
1628 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1630 data.width = item.width;
1631 data.height = item.height;
1632 if(0u != item.pixelBufferId)
1634 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId - 1u].pixelBuffer;
1637 ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer());
1639 // Sets the pixel format.
1640 data.format = pixelBuffer.GetPixelFormat();
1645 // Creates the output buffer
1646 const unsigned int bufferSize = data.width * data.height * 4u;
1647 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1649 memset(data.buffer, 0u, bufferSize);
1651 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1657 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
1659 TextAbstraction::FontClient::GlyphBufferData data;
1661 CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
1663 return PixelData::New(data.buffer,
1664 data.width * data.height * Pixel::GetBytesPerPixel(data.format),
1668 PixelData::DELETE_ARRAY);
1671 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
1676 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1678 (fontId - 1u < mFontIdCache.Count()))
1680 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1681 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1683 if(!font.mVectorFontId)
1685 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1688 mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1693 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
1695 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1696 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize);
1698 // First look into the cache if there is an ellipsis glyph for the requested point size.
1699 for(const auto& item : mEllipsisCache)
1701 if(item.requestedPointSize == requestedPointSize)
1703 // Use the glyph in the cache.
1704 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1705 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1710 // No glyph has been found. Create one.
1711 mEllipsisCache.PushBack(EllipsisItem());
1712 EllipsisItem& item = *(mEllipsisCache.End() - 1u);
1714 item.requestedPointSize = requestedPointSize;
1716 // Find a font for the ellipsis glyph.
1717 item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1721 // Set the character index to access the glyph inside the font.
1722 item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].id].mFreeTypeFace,
1723 ELLIPSIS_CHARACTER);
1725 GetBitmapMetrics(&item.glyph, 1u, true);
1727 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1728 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1732 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
1734 FT_Error error = -1;
1736 const FontId index = fontId - 1u;
1739 (index < mFontIdCache.Count()))
1741 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1743 switch(fontIdCacheItem.type)
1745 case FontDescription::FACE_FONT:
1747 #ifdef FREETYPE_BITMAP_SUPPORT
1748 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1749 FT_Face ftFace = item.mFreeTypeFace;
1751 // Check to see if this is fixed size bitmap
1752 if(item.mHasColorTables)
1754 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1759 case FontDescription::BITMAP_FONT:
1761 error = FT_Err_Ok; // Will return true;
1766 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
1771 return FT_Err_Ok == error;
1774 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId)
1776 FT_Face fontFace = nullptr;
1778 const FontId index = fontId - 1u;
1780 (index < mFontIdCache.Count()))
1782 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1784 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1786 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1792 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId)
1794 const FontId index = fontId - 1u;
1796 (index < mFontIdCache.Count()))
1798 return mFontIdCache[index].type;
1800 return FontDescription::INVALID;
1803 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1805 // nullptr as first parameter means the current configuration is used.
1806 return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1809 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
1811 EmbeddedItem embeddedItem;
1813 embeddedItem.pixelBufferId = 0u;
1814 embeddedItem.width = description.width;
1815 embeddedItem.height = description.height;
1817 pixelFormat = Pixel::A8;
1819 if(!description.url.empty())
1821 // Check if the url is in the cache.
1822 PixelBufferId index = 0u;
1824 for(const auto& cacheItem : mPixelBufferCache)
1827 if(cacheItem.url == description.url)
1829 // The url is in the pixel buffer cache.
1830 // Set the index +1 to the vector.
1831 embeddedItem.pixelBufferId = index;
1836 Devel::PixelBuffer pixelBuffer;
1837 if(0u == embeddedItem.pixelBufferId)
1839 // The pixel buffer is not in the cache. Create one and cache it.
1841 // Load the image from the url.
1842 pixelBuffer = LoadImageFromFile(description.url);
1844 // Create the cache item.
1845 PixelBufferCacheItem pixelBufferCacheItem;
1846 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1847 pixelBufferCacheItem.url = description.url;
1849 // Store the cache item in the cache.
1850 mPixelBufferCache.push_back(std::move(pixelBufferCacheItem));
1852 // Set the pixel buffer id to the embedded item.
1853 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1857 // Retrieve the pixel buffer from the cache to set the pixel format.
1858 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1863 // Set the size of the embedded item if it has not been set.
1864 if(0u == embeddedItem.width)
1866 embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1869 if(0u == embeddedItem.height)
1871 embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1874 // Set the pixel format.
1875 pixelFormat = pixelBuffer.GetPixelFormat();
1879 // Find if the same embeddedItem has already been created.
1880 unsigned int index = 0u;
1881 for(const auto& item : mEmbeddedItemCache)
1884 if((item.pixelBufferId == embeddedItem.pixelBufferId) &&
1885 (item.width == embeddedItem.width) &&
1886 (item.height == embeddedItem.height))
1892 // Cache the embedded item.
1893 mEmbeddedItemCache.PushBack(embeddedItem);
1895 return mEmbeddedItemCache.Count();
1898 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
1900 mIsAtlasLimitationEnabled = enabled;
1903 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
1905 return mIsAtlasLimitationEnabled;
1908 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
1910 return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1913 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
1915 return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
1918 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
1920 return mCurrentMaximumBlockSizeFitInAtlas;
1923 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
1925 bool isChanged = false;
1926 const Size& maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1927 const uint16_t& padding = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
1929 if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
1931 mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
1938 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
1940 return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
1944 void FontClient::Plugin::InitSystemFonts()
1946 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1948 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1952 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont);
1954 // Reserve some space to avoid reallocations.
1955 mSystemFonts.reserve(fontSet->nfont);
1957 for(int i = 0u; i < fontSet->nfont; ++i)
1959 FcPattern* fontPattern = fontSet->fonts[i];
1963 // Skip fonts with no path
1964 if(GetFcString(fontPattern, FC_FILE, path))
1966 mSystemFonts.push_back(FontDescription());
1967 FontDescription& fontDescription = mSystemFonts.back();
1969 fontDescription.path = std::move(path);
1974 GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
1975 GetFcInt(fontPattern, FC_WIDTH, width);
1976 GetFcInt(fontPattern, FC_WEIGHT, weight);
1977 GetFcInt(fontPattern, FC_SLANT, slant);
1978 fontDescription.width = IntToWidthType(width);
1979 fontDescription.weight = IntToWeightType(weight);
1980 fontDescription.slant = IntToSlantType(slant);
1982 FONT_LOG_DESCRIPTION(fontDescription, "");
1986 // Destroys the font set created.
1987 FcFontSetDestroy(fontSet);
1991 bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
1993 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1995 FcResult result = FcResultMatch;
1996 FcPattern* match = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1998 const bool matched = nullptr != match;
1999 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " pattern matched : %s\n", (matched ? "true" : "false"));
2006 GetFcString(match, FC_FILE, fontDescription.path);
2007 GetFcString(match, FC_FAMILY, fontDescription.family);
2008 GetFcInt(match, FC_WIDTH, width);
2009 GetFcInt(match, FC_WEIGHT, weight);
2010 GetFcInt(match, FC_SLANT, slant);
2011 fontDescription.width = IntToWidthType(width);
2012 fontDescription.weight = IntToWeightType(weight);
2013 fontDescription.slant = IntToSlantType(slant);
2015 // Retrieve the character set and increase the reference counter.
2016 FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
2017 *characterSet = FcCharSetCopy(*characterSet);
2019 // destroyed the matched pattern
2020 FcPatternDestroy(match);
2021 FONT_LOG_DESCRIPTION(fontDescription, "");
2026 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
2028 FcFontSet* fontset = nullptr;
2030 // create a new pattern.
2031 // a pattern holds a set of names, each name refers to a property of the font
2032 FcPattern* pattern = FcPatternCreate();
2034 if(nullptr != pattern)
2036 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
2037 FcObjectSet* objectSet = FcObjectSetCreate();
2039 if(nullptr != objectSet)
2041 // build an object set from a list of property names
2042 FcObjectSetAdd(objectSet, FC_FILE);
2043 FcObjectSetAdd(objectSet, FC_FAMILY);
2044 FcObjectSetAdd(objectSet, FC_WIDTH);
2045 FcObjectSetAdd(objectSet, FC_WEIGHT);
2046 FcObjectSetAdd(objectSet, FC_SLANT);
2048 // get a list of fonts
2049 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
2050 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.
2052 // clear up the object set
2053 FcObjectSetDestroy(objectSet);
2056 // clear up the pattern
2057 FcPatternDestroy(pattern);
2063 bool FontClient::Plugin::GetFcString(const FcPattern* const pattern,
2064 const char* const n,
2065 std::string& string)
2067 FcChar8* file = nullptr;
2068 const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
2070 if(FcResultMatch == retVal)
2072 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
2073 string.assign(reinterpret_cast<const char*>(file));
2081 bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
2083 const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
2085 if(FcResultMatch == retVal)
2093 FontId FontClient::Plugin::CreateFont(const FontPath& path,
2094 PointSize26Dot6 requestedPointSize,
2095 FaceIndex faceIndex,
2096 bool cacheDescription)
2098 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2099 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
2100 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2104 // Create & cache new font face
2106 int error = FT_New_Face(mFreeTypeLibrary,
2111 if(FT_Err_Ok == error)
2113 // Check if a font is scalable.
2114 const bool isScalable = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
2115 const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
2116 const bool hasColorTables = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
2117 FontId fontFaceId = 0u;
2119 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " isScalable : [%s]\n", (isScalable ? "true" : "false"));
2120 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
2121 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
2123 // Check to see if the font contains fixed sizes?
2124 if(!isScalable && hasFixedSizedBitmaps)
2126 PointSize26Dot6 actualPointSize = 0u;
2127 int fixedSizeIndex = 0;
2128 for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
2130 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2131 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
2133 if(fixedSize >= requestedPointSize)
2135 actualPointSize = fixedSize;
2140 if(0u == actualPointSize)
2142 // The requested point size is bigger than the bigest fixed size.
2143 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2144 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2147 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
2149 // Tell Freetype to use this size
2150 error = FT_Select_Size(ftFace, fixedSizeIndex);
2151 if(FT_Err_Ok != error)
2153 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
2157 const float fixedWidth = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
2158 const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
2160 // Indicate that the font is a fixed sized bitmap
2161 FontMetrics metrics(fixedHeight, // The ascender in pixels.
2163 fixedHeight, // The height in pixels.
2167 // Create the FreeType font face item to cache.
2168 FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
2170 // Set the index to the font's id cache.
2171 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2173 // Create the font id item to cache.
2174 FontIdCacheItem fontIdCacheItem;
2175 fontIdCacheItem.type = FontDescription::FACE_FONT;
2177 // Set the index to the FreeType font face cache.
2178 fontIdCacheItem.id = mFontFaceCache.size();
2179 fontFaceId = fontIdCacheItem.id + 1u;
2182 mFontFaceCache.push_back(fontFaceCacheItem);
2183 mFontIdCache.PushBack(fontIdCacheItem);
2185 // Set the font id to be returned.
2186 id = mFontIdCache.Count();
2191 if(mIsAtlasLimitationEnabled)
2193 //There is limitation on block size to fit in predefined atlas size.
2194 //If the block size cannot fit into atlas size, then the system cannot draw block.
2195 //This is workaround to avoid issue in advance
2196 //Decrementing point-size until arriving to maximum allowed block size.
2197 auto requestedPointSizeBackup = requestedPointSize;
2198 const Size& maxSizeFitInAtlas = GetCurrentMaximumBlockSizeFitInAtlas();
2199 error = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
2201 if(requestedPointSize != requestedPointSizeBackup)
2203 DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
2208 error = FT_Set_Char_Size(ftFace,
2215 if(FT_Err_Ok == error)
2217 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2219 FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
2220 static_cast<float>(ftMetrics.descender) * FROM_266,
2221 static_cast<float>(ftMetrics.height) * FROM_266,
2222 static_cast<float>(ftFace->underline_position) * FROM_266,
2223 static_cast<float>(ftFace->underline_thickness) * FROM_266);
2225 // Create the FreeType font face item to cache.
2226 FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics);
2228 // Set the index to the font's id cache.
2229 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2231 // Create the font id item to cache.
2232 FontIdCacheItem fontIdCacheItem;
2233 fontIdCacheItem.type = FontDescription::FACE_FONT;
2235 // Set the index to the FreeType font face cache.
2236 fontIdCacheItem.id = mFontFaceCache.size();
2237 fontFaceId = fontIdCacheItem.id + 1u;
2240 mFontFaceCache.push_back(fontFaceCacheItem);
2241 mFontIdCache.PushBack(fontIdCacheItem);
2243 // Set the font id to be returned.
2244 id = mFontIdCache.Count();
2248 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
2252 if(0u != fontFaceId)
2254 if(cacheDescription)
2256 CacheFontPath(ftFace, fontFaceId, requestedPointSize, path);
2262 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str());
2265 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
2269 bool FontClient::Plugin::FindFont(const FontPath& path,
2270 PointSize26Dot6 requestedPointSize,
2271 FaceIndex faceIndex,
2272 FontId& fontId) const
2274 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2275 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
2276 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2277 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size());
2280 for(const auto& cacheItem : mFontFaceCache)
2282 if(cacheItem.mRequestedPointSize == requestedPointSize &&
2283 cacheItem.mFaceIndex == faceIndex &&
2284 cacheItem.mPath == path)
2286 fontId = cacheItem.mFontId + 1u;
2288 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font found, id : %d\n", fontId);
2293 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font not found\n");
2297 bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
2298 FontDescriptionId& validatedFontId)
2300 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2301 FONT_LOG_DESCRIPTION(fontDescription, "");
2302 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
2304 validatedFontId = 0u;
2306 for(const auto& item : mValidatedFontCache)
2308 if(!fontDescription.family.empty() &&
2309 (fontDescription.family == item.fontDescription.family) &&
2310 (fontDescription.width == item.fontDescription.width) &&
2311 (fontDescription.weight == item.fontDescription.weight) &&
2312 (fontDescription.slant == item.fontDescription.slant))
2314 validatedFontId = item.index;
2316 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId);
2321 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font not found\n");
2325 bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription,
2326 FontList*& fontList,
2327 CharacterSetList*& characterSetList)
2329 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2330 FONT_LOG_DESCRIPTION(fontDescription, "");
2331 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size());
2335 for(const auto& item : mFallbackCache)
2337 if(!fontDescription.family.empty() &&
2338 (fontDescription.family == item.fontDescription.family) &&
2339 (fontDescription.width == item.fontDescription.width) &&
2340 (fontDescription.weight == item.fontDescription.weight) &&
2341 (fontDescription.slant == item.fontDescription.slant))
2343 fontList = item.fallbackFonts;
2344 characterSetList = item.characterSets;
2346 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fallback font list found.\n");
2351 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fallback font list not found.\n");
2355 bool FontClient::Plugin::FindFont(FontDescriptionId validatedFontId,
2356 PointSize26Dot6 requestedPointSize,
2359 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2360 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
2361 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2365 for(const auto& item : mFontDescriptionSizeCache)
2367 if((validatedFontId == item.validatedFontId) &&
2368 (requestedPointSize == item.requestedPointSize))
2370 fontId = item.fontId;
2372 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font found, id : %d\n", fontId);
2377 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font not found.\n");
2381 bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const
2385 for(const auto& item : mBitmapFontCache)
2387 if(bitmapFont == item.font.name)
2389 fontId = item.id + 1u;
2397 bool FontClient::Plugin::IsScalable(const FontPath& path)
2399 bool isScalable = false;
2402 int error = FT_New_Face(mFreeTypeLibrary,
2406 if(FT_Err_Ok != error)
2408 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
2412 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2418 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription)
2420 // Create a font pattern.
2421 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2423 FcResult result = FcResultMatch;
2425 // match the pattern
2426 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2427 bool isScalable = false;
2431 // Get the path to the font file name.
2433 GetFcString(match, FC_FILE, path);
2434 isScalable = IsScalable(path);
2438 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2441 // Destroys the created patterns.
2442 FcPatternDestroy(match);
2443 FcPatternDestroy(fontFamilyPattern);
2448 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes)
2450 // Empty the caller container
2454 int error = FT_New_Face(mFreeTypeLibrary,
2458 if(FT_Err_Ok != error)
2460 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
2463 // Fetch the number of fixed sizes available
2464 if(ftFace->num_fixed_sizes && ftFace->available_sizes)
2466 for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
2468 sizes.PushBack(ftFace->available_sizes[i].size);
2473 void FontClient::Plugin::GetFixedSizes(const FontDescription& fontDescription,
2474 Vector<PointSize26Dot6>& sizes)
2476 // Create a font pattern.
2477 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2479 FcResult result = FcResultMatch;
2481 // match the pattern
2482 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2486 // Get the path to the font file name.
2488 GetFcString(match, FC_FILE, path);
2489 GetFixedSizes(path, sizes);
2493 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2496 // Destroys the created patterns.
2497 FcPatternDestroy(match);
2498 FcPatternDestroy(fontFamilyPattern);
2501 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
2503 bool hasItalicStyle = false;
2505 const FontId index = fontId - 1u;
2508 (index < mFontIdCache.Count()))
2510 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2512 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
2514 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2516 hasItalicStyle = 0u != (font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC);
2521 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
2524 return hasItalicStyle;
2527 void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path)
2529 FontDescription description;
2530 description.path = path;
2531 description.family = std::move(FontFamily(ftFace->family_name));
2532 description.weight = FontWeight::NONE;
2533 description.width = FontWidth::NONE;
2534 description.slant = FontSlant::NONE;
2536 // Note FreeType doesn't give too much info to build a proper font style.
2537 if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
2539 description.slant = FontSlant::ITALIC;
2541 if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
2543 description.weight = FontWeight::BOLD;
2546 FontDescriptionId validatedFontId = 0u;
2547 if(!FindValidatedFont(description,
2550 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2552 FcResult result = FcResultMatch;
2553 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2555 FcCharSet* characterSet = nullptr;
2556 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2558 const FontId fontFaceId = id - 1u;
2559 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
2561 // Destroys the created patterns.
2562 FcPatternDestroy(match);
2563 FcPatternDestroy(pattern);
2565 // Add the path to the cache.
2566 description.type = FontDescription::FACE_FONT;
2567 mFontDescriptionCache.push_back(description);
2569 // Set the index to the vector of paths to font file names.
2570 validatedFontId = mFontDescriptionCache.size();
2572 // Increase the reference counter and add the character set to the cache.
2573 mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
2575 // Cache the index and the font's description.
2576 mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
2579 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2580 mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
2586 void FontClient::Plugin::ClearFallbackCache(std::vector<FallbackCacheItem>& fallbackCache)
2588 for(auto& item : fallbackCache)
2590 if(nullptr != item.fallbackFonts)
2592 delete item.fallbackFonts;
2595 if(nullptr != item.characterSets)
2597 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2598 DestroyCharacterSets(*item.characterSets);
2599 delete item.characterSets;
2604 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2606 for(auto& item : mFontFaceCache)
2608 FcCharSetDestroy(item.mCharacterSet);
2609 item.mCharacterSet = nullptr;
2613 } // namespace Internal
2615 } // namespace TextAbstraction