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 && (static_cast<float>(ftFace->size->metrics.ascender) - static_cast<float>(ftFace->size->metrics.descender)) * FROM_266 <= maxSizeFitInAtlas.width)
191 * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
193 * @param[in/out] ftFace Face type object.
194 * @param[in] horizontalDpi The horizontal dpi.
195 * @param[in] verticalDpi The vertical dpi.
196 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
197 * @param[in/out] requestedPointSize The requested point-size.
198 * @return FreeType error code. 0 means success when requesting the nominal size (in points).
200 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
202 //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
203 const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
205 int error; // FreeType error code.
207 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
208 if(FT_Err_Ok != error)
216 uint32_t exponentialDecrement = 1;
218 while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit * exponentialDecrement)
220 requestedPointSize -= (pointSizePerOneUnit * exponentialDecrement);
221 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
222 if(FT_Err_Ok != error)
227 exponentialDecrement *= 2;
231 uint32_t minPointSize;
232 uint32_t maxPointSize;
236 exponentialDecrement /= 2;
237 minPointSize = requestedPointSize;
238 maxPointSize = requestedPointSize + (pointSizePerOneUnit * exponentialDecrement);
243 maxPointSize = requestedPointSize;
246 while(minPointSize < maxPointSize)
248 requestedPointSize = ((maxPointSize / pointSizePerOneUnit - minPointSize / pointSizePerOneUnit) / 2) * pointSizePerOneUnit + minPointSize;
249 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
250 if(FT_Err_Ok != error)
257 if(minPointSize == requestedPointSize)
259 //Found targeted point-size
263 minPointSize = requestedPointSize;
267 maxPointSize = requestedPointSize;
275 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
276 : fontDescription{std::move(font)},
277 fallbackFonts{fallbackFonts},
278 characterSets{characterSets}
282 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
283 FontDescriptionId index)
284 : fontDescription{fontDescription},
289 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
290 FontDescriptionId index)
291 : fontDescription{std::move(fontDescription)},
296 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem(FontDescriptionId validatedFontId,
297 PointSize26Dot6 requestedPointSize,
299 : validatedFontId(validatedFontId),
300 requestedPointSize(requestedPointSize),
305 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace,
306 const FontPath& path,
307 PointSize26Dot6 requestedPointSize,
309 const FontMetrics& metrics)
310 : mFreeTypeFace(ftFace),
312 mRequestedPointSize(requestedPointSize),
315 mCharacterSet(nullptr),
317 mFixedWidthPixels(0.f),
318 mFixedHeightPixels(0.f),
321 mIsFixedSizeBitmap(false),
322 mHasColorTables(false)
326 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace,
327 const FontPath& path,
328 PointSize26Dot6 requestedPointSize,
330 const FontMetrics& metrics,
335 : mFreeTypeFace(ftFace),
337 mRequestedPointSize(requestedPointSize),
340 mCharacterSet(nullptr),
341 mFixedSizeIndex(fixedSizeIndex),
342 mFixedWidthPixels(fixedWidth),
343 mFixedHeightPixels(fixedHeight),
346 mIsFixedSizeBitmap(true),
347 mHasColorTables(hasColorTables)
351 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
352 unsigned int verticalDpi)
353 : mFreeTypeLibrary(nullptr),
354 mDpiHorizontal(horizontalDpi),
355 mDpiVertical(verticalDpi),
356 mDefaultFontDescription(),
361 mValidatedFontCache(),
362 mFontDescriptionCache(),
363 mCharacterSetCache(),
364 mFontDescriptionSizeCache(),
365 mVectorFontCache(nullptr),
367 mEmbeddedItemCache(),
368 mDefaultFontDescriptionCached(false),
369 mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
370 mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS)
373 int error = FT_Init_FreeType(&mFreeTypeLibrary);
374 if(FT_Err_Ok != error)
376 DALI_LOG_INFO(gLogFilter, Debug::General, "FreeType Init error: %d\n", error);
379 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
380 mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
384 FontClient::Plugin::~Plugin()
386 ClearFallbackCache(mFallbackCache);
388 // Free the resources allocated by the FcCharSet objects.
389 DestroyCharacterSets(mDefaultFontCharacterSets);
390 DestroyCharacterSets(mCharacterSetCache);
391 ClearCharacterSetFromFontFaceCache();
393 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
394 delete mVectorFontCache;
396 FT_Done_FreeType(mFreeTypeLibrary);
399 void FontClient::Plugin::ClearCache()
401 mDefaultFontDescription = FontDescription();
403 mSystemFonts.clear();
404 mDefaultFonts.clear();
406 DestroyCharacterSets(mDefaultFontCharacterSets);
407 mDefaultFontCharacterSets.Clear();
409 ClearFallbackCache(mFallbackCache);
410 mFallbackCache.clear();
412 mFontIdCache.Clear();
414 ClearCharacterSetFromFontFaceCache();
415 mFontFaceCache.clear();
417 mValidatedFontCache.clear();
418 mFontDescriptionCache.clear();
420 DestroyCharacterSets(mCharacterSetCache);
421 mCharacterSetCache.Clear();
423 mFontDescriptionSizeCache.clear();
425 mEllipsisCache.Clear();
426 mPixelBufferCache.clear();
427 mEmbeddedItemCache.Clear();
428 mBitmapFontCache.clear();
430 mDefaultFontDescriptionCached = false;
433 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
434 unsigned int verticalDpi)
436 mDpiHorizontal = horizontalDpi;
437 mDpiVertical = verticalDpi;
440 void FontClient::Plugin::ResetSystemDefaults()
442 mDefaultFontDescriptionCached = false;
445 void FontClient::Plugin::SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
447 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n");
448 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
449 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
450 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
451 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
455 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
457 FcResult result = FcResultMatch;
459 // Match the pattern.
460 FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
462 false /* don't trim */,
464 &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
466 if(nullptr != fontSet)
468 DALI_LOG_INFO(gLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont);
469 // Reserve some space to avoid reallocations.
470 fontList.reserve(fontSet->nfont);
472 for(int i = 0u; i < fontSet->nfont; ++i)
474 FcPattern* fontPattern = fontSet->fonts[i];
478 // Skip fonts with no path
479 if(GetFcString(fontPattern, FC_FILE, path))
481 // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
482 FcCharSet* characterSet = nullptr;
483 FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
485 // Increase the reference counter of the character set.
486 characterSetList.PushBack(FcCharSetCopy(characterSet));
488 fontList.push_back(FontDescription());
489 FontDescription& newFontDescription = fontList.back();
491 newFontDescription.path = std::move(path);
496 GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
497 GetFcInt(fontPattern, FC_WIDTH, width);
498 GetFcInt(fontPattern, FC_WEIGHT, weight);
499 GetFcInt(fontPattern, FC_SLANT, slant);
500 newFontDescription.width = IntToWidthType(width);
501 newFontDescription.weight = IntToWeightType(weight);
502 newFontDescription.slant = IntToSlantType(slant);
504 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", newFontDescription.family.c_str());
505 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", newFontDescription.path.c_str());
506 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[newFontDescription.width]);
507 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[newFontDescription.weight]);
508 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant]);
512 // Destroys the font set created by FcFontSort.
513 FcFontSetDestroy(fontSet);
517 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " No fonts found.\n");
520 // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
521 FcPatternDestroy(fontFamilyPattern);
523 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n");
526 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts)
528 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n");
530 if(mDefaultFonts.empty())
532 FontDescription fontDescription;
533 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
534 fontDescription.width = IntToWidthType(DEFAULT_FONT_WIDTH);
535 fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT);
536 fontDescription.slant = IntToSlantType(DEFAULT_FONT_SLANT);
537 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
540 defaultFonts = mDefaultFonts;
542 DALI_LOG_INFO(gLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size());
543 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n");
546 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
548 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
550 if(!mDefaultFontDescriptionCached)
552 // Clear any font config stored info in the caches.
554 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
555 DestroyCharacterSets(mDefaultFontCharacterSets);
556 DestroyCharacterSets(mCharacterSetCache);
557 mDefaultFontCharacterSets.Clear();
558 mCharacterSetCache.Clear();
560 for(auto& item : mFallbackCache)
562 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
563 DestroyCharacterSets(*item.characterSets);
565 delete item.characterSets;
566 item.characterSets = nullptr;
569 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
570 ClearCharacterSetFromFontFaceCache();
572 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
573 FcInitReinitialize();
575 FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
577 if(nullptr != matchPattern)
579 FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
580 FcDefaultSubstitute(matchPattern);
582 FcCharSet* characterSet = nullptr;
583 MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
584 // Decrease the reference counter of the character set as it's not stored.
585 FcCharSetDestroy(characterSet);
587 // Destroys the pattern created.
588 FcPatternDestroy(matchPattern);
591 // Create again the character sets as they are not valid after FcInitReinitialize()
593 for(const auto& description : mDefaultFonts)
595 mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
598 for(const auto& description : mFontDescriptionCache)
600 mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
603 for(auto& item : mFallbackCache)
605 if(nullptr != item.fallbackFonts)
607 if(nullptr == item.characterSets)
609 item.characterSets = new CharacterSetList;
612 for(const auto& description : *(item.fallbackFonts))
614 item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
619 mDefaultFontDescriptionCached = true;
622 fontDescription.path = mDefaultFontDescription.path;
623 fontDescription.family = mDefaultFontDescription.family;
624 fontDescription.width = mDefaultFontDescription.width;
625 fontDescription.weight = mDefaultFontDescription.weight;
626 fontDescription.slant = mDefaultFontDescription.slant;
628 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
629 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
630 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
631 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
632 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
633 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
636 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts)
638 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
640 if(mSystemFonts.empty())
645 systemFonts = mSystemFonts;
646 DALI_LOG_INFO(gLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size());
647 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
650 void FontClient::Plugin::GetDescription(FontId id,
651 FontDescription& fontDescription) const
653 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
654 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
655 const FontId index = id - 1u;
657 if((id > 0u) && (index < mFontIdCache.Count()))
659 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
660 switch(fontIdCacheItem.type)
662 case FontDescription::FACE_FONT:
664 for(const auto& item : mFontDescriptionSizeCache)
666 if(item.fontId == fontIdCacheItem.id)
668 fontDescription = *(mFontDescriptionCache.begin() + item.validatedFontId - 1u);
670 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
671 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
672 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
673 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
674 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
675 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
681 case FontDescription::BITMAP_FONT:
683 fontDescription.type = FontDescription::BITMAP_FONT;
684 fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
689 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
690 fontDescription.type = FontDescription::INVALID;
691 fontDescription.family.clear();
696 DALI_LOG_INFO(gLogFilter, Debug::General, " No description found for the font ID %d\n", id);
697 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
700 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id)
702 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
703 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
704 const FontId index = id - 1u;
707 (index < mFontIdCache.Count()))
709 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
711 switch(fontIdCacheItem.type)
713 case FontDescription::FACE_FONT:
715 DALI_LOG_INFO(gLogFilter, Debug::General, " point size : %d\n", (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize);
716 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
717 return (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize;
719 case FontDescription::BITMAP_FONT:
721 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
725 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
731 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid font ID %d\n", id);
734 DALI_LOG_INFO(gLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE);
735 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
736 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
739 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character)
741 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
742 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
743 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", character);
745 if((fontId < 1u) || (fontId > mFontIdCache.Count()))
747 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n", mFontIdCache.Count());
748 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
754 bool isSupported = false;
756 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
758 switch(fontIdCacheItem.type)
760 case FontDescription::FACE_FONT:
762 if(fontIdCacheItem.id < mFontFaceCache.size())
764 FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
766 if(nullptr == cacheItem.mCharacterSet)
768 // Create again the character set.
769 // It can be null if the ResetSystemDefaults() method has been called.
771 FontDescription description;
772 description.path = cacheItem.mPath;
773 description.family = std::move(FontFamily(cacheItem.mFreeTypeFace->family_name));
774 description.weight = FontWeight::NONE;
775 description.width = FontWidth::NONE;
776 description.slant = FontSlant::NONE;
778 // Note FreeType doesn't give too much info to build a proper font style.
779 if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
781 description.slant = FontSlant::ITALIC;
783 if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
785 description.weight = FontWeight::BOLD;
788 cacheItem.mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
791 isSupported = FcCharSetHasChar(cacheItem.mCharacterSet, character);
795 case FontDescription::BITMAP_FONT:
797 const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
799 for(const auto& glyph : bitmapFont.glyphs)
801 if(glyph.utf32 == character)
811 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
815 DALI_LOG_INFO(gLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false"));
816 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
820 FontId FontClient::Plugin::FindFontForCharacter(const FontList& fontList,
821 const CharacterSetList& characterSetList,
823 PointSize26Dot6 requestedPointSize,
826 DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
828 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n");
829 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", character);
830 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
831 DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
834 bool foundColor = false;
836 DALI_LOG_INFO(gLogFilter, Debug::General, " number of fonts : %d\n", fontList.size());
838 // Traverse the list of fonts.
839 // Check for each font if supports the character.
840 for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
842 const FontDescription& description = fontList[index];
843 const FcCharSet* const characterSet = characterSetList[index];
845 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", description.family.c_str());
846 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str());
847 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width]);
848 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight]);
849 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant]);
851 bool foundInRanges = false;
852 if(nullptr != characterSet)
854 foundInRanges = FcCharSetHasChar(characterSet, character);
859 fontId = GetFontId(description,
863 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " font id : %d\n", fontId);
868 (fontId - 1u < mFontIdCache.Count()))
870 const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
872 foundColor = item.mHasColorTables;
875 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " foundColor : %s\n", (foundColor ? "true" : "false"));
878 // Keep going unless we prefer a different (color) font.
879 if(!preferColor || foundColor)
886 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
887 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n");
891 FontId FontClient::Plugin::FindDefaultFont(Character charcode,
892 PointSize26Dot6 requestedPointSize,
895 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n");
896 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", charcode);
897 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
898 DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
902 // Create the list of default fonts if it has not been created.
903 if(mDefaultFonts.empty())
905 FontDescription fontDescription;
906 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
907 fontDescription.width = IntToWidthType(DEFAULT_FONT_WIDTH);
908 fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT);
909 fontDescription.slant = IntToSlantType(DEFAULT_FONT_SLANT);
911 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
913 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size());
915 // Traverse the list of default fonts.
916 // Check for each default font if supports the character.
917 fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
919 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
920 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n");
925 FontId FontClient::Plugin::FindFallbackFont(Character charcode,
926 const FontDescription& preferredFontDescription,
927 PointSize26Dot6 requestedPointSize,
930 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n");
931 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", charcode);
932 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
933 DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
935 // The font id to be returned.
938 FontDescription fontDescription;
940 // Fill the font description with the preferred font description and complete with the defaults.
941 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
942 fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? IntToWeightType(DEFAULT_FONT_WEIGHT) : preferredFontDescription.weight);
943 fontDescription.width = ((FontWidth::NONE == preferredFontDescription.width) ? IntToWidthType(DEFAULT_FONT_WIDTH) : preferredFontDescription.width);
944 fontDescription.slant = ((FontSlant::NONE == preferredFontDescription.slant) ? IntToSlantType(DEFAULT_FONT_SLANT) : preferredFontDescription.slant);
946 DALI_LOG_INFO(gLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n");
947 DALI_LOG_INFO(gLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
948 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
949 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
950 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
952 // Check first if the font's description has been queried before.
953 FontList* fontList = nullptr;
954 CharacterSetList* characterSetList = nullptr;
956 if(!FindFallbackFontList(fontDescription, fontList, characterSetList))
958 fontList = new FontList;
959 characterSetList = new CharacterSetList;
961 SetFontList(fontDescription, *fontList, *characterSetList);
963 FontDescription appleColorEmoji;
964 appleColorEmoji.family = "Apple Color Emoji";
965 appleColorEmoji.width = fontDescription.width;
966 appleColorEmoji.weight = fontDescription.weight;
967 appleColorEmoji.slant = fontDescription.slant;
968 FontList emojiFontList;
969 CharacterSetList emojiCharSetList;
970 SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
972 std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
973 emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
974 *fontList = std::move(emojiFontList);
975 *characterSetList = std::move(emojiCharSetList);
978 // Add the font-list to the cache.
979 mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
982 if(fontList && characterSetList)
984 fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
987 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
988 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
992 FontId FontClient::Plugin::GetFontId(const FontPath& path,
993 PointSize26Dot6 requestedPointSize,
995 bool cacheDescription)
997 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n");
998 DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str());
999 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
1003 if(nullptr != mFreeTypeLibrary)
1005 FontId foundId = 0u;
1006 if(FindFont(path, requestedPointSize, faceIndex, foundId))
1012 id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
1016 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
1017 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n");
1022 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
1023 PointSize26Dot6 requestedPointSize,
1024 FaceIndex faceIndex)
1026 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n");
1027 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
1028 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
1029 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
1030 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
1031 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
1032 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
1034 // This method uses three vectors which caches:
1035 // * The bitmap font cache
1036 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
1037 // * The path to font file names.
1038 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
1040 // 1) Checks if the font description matches with a previously loaded bitmap font.
1041 // Returns if a font is found.
1042 // 2) Checks in the cache if the font's description has been validated before.
1043 // If it was it gets an index to the vector with paths to font file names. Otherwise,
1044 // retrieves using font config a path to a font file name which matches with the
1045 // font's description. The path is stored in the cache.
1047 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
1048 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
1049 // the GetFontId() method with the path to the font file name and the point size to
1052 // The font id to be returned.
1055 // Check first if the font description matches with a previously loaded bitmap font.
1056 if(FindBitmapFont(fontDescription.family, fontId))
1061 // Check if the font's description have been validated before.
1062 FontDescriptionId validatedFontId = 0u;
1064 if(!FindValidatedFont(fontDescription,
1067 // Use font config to validate the font's description.
1068 ValidateFont(fontDescription,
1072 FontId fontFaceId = 0u;
1073 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
1074 if(!FindFont(validatedFontId, requestedPointSize, fontFaceId))
1076 // Retrieve the font file name path.
1077 const FontDescription& description = *(mFontDescriptionCache.begin() + validatedFontId - 1u);
1079 // Retrieve the font id. Do not cache the description as it has been already cached.
1080 fontId = GetFontId(description.path,
1085 fontFaceId = mFontIdCache[fontId - 1u].id;
1086 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(mCharacterSetCache[validatedFontId - 1u]);
1088 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
1089 mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
1095 fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
1098 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
1099 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n");
1104 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont)
1106 for(const auto& item : mBitmapFontCache)
1108 if(bitmapFont.name == item.font.name)
1110 return item.id + 1u;
1114 BitmapFontCacheItem bitmapFontCacheItem;
1115 bitmapFontCacheItem.font = bitmapFont;
1116 bitmapFontCacheItem.id = mFontIdCache.Count();
1118 // Resize the vector with the pixel buffers.
1119 bitmapFontCacheItem.pixelBuffers.resize(bitmapFont.glyphs.size());
1121 // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
1122 unsigned int index = 0u;
1123 for(auto& glyph : bitmapFontCacheItem.font.glyphs)
1125 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1127 if(EqualsZero(glyph.ascender) && EqualsZero(glyph.descender))
1130 pixelBuffer = LoadImageFromFile(glyph.url);
1134 glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1138 bitmapFontCacheItem.font.ascender = std::max(glyph.ascender, bitmapFontCacheItem.font.ascender);
1139 bitmapFontCacheItem.font.descender = std::min(glyph.descender, bitmapFontCacheItem.font.descender);
1144 FontIdCacheItem fontIdCacheItem;
1145 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1146 fontIdCacheItem.id = mBitmapFontCache.size();
1148 mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
1149 mFontIdCache.PushBack(fontIdCacheItem);
1151 return bitmapFontCacheItem.id + 1u;
1154 void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
1155 FontDescriptionId& validatedFontId)
1157 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n");
1158 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
1159 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
1160 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
1161 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
1162 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
1164 // Create a font pattern.
1165 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
1167 FontDescription description;
1169 FcCharSet* characterSet = nullptr;
1170 bool matched = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
1171 FcPatternDestroy(fontFamilyPattern);
1173 if(matched && (nullptr != characterSet))
1175 // Add the path to the cache.
1176 description.type = FontDescription::FACE_FONT;
1177 mFontDescriptionCache.push_back(description);
1179 // Set the index to the vector of paths to font file names.
1180 validatedFontId = mFontDescriptionCache.size();
1182 DALI_LOG_INFO(gLogFilter, Debug::General, " matched description; family : [%s]\n", description.family.c_str());
1183 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str());
1184 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width]);
1185 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight]);
1186 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant]);
1187 DALI_LOG_INFO(gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
1189 // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1190 mCharacterSetCache.PushBack(characterSet);
1192 // Cache the index and the matched font's description.
1193 FontDescriptionCacheItem item(description,
1196 mValidatedFontCache.push_back(std::move(item));
1198 if((fontDescription.family != description.family) ||
1199 (fontDescription.width != description.width) ||
1200 (fontDescription.weight != description.weight) ||
1201 (fontDescription.slant != description.slant))
1203 // Cache the given font's description if it's different than the matched.
1204 FontDescriptionCacheItem item(fontDescription,
1207 mValidatedFontCache.push_back(std::move(item));
1212 DALI_LOG_INFO(gLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str());
1215 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n");
1218 void FontClient::Plugin::GetFontMetrics(FontId fontId,
1219 FontMetrics& metrics)
1221 const FontId index = fontId - 1u;
1224 (index < mFontIdCache.Count()))
1226 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1228 switch(fontIdCacheItem.type)
1230 case FontDescription::FACE_FONT:
1232 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1234 metrics = font.mMetrics;
1236 // Adjust the metrics if the fixed-size font should be down-scaled
1237 if(font.mIsFixedSizeBitmap)
1239 const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1241 if(desiredFixedSize > 0.f)
1243 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1245 metrics.ascender = metrics.ascender * scaleFactor;
1246 metrics.descender = metrics.descender * scaleFactor;
1247 metrics.height = metrics.height * scaleFactor;
1248 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1249 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1254 case FontDescription::BITMAP_FONT:
1256 const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1258 metrics.ascender = bitmapFontCacheItem.font.ascender;
1259 metrics.descender = bitmapFontCacheItem.font.descender;
1260 metrics.height = metrics.ascender - metrics.descender;
1261 metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1262 metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1267 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1273 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
1277 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId fontId,
1280 GlyphIndex glyphIndex = 0u;
1281 const FontId index = fontId - 1u;
1284 (index < mFontIdCache.Count()))
1286 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1288 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1290 FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1292 glyphIndex = FT_Get_Char_Index(ftFace, charcode);
1299 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
1304 if(VECTOR_GLYPH == type)
1306 return GetVectorMetrics(array, size, horizontal);
1309 return GetBitmapMetrics(array, size, horizontal);
1312 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
1318 for(unsigned int i = 0; i < size; ++i)
1320 GlyphInfo& glyph = array[i];
1322 FontId index = glyph.fontId - 1u;
1324 if((glyph.fontId > 0u) &&
1325 (index < mFontIdCache.Count()))
1327 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1329 switch(fontIdCacheItem.type)
1331 case FontDescription::FACE_FONT:
1333 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1335 FT_Face ftFace = font.mFreeTypeFace;
1337 #ifdef FREETYPE_BITMAP_SUPPORT
1338 // Check to see if we should be loading a Fixed Size bitmap?
1339 if(font.mIsFixedSizeBitmap)
1341 FT_Select_Size(ftFace, font.mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
1342 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
1343 if(FT_Err_Ok == error)
1345 glyph.width = font.mFixedWidthPixels;
1346 glyph.height = font.mFixedHeightPixels;
1347 glyph.advance = font.mFixedWidthPixels;
1348 glyph.xBearing = 0.0f;
1349 glyph.yBearing = font.mFixedHeightPixels;
1351 // Adjust the metrics if the fixed-size font should be down-scaled
1352 const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1354 if(desiredFixedSize > 0.f)
1356 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1358 glyph.width = glyph.width * scaleFactor;
1359 glyph.height = glyph.height * scaleFactor;
1360 glyph.advance = glyph.advance * scaleFactor;
1361 glyph.xBearing = glyph.xBearing * scaleFactor;
1362 glyph.yBearing = glyph.yBearing * scaleFactor;
1364 glyph.scaleFactor = scaleFactor;
1369 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
1376 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1377 // i.e. with the SNum-3R font.
1378 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1379 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);
1381 // Keep the width of the glyph before doing the software emboldening.
1382 // It will be used to calculate a scale factor to be applied to the
1383 // advance as Harfbuzz doesn't apply any SW emboldening to calculate
1384 // the advance of the glyph.
1385 const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1387 if(FT_Err_Ok == error)
1389 const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
1390 if(isEmboldeningRequired)
1392 // Does the software bold.
1393 FT_GlyphSlot_Embolden(ftFace->glyph);
1396 glyph.width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1397 glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
1400 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
1401 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
1405 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
1406 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
1409 if(isEmboldeningRequired && !Dali::EqualsZero(width))
1411 // If the glyph is emboldened by software, the advance is multiplied by a
1412 // scale factor to make it slightly bigger.
1413 glyph.advance *= (glyph.width / width);
1416 // Use the bounding box of the bitmap to correct the metrics.
1417 // For some fonts i.e the SNum-3R the metrics need to be corrected,
1418 // otherwise the glyphs 'dance' up and down depending on the
1419 // font's point size.
1422 error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);
1425 FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
1427 const float descender = glyph.height - glyph.yBearing;
1428 glyph.height = (bbox.yMax - bbox.yMin) * FROM_266;
1429 glyph.yBearing = glyph.height - round(descender);
1431 // Created FT_Glyph object must be released with FT_Done_Glyph
1432 FT_Done_Glyph(ftGlyph);
1441 case FontDescription::BITMAP_FONT:
1443 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1445 unsigned int index = 0u;
1446 for(auto& item : bitmapFontCacheItem.font.glyphs)
1448 if(item.utf32 == glyph.index)
1450 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1453 pixelBuffer = LoadImageFromFile(item.url);
1456 glyph.width = static_cast<float>(pixelBuffer.GetWidth());
1457 glyph.height = static_cast<float>(pixelBuffer.GetHeight());
1458 glyph.xBearing = 0.f;
1459 glyph.yBearing = glyph.height + item.descender;
1460 glyph.advance = glyph.width;
1461 glyph.scaleFactor = 1.f;
1472 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1478 // Check if it's an embedded image.
1479 if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count()))
1481 const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1483 glyph.width = static_cast<float>(item.width);
1484 glyph.height = static_cast<float>(item.height);
1485 glyph.xBearing = 0.f;
1486 glyph.yBearing = glyph.height;
1487 glyph.advance = glyph.width;
1488 glyph.scaleFactor = 1.f;
1500 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
1504 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1507 for(unsigned int i = 0u; i < size; ++i)
1509 FontId fontId = array[i].fontId;
1512 (fontId - 1u) < mFontIdCache.Count())
1514 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1516 if(!font.mVectorFontId)
1518 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1521 mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
1523 // Vector metrics are in EMs, convert to pixels
1524 const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
1525 array[i].width *= scale;
1526 array[i].height *= scale;
1527 array[i].xBearing *= scale;
1528 array[i].yBearing *= scale;
1529 array[i].advance *= scale;
1543 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
1545 const FontId index = fontId - 1u;
1548 (index < mFontIdCache.Count()))
1550 data.isColorBitmap = false;
1551 data.isColorEmoji = false;
1553 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1555 switch(fontIdCacheItem.type)
1557 case FontDescription::FACE_FONT:
1559 // For the software italics.
1560 bool isShearRequired = false;
1562 const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1563 FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1567 #ifdef FREETYPE_BITMAP_SUPPORT
1568 // Check to see if this is fixed size bitmap
1569 if(fontFaceCacheItem.mIsFixedSizeBitmap)
1571 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1576 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1577 // i.e. with the SNum-3R font.
1578 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1579 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
1581 if(FT_Err_Ok == error)
1583 if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
1585 // Does the software bold.
1586 FT_GlyphSlot_Embolden(ftFace->glyph);
1589 if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
1591 // Will do the software italic.
1592 isShearRequired = true;
1596 error = FT_Get_Glyph(ftFace->glyph, &glyph);
1598 // Convert to bitmap if necessary
1599 if(FT_Err_Ok == error)
1601 if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
1603 int offsetX = 0, offsetY = 0;
1604 bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
1606 // Create a bitmap for the outline
1609 // Retrieve the horizontal and vertical distance from the current pen position to the
1610 // left and top border of the glyph bitmap for a normal glyph before applying the outline.
1611 if(FT_Err_Ok == error)
1613 FT_Glyph normalGlyph;
1614 error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);
1616 error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
1617 if(FT_Err_Ok == error)
1619 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);
1621 offsetX = bitmapGlyph->left;
1622 offsetY = bitmapGlyph->top;
1625 // Created FT_Glyph object must be released with FT_Done_Glyph
1626 FT_Done_Glyph(normalGlyph);
1629 // Now apply the outline
1633 error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
1635 if(FT_Err_Ok == error)
1637 FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
1638 error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
1640 if(FT_Err_Ok == error)
1642 FT_Stroker_Done(stroker);
1646 DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
1651 DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
1655 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1656 if(FT_Err_Ok == error)
1658 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
1662 // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
1663 data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
1664 data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
1667 ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
1671 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
1676 ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
1679 data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1681 // Created FT_Glyph object must be released with FT_Done_Glyph
1682 FT_Done_Glyph(glyph);
1687 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
1691 case FontDescription::BITMAP_FONT:
1693 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1695 unsigned int index = 0u;
1696 for(auto& item : bitmapFontCacheItem.font.glyphs)
1698 if(item.utf32 == glyphIndex)
1700 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1703 pixelBuffer = LoadImageFromFile(item.url);
1706 data.width = pixelBuffer.GetWidth();
1707 data.height = pixelBuffer.GetHeight();
1709 data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1711 ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer());
1713 // Sets the pixel format.
1714 data.format = pixelBuffer.GetPixelFormat();
1723 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1729 if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count()))
1731 // It's an embedded item.
1732 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1734 data.width = item.width;
1735 data.height = item.height;
1736 if(0u != item.pixelBufferId)
1738 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId - 1u].pixelBuffer;
1741 ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer());
1743 // Sets the pixel format.
1744 data.format = pixelBuffer.GetPixelFormat();
1749 // Creates the output buffer
1750 const unsigned int bufferSize = data.width * data.height * 4u;
1751 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1753 memset(data.buffer, 0u, bufferSize);
1755 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1761 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
1763 TextAbstraction::FontClient::GlyphBufferData data;
1765 CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
1767 return PixelData::New(data.buffer,
1768 data.width * data.height * Pixel::GetBytesPerPixel(data.format),
1772 PixelData::DELETE_ARRAY);
1775 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
1780 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1782 (fontId - 1u < mFontIdCache.Count()))
1784 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1785 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1787 if(!font.mVectorFontId)
1789 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1792 mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1797 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
1799 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n");
1800 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize);
1802 // First look into the cache if there is an ellipsis glyph for the requested point size.
1803 for(const auto& item : mEllipsisCache)
1805 if(item.requestedPointSize == requestedPointSize)
1807 // Use the glyph in the cache.
1809 DALI_LOG_INFO(gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1810 DALI_LOG_INFO(gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1811 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n");
1817 // No glyph has been found. Create one.
1818 mEllipsisCache.PushBack(EllipsisItem());
1819 EllipsisItem& item = *(mEllipsisCache.End() - 1u);
1821 item.requestedPointSize = requestedPointSize;
1823 // Find a font for the ellipsis glyph.
1824 item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1828 // Set the character index to access the glyph inside the font.
1829 item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].id].mFreeTypeFace,
1830 ELLIPSIS_CHARACTER);
1832 GetBitmapMetrics(&item.glyph, 1u, true);
1834 DALI_LOG_INFO(gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1835 DALI_LOG_INFO(gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1836 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n");
1841 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
1843 FT_Error error = -1;
1845 const FontId index = fontId - 1u;
1848 (index < mFontIdCache.Count()))
1850 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1852 switch(fontIdCacheItem.type)
1854 case FontDescription::FACE_FONT:
1856 #ifdef FREETYPE_BITMAP_SUPPORT
1857 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1858 FT_Face ftFace = item.mFreeTypeFace;
1860 // Check to see if this is fixed size bitmap
1861 if(item.mHasColorTables)
1863 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1868 case FontDescription::BITMAP_FONT:
1870 error = FT_Err_Ok; // Will return true;
1875 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1880 return FT_Err_Ok == error;
1883 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId)
1885 FT_Face fontFace = nullptr;
1887 const FontId index = fontId - 1u;
1889 (index < mFontIdCache.Count()))
1891 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1893 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1895 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1901 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId)
1903 const FontId index = fontId - 1u;
1905 (index < mFontIdCache.Count()))
1907 return mFontIdCache[index].type;
1909 return FontDescription::INVALID;
1912 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1914 // nullptr as first parameter means the current configuration is used.
1915 return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1918 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
1920 EmbeddedItem embeddedItem;
1922 embeddedItem.pixelBufferId = 0u;
1923 embeddedItem.width = description.width;
1924 embeddedItem.height = description.height;
1926 pixelFormat = Pixel::A8;
1928 if(!description.url.empty())
1930 // Check if the url is in the cache.
1931 PixelBufferId index = 0u;
1933 for(const auto& cacheItem : mPixelBufferCache)
1936 if(cacheItem.url == description.url)
1938 // The url is in the pixel buffer cache.
1939 // Set the index +1 to the vector.
1940 embeddedItem.pixelBufferId = index;
1945 Devel::PixelBuffer pixelBuffer;
1946 if(0u == embeddedItem.pixelBufferId)
1948 // The pixel buffer is not in the cache. Create one and cache it.
1950 // Load the image from the url.
1951 pixelBuffer = LoadImageFromFile(description.url);
1953 // Create the cache item.
1954 PixelBufferCacheItem pixelBufferCacheItem;
1955 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1956 pixelBufferCacheItem.url = description.url;
1958 // Store the cache item in the cache.
1959 mPixelBufferCache.push_back(std::move(pixelBufferCacheItem));
1961 // Set the pixel buffer id to the embedded item.
1962 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1966 // Retrieve the pixel buffer from the cache to set the pixel format.
1967 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1972 // Set the size of the embedded item if it has not been set.
1973 if(0u == embeddedItem.width)
1975 embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1978 if(0u == embeddedItem.height)
1980 embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1983 // Set the pixel format.
1984 pixelFormat = pixelBuffer.GetPixelFormat();
1988 // Find if the same embeddedItem has already been created.
1989 unsigned int index = 0u;
1990 for(const auto& item : mEmbeddedItemCache)
1993 if((item.pixelBufferId == embeddedItem.pixelBufferId) &&
1994 (item.width == embeddedItem.width) &&
1995 (item.height == embeddedItem.height))
2001 // Cache the embedded item.
2002 mEmbeddedItemCache.PushBack(embeddedItem);
2004 return mEmbeddedItemCache.Count();
2008 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
2010 mIsAtlasLimitationEnabled = enabled;
2013 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
2015 return mIsAtlasLimitationEnabled;
2018 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
2020 return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
2023 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
2025 return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
2028 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
2030 return mCurrentMaximumBlockSizeFitInAtlas;
2033 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
2035 bool isChanged = false;
2036 const Size& maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
2037 const uint16_t& padding = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
2039 if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
2041 mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
2048 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
2050 return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
2054 void FontClient::Plugin::InitSystemFonts()
2056 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n");
2058 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
2062 DALI_LOG_INFO(gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont);
2064 // Reserve some space to avoid reallocations.
2065 mSystemFonts.reserve(fontSet->nfont);
2067 for(int i = 0u; i < fontSet->nfont; ++i)
2069 FcPattern* fontPattern = fontSet->fonts[i];
2073 // Skip fonts with no path
2074 if(GetFcString(fontPattern, FC_FILE, path))
2076 mSystemFonts.push_back(FontDescription());
2077 FontDescription& fontDescription = mSystemFonts.back();
2079 fontDescription.path = std::move(path);
2084 GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
2085 GetFcInt(fontPattern, FC_WIDTH, width);
2086 GetFcInt(fontPattern, FC_WEIGHT, weight);
2087 GetFcInt(fontPattern, FC_SLANT, slant);
2088 fontDescription.width = IntToWidthType(width);
2089 fontDescription.weight = IntToWeightType(weight);
2090 fontDescription.slant = IntToSlantType(slant);
2092 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str());
2093 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
2094 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
2095 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2096 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2100 // Destroys the font set created.
2101 FcFontSetDestroy(fontSet);
2103 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n");
2106 bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
2108 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n");
2110 FcResult result = FcResultMatch;
2111 FcPattern* match = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
2113 const bool matched = nullptr != match;
2114 DALI_LOG_INFO(gLogFilter, Debug::General, " pattern matched : %s\n", (matched ? "true" : "false"));
2121 GetFcString(match, FC_FILE, fontDescription.path);
2122 GetFcString(match, FC_FAMILY, fontDescription.family);
2123 GetFcInt(match, FC_WIDTH, width);
2124 GetFcInt(match, FC_WEIGHT, weight);
2125 GetFcInt(match, FC_SLANT, slant);
2126 fontDescription.width = IntToWidthType(width);
2127 fontDescription.weight = IntToWeightType(weight);
2128 fontDescription.slant = IntToSlantType(slant);
2130 // Retrieve the character set and increase the reference counter.
2131 FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
2132 *characterSet = FcCharSetCopy(*characterSet);
2134 // destroyed the matched pattern
2135 FcPatternDestroy(match);
2137 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
2138 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
2139 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
2140 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2141 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2144 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n");
2148 FcPattern* FontClient::Plugin::CreateFontFamilyPattern(const FontDescription& fontDescription) const
2150 // create the cached font family lookup pattern
2151 // a pattern holds a set of names, each name refers to a property of the font
2152 FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2154 if(!fontFamilyPattern)
2159 // add a property to the pattern for the font family
2160 FcPatternAddString(fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fontDescription.family.c_str()));
2162 // add a property to the pattern for local setting.
2163 const char* locale = setlocale(LC_MESSAGES, nullptr);
2164 if(locale != nullptr)
2166 FcPatternAddString(fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale));
2169 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
2173 width = DEFAULT_FONT_WIDTH;
2176 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
2180 weight = DEFAULT_FONT_WEIGHT;
2183 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
2187 slant = DEFAULT_FONT_SLANT;
2190 FcPatternAddInteger(fontFamilyPattern, FC_WIDTH, width);
2191 FcPatternAddInteger(fontFamilyPattern, FC_WEIGHT, weight);
2192 FcPatternAddInteger(fontFamilyPattern, FC_SLANT, slant);
2194 // modify the config, with the mFontFamilyPatterm
2195 FcConfigSubstitute(nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern);
2197 // provide default values for unspecified properties in the font pattern
2198 // e.g. patterns without a specified style or weight are set to Medium
2199 FcDefaultSubstitute(fontFamilyPattern);
2201 return fontFamilyPattern;
2204 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
2206 FcFontSet* fontset = nullptr;
2208 // create a new pattern.
2209 // a pattern holds a set of names, each name refers to a property of the font
2210 FcPattern* pattern = FcPatternCreate();
2212 if(nullptr != pattern)
2214 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
2215 FcObjectSet* objectSet = FcObjectSetCreate();
2217 if(nullptr != objectSet)
2219 // build an object set from a list of property names
2220 FcObjectSetAdd(objectSet, FC_FILE);
2221 FcObjectSetAdd(objectSet, FC_FAMILY);
2222 FcObjectSetAdd(objectSet, FC_WIDTH);
2223 FcObjectSetAdd(objectSet, FC_WEIGHT);
2224 FcObjectSetAdd(objectSet, FC_SLANT);
2226 // get a list of fonts
2227 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
2228 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.
2230 // clear up the object set
2231 FcObjectSetDestroy(objectSet);
2234 // clear up the pattern
2235 FcPatternDestroy(pattern);
2241 bool FontClient::Plugin::GetFcString(const FcPattern* const pattern,
2242 const char* const n,
2243 std::string& string)
2245 FcChar8* file = nullptr;
2246 const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
2248 if(FcResultMatch == retVal)
2250 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
2251 string.assign(reinterpret_cast<const char*>(file));
2259 bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
2261 const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
2263 if(FcResultMatch == retVal)
2271 FontId FontClient::Plugin::CreateFont(const FontPath& path,
2272 PointSize26Dot6 requestedPointSize,
2273 FaceIndex faceIndex,
2274 bool cacheDescription)
2276 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n");
2277 DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str());
2278 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2282 // Create & cache new font face
2284 int error = FT_New_Face(mFreeTypeLibrary,
2289 if(FT_Err_Ok == error)
2291 // Check if a font is scalable.
2292 const bool isScalable = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
2293 const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
2294 const bool hasColorTables = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
2295 FontId fontFaceId = 0u;
2297 DALI_LOG_INFO(gLogFilter, Debug::General, " isScalable : [%s]\n", (isScalable ? "true" : "false"));
2298 DALI_LOG_INFO(gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
2299 DALI_LOG_INFO(gLogFilter, Debug::General, " hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
2301 // Check to see if the font contains fixed sizes?
2302 if(!isScalable && hasFixedSizedBitmaps)
2304 PointSize26Dot6 actualPointSize = 0u;
2305 int fixedSizeIndex = 0;
2306 for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
2308 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2309 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
2311 if(fixedSize >= requestedPointSize)
2313 actualPointSize = fixedSize;
2318 if(0u == actualPointSize)
2320 // The requested point size is bigger than the bigest fixed size.
2321 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2322 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2325 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
2327 // Tell Freetype to use this size
2328 error = FT_Select_Size(ftFace, fixedSizeIndex);
2329 if(FT_Err_Ok != error)
2331 DALI_LOG_INFO(gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
2335 const float fixedWidth = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
2336 const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
2338 // Indicate that the font is a fixed sized bitmap
2339 FontMetrics metrics(fixedHeight, // The ascender in pixels.
2341 fixedHeight, // The height in pixels.
2345 // Create the FreeType font face item to cache.
2346 FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
2348 // Set the index to the font's id cache.
2349 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2351 // Create the font id item to cache.
2352 FontIdCacheItem fontIdCacheItem;
2353 fontIdCacheItem.type = FontDescription::FACE_FONT;
2355 // Set the index to the FreeType font face cache.
2356 fontIdCacheItem.id = mFontFaceCache.size();
2357 fontFaceId = fontIdCacheItem.id + 1u;
2360 mFontFaceCache.push_back(fontFaceCacheItem);
2361 mFontIdCache.PushBack(fontIdCacheItem);
2363 // Set the font id to be returned.
2364 id = mFontIdCache.Count();
2369 if(mIsAtlasLimitationEnabled)
2371 //There is limitation on block size to fit in predefined atlas size.
2372 //If the block size cannot fit into atlas size, then the system cannot draw block.
2373 //This is workaround to avoid issue in advance
2374 //Decrementing point-size until arriving to maximum allowed block size.
2375 auto requestedPointSizeBackup = requestedPointSize;
2376 const Size& maxSizeFitInAtlas = GetCurrentMaximumBlockSizeFitInAtlas();
2377 error = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
2379 if(requestedPointSize != requestedPointSizeBackup)
2381 DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
2386 error = FT_Set_Char_Size(ftFace,
2393 if(FT_Err_Ok == error)
2395 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2397 FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
2398 static_cast<float>(ftMetrics.descender) * FROM_266,
2399 static_cast<float>(ftMetrics.height) * FROM_266,
2400 static_cast<float>(ftFace->underline_position) * FROM_266,
2401 static_cast<float>(ftFace->underline_thickness) * FROM_266);
2403 // Create the FreeType font face item to cache.
2404 FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics);
2406 // Set the index to the font's id cache.
2407 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2409 // Create the font id item to cache.
2410 FontIdCacheItem fontIdCacheItem;
2411 fontIdCacheItem.type = FontDescription::FACE_FONT;
2413 // Set the index to the FreeType font face cache.
2414 fontIdCacheItem.id = mFontFaceCache.size();
2415 fontFaceId = fontIdCacheItem.id + 1u;
2418 mFontFaceCache.push_back(fontFaceCacheItem);
2419 mFontIdCache.PushBack(fontIdCacheItem);
2421 // Set the font id to be returned.
2422 id = mFontIdCache.Count();
2426 DALI_LOG_INFO(gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
2430 if(0u != fontFaceId)
2432 if(cacheDescription)
2434 CacheFontPath(ftFace, fontFaceId, requestedPointSize, path);
2440 DALI_LOG_INFO(gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str());
2443 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
2444 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n");
2449 void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer)
2451 // Set the input dimensions.
2452 const ImageDimensions inputDimensions(srcWidth, srcHeight);
2454 // Set the output dimensions.
2455 // If the output dimension is not given, the input dimension is set
2456 // and won't be downscaling.
2457 data.width = (data.width == 0) ? srcWidth : data.width;
2458 data.height = (data.height == 0) ? srcHeight : data.height;
2459 const ImageDimensions desiredDimensions(data.width, data.height);
2461 // Creates the output buffer
2462 const unsigned int bufferSize = data.width * data.height * 4u;
2463 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2465 if(inputDimensions == desiredDimensions)
2467 // There isn't downscaling.
2468 memcpy(data.buffer, srcBuffer, bufferSize);
2472 Dali::Internal::Platform::LanczosSample4BPP(srcBuffer,
2479 void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired)
2481 if(srcBitmap.width * srcBitmap.rows > 0)
2483 switch(srcBitmap.pixel_mode)
2485 case FT_PIXEL_MODE_GRAY:
2487 if(srcBitmap.pitch == static_cast<int>(srcBitmap.width))
2489 uint8_t* pixelsIn = srcBitmap.buffer;
2490 unsigned int width = srcBitmap.width;
2491 unsigned height = srcBitmap.rows;
2493 std::unique_ptr<uint8_t, void (*)(void*)> pixelsOutPtr(nullptr, free);
2498 * Glyphs' bitmaps with no slant retrieved from FreeType:
2508 * Expected glyphs' bitmaps with italic slant:
2509 * ____________ ______
2516 * ------------ ------
2518 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2528 * 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.
2530 unsigned int widthOut = 0u;
2531 unsigned int heightOut = 0u;
2532 uint8_t* pixelsOut = nullptr;
2534 Dali::Internal::Platform::HorizontalShear(pixelsIn,
2538 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2545 pixelsIn = pixelsOut;
2546 pixelsOutPtr.reset(pixelsOut);
2549 const unsigned int bufferSize = width * height;
2550 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2552 data.height = height;
2553 data.format = Pixel::L8; // Sets the pixel format.
2554 memcpy(data.buffer, pixelsIn, bufferSize);
2559 #ifdef FREETYPE_BITMAP_SUPPORT
2560 case FT_PIXEL_MODE_BGRA:
2562 if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
2564 ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer);
2566 // Sets the pixel format.
2567 data.format = Pixel::BGRA8888;
2574 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n");
2581 bool FontClient::Plugin::FindFont(const FontPath& path,
2582 PointSize26Dot6 requestedPointSize,
2583 FaceIndex faceIndex,
2584 FontId& fontId) const
2586 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n");
2587 DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str());
2588 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2589 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size());
2592 for(const auto& cacheItem : mFontFaceCache)
2594 if(cacheItem.mRequestedPointSize == requestedPointSize &&
2595 cacheItem.mFaceIndex == faceIndex &&
2596 cacheItem.mPath == path)
2598 fontId = cacheItem.mFontId + 1u;
2600 DALI_LOG_INFO(gLogFilter, Debug::General, " font found, id : %d\n", fontId);
2601 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2607 DALI_LOG_INFO(gLogFilter, Debug::General, " font not found\n");
2608 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2613 bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
2614 FontDescriptionId& validatedFontId)
2616 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n");
2617 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
2618 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
2619 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
2620 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2621 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2622 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
2624 validatedFontId = 0u;
2626 for(const auto& item : mValidatedFontCache)
2628 if(!fontDescription.family.empty() &&
2629 (fontDescription.family == item.fontDescription.family) &&
2630 (fontDescription.width == item.fontDescription.width) &&
2631 (fontDescription.weight == item.fontDescription.weight) &&
2632 (fontDescription.slant == item.fontDescription.slant))
2634 validatedFontId = item.index;
2636 DALI_LOG_INFO(gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId);
2637 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n");
2642 DALI_LOG_INFO(gLogFilter, Debug::General, " validated font not found\n");
2643 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n");
2647 bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription,
2648 FontList*& fontList,
2649 CharacterSetList*& characterSetList)
2651 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n");
2652 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
2653 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
2654 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
2655 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2656 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2657 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size());
2661 for(const auto& item : mFallbackCache)
2663 if(!fontDescription.family.empty() &&
2664 (fontDescription.family == item.fontDescription.family) &&
2665 (fontDescription.width == item.fontDescription.width) &&
2666 (fontDescription.weight == item.fontDescription.weight) &&
2667 (fontDescription.slant == item.fontDescription.slant))
2669 fontList = item.fallbackFonts;
2670 characterSetList = item.characterSets;
2672 DALI_LOG_INFO(gLogFilter, Debug::General, " fallback font list found.\n");
2673 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n");
2678 DALI_LOG_INFO(gLogFilter, Debug::General, " fallback font list not found.\n");
2679 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n");
2683 bool FontClient::Plugin::FindFont(FontDescriptionId validatedFontId,
2684 PointSize26Dot6 requestedPointSize,
2687 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n");
2688 DALI_LOG_INFO(gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
2689 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2693 for(const auto& item : mFontDescriptionSizeCache)
2695 if((validatedFontId == item.validatedFontId) &&
2696 (requestedPointSize == item.requestedPointSize))
2698 fontId = item.fontId;
2700 DALI_LOG_INFO(gLogFilter, Debug::General, " font found, id : %d\n", fontId);
2701 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2706 DALI_LOG_INFO(gLogFilter, Debug::General, " font not found.\n");
2707 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2711 bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const
2715 for(const auto& item : mBitmapFontCache)
2717 if(bitmapFont == item.font.name)
2719 fontId = item.id + 1u;
2727 bool FontClient::Plugin::IsScalable(const FontPath& path)
2729 bool isScalable = false;
2732 int error = FT_New_Face(mFreeTypeLibrary,
2736 if(FT_Err_Ok != error)
2738 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
2742 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2748 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription)
2750 // Create a font pattern.
2751 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2753 FcResult result = FcResultMatch;
2755 // match the pattern
2756 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2757 bool isScalable = false;
2761 // Get the path to the font file name.
2763 GetFcString(match, FC_FILE, path);
2764 isScalable = IsScalable(path);
2768 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2771 // Destroys the created patterns.
2772 FcPatternDestroy(match);
2773 FcPatternDestroy(fontFamilyPattern);
2778 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes)
2780 // Empty the caller container
2784 int error = FT_New_Face(mFreeTypeLibrary,
2788 if(FT_Err_Ok != error)
2790 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
2793 // Fetch the number of fixed sizes available
2794 if(ftFace->num_fixed_sizes && ftFace->available_sizes)
2796 for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
2798 sizes.PushBack(ftFace->available_sizes[i].size);
2803 void FontClient::Plugin::GetFixedSizes(const FontDescription& fontDescription,
2804 Vector<PointSize26Dot6>& sizes)
2806 // Create a font pattern.
2807 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2809 FcResult result = FcResultMatch;
2811 // match the pattern
2812 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2816 // Get the path to the font file name.
2818 GetFcString(match, FC_FILE, path);
2819 GetFixedSizes(path, sizes);
2823 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2826 // Destroys the created patterns.
2827 FcPatternDestroy(match);
2828 FcPatternDestroy(fontFamilyPattern);
2831 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
2833 bool hasItalicStyle = false;
2835 const FontId index = fontId - 1u;
2838 (index < mFontIdCache.Count()))
2840 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2842 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
2844 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2846 hasItalicStyle = 0u != (font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC);
2851 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
2854 return hasItalicStyle;
2857 void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path)
2859 FontDescription description;
2860 description.path = path;
2861 description.family = std::move(FontFamily(ftFace->family_name));
2862 description.weight = FontWeight::NONE;
2863 description.width = FontWidth::NONE;
2864 description.slant = FontSlant::NONE;
2866 // Note FreeType doesn't give too much info to build a proper font style.
2867 if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
2869 description.slant = FontSlant::ITALIC;
2871 if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
2873 description.weight = FontWeight::BOLD;
2876 FontDescriptionId validatedFontId = 0u;
2877 if(!FindValidatedFont(description,
2880 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2882 FcResult result = FcResultMatch;
2883 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2885 FcCharSet* characterSet = nullptr;
2886 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2888 const FontId fontFaceId = id - 1u;
2889 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
2891 // Destroys the created patterns.
2892 FcPatternDestroy(match);
2893 FcPatternDestroy(pattern);
2895 // Add the path to the cache.
2896 description.type = FontDescription::FACE_FONT;
2897 mFontDescriptionCache.push_back(description);
2899 // Set the index to the vector of paths to font file names.
2900 validatedFontId = mFontDescriptionCache.size();
2902 // Increase the reference counter and add the character set to the cache.
2903 mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
2905 // Cache the index and the font's description.
2906 mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
2909 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2910 mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
2916 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription(const FontDescription& description)
2918 FcCharSet* characterSet = nullptr;
2920 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2922 if(nullptr != pattern)
2924 FcResult result = FcResultMatch;
2925 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2927 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2929 // Destroys the created patterns.
2930 FcPatternDestroy(match);
2931 FcPatternDestroy(pattern);
2934 return characterSet;
2937 void FontClient::Plugin::ClearFallbackCache(std::vector<FallbackCacheItem>& fallbackCache)
2939 for(auto& item : fallbackCache)
2941 if(nullptr != item.fallbackFonts)
2943 delete item.fallbackFonts;
2946 if(nullptr != item.characterSets)
2948 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2949 DestroyCharacterSets(*item.characterSets);
2950 delete item.characterSets;
2955 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2957 for(auto& item : mFontFaceCache)
2959 FcCharSetDestroy(item.mCharacterSet);
2960 item.mCharacterSet = nullptr;
2964 } // namespace Internal
2966 } // namespace TextAbstraction