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/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/font-client-helper.h>
30 #include <dali/public-api/common/dali-vector.h>
31 #include <dali/public-api/common/vector-wrapper.h>
34 #include <fontconfig/fontconfig.h>
40 #if defined(DEBUG_ENABLED)
41 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
45 * Conversion from Fractional26.6 to float
47 const float FROM_266 = 1.0f / 64.0f;
48 const float POINTS_PER_INCH = 72.f;
50 const std::string DEFAULT_FONT_FAMILY_NAME("Tizen");
51 const int DEFAULT_FONT_WIDTH = 100; // normal
52 const int DEFAULT_FONT_WEIGHT = 80; // normal
53 const int DEFAULT_FONT_SLANT = 0; // normal
55 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
57 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
59 // NONE -1 --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
69 const int FONT_WIDTH_TYPE_TO_INT[] = {-1, 50, 63, 75, 87, 100, 113, 125, 150, 200};
70 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof(FONT_WIDTH_TYPE_TO_INT) / sizeof(int);
72 // NONE -1 --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
74 // ULTRA_LIGHT, EXTRA_LIGHT 40
76 // DEMI_LIGHT, SEMI_LIGHT 55
80 // DEMI_BOLD, SEMI_BOLD 180
82 // ULTRA_BOLD, EXTRA_BOLD 205
83 // BLACK, HEAVY, EXTRA_BLACK 210
84 const int FONT_WEIGHT_TYPE_TO_INT[] = {-1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210};
85 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof(FONT_WEIGHT_TYPE_TO_INT) / sizeof(int);
87 // NONE -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
91 const int FONT_SLANT_TYPE_TO_INT[] = {-1, 0, 100, 110};
92 const unsigned int NUM_FONT_SLANT_TYPE = sizeof(FONT_SLANT_TYPE_TO_INT) / sizeof(int);
101 namespace TextAbstraction
106 * @brief Returns the FontWidth's enum index for the given width value.
108 * @param[in] width The width value.
110 * @return The FontWidth's enum index.
112 FontWidth::Type IntToWidthType(int width)
114 return static_cast<FontWidth::Type>(ValueToIndex(width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u));
118 * @brief Returns the FontWeight's enum index for the given weight value.
120 * @param[in] weight The weight value.
122 * @return The FontWeight's enum index.
124 FontWeight::Type IntToWeightType(int weight)
126 return static_cast<FontWeight::Type>(ValueToIndex(weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u));
130 * @brief Returns the FontSlant's enum index for the given slant value.
132 * @param[in] slant The slant value.
134 * @return The FontSlant's enum index.
136 FontSlant::Type IntToSlantType(int slant)
138 return static_cast<FontSlant::Type>(ValueToIndex(slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u));
142 * @brief Free the resources allocated by the FcCharSet objects.
144 * @param[in] characterSets The vector of character sets.
146 void DestroyCharacterSets(CharacterSetList& characterSets)
148 for(auto& item : characterSets)
152 FcCharSetDestroy(item);
158 * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
160 * @param[in/out] ftFace Face type object.
161 * @param[in] horizontalDpi The horizontal dpi.
162 * @param[in] verticalDpi The vertical dpi.
163 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
164 * @param[in] requestedPointSize The requested point-size.
165 * @return whether the ftFace's block can fit into atlas
167 bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
171 error = FT_Set_Char_Size(ftFace,
177 if( error == FT_Err_Ok)
179 //Check width and height of block for requestedPointSize
180 //If the width or height is greater than the maximum-size then decrement by one unit of point-size.
181 if( static_cast<float>(ftFace->size->metrics.height) * FROM_266 <= maxSizeFitInAtlas.height
182 && (static_cast<float>(ftFace->size->metrics.ascender)-static_cast<float>(ftFace->size->metrics.descender))* FROM_266 <= maxSizeFitInAtlas.width)
192 * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
194 * @param[in/out] ftFace Face type object.
195 * @param[in] horizontalDpi The horizontal dpi.
196 * @param[in] verticalDpi The vertical dpi.
197 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
198 * @param[in/out] requestedPointSize The requested point-size.
199 * @return FreeType error code. 0 means success when requesting the nominal size (in points).
201 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
203 //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
204 const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
206 int error; // FreeType error code.
208 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
209 if(FT_Err_Ok != error)
217 uint32_t exponentialDecrement = 1;
219 while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit*exponentialDecrement)
221 requestedPointSize-=(pointSizePerOneUnit*exponentialDecrement);
222 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
223 if(FT_Err_Ok != error)
228 exponentialDecrement*=2;
232 uint32_t minPointSize;
233 uint32_t maxPointSize;
237 exponentialDecrement/=2;
238 minPointSize = requestedPointSize;
239 maxPointSize = requestedPointSize + (pointSizePerOneUnit*exponentialDecrement);
244 maxPointSize = requestedPointSize;
247 while(minPointSize < maxPointSize)
249 requestedPointSize = ((maxPointSize/pointSizePerOneUnit - minPointSize/pointSizePerOneUnit)/2) * pointSizePerOneUnit + minPointSize;
250 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
251 if(FT_Err_Ok != error)
258 if(minPointSize == requestedPointSize)
260 //Found targeted point-size
264 minPointSize = requestedPointSize;
268 maxPointSize = requestedPointSize;
277 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
278 : fontDescription{std::move(font)},
279 fallbackFonts{fallbackFonts},
280 characterSets{characterSets}
284 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
285 FontDescriptionId index)
286 : fontDescription{fontDescription},
291 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
292 FontDescriptionId index)
293 : fontDescription{std::move(fontDescription)},
298 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem(FontDescriptionId validatedFontId,
299 PointSize26Dot6 requestedPointSize,
301 : validatedFontId(validatedFontId),
302 requestedPointSize(requestedPointSize),
307 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace,
308 const FontPath& path,
309 PointSize26Dot6 requestedPointSize,
311 const FontMetrics& metrics)
312 : mFreeTypeFace(ftFace),
314 mRequestedPointSize(requestedPointSize),
317 mCharacterSet(nullptr),
319 mFixedWidthPixels(0.f),
320 mFixedHeightPixels(0.f),
323 mIsFixedSizeBitmap(false),
324 mHasColorTables(false)
328 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace,
329 const FontPath& path,
330 PointSize26Dot6 requestedPointSize,
332 const FontMetrics& metrics,
337 : mFreeTypeFace(ftFace),
339 mRequestedPointSize(requestedPointSize),
342 mCharacterSet(nullptr),
343 mFixedSizeIndex(fixedSizeIndex),
344 mFixedWidthPixels(fixedWidth),
345 mFixedHeightPixels(fixedHeight),
348 mIsFixedSizeBitmap(true),
349 mHasColorTables(hasColorTables)
353 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
354 unsigned int verticalDpi)
355 : mFreeTypeLibrary(nullptr),
356 mDpiHorizontal(horizontalDpi),
357 mDpiVertical(verticalDpi),
358 mDefaultFontDescription(),
363 mValidatedFontCache(),
364 mFontDescriptionCache(),
365 mCharacterSetCache(),
366 mFontDescriptionSizeCache(),
367 mVectorFontCache(nullptr),
369 mEmbeddedItemCache(),
370 mDefaultFontDescriptionCached(false),
371 mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
372 mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS)
375 int error = FT_Init_FreeType(&mFreeTypeLibrary);
376 if(FT_Err_Ok != error)
378 DALI_LOG_INFO(gLogFilter, Debug::General, "FreeType Init error: %d\n", error);
381 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
382 mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
386 FontClient::Plugin::~Plugin()
388 ClearFallbackCache(mFallbackCache);
390 // Free the resources allocated by the FcCharSet objects.
391 DestroyCharacterSets(mDefaultFontCharacterSets);
392 DestroyCharacterSets(mCharacterSetCache);
393 ClearCharacterSetFromFontFaceCache();
395 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
396 delete mVectorFontCache;
398 FT_Done_FreeType(mFreeTypeLibrary);
401 void FontClient::Plugin::ClearCache()
403 mDefaultFontDescription = FontDescription();
405 mSystemFonts.clear();
406 mDefaultFonts.clear();
408 DestroyCharacterSets(mDefaultFontCharacterSets);
409 mDefaultFontCharacterSets.Clear();
411 ClearFallbackCache(mFallbackCache);
412 mFallbackCache.clear();
414 mFontIdCache.Clear();
416 ClearCharacterSetFromFontFaceCache();
417 mFontFaceCache.clear();
419 mValidatedFontCache.clear();
420 mFontDescriptionCache.clear();
422 DestroyCharacterSets(mCharacterSetCache);
423 mCharacterSetCache.Clear();
425 mFontDescriptionSizeCache.clear();
427 mEllipsisCache.Clear();
428 mPixelBufferCache.clear();
429 mEmbeddedItemCache.Clear();
430 mBitmapFontCache.clear();
432 mDefaultFontDescriptionCached = false;
435 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
436 unsigned int verticalDpi)
438 mDpiHorizontal = horizontalDpi;
439 mDpiVertical = verticalDpi;
442 void FontClient::Plugin::ResetSystemDefaults()
444 mDefaultFontDescriptionCached = false;
447 void FontClient::Plugin::SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
449 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n");
450 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
451 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
452 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
453 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
457 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
459 FcResult result = FcResultMatch;
461 // Match the pattern.
462 FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
464 false /* don't trim */,
466 &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
468 if(nullptr != fontSet)
470 DALI_LOG_INFO(gLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont);
471 // Reserve some space to avoid reallocations.
472 fontList.reserve(fontSet->nfont);
474 for(int i = 0u; i < fontSet->nfont; ++i)
476 FcPattern* fontPattern = fontSet->fonts[i];
480 // Skip fonts with no path
481 if(GetFcString(fontPattern, FC_FILE, path))
483 // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
484 FcCharSet* characterSet = nullptr;
485 FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
487 // Increase the reference counter of the character set.
488 characterSetList.PushBack(FcCharSetCopy(characterSet));
490 fontList.push_back(FontDescription());
491 FontDescription& newFontDescription = fontList.back();
493 newFontDescription.path = std::move(path);
498 GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
499 GetFcInt(fontPattern, FC_WIDTH, width);
500 GetFcInt(fontPattern, FC_WEIGHT, weight);
501 GetFcInt(fontPattern, FC_SLANT, slant);
502 newFontDescription.width = IntToWidthType(width);
503 newFontDescription.weight = IntToWeightType(weight);
504 newFontDescription.slant = IntToSlantType(slant);
506 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", newFontDescription.family.c_str());
507 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", newFontDescription.path.c_str());
508 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[newFontDescription.width]);
509 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[newFontDescription.weight]);
510 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant]);
514 // Destroys the font set created by FcFontSort.
515 FcFontSetDestroy(fontSet);
519 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " No fonts found.\n");
522 // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
523 FcPatternDestroy(fontFamilyPattern);
525 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n");
528 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts)
530 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n");
532 if(mDefaultFonts.empty())
534 FontDescription fontDescription;
535 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
536 fontDescription.width = IntToWidthType(DEFAULT_FONT_WIDTH);
537 fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT);
538 fontDescription.slant = IntToSlantType(DEFAULT_FONT_SLANT);
539 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
542 defaultFonts = mDefaultFonts;
544 DALI_LOG_INFO(gLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size());
545 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n");
548 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
550 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
552 if(!mDefaultFontDescriptionCached)
554 // Clear any font config stored info in the caches.
556 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
557 DestroyCharacterSets(mDefaultFontCharacterSets);
558 DestroyCharacterSets(mCharacterSetCache);
559 mDefaultFontCharacterSets.Clear();
560 mCharacterSetCache.Clear();
562 for(auto& item : mFallbackCache)
564 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
565 DestroyCharacterSets(*item.characterSets);
567 delete item.characterSets;
568 item.characterSets = nullptr;
571 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
572 ClearCharacterSetFromFontFaceCache();
574 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
575 FcInitReinitialize();
577 FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
579 if(nullptr != matchPattern)
581 FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
582 FcDefaultSubstitute(matchPattern);
584 FcCharSet* characterSet = nullptr;
585 MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
586 // Decrease the reference counter of the character set as it's not stored.
587 FcCharSetDestroy(characterSet);
589 // Destroys the pattern created.
590 FcPatternDestroy(matchPattern);
593 // Create again the character sets as they are not valid after FcInitReinitialize()
595 for(const auto& description : mDefaultFonts)
597 mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
600 for(const auto& description : mFontDescriptionCache)
602 mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
605 for(auto& item : mFallbackCache)
607 if(nullptr != item.fallbackFonts)
609 if(nullptr == item.characterSets)
611 item.characterSets = new CharacterSetList;
614 for(const auto& description : *(item.fallbackFonts))
616 item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
621 mDefaultFontDescriptionCached = true;
624 fontDescription.path = mDefaultFontDescription.path;
625 fontDescription.family = mDefaultFontDescription.family;
626 fontDescription.width = mDefaultFontDescription.width;
627 fontDescription.weight = mDefaultFontDescription.weight;
628 fontDescription.slant = mDefaultFontDescription.slant;
630 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
631 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
632 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
633 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
634 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
635 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
638 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts)
640 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
642 if(mSystemFonts.empty())
647 systemFonts = mSystemFonts;
648 DALI_LOG_INFO(gLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size());
649 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
652 void FontClient::Plugin::GetDescription(FontId id,
653 FontDescription& fontDescription) const
655 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
656 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
657 const FontId index = id - 1u;
659 if((id > 0u) && (index < mFontIdCache.Count()))
661 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
662 switch(fontIdCacheItem.type)
664 case FontDescription::FACE_FONT:
666 for(const auto& item : mFontDescriptionSizeCache)
668 if(item.fontId == fontIdCacheItem.id)
670 fontDescription = *(mFontDescriptionCache.begin() + item.validatedFontId - 1u);
672 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
673 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
674 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
675 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
676 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
677 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
683 case FontDescription::BITMAP_FONT:
685 fontDescription.type = FontDescription::BITMAP_FONT;
686 fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
691 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
692 fontDescription.type = FontDescription::INVALID;
693 fontDescription.family.clear();
698 DALI_LOG_INFO(gLogFilter, Debug::General, " No description found for the font ID %d\n", id);
699 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
702 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id)
704 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
705 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
706 const FontId index = id - 1u;
709 (index < mFontIdCache.Count()))
711 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
713 switch(fontIdCacheItem.type)
715 case FontDescription::FACE_FONT:
717 DALI_LOG_INFO(gLogFilter, Debug::General, " point size : %d\n", (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize);
718 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
719 return (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize;
721 case FontDescription::BITMAP_FONT:
723 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
727 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
733 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid font ID %d\n", id);
736 DALI_LOG_INFO(gLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE);
737 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
738 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
741 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character)
743 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
744 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
745 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", character);
747 if((fontId < 1u) || (fontId > mFontIdCache.Count()))
749 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n", mFontIdCache.Count());
750 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
756 bool isSupported = false;
758 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
760 switch(fontIdCacheItem.type)
762 case FontDescription::FACE_FONT:
764 if(fontIdCacheItem.id < mFontFaceCache.size())
766 FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
768 if(nullptr == cacheItem.mCharacterSet)
770 // Create again the character set.
771 // It can be null if the ResetSystemDefaults() method has been called.
773 FontDescription description;
774 description.path = cacheItem.mPath;
775 description.family = std::move(FontFamily(cacheItem.mFreeTypeFace->family_name));
776 description.weight = FontWeight::NONE;
777 description.width = FontWidth::NONE;
778 description.slant = FontSlant::NONE;
780 // Note FreeType doesn't give too much info to build a proper font style.
781 if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
783 description.slant = FontSlant::ITALIC;
785 if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
787 description.weight = FontWeight::BOLD;
790 cacheItem.mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
793 isSupported = FcCharSetHasChar(cacheItem.mCharacterSet, character);
797 case FontDescription::BITMAP_FONT:
799 const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
801 for(const auto& glyph : bitmapFont.glyphs)
803 if(glyph.utf32 == character)
813 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
817 DALI_LOG_INFO(gLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false"));
818 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
822 FontId FontClient::Plugin::FindFontForCharacter(const FontList& fontList,
823 const CharacterSetList& characterSetList,
825 PointSize26Dot6 requestedPointSize,
828 DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
830 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n");
831 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", character);
832 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
833 DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
836 bool foundColor = false;
838 DALI_LOG_INFO(gLogFilter, Debug::General, " number of fonts : %d\n", fontList.size());
840 // Traverse the list of fonts.
841 // Check for each font if supports the character.
842 for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
844 const FontDescription& description = fontList[index];
845 const FcCharSet* const characterSet = characterSetList[index];
847 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", description.family.c_str());
848 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str());
849 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width]);
850 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight]);
851 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant]);
853 bool foundInRanges = false;
854 if(nullptr != characterSet)
856 foundInRanges = FcCharSetHasChar(characterSet, character);
861 fontId = GetFontId(description,
865 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " font id : %d\n", fontId);
870 (fontId - 1u < mFontIdCache.Count()))
872 const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
874 foundColor = item.mHasColorTables;
877 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " foundColor : %s\n", (foundColor ? "true" : "false"));
880 // Keep going unless we prefer a different (color) font.
881 if(!preferColor || foundColor)
888 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
889 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n");
893 FontId FontClient::Plugin::FindDefaultFont(Character charcode,
894 PointSize26Dot6 requestedPointSize,
897 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n");
898 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", charcode);
899 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
900 DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
904 // Create the list of default fonts if it has not been created.
905 if(mDefaultFonts.empty())
907 FontDescription fontDescription;
908 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
909 fontDescription.width = IntToWidthType(DEFAULT_FONT_WIDTH);
910 fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT);
911 fontDescription.slant = IntToSlantType(DEFAULT_FONT_SLANT);
913 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
915 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size());
917 // Traverse the list of default fonts.
918 // Check for each default font if supports the character.
919 fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
921 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
922 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n");
927 FontId FontClient::Plugin::FindFallbackFont(Character charcode,
928 const FontDescription& preferredFontDescription,
929 PointSize26Dot6 requestedPointSize,
932 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n");
933 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", charcode);
934 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
935 DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
937 // The font id to be returned.
940 FontDescription fontDescription;
942 // Fill the font description with the preferred font description and complete with the defaults.
943 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
944 fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? IntToWeightType(DEFAULT_FONT_WEIGHT) : preferredFontDescription.weight);
945 fontDescription.width = ((FontWidth::NONE == preferredFontDescription.width) ? IntToWidthType(DEFAULT_FONT_WIDTH) : preferredFontDescription.width);
946 fontDescription.slant = ((FontSlant::NONE == preferredFontDescription.slant) ? IntToSlantType(DEFAULT_FONT_SLANT) : preferredFontDescription.slant);
948 DALI_LOG_INFO(gLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n");
949 DALI_LOG_INFO(gLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
950 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
951 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
952 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
954 // Check first if the font's description has been queried before.
955 FontList* fontList = nullptr;
956 CharacterSetList* characterSetList = nullptr;
958 if(!FindFallbackFontList(fontDescription, fontList, characterSetList))
960 fontList = new FontList;
961 characterSetList = new CharacterSetList;
963 SetFontList(fontDescription, *fontList, *characterSetList);
965 FontDescription appleColorEmoji;
966 appleColorEmoji.family = "Apple Color Emoji";
967 appleColorEmoji.width = fontDescription.width;
968 appleColorEmoji.weight = fontDescription.weight;
969 appleColorEmoji.slant = fontDescription.slant;
970 FontList emojiFontList;
971 CharacterSetList emojiCharSetList;
972 SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
974 std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
975 emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
976 *fontList = std::move(emojiFontList);
977 *characterSetList = std::move(emojiCharSetList);
980 // Add the font-list to the cache.
981 mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
984 if(fontList && characterSetList)
986 fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
989 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
990 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
994 FontId FontClient::Plugin::GetFontId(const FontPath& path,
995 PointSize26Dot6 requestedPointSize,
997 bool cacheDescription)
999 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n");
1000 DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str());
1001 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
1005 if(nullptr != mFreeTypeLibrary)
1007 FontId foundId = 0u;
1008 if(FindFont(path, requestedPointSize, faceIndex, foundId))
1014 id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
1018 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
1019 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n");
1024 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
1025 PointSize26Dot6 requestedPointSize,
1026 FaceIndex faceIndex)
1028 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n");
1029 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
1030 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
1031 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
1032 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
1033 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
1034 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
1036 // This method uses three vectors which caches:
1037 // * The bitmap font cache
1038 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
1039 // * The path to font file names.
1040 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
1042 // 1) Checks if the font description matches with a previously loaded bitmap font.
1043 // Returns if a font is found.
1044 // 2) Checks in the cache if the font's description has been validated before.
1045 // If it was it gets an index to the vector with paths to font file names. Otherwise,
1046 // retrieves using font config a path to a font file name which matches with the
1047 // font's description. The path is stored in the cache.
1049 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
1050 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
1051 // the GetFontId() method with the path to the font file name and the point size to
1054 // The font id to be returned.
1057 // Check first if the font description matches with a previously loaded bitmap font.
1058 if(FindBitmapFont(fontDescription.family, fontId))
1063 // Check if the font's description have been validated before.
1064 FontDescriptionId validatedFontId = 0u;
1066 if(!FindValidatedFont(fontDescription,
1069 // Use font config to validate the font's description.
1070 ValidateFont(fontDescription,
1074 FontId fontFaceId = 0u;
1075 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
1076 if(!FindFont(validatedFontId, requestedPointSize, fontFaceId))
1078 // Retrieve the font file name path.
1079 const FontDescription& description = *(mFontDescriptionCache.begin() + validatedFontId - 1u);
1081 // Retrieve the font id. Do not cache the description as it has been already cached.
1082 fontId = GetFontId(description.path,
1087 fontFaceId = mFontIdCache[fontId - 1u].id;
1088 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(mCharacterSetCache[validatedFontId - 1u]);
1090 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
1091 mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
1097 fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
1100 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
1101 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n");
1106 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont)
1108 for(const auto& item : mBitmapFontCache)
1110 if(bitmapFont.name == item.font.name)
1112 return item.id + 1u;
1116 BitmapFontCacheItem bitmapFontCacheItem;
1117 bitmapFontCacheItem.font = bitmapFont;
1118 bitmapFontCacheItem.id = mFontIdCache.Count();
1120 // Resize the vector with the pixel buffers.
1121 bitmapFontCacheItem.pixelBuffers.resize(bitmapFont.glyphs.size());
1123 // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
1124 unsigned int index = 0u;
1125 for(auto& glyph : bitmapFontCacheItem.font.glyphs)
1127 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1129 if(EqualsZero(glyph.ascender) && EqualsZero(glyph.descender))
1132 pixelBuffer = LoadImageFromFile(glyph.url);
1136 glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1140 bitmapFontCacheItem.font.ascender = std::max(glyph.ascender, bitmapFontCacheItem.font.ascender);
1141 bitmapFontCacheItem.font.descender = std::min(glyph.descender, bitmapFontCacheItem.font.descender);
1146 FontIdCacheItem fontIdCacheItem;
1147 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1148 fontIdCacheItem.id = mBitmapFontCache.size();
1150 mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
1151 mFontIdCache.PushBack(fontIdCacheItem);
1153 return bitmapFontCacheItem.id + 1u;
1156 void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
1157 FontDescriptionId& validatedFontId)
1159 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n");
1160 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
1161 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
1162 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
1163 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
1164 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
1166 // Create a font pattern.
1167 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
1169 FontDescription description;
1171 FcCharSet* characterSet = nullptr;
1172 bool matched = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
1173 FcPatternDestroy(fontFamilyPattern);
1175 if(matched && (nullptr != characterSet))
1177 // Add the path to the cache.
1178 description.type = FontDescription::FACE_FONT;
1179 mFontDescriptionCache.push_back(description);
1181 // Set the index to the vector of paths to font file names.
1182 validatedFontId = mFontDescriptionCache.size();
1184 DALI_LOG_INFO(gLogFilter, Debug::General, " matched description; family : [%s]\n", description.family.c_str());
1185 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str());
1186 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width]);
1187 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight]);
1188 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant]);
1189 DALI_LOG_INFO(gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
1191 // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1192 mCharacterSetCache.PushBack(characterSet);
1194 // Cache the index and the matched font's description.
1195 FontDescriptionCacheItem item(description,
1198 mValidatedFontCache.push_back(std::move(item));
1200 if((fontDescription.family != description.family) ||
1201 (fontDescription.width != description.width) ||
1202 (fontDescription.weight != description.weight) ||
1203 (fontDescription.slant != description.slant))
1205 // Cache the given font's description if it's different than the matched.
1206 FontDescriptionCacheItem item(fontDescription,
1209 mValidatedFontCache.push_back(std::move(item));
1214 DALI_LOG_INFO(gLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str());
1217 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n");
1220 void FontClient::Plugin::GetFontMetrics(FontId fontId,
1221 FontMetrics& metrics)
1223 const FontId index = fontId - 1u;
1226 (index < mFontIdCache.Count()))
1228 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1230 switch(fontIdCacheItem.type)
1232 case FontDescription::FACE_FONT:
1234 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1236 metrics = font.mMetrics;
1238 // Adjust the metrics if the fixed-size font should be down-scaled
1239 if(font.mIsFixedSizeBitmap)
1241 const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1243 if(desiredFixedSize > 0.f)
1245 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1247 metrics.ascender = metrics.ascender * scaleFactor;
1248 metrics.descender = metrics.descender * scaleFactor;
1249 metrics.height = metrics.height * scaleFactor;
1250 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1251 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1256 case FontDescription::BITMAP_FONT:
1258 const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1260 metrics.ascender = bitmapFontCacheItem.font.ascender;
1261 metrics.descender = bitmapFontCacheItem.font.descender;
1262 metrics.height = metrics.ascender - metrics.descender;
1263 metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1264 metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1269 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1275 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
1279 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId fontId,
1282 GlyphIndex glyphIndex = 0u;
1283 const FontId index = fontId - 1u;
1286 (index < mFontIdCache.Count()))
1288 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1290 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1292 FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1294 glyphIndex = FT_Get_Char_Index(ftFace, charcode);
1301 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
1306 if(VECTOR_GLYPH == type)
1308 return GetVectorMetrics(array, size, horizontal);
1311 return GetBitmapMetrics(array, size, horizontal);
1314 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
1320 for(unsigned int i = 0; i < size; ++i)
1322 GlyphInfo& glyph = array[i];
1324 FontId index = glyph.fontId - 1u;
1326 if((glyph.fontId > 0u) &&
1327 (index < mFontIdCache.Count()))
1329 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1331 switch(fontIdCacheItem.type)
1333 case FontDescription::FACE_FONT:
1335 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1337 FT_Face ftFace = font.mFreeTypeFace;
1339 #ifdef FREETYPE_BITMAP_SUPPORT
1340 // Check to see if we should be loading a Fixed Size bitmap?
1341 if(font.mIsFixedSizeBitmap)
1343 FT_Select_Size(ftFace, font.mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
1344 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
1345 if(FT_Err_Ok == error)
1347 glyph.width = font.mFixedWidthPixels;
1348 glyph.height = font.mFixedHeightPixels;
1349 glyph.advance = font.mFixedWidthPixels;
1350 glyph.xBearing = 0.0f;
1351 glyph.yBearing = font.mFixedHeightPixels;
1353 // Adjust the metrics if the fixed-size font should be down-scaled
1354 const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1356 if(desiredFixedSize > 0.f)
1358 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1360 glyph.width = glyph.width * scaleFactor;
1361 glyph.height = glyph.height * scaleFactor;
1362 glyph.advance = glyph.advance * scaleFactor;
1363 glyph.xBearing = glyph.xBearing * scaleFactor;
1364 glyph.yBearing = glyph.yBearing * scaleFactor;
1366 glyph.scaleFactor = scaleFactor;
1371 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
1378 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1379 // i.e. with the SNum-3R font.
1380 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1381 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);
1383 // Keep the width of the glyph before doing the software emboldening.
1384 // It will be used to calculate a scale factor to be applied to the
1385 // advance as Harfbuzz doesn't apply any SW emboldening to calculate
1386 // the advance of the glyph.
1387 const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1389 if(FT_Err_Ok == error)
1391 const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
1392 if(isEmboldeningRequired)
1394 // Does the software bold.
1395 FT_GlyphSlot_Embolden(ftFace->glyph);
1398 glyph.width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1399 glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
1402 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
1403 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
1407 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
1408 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
1411 if(isEmboldeningRequired && !Dali::EqualsZero(width))
1413 // If the glyph is emboldened by software, the advance is multiplied by a
1414 // scale factor to make it slightly bigger.
1415 glyph.advance *= (glyph.width / width);
1418 // Use the bounding box of the bitmap to correct the metrics.
1419 // For some fonts i.e the SNum-3R the metrics need to be corrected,
1420 // otherwise the glyphs 'dance' up and down depending on the
1421 // font's point size.
1424 error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);
1427 FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
1429 const float descender = glyph.height - glyph.yBearing;
1430 glyph.height = (bbox.yMax - bbox.yMin) * FROM_266;
1431 glyph.yBearing = glyph.height - round(descender);
1433 // Created FT_Glyph object must be released with FT_Done_Glyph
1434 FT_Done_Glyph(ftGlyph);
1443 case FontDescription::BITMAP_FONT:
1445 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1447 unsigned int index = 0u;
1448 for(auto& item : bitmapFontCacheItem.font.glyphs)
1450 if(item.utf32 == glyph.index)
1452 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1455 pixelBuffer = LoadImageFromFile(item.url);
1458 glyph.width = static_cast<float>(pixelBuffer.GetWidth());
1459 glyph.height = static_cast<float>(pixelBuffer.GetHeight());
1460 glyph.xBearing = 0.f;
1461 glyph.yBearing = glyph.height + item.descender;
1462 glyph.advance = glyph.width;
1463 glyph.scaleFactor = 1.f;
1474 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1480 // Check if it's an embedded image.
1481 if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count()))
1483 const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1485 glyph.width = static_cast<float>(item.width);
1486 glyph.height = static_cast<float>(item.height);
1487 glyph.xBearing = 0.f;
1488 glyph.yBearing = glyph.height;
1489 glyph.advance = glyph.width;
1490 glyph.scaleFactor = 1.f;
1502 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
1506 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1509 for(unsigned int i = 0u; i < size; ++i)
1511 FontId fontId = array[i].fontId;
1514 (fontId - 1u) < mFontIdCache.Count())
1516 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1518 if(!font.mVectorFontId)
1520 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1523 mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
1525 // Vector metrics are in EMs, convert to pixels
1526 const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
1527 array[i].width *= scale;
1528 array[i].height *= scale;
1529 array[i].xBearing *= scale;
1530 array[i].yBearing *= scale;
1531 array[i].advance *= scale;
1545 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
1547 const FontId index = fontId - 1u;
1550 (index < mFontIdCache.Count()))
1552 data.isColorBitmap = false;
1553 data.isColorEmoji = false;
1555 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1557 switch(fontIdCacheItem.type)
1559 case FontDescription::FACE_FONT:
1561 // For the software italics.
1562 bool isShearRequired = false;
1564 const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1565 FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1569 #ifdef FREETYPE_BITMAP_SUPPORT
1570 // Check to see if this is fixed size bitmap
1571 if(fontFaceCacheItem.mIsFixedSizeBitmap)
1573 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1578 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1579 // i.e. with the SNum-3R font.
1580 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1581 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
1583 if(FT_Err_Ok == error)
1585 if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
1587 // Does the software bold.
1588 FT_GlyphSlot_Embolden(ftFace->glyph);
1591 if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
1593 // Will do the software italic.
1594 isShearRequired = true;
1598 error = FT_Get_Glyph(ftFace->glyph, &glyph);
1600 // Convert to bitmap if necessary
1601 if(FT_Err_Ok == error)
1603 if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
1605 int offsetX = 0, offsetY = 0;
1606 bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
1608 // Create a bitmap for the outline
1611 // Retrieve the horizontal and vertical distance from the current pen position to the
1612 // left and top border of the glyph bitmap for a normal glyph before applying the outline.
1613 if(FT_Err_Ok == error)
1615 FT_Glyph normalGlyph;
1616 error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);
1618 error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
1619 if(FT_Err_Ok == error)
1621 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);
1623 offsetX = bitmapGlyph->left;
1624 offsetY = bitmapGlyph->top;
1627 // Created FT_Glyph object must be released with FT_Done_Glyph
1628 FT_Done_Glyph(normalGlyph);
1631 // Now apply the outline
1635 error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
1637 if(FT_Err_Ok == error)
1639 FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
1640 error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
1642 if(FT_Err_Ok == error)
1644 FT_Stroker_Done(stroker);
1648 DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
1653 DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
1657 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1658 if(FT_Err_Ok == error)
1660 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
1664 // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
1665 data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
1666 data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
1669 ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
1673 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
1678 ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
1681 data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1683 // Created FT_Glyph object must be released with FT_Done_Glyph
1684 FT_Done_Glyph(glyph);
1689 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
1693 case FontDescription::BITMAP_FONT:
1695 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1697 unsigned int index = 0u;
1698 for(auto& item : bitmapFontCacheItem.font.glyphs)
1700 if(item.utf32 == glyphIndex)
1702 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1705 pixelBuffer = LoadImageFromFile(item.url);
1708 data.width = pixelBuffer.GetWidth();
1709 data.height = pixelBuffer.GetHeight();
1711 data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1713 ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer());
1715 // Sets the pixel format.
1716 data.format = pixelBuffer.GetPixelFormat();
1725 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1731 if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count()))
1733 // It's an embedded item.
1734 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1736 data.width = item.width;
1737 data.height = item.height;
1738 if(0u != item.pixelBufferId)
1740 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId - 1u].pixelBuffer;
1743 ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer());
1745 // Sets the pixel format.
1746 data.format = pixelBuffer.GetPixelFormat();
1751 // Creates the output buffer
1752 const unsigned int bufferSize = data.width * data.height * 4u;
1753 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1755 memset(data.buffer, 0u, bufferSize);
1757 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1763 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
1765 TextAbstraction::FontClient::GlyphBufferData data;
1767 CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
1769 return PixelData::New(data.buffer,
1770 data.width * data.height * Pixel::GetBytesPerPixel(data.format),
1774 PixelData::DELETE_ARRAY);
1777 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
1782 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1784 (fontId - 1u < mFontIdCache.Count()))
1786 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1787 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1789 if(!font.mVectorFontId)
1791 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1794 mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1799 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
1801 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n");
1802 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize);
1804 // First look into the cache if there is an ellipsis glyph for the requested point size.
1805 for(const auto& item : mEllipsisCache)
1807 if(item.requestedPointSize == requestedPointSize)
1809 // Use the glyph in the cache.
1811 DALI_LOG_INFO(gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1812 DALI_LOG_INFO(gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1813 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n");
1819 // No glyph has been found. Create one.
1820 mEllipsisCache.PushBack(EllipsisItem());
1821 EllipsisItem& item = *(mEllipsisCache.End() - 1u);
1823 item.requestedPointSize = requestedPointSize;
1825 // Find a font for the ellipsis glyph.
1826 item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1830 // Set the character index to access the glyph inside the font.
1831 item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].id].mFreeTypeFace,
1832 ELLIPSIS_CHARACTER);
1834 GetBitmapMetrics(&item.glyph, 1u, true);
1836 DALI_LOG_INFO(gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1837 DALI_LOG_INFO(gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1838 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n");
1843 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
1845 FT_Error error = -1;
1847 const FontId index = fontId - 1u;
1850 (index < mFontIdCache.Count()))
1852 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1854 switch(fontIdCacheItem.type)
1856 case FontDescription::FACE_FONT:
1858 #ifdef FREETYPE_BITMAP_SUPPORT
1859 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1860 FT_Face ftFace = item.mFreeTypeFace;
1862 // Check to see if this is fixed size bitmap
1863 if(item.mHasColorTables)
1865 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1870 case FontDescription::BITMAP_FONT:
1872 error = FT_Err_Ok; // Will return true;
1877 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1882 return FT_Err_Ok == error;
1885 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId)
1887 FT_Face fontFace = nullptr;
1889 const FontId index = fontId - 1u;
1891 (index < mFontIdCache.Count()))
1893 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1895 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1897 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1903 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId)
1905 const FontId index = fontId - 1u;
1907 (index < mFontIdCache.Count()))
1909 return mFontIdCache[index].type;
1911 return FontDescription::INVALID;
1914 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1916 // nullptr as first parameter means the current configuration is used.
1917 return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1920 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
1922 EmbeddedItem embeddedItem;
1924 embeddedItem.pixelBufferId = 0u;
1925 embeddedItem.width = description.width;
1926 embeddedItem.height = description.height;
1928 pixelFormat = Pixel::A8;
1930 if(!description.url.empty())
1932 // Check if the url is in the cache.
1933 PixelBufferId index = 0u;
1935 for(const auto& cacheItem : mPixelBufferCache)
1938 if(cacheItem.url == description.url)
1940 // The url is in the pixel buffer cache.
1941 // Set the index +1 to the vector.
1942 embeddedItem.pixelBufferId = index;
1947 Devel::PixelBuffer pixelBuffer;
1948 if(0u == embeddedItem.pixelBufferId)
1950 // The pixel buffer is not in the cache. Create one and cache it.
1952 // Load the image from the url.
1953 pixelBuffer = LoadImageFromFile(description.url);
1955 // Create the cache item.
1956 PixelBufferCacheItem pixelBufferCacheItem;
1957 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1958 pixelBufferCacheItem.url = description.url;
1960 // Store the cache item in the cache.
1961 mPixelBufferCache.push_back(std::move(pixelBufferCacheItem));
1963 // Set the pixel buffer id to the embedded item.
1964 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1968 // Retrieve the pixel buffer from the cache to set the pixel format.
1969 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1974 // Set the size of the embedded item if it has not been set.
1975 if(0u == embeddedItem.width)
1977 embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1980 if(0u == embeddedItem.height)
1982 embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1985 // Set the pixel format.
1986 pixelFormat = pixelBuffer.GetPixelFormat();
1990 // Find if the same embeddedItem has already been created.
1991 unsigned int index = 0u;
1992 for(const auto& item : mEmbeddedItemCache)
1995 if((item.pixelBufferId == embeddedItem.pixelBufferId) &&
1996 (item.width == embeddedItem.width) &&
1997 (item.height == embeddedItem.height))
2003 // Cache the embedded item.
2004 mEmbeddedItemCache.PushBack(embeddedItem);
2006 return mEmbeddedItemCache.Count();
2010 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
2012 mIsAtlasLimitationEnabled = enabled;
2015 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
2017 return mIsAtlasLimitationEnabled;
2020 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
2022 return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
2025 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
2027 return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
2030 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
2032 return mCurrentMaximumBlockSizeFitInAtlas;
2035 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
2037 bool isChanged = false;
2038 const Size& maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
2039 const uint16_t& padding = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
2041 if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding
2042 && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
2044 mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
2051 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
2053 return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;;
2057 void FontClient::Plugin::InitSystemFonts()
2059 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n");
2061 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
2065 DALI_LOG_INFO(gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont);
2067 // Reserve some space to avoid reallocations.
2068 mSystemFonts.reserve(fontSet->nfont);
2070 for(int i = 0u; i < fontSet->nfont; ++i)
2072 FcPattern* fontPattern = fontSet->fonts[i];
2076 // Skip fonts with no path
2077 if(GetFcString(fontPattern, FC_FILE, path))
2079 mSystemFonts.push_back(FontDescription());
2080 FontDescription& fontDescription = mSystemFonts.back();
2082 fontDescription.path = std::move(path);
2087 GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
2088 GetFcInt(fontPattern, FC_WIDTH, width);
2089 GetFcInt(fontPattern, FC_WEIGHT, weight);
2090 GetFcInt(fontPattern, FC_SLANT, slant);
2091 fontDescription.width = IntToWidthType(width);
2092 fontDescription.weight = IntToWeightType(weight);
2093 fontDescription.slant = IntToSlantType(slant);
2095 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str());
2096 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
2097 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
2098 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2099 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2103 // Destroys the font set created.
2104 FcFontSetDestroy(fontSet);
2106 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n");
2109 bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
2111 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n");
2113 FcResult result = FcResultMatch;
2114 FcPattern* match = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
2116 const bool matched = nullptr != match;
2117 DALI_LOG_INFO(gLogFilter, Debug::General, " pattern matched : %s\n", (matched ? "true" : "false"));
2124 GetFcString(match, FC_FILE, fontDescription.path);
2125 GetFcString(match, FC_FAMILY, fontDescription.family);
2126 GetFcInt(match, FC_WIDTH, width);
2127 GetFcInt(match, FC_WEIGHT, weight);
2128 GetFcInt(match, FC_SLANT, slant);
2129 fontDescription.width = IntToWidthType(width);
2130 fontDescription.weight = IntToWeightType(weight);
2131 fontDescription.slant = IntToSlantType(slant);
2133 // Retrieve the character set and increase the reference counter.
2134 FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
2135 *characterSet = FcCharSetCopy(*characterSet);
2137 // destroyed the matched pattern
2138 FcPatternDestroy(match);
2140 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
2141 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
2142 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
2143 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2144 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2147 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n");
2151 FcPattern* FontClient::Plugin::CreateFontFamilyPattern(const FontDescription& fontDescription) const
2153 // create the cached font family lookup pattern
2154 // a pattern holds a set of names, each name refers to a property of the font
2155 FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2157 if(!fontFamilyPattern)
2162 // add a property to the pattern for the font family
2163 FcPatternAddString(fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fontDescription.family.c_str()));
2165 // add a property to the pattern for local setting.
2166 const char* locale = setlocale(LC_MESSAGES, nullptr);
2167 if(locale != nullptr)
2169 FcPatternAddString(fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale));
2172 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
2176 width = DEFAULT_FONT_WIDTH;
2179 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
2183 weight = DEFAULT_FONT_WEIGHT;
2186 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
2190 slant = DEFAULT_FONT_SLANT;
2193 FcPatternAddInteger(fontFamilyPattern, FC_WIDTH, width);
2194 FcPatternAddInteger(fontFamilyPattern, FC_WEIGHT, weight);
2195 FcPatternAddInteger(fontFamilyPattern, FC_SLANT, slant);
2197 // modify the config, with the mFontFamilyPatterm
2198 FcConfigSubstitute(nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern);
2200 // provide default values for unspecified properties in the font pattern
2201 // e.g. patterns without a specified style or weight are set to Medium
2202 FcDefaultSubstitute(fontFamilyPattern);
2204 return fontFamilyPattern;
2207 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
2209 FcFontSet* fontset = nullptr;
2211 // create a new pattern.
2212 // a pattern holds a set of names, each name refers to a property of the font
2213 FcPattern* pattern = FcPatternCreate();
2215 if(nullptr != pattern)
2217 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
2218 FcObjectSet* objectSet = FcObjectSetCreate();
2220 if(nullptr != objectSet)
2222 // build an object set from a list of property names
2223 FcObjectSetAdd(objectSet, FC_FILE);
2224 FcObjectSetAdd(objectSet, FC_FAMILY);
2225 FcObjectSetAdd(objectSet, FC_WIDTH);
2226 FcObjectSetAdd(objectSet, FC_WEIGHT);
2227 FcObjectSetAdd(objectSet, FC_SLANT);
2229 // get a list of fonts
2230 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
2231 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.
2233 // clear up the object set
2234 FcObjectSetDestroy(objectSet);
2237 // clear up the pattern
2238 FcPatternDestroy(pattern);
2244 bool FontClient::Plugin::GetFcString(const FcPattern* const pattern,
2245 const char* const n,
2246 std::string& string)
2248 FcChar8* file = nullptr;
2249 const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
2251 if(FcResultMatch == retVal)
2253 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
2254 string.assign(reinterpret_cast<const char*>(file));
2262 bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
2264 const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
2266 if(FcResultMatch == retVal)
2274 FontId FontClient::Plugin::CreateFont(const FontPath& path,
2275 PointSize26Dot6 requestedPointSize,
2276 FaceIndex faceIndex,
2277 bool cacheDescription)
2279 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n");
2280 DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str());
2281 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2285 // Create & cache new font face
2287 int error = FT_New_Face(mFreeTypeLibrary,
2292 if(FT_Err_Ok == error)
2294 // Check if a font is scalable.
2295 const bool isScalable = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
2296 const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
2297 const bool hasColorTables = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
2298 FontId fontFaceId = 0u;
2300 DALI_LOG_INFO(gLogFilter, Debug::General, " isScalable : [%s]\n", (isScalable ? "true" : "false"));
2301 DALI_LOG_INFO(gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
2302 DALI_LOG_INFO(gLogFilter, Debug::General, " hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
2304 // Check to see if the font contains fixed sizes?
2305 if(!isScalable && hasFixedSizedBitmaps)
2307 PointSize26Dot6 actualPointSize = 0u;
2308 int fixedSizeIndex = 0;
2309 for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
2311 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2312 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
2314 if(fixedSize >= requestedPointSize)
2316 actualPointSize = fixedSize;
2321 if(0u == actualPointSize)
2323 // The requested point size is bigger than the bigest fixed size.
2324 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2325 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2328 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
2330 // Tell Freetype to use this size
2331 error = FT_Select_Size(ftFace, fixedSizeIndex);
2332 if(FT_Err_Ok != error)
2334 DALI_LOG_INFO(gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
2338 const float fixedWidth = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
2339 const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
2341 // Indicate that the font is a fixed sized bitmap
2342 FontMetrics metrics(fixedHeight, // The ascender in pixels.
2344 fixedHeight, // The height in pixels.
2348 // Create the FreeType font face item to cache.
2349 FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
2351 // Set the index to the font's id cache.
2352 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2354 // Create the font id item to cache.
2355 FontIdCacheItem fontIdCacheItem;
2356 fontIdCacheItem.type = FontDescription::FACE_FONT;
2358 // Set the index to the FreeType font face cache.
2359 fontIdCacheItem.id = mFontFaceCache.size();
2360 fontFaceId = fontIdCacheItem.id + 1u;
2363 mFontFaceCache.push_back(fontFaceCacheItem);
2364 mFontIdCache.PushBack(fontIdCacheItem);
2366 // Set the font id to be returned.
2367 id = mFontIdCache.Count();
2372 if(mIsAtlasLimitationEnabled)
2374 //There is limitation on block size to fit in predefined atlas size.
2375 //If the block size cannot fit into atlas size, then the system cannot draw block.
2376 //This is workaround to avoid issue in advance
2377 //Decrementing point-size until arriving to maximum allowed block size.
2378 auto requestedPointSizeBackup= requestedPointSize;
2379 const Size& maxSizeFitInAtlas = GetCurrentMaximumBlockSizeFitInAtlas();
2380 error = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
2382 if(requestedPointSize != requestedPointSizeBackup)
2384 DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
2389 error = FT_Set_Char_Size(ftFace,
2396 if(FT_Err_Ok == error)
2398 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2400 FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
2401 static_cast<float>(ftMetrics.descender) * FROM_266,
2402 static_cast<float>(ftMetrics.height) * FROM_266,
2403 static_cast<float>(ftFace->underline_position) * FROM_266,
2404 static_cast<float>(ftFace->underline_thickness) * FROM_266);
2406 // Create the FreeType font face item to cache.
2407 FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics);
2409 // Set the index to the font's id cache.
2410 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2412 // Create the font id item to cache.
2413 FontIdCacheItem fontIdCacheItem;
2414 fontIdCacheItem.type = FontDescription::FACE_FONT;
2416 // Set the index to the FreeType font face cache.
2417 fontIdCacheItem.id = mFontFaceCache.size();
2418 fontFaceId = fontIdCacheItem.id + 1u;
2421 mFontFaceCache.push_back(fontFaceCacheItem);
2422 mFontIdCache.PushBack(fontIdCacheItem);
2424 // Set the font id to be returned.
2425 id = mFontIdCache.Count();
2429 DALI_LOG_INFO(gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
2433 if(0u != fontFaceId)
2435 if(cacheDescription)
2437 CacheFontPath(ftFace, fontFaceId, requestedPointSize, path);
2443 DALI_LOG_INFO(gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str());
2446 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
2447 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n");
2452 void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer)
2454 // Set the input dimensions.
2455 const ImageDimensions inputDimensions(srcWidth, srcHeight);
2457 // Set the output dimensions.
2458 // If the output dimension is not given, the input dimension is set
2459 // and won't be downscaling.
2460 data.width = (data.width == 0) ? srcWidth : data.width;
2461 data.height = (data.height == 0) ? srcHeight : data.height;
2462 const ImageDimensions desiredDimensions(data.width, data.height);
2464 // Creates the output buffer
2465 const unsigned int bufferSize = data.width * data.height * 4u;
2466 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2468 if(inputDimensions == desiredDimensions)
2470 // There isn't downscaling.
2471 memcpy(data.buffer, srcBuffer, bufferSize);
2475 Dali::Internal::Platform::LanczosSample4BPP(srcBuffer,
2482 void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired)
2484 if(srcBitmap.width * srcBitmap.rows > 0)
2486 switch(srcBitmap.pixel_mode)
2488 case FT_PIXEL_MODE_GRAY:
2490 if(srcBitmap.pitch == static_cast<int>(srcBitmap.width))
2492 uint8_t* pixelsIn = srcBitmap.buffer;
2493 unsigned int width = srcBitmap.width;
2494 unsigned height = srcBitmap.rows;
2496 std::unique_ptr<uint8_t, void (*)(void*)> pixelsOutPtr(nullptr, free);
2501 * Glyphs' bitmaps with no slant retrieved from FreeType:
2511 * Expected glyphs' bitmaps with italic slant:
2512 * ____________ ______
2519 * ------------ ------
2521 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2531 * This difference in some bitmaps' width causes an overlap of some glyphs. This is the reason why a shear operation is done here instead of relying on the experimental FT_GlyphSlot_Oblique() implementation.
2533 unsigned int widthOut = 0u;
2534 unsigned int heightOut = 0u;
2535 uint8_t* pixelsOut = nullptr;
2537 Dali::Internal::Platform::HorizontalShear(pixelsIn,
2541 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2548 pixelsIn = pixelsOut;
2549 pixelsOutPtr.reset(pixelsOut);
2552 const unsigned int bufferSize = width * height;
2553 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2555 data.height = height;
2556 data.format = Pixel::L8; // Sets the pixel format.
2557 memcpy(data.buffer, pixelsIn, bufferSize);
2562 #ifdef FREETYPE_BITMAP_SUPPORT
2563 case FT_PIXEL_MODE_BGRA:
2565 if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
2567 ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer);
2569 // Sets the pixel format.
2570 data.format = Pixel::BGRA8888;
2577 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n");
2584 bool FontClient::Plugin::FindFont(const FontPath& path,
2585 PointSize26Dot6 requestedPointSize,
2586 FaceIndex faceIndex,
2587 FontId& fontId) const
2589 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n");
2590 DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str());
2591 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2592 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size());
2595 for(const auto& cacheItem : mFontFaceCache)
2597 if(cacheItem.mRequestedPointSize == requestedPointSize &&
2598 cacheItem.mFaceIndex == faceIndex &&
2599 cacheItem.mPath == path)
2601 fontId = cacheItem.mFontId + 1u;
2603 DALI_LOG_INFO(gLogFilter, Debug::General, " font found, id : %d\n", fontId);
2604 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2610 DALI_LOG_INFO(gLogFilter, Debug::General, " font not found\n");
2611 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2616 bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
2617 FontDescriptionId& validatedFontId)
2619 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n");
2620 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
2621 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
2622 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
2623 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2624 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2625 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
2627 validatedFontId = 0u;
2629 for(const auto& item : mValidatedFontCache)
2631 if(!fontDescription.family.empty() &&
2632 (fontDescription.family == item.fontDescription.family) &&
2633 (fontDescription.width == item.fontDescription.width) &&
2634 (fontDescription.weight == item.fontDescription.weight) &&
2635 (fontDescription.slant == item.fontDescription.slant))
2637 validatedFontId = item.index;
2639 DALI_LOG_INFO(gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId);
2640 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n");
2645 DALI_LOG_INFO(gLogFilter, Debug::General, " validated font not found\n");
2646 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n");
2650 bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription,
2651 FontList*& fontList,
2652 CharacterSetList*& characterSetList)
2654 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n");
2655 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
2656 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
2657 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
2658 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2659 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2660 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size());
2664 for(const auto& item : mFallbackCache)
2666 if(!fontDescription.family.empty() &&
2667 (fontDescription.family == item.fontDescription.family) &&
2668 (fontDescription.width == item.fontDescription.width) &&
2669 (fontDescription.weight == item.fontDescription.weight) &&
2670 (fontDescription.slant == item.fontDescription.slant))
2672 fontList = item.fallbackFonts;
2673 characterSetList = item.characterSets;
2675 DALI_LOG_INFO(gLogFilter, Debug::General, " fallback font list found.\n");
2676 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n");
2681 DALI_LOG_INFO(gLogFilter, Debug::General, " fallback font list not found.\n");
2682 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n");
2686 bool FontClient::Plugin::FindFont(FontDescriptionId validatedFontId,
2687 PointSize26Dot6 requestedPointSize,
2690 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n");
2691 DALI_LOG_INFO(gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
2692 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2696 for(const auto& item : mFontDescriptionSizeCache)
2698 if((validatedFontId == item.validatedFontId) &&
2699 (requestedPointSize == item.requestedPointSize))
2701 fontId = item.fontId;
2703 DALI_LOG_INFO(gLogFilter, Debug::General, " font found, id : %d\n", fontId);
2704 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2709 DALI_LOG_INFO(gLogFilter, Debug::General, " font not found.\n");
2710 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2714 bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const
2718 for(const auto& item : mBitmapFontCache)
2720 if(bitmapFont == item.font.name)
2722 fontId = item.id + 1u;
2730 bool FontClient::Plugin::IsScalable(const FontPath& path)
2732 bool isScalable = false;
2735 int error = FT_New_Face(mFreeTypeLibrary,
2739 if(FT_Err_Ok != error)
2741 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
2745 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2751 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription)
2753 // Create a font pattern.
2754 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2756 FcResult result = FcResultMatch;
2758 // match the pattern
2759 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2760 bool isScalable = false;
2764 // Get the path to the font file name.
2766 GetFcString(match, FC_FILE, path);
2767 isScalable = IsScalable(path);
2771 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2774 // Destroys the created patterns.
2775 FcPatternDestroy(match);
2776 FcPatternDestroy(fontFamilyPattern);
2781 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes)
2783 // Empty the caller container
2787 int error = FT_New_Face(mFreeTypeLibrary,
2791 if(FT_Err_Ok != error)
2793 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
2796 // Fetch the number of fixed sizes available
2797 if(ftFace->num_fixed_sizes && ftFace->available_sizes)
2799 for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
2801 sizes.PushBack(ftFace->available_sizes[i].size);
2806 void FontClient::Plugin::GetFixedSizes(const FontDescription& fontDescription,
2807 Vector<PointSize26Dot6>& sizes)
2809 // Create a font pattern.
2810 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2812 FcResult result = FcResultMatch;
2814 // match the pattern
2815 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2819 // Get the path to the font file name.
2821 GetFcString(match, FC_FILE, path);
2822 GetFixedSizes(path, sizes);
2826 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2829 // Destroys the created patterns.
2830 FcPatternDestroy(match);
2831 FcPatternDestroy(fontFamilyPattern);
2834 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
2836 bool hasItalicStyle = false;
2838 const FontId index = fontId - 1u;
2841 (index < mFontIdCache.Count()))
2843 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2845 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
2847 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2849 hasItalicStyle = 0u != (font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC);
2854 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
2857 return hasItalicStyle;
2860 void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path)
2862 FontDescription description;
2863 description.path = path;
2864 description.family = std::move(FontFamily(ftFace->family_name));
2865 description.weight = FontWeight::NONE;
2866 description.width = FontWidth::NONE;
2867 description.slant = FontSlant::NONE;
2869 // Note FreeType doesn't give too much info to build a proper font style.
2870 if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
2872 description.slant = FontSlant::ITALIC;
2874 if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
2876 description.weight = FontWeight::BOLD;
2879 FontDescriptionId validatedFontId = 0u;
2880 if(!FindValidatedFont(description,
2883 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2885 FcResult result = FcResultMatch;
2886 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2888 FcCharSet* characterSet = nullptr;
2889 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2891 const FontId fontFaceId = id - 1u;
2892 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
2894 // Destroys the created patterns.
2895 FcPatternDestroy(match);
2896 FcPatternDestroy(pattern);
2898 // Add the path to the cache.
2899 description.type = FontDescription::FACE_FONT;
2900 mFontDescriptionCache.push_back(description);
2902 // Set the index to the vector of paths to font file names.
2903 validatedFontId = mFontDescriptionCache.size();
2905 // Increase the reference counter and add the character set to the cache.
2906 mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
2908 // Cache the index and the font's description.
2909 mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
2912 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2913 mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
2919 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription(const FontDescription& description)
2921 FcCharSet* characterSet = nullptr;
2923 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2925 if(nullptr != pattern)
2927 FcResult result = FcResultMatch;
2928 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2930 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2932 // Destroys the created patterns.
2933 FcPatternDestroy(match);
2934 FcPatternDestroy(pattern);
2937 return characterSet;
2940 void FontClient::Plugin::ClearFallbackCache(std::vector<FallbackCacheItem>& fallbackCache)
2942 for(auto& item : fallbackCache)
2944 if(nullptr != item.fallbackFonts)
2946 delete item.fallbackFonts;
2949 if(nullptr != item.characterSets)
2951 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2952 DestroyCharacterSets(*item.characterSets);
2953 delete item.characterSets;
2958 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2960 for(auto& item : mFontFaceCache)
2962 FcCharSetDestroy(item.mCharacterSet);
2963 item.mCharacterSet = nullptr;
2967 } // namespace Internal
2969 } // namespace TextAbstraction