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)
42 // Note, to turn on trace and verbose logging, use "export LOG_FONT_CLIENT=3,true"
43 // Or re-define the following filter using Verbose,true instead of NoLogging,false,
44 // Or, add DALI_LOG_FILTER_ENABLE_TRACE(gFontClientLogFilter) in the code below.
46 Dali::Integration::Log::Filter* gFontClientLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
48 #define FONT_LOG_DESCRIPTION(fontDescription, prefix) \
49 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix " description; family : [%s]\n", fontDescription.family.c_str()); \
50 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, \
54 " slant : [%s]\n\n", \
55 fontDescription.path.c_str(), \
56 FontWidth::Name[fontDescription.width], \
57 FontWeight::Name[fontDescription.weight], \
58 FontSlant::Name[fontDescription.slant])
60 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
61 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, \
63 " requestedPointSize : %d\n" \
64 " preferColor : %s\n", \
67 (preferColor ? "true" : "false"))
71 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
72 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
77 * Conversion from Fractional26.6 to float
79 const float FROM_266 = 1.0f / 64.0f;
80 const float POINTS_PER_INCH = 72.f;
82 const std::string DEFAULT_FONT_FAMILY_NAME("Tizen");
83 const int DEFAULT_FONT_WIDTH = 100; // normal
84 const int DEFAULT_FONT_WEIGHT = 80; // normal
85 const int DEFAULT_FONT_SLANT = 0; // normal
87 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
89 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
91 // NONE -1 --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
100 // ULTRA_EXPANDED 200
101 const int FONT_WIDTH_TYPE_TO_INT[] = {-1, 50, 63, 75, 87, 100, 113, 125, 150, 200};
102 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof(FONT_WIDTH_TYPE_TO_INT) / sizeof(int);
104 // NONE -1 --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
106 // ULTRA_LIGHT, EXTRA_LIGHT 40
108 // DEMI_LIGHT, SEMI_LIGHT 55
110 // NORMAL, REGULAR 80
112 // DEMI_BOLD, SEMI_BOLD 180
114 // ULTRA_BOLD, EXTRA_BOLD 205
115 // BLACK, HEAVY, EXTRA_BLACK 210
116 const int FONT_WEIGHT_TYPE_TO_INT[] = {-1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210};
117 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof(FONT_WEIGHT_TYPE_TO_INT) / sizeof(int);
119 // NONE -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
123 const int FONT_SLANT_TYPE_TO_INT[] = {-1, 0, 100, 110};
124 const unsigned int NUM_FONT_SLANT_TYPE = sizeof(FONT_SLANT_TYPE_TO_INT) / sizeof(int);
133 namespace TextAbstraction
138 * @brief Returns the FontWidth's enum index for the given width value.
140 * @param[in] width The width value.
142 * @return The FontWidth's enum index.
144 FontWidth::Type IntToWidthType(int width)
146 return static_cast<FontWidth::Type>(ValueToIndex(width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u));
150 * @brief Returns the FontWeight's enum index for the given weight value.
152 * @param[in] weight The weight value.
154 * @return The FontWeight's enum index.
156 FontWeight::Type IntToWeightType(int weight)
158 return static_cast<FontWeight::Type>(ValueToIndex(weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u));
162 * @brief Returns the FontSlant's enum index for the given slant value.
164 * @param[in] slant The slant value.
166 * @return The FontSlant's enum index.
168 FontSlant::Type IntToSlantType(int slant)
170 return static_cast<FontSlant::Type>(ValueToIndex(slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u));
174 * @brief Free the resources allocated by the FcCharSet objects.
176 * @param[in] characterSets The vector of character sets.
178 void DestroyCharacterSets(CharacterSetList& characterSets)
180 for(auto& item : characterSets)
184 FcCharSetDestroy(item);
190 * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
192 * @param[in/out] ftFace Face type object.
193 * @param[in] horizontalDpi The horizontal dpi.
194 * @param[in] verticalDpi The vertical dpi.
195 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
196 * @param[in] requestedPointSize The requested point-size.
197 * @return whether the ftFace's block can fit into atlas
199 bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
203 error = FT_Set_Char_Size(ftFace,
209 if(error == FT_Err_Ok)
211 //Check width and height of block for requestedPointSize
212 //If the width or height is greater than the maximum-size then decrement by one unit of point-size.
213 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)
223 * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
225 * @param[in/out] ftFace Face type object.
226 * @param[in] horizontalDpi The horizontal dpi.
227 * @param[in] verticalDpi The vertical dpi.
228 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
229 * @param[in/out] requestedPointSize The requested point-size.
230 * @return FreeType error code. 0 means success when requesting the nominal size (in points).
232 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
234 //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
235 const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
237 int error; // FreeType error code.
239 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
240 if(FT_Err_Ok != error)
248 uint32_t exponentialDecrement = 1;
250 while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit * exponentialDecrement)
252 requestedPointSize -= (pointSizePerOneUnit * exponentialDecrement);
253 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
254 if(FT_Err_Ok != error)
259 exponentialDecrement *= 2;
263 uint32_t minPointSize;
264 uint32_t maxPointSize;
268 exponentialDecrement /= 2;
269 minPointSize = requestedPointSize;
270 maxPointSize = requestedPointSize + (pointSizePerOneUnit * exponentialDecrement);
275 maxPointSize = requestedPointSize;
278 while(minPointSize < maxPointSize)
280 requestedPointSize = ((maxPointSize / pointSizePerOneUnit - minPointSize / pointSizePerOneUnit) / 2) * pointSizePerOneUnit + minPointSize;
281 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
282 if(FT_Err_Ok != error)
289 if(minPointSize == requestedPointSize)
291 //Found targeted point-size
295 minPointSize = requestedPointSize;
299 maxPointSize = requestedPointSize;
307 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
308 : fontDescription{std::move(font)},
309 fallbackFonts{fallbackFonts},
310 characterSets{characterSets}
314 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
315 FontDescriptionId index)
316 : fontDescription{fontDescription},
321 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
322 FontDescriptionId index)
323 : fontDescription{std::move(fontDescription)},
328 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem(FontDescriptionId validatedFontId,
329 PointSize26Dot6 requestedPointSize,
331 : validatedFontId(validatedFontId),
332 requestedPointSize(requestedPointSize),
337 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace,
338 const FontPath& path,
339 PointSize26Dot6 requestedPointSize,
341 const FontMetrics& metrics)
342 : mFreeTypeFace(ftFace),
344 mRequestedPointSize(requestedPointSize),
347 mCharacterSet(nullptr),
349 mFixedWidthPixels(0.f),
350 mFixedHeightPixels(0.f),
353 mIsFixedSizeBitmap(false),
354 mHasColorTables(false)
358 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace,
359 const FontPath& path,
360 PointSize26Dot6 requestedPointSize,
362 const FontMetrics& metrics,
367 : mFreeTypeFace(ftFace),
369 mRequestedPointSize(requestedPointSize),
372 mCharacterSet(nullptr),
373 mFixedSizeIndex(fixedSizeIndex),
374 mFixedWidthPixels(fixedWidth),
375 mFixedHeightPixels(fixedHeight),
378 mIsFixedSizeBitmap(true),
379 mHasColorTables(hasColorTables)
383 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
384 unsigned int verticalDpi)
385 : mFreeTypeLibrary(nullptr),
386 mDpiHorizontal(horizontalDpi),
387 mDpiVertical(verticalDpi),
388 mDefaultFontDescription(),
393 mValidatedFontCache(),
394 mFontDescriptionCache(),
395 mCharacterSetCache(),
396 mFontDescriptionSizeCache(),
397 mVectorFontCache(nullptr),
399 mEmbeddedItemCache(),
400 mDefaultFontDescriptionCached(false),
401 mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
402 mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS)
405 int error = FT_Init_FreeType(&mFreeTypeLibrary);
406 if(FT_Err_Ok != error)
408 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Init error: %d\n", error);
411 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
412 mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
416 FontClient::Plugin::~Plugin()
418 ClearFallbackCache(mFallbackCache);
420 // Free the resources allocated by the FcCharSet objects.
421 DestroyCharacterSets(mDefaultFontCharacterSets);
422 DestroyCharacterSets(mCharacterSetCache);
423 ClearCharacterSetFromFontFaceCache();
425 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
426 delete mVectorFontCache;
428 FT_Done_FreeType(mFreeTypeLibrary);
431 void FontClient::Plugin::ClearCache()
433 mDefaultFontDescription = FontDescription();
435 mSystemFonts.clear();
436 mDefaultFonts.clear();
438 DestroyCharacterSets(mDefaultFontCharacterSets);
439 mDefaultFontCharacterSets.Clear();
441 ClearFallbackCache(mFallbackCache);
442 mFallbackCache.clear();
444 mFontIdCache.Clear();
446 ClearCharacterSetFromFontFaceCache();
447 mFontFaceCache.clear();
449 mValidatedFontCache.clear();
450 mFontDescriptionCache.clear();
452 DestroyCharacterSets(mCharacterSetCache);
453 mCharacterSetCache.Clear();
455 mFontDescriptionSizeCache.clear();
457 mEllipsisCache.Clear();
458 mPixelBufferCache.clear();
459 mEmbeddedItemCache.Clear();
460 mBitmapFontCache.clear();
462 mDefaultFontDescriptionCached = false;
465 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
466 unsigned int verticalDpi)
468 mDpiHorizontal = horizontalDpi;
469 mDpiVertical = verticalDpi;
472 void FontClient::Plugin::ResetSystemDefaults()
474 mDefaultFontDescriptionCached = false;
477 void FontClient::Plugin::SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
479 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
480 FONT_LOG_DESCRIPTION(fontDescription, "");
483 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
485 FcResult result = FcResultMatch;
487 // Match the pattern.
488 FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
490 false /* don't trim */,
492 &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
494 if(nullptr != fontSet)
496 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont);
497 // Reserve some space to avoid reallocations.
498 fontList.reserve(fontSet->nfont);
500 for(int i = 0u; i < fontSet->nfont; ++i)
502 FcPattern* fontPattern = fontSet->fonts[i];
506 // Skip fonts with no path
507 if(GetFcString(fontPattern, FC_FILE, path))
509 // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
510 FcCharSet* characterSet = nullptr;
511 FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
513 // Increase the reference counter of the character set.
514 characterSetList.PushBack(FcCharSetCopy(characterSet));
516 fontList.push_back(FontDescription());
517 FontDescription& newFontDescription = fontList.back();
519 newFontDescription.path = std::move(path);
524 GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
525 GetFcInt(fontPattern, FC_WIDTH, width);
526 GetFcInt(fontPattern, FC_WEIGHT, weight);
527 GetFcInt(fontPattern, FC_SLANT, slant);
528 newFontDescription.width = IntToWidthType(width);
529 newFontDescription.weight = IntToWeightType(weight);
530 newFontDescription.slant = IntToSlantType(slant);
532 FONT_LOG_DESCRIPTION(newFontDescription, "");
536 // Destroys the font set created by FcFontSort.
537 FcFontSetDestroy(fontSet);
541 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " No fonts found.\n");
544 // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
545 FcPatternDestroy(fontFamilyPattern);
548 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts)
550 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
552 if(mDefaultFonts.empty())
554 FontDescription fontDescription;
555 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
556 fontDescription.width = IntToWidthType(DEFAULT_FONT_WIDTH);
557 fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT);
558 fontDescription.slant = IntToSlantType(DEFAULT_FONT_SLANT);
559 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
562 defaultFonts = mDefaultFonts;
564 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size());
567 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
569 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
571 if(!mDefaultFontDescriptionCached)
573 // Clear any font config stored info in the caches.
575 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
576 DestroyCharacterSets(mDefaultFontCharacterSets);
577 DestroyCharacterSets(mCharacterSetCache);
578 mDefaultFontCharacterSets.Clear();
579 mCharacterSetCache.Clear();
581 for(auto& item : mFallbackCache)
583 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
584 DestroyCharacterSets(*item.characterSets);
586 delete item.characterSets;
587 item.characterSets = nullptr;
590 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
591 ClearCharacterSetFromFontFaceCache();
593 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
594 FcInitReinitialize();
596 FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
598 if(nullptr != matchPattern)
600 FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
601 FcDefaultSubstitute(matchPattern);
603 FcCharSet* characterSet = nullptr;
604 MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
605 // Decrease the reference counter of the character set as it's not stored.
606 FcCharSetDestroy(characterSet);
608 // Destroys the pattern created.
609 FcPatternDestroy(matchPattern);
612 // Create again the character sets as they are not valid after FcInitReinitialize()
614 for(const auto& description : mDefaultFonts)
616 mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
619 for(const auto& description : mFontDescriptionCache)
621 mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
624 for(auto& item : mFallbackCache)
626 if(nullptr != item.fallbackFonts)
628 if(nullptr == item.characterSets)
630 item.characterSets = new CharacterSetList;
633 for(const auto& description : *(item.fallbackFonts))
635 item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
640 mDefaultFontDescriptionCached = true;
643 fontDescription.path = mDefaultFontDescription.path;
644 fontDescription.family = mDefaultFontDescription.family;
645 fontDescription.width = mDefaultFontDescription.width;
646 fontDescription.weight = mDefaultFontDescription.weight;
647 fontDescription.slant = mDefaultFontDescription.slant;
649 FONT_LOG_DESCRIPTION(fontDescription, "");
652 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts)
654 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
656 if(mSystemFonts.empty())
661 systemFonts = mSystemFonts;
662 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size());
665 void FontClient::Plugin::GetDescription(FontId id,
666 FontDescription& fontDescription) const
668 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
669 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
670 const FontId index = id - 1u;
672 if((id > 0u) && (index < mFontIdCache.Count()))
674 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
675 switch(fontIdCacheItem.type)
677 case FontDescription::FACE_FONT:
679 for(const auto& item : mFontDescriptionSizeCache)
681 if(item.fontId == fontIdCacheItem.id)
683 fontDescription = *(mFontDescriptionCache.begin() + item.validatedFontId - 1u);
685 FONT_LOG_DESCRIPTION(fontDescription, "");
691 case FontDescription::BITMAP_FONT:
693 fontDescription.type = FontDescription::BITMAP_FONT;
694 fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
699 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
700 fontDescription.type = FontDescription::INVALID;
701 fontDescription.family.clear();
706 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " No description found for the font ID %d\n", id);
709 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id)
711 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
712 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
713 const FontId index = id - 1u;
716 (index < mFontIdCache.Count()))
718 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
720 switch(fontIdCacheItem.type)
722 case FontDescription::FACE_FONT:
724 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " point size : %d\n", (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize);
725 return (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize;
727 case FontDescription::BITMAP_FONT:
729 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
733 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
739 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid font ID %d\n", id);
742 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE);
743 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
746 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character)
748 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
749 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
750 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " character : %p\n", character);
752 if((fontId < 1u) || (fontId > mFontIdCache.Count()))
754 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n", mFontIdCache.Count());
760 bool isSupported = false;
762 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
764 switch(fontIdCacheItem.type)
766 case FontDescription::FACE_FONT:
768 if(fontIdCacheItem.id < mFontFaceCache.size())
770 FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
772 if(nullptr == cacheItem.mCharacterSet)
774 // Create again the character set.
775 // It can be null if the ResetSystemDefaults() method has been called.
777 FontDescription description;
778 description.path = cacheItem.mPath;
779 description.family = std::move(FontFamily(cacheItem.mFreeTypeFace->family_name));
780 description.weight = FontWeight::NONE;
781 description.width = FontWidth::NONE;
782 description.slant = FontSlant::NONE;
784 // Note FreeType doesn't give too much info to build a proper font style.
785 if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
787 description.slant = FontSlant::ITALIC;
789 if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
791 description.weight = FontWeight::BOLD;
794 cacheItem.mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
797 isSupported = FcCharSetHasChar(cacheItem.mCharacterSet, character);
801 case FontDescription::BITMAP_FONT:
803 const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
805 for(const auto& glyph : bitmapFont.glyphs)
807 if(glyph.utf32 == character)
817 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
821 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false"));
825 FontId FontClient::Plugin::FindFontForCharacter(const FontList& fontList,
826 const CharacterSetList& characterSetList,
828 PointSize26Dot6 requestedPointSize,
831 DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
832 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
833 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " character : %p\n", character);
834 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
835 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
838 bool foundColor = false;
840 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of fonts : %d\n", fontList.size());
842 // Traverse the list of fonts.
843 // Check for each font if supports the character.
844 for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
846 const FontDescription& description = fontList[index];
847 const FcCharSet* const characterSet = characterSetList[index];
849 FONT_LOG_DESCRIPTION(description, "");
851 bool foundInRanges = false;
852 if(nullptr != characterSet)
854 foundInRanges = FcCharSetHasChar(characterSet, character);
859 fontId = GetFontId(description, requestedPointSize, 0u);
861 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " font id : %d\n", fontId);
866 (fontId - 1u < mFontIdCache.Count()))
868 const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
870 foundColor = item.mHasColorTables;
873 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " foundColor : %s\n", (foundColor ? "true" : "false"));
876 // Keep going unless we prefer a different (color) font.
877 if(!preferColor || foundColor)
884 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
888 FontId FontClient::Plugin::FindDefaultFont(Character charcode,
889 PointSize26Dot6 requestedPointSize,
892 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
893 FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
897 // Create the list of default fonts if it has not been created.
898 if(mDefaultFonts.empty())
900 FontDescription fontDescription;
901 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
902 fontDescription.width = IntToWidthType(DEFAULT_FONT_WIDTH);
903 fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT);
904 fontDescription.slant = IntToSlantType(DEFAULT_FONT_SLANT);
906 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
908 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size());
910 // Traverse the list of default fonts.
911 // Check for each default font if supports the character.
912 fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
914 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
918 FontId FontClient::Plugin::FindFallbackFont(Character charcode,
919 const FontDescription& preferredFontDescription,
920 PointSize26Dot6 requestedPointSize,
923 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
924 FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
926 // The font id to be returned.
929 FontDescription fontDescription;
931 // Fill the font description with the preferred font description and complete with the defaults.
932 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
933 fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? IntToWeightType(DEFAULT_FONT_WEIGHT) : preferredFontDescription.weight);
934 fontDescription.width = ((FontWidth::NONE == preferredFontDescription.width) ? IntToWidthType(DEFAULT_FONT_WIDTH) : preferredFontDescription.width);
935 fontDescription.slant = ((FontSlant::NONE == preferredFontDescription.slant) ? IntToSlantType(DEFAULT_FONT_SLANT) : preferredFontDescription.slant);
937 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n");
938 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
939 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
940 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
941 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
943 // Check first if the font's description has been queried before.
944 FontList* fontList = nullptr;
945 CharacterSetList* characterSetList = nullptr;
947 if(!FindFallbackFontList(fontDescription, fontList, characterSetList))
949 fontList = new FontList;
950 characterSetList = new CharacterSetList;
952 SetFontList(fontDescription, *fontList, *characterSetList);
954 FontDescription appleColorEmoji;
955 appleColorEmoji.family = "Apple Color Emoji";
956 appleColorEmoji.width = fontDescription.width;
957 appleColorEmoji.weight = fontDescription.weight;
958 appleColorEmoji.slant = fontDescription.slant;
959 FontList emojiFontList;
960 CharacterSetList emojiCharSetList;
961 SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
963 std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
964 emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
965 *fontList = std::move(emojiFontList);
966 *characterSetList = std::move(emojiCharSetList);
969 // Add the font-list to the cache.
970 mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
973 if(fontList && characterSetList)
975 fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
978 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
982 FontId FontClient::Plugin::GetFontId(const FontPath& path,
983 PointSize26Dot6 requestedPointSize,
985 bool cacheDescription)
987 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
988 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
989 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
993 if(nullptr != mFreeTypeLibrary)
996 if(FindFont(path, requestedPointSize, faceIndex, foundId))
1002 id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
1006 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
1010 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
1011 PointSize26Dot6 requestedPointSize,
1012 FaceIndex faceIndex)
1014 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1015 FONT_LOG_DESCRIPTION(fontDescription, "");
1016 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
1018 // This method uses three vectors which caches:
1019 // * The bitmap font cache
1020 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
1021 // * The path to font file names.
1022 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
1024 // 1) Checks if the font description matches with a previously loaded bitmap font.
1025 // Returns if a font is found.
1026 // 2) Checks in the cache if the font's description has been validated before.
1027 // If it was it gets an index to the vector with paths to font file names. Otherwise,
1028 // retrieves using font config a path to a font file name which matches with the
1029 // font's description. The path is stored in the cache.
1031 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
1032 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
1033 // the GetFontId() method with the path to the font file name and the point size to
1036 // The font id to be returned.
1039 // Check first if the font description matches with a previously loaded bitmap font.
1040 if(FindBitmapFont(fontDescription.family, fontId))
1045 // Check if the font's description have been validated before.
1046 FontDescriptionId validatedFontId = 0u;
1048 if(!FindValidatedFont(fontDescription,
1051 // Use font config to validate the font's description.
1052 ValidateFont(fontDescription,
1056 FontId fontFaceId = 0u;
1057 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
1058 if(!FindFont(validatedFontId, requestedPointSize, fontFaceId))
1060 // Retrieve the font file name path.
1061 const FontDescription& description = *(mFontDescriptionCache.begin() + validatedFontId - 1u);
1063 // Retrieve the font id. Do not cache the description as it has been already cached.
1064 fontId = GetFontId(description.path,
1069 fontFaceId = mFontIdCache[fontId - 1u].id;
1070 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(mCharacterSetCache[validatedFontId - 1u]);
1072 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
1073 mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
1079 fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
1082 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
1086 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont)
1088 for(const auto& item : mBitmapFontCache)
1090 if(bitmapFont.name == item.font.name)
1092 return item.id + 1u;
1096 BitmapFontCacheItem bitmapFontCacheItem;
1097 bitmapFontCacheItem.font = bitmapFont;
1098 bitmapFontCacheItem.id = mFontIdCache.Count();
1100 // Resize the vector with the pixel buffers.
1101 bitmapFontCacheItem.pixelBuffers.resize(bitmapFont.glyphs.size());
1103 // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
1104 unsigned int index = 0u;
1105 for(auto& glyph : bitmapFontCacheItem.font.glyphs)
1107 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1109 if(EqualsZero(glyph.ascender) && EqualsZero(glyph.descender))
1112 pixelBuffer = LoadImageFromFile(glyph.url);
1116 glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1120 bitmapFontCacheItem.font.ascender = std::max(glyph.ascender, bitmapFontCacheItem.font.ascender);
1121 bitmapFontCacheItem.font.descender = std::min(glyph.descender, bitmapFontCacheItem.font.descender);
1126 FontIdCacheItem fontIdCacheItem;
1127 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1128 fontIdCacheItem.id = mBitmapFontCache.size();
1130 mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
1131 mFontIdCache.PushBack(fontIdCacheItem);
1133 return bitmapFontCacheItem.id + 1u;
1136 void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
1137 FontDescriptionId& validatedFontId)
1139 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1140 FONT_LOG_DESCRIPTION(fontDescription, "");
1142 // Create a font pattern.
1143 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
1145 FontDescription description;
1147 FcCharSet* characterSet = nullptr;
1148 bool matched = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
1149 FcPatternDestroy(fontFamilyPattern);
1151 if(matched && (nullptr != characterSet))
1153 // Add the path to the cache.
1154 description.type = FontDescription::FACE_FONT;
1155 mFontDescriptionCache.push_back(description);
1157 // Set the index to the vector of paths to font file names.
1158 validatedFontId = mFontDescriptionCache.size();
1160 FONT_LOG_DESCRIPTION(description, "matched");
1161 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
1163 // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1164 mCharacterSetCache.PushBack(characterSet);
1166 // Cache the index and the matched font's description.
1167 FontDescriptionCacheItem item(description,
1170 mValidatedFontCache.push_back(std::move(item));
1172 if((fontDescription.family != description.family) ||
1173 (fontDescription.width != description.width) ||
1174 (fontDescription.weight != description.weight) ||
1175 (fontDescription.slant != description.slant))
1177 // Cache the given font's description if it's different than the matched.
1178 FontDescriptionCacheItem item(fontDescription,
1181 mValidatedFontCache.push_back(std::move(item));
1186 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str());
1190 void FontClient::Plugin::GetFontMetrics(FontId fontId,
1191 FontMetrics& metrics)
1193 const FontId index = fontId - 1u;
1196 (index < mFontIdCache.Count()))
1198 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1200 switch(fontIdCacheItem.type)
1202 case FontDescription::FACE_FONT:
1204 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1206 metrics = font.mMetrics;
1208 // Adjust the metrics if the fixed-size font should be down-scaled
1209 if(font.mIsFixedSizeBitmap)
1211 const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1213 if(desiredFixedSize > 0.f)
1215 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1217 metrics.ascender = metrics.ascender * scaleFactor;
1218 metrics.descender = metrics.descender * scaleFactor;
1219 metrics.height = metrics.height * scaleFactor;
1220 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1221 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1226 case FontDescription::BITMAP_FONT:
1228 const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1230 metrics.ascender = bitmapFontCacheItem.font.ascender;
1231 metrics.descender = bitmapFontCacheItem.font.descender;
1232 metrics.height = metrics.ascender - metrics.descender;
1233 metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1234 metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1239 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
1245 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
1249 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId fontId,
1252 GlyphIndex glyphIndex = 0u;
1253 const FontId index = fontId - 1u;
1256 (index < mFontIdCache.Count()))
1258 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1260 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1262 FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1264 glyphIndex = FT_Get_Char_Index(ftFace, charcode);
1271 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
1276 if(VECTOR_GLYPH == type)
1278 return GetVectorMetrics(array, size, horizontal);
1281 return GetBitmapMetrics(array, size, horizontal);
1284 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
1290 for(unsigned int i = 0; i < size; ++i)
1292 GlyphInfo& glyph = array[i];
1294 FontId index = glyph.fontId - 1u;
1296 if((glyph.fontId > 0u) &&
1297 (index < mFontIdCache.Count()))
1299 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1301 switch(fontIdCacheItem.type)
1303 case FontDescription::FACE_FONT:
1305 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1307 FT_Face ftFace = font.mFreeTypeFace;
1309 #ifdef FREETYPE_BITMAP_SUPPORT
1310 // Check to see if we should be loading a Fixed Size bitmap?
1311 if(font.mIsFixedSizeBitmap)
1313 FT_Select_Size(ftFace, font.mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
1314 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
1315 if(FT_Err_Ok == error)
1317 glyph.width = font.mFixedWidthPixels;
1318 glyph.height = font.mFixedHeightPixels;
1319 glyph.advance = font.mFixedWidthPixels;
1320 glyph.xBearing = 0.0f;
1321 glyph.yBearing = font.mFixedHeightPixels;
1323 // Adjust the metrics if the fixed-size font should be down-scaled
1324 const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1326 if(desiredFixedSize > 0.f)
1328 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1330 glyph.width = glyph.width * scaleFactor;
1331 glyph.height = glyph.height * scaleFactor;
1332 glyph.advance = glyph.advance * scaleFactor;
1333 glyph.xBearing = glyph.xBearing * scaleFactor;
1334 glyph.yBearing = glyph.yBearing * scaleFactor;
1336 glyph.scaleFactor = scaleFactor;
1341 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
1348 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1349 // i.e. with the SNum-3R font.
1350 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1351 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);
1353 // Keep the width of the glyph before doing the software emboldening.
1354 // It will be used to calculate a scale factor to be applied to the
1355 // advance as Harfbuzz doesn't apply any SW emboldening to calculate
1356 // the advance of the glyph.
1357 const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1359 if(FT_Err_Ok == error)
1361 const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
1362 if(isEmboldeningRequired)
1364 // Does the software bold.
1365 FT_GlyphSlot_Embolden(ftFace->glyph);
1368 glyph.width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1369 glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
1372 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
1373 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
1377 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
1378 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
1381 if(isEmboldeningRequired && !Dali::EqualsZero(width))
1383 // If the glyph is emboldened by software, the advance is multiplied by a
1384 // scale factor to make it slightly bigger.
1385 glyph.advance *= (glyph.width / width);
1388 // Use the bounding box of the bitmap to correct the metrics.
1389 // For some fonts i.e the SNum-3R the metrics need to be corrected,
1390 // otherwise the glyphs 'dance' up and down depending on the
1391 // font's point size.
1394 error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);
1397 FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
1399 const float descender = glyph.height - glyph.yBearing;
1400 glyph.height = (bbox.yMax - bbox.yMin) * FROM_266;
1401 glyph.yBearing = glyph.height - round(descender);
1403 // Created FT_Glyph object must be released with FT_Done_Glyph
1404 FT_Done_Glyph(ftGlyph);
1413 case FontDescription::BITMAP_FONT:
1415 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1417 unsigned int index = 0u;
1418 for(auto& item : bitmapFontCacheItem.font.glyphs)
1420 if(item.utf32 == glyph.index)
1422 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1425 pixelBuffer = LoadImageFromFile(item.url);
1428 glyph.width = static_cast<float>(pixelBuffer.GetWidth());
1429 glyph.height = static_cast<float>(pixelBuffer.GetHeight());
1430 glyph.xBearing = 0.f;
1431 glyph.yBearing = glyph.height + item.descender;
1432 glyph.advance = glyph.width;
1433 glyph.scaleFactor = 1.f;
1444 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
1450 // Check if it's an embedded image.
1451 if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count()))
1453 const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1455 glyph.width = static_cast<float>(item.width);
1456 glyph.height = static_cast<float>(item.height);
1457 glyph.xBearing = 0.f;
1458 glyph.yBearing = glyph.height;
1459 glyph.advance = glyph.width;
1460 glyph.scaleFactor = 1.f;
1472 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
1476 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1479 for(unsigned int i = 0u; i < size; ++i)
1481 FontId fontId = array[i].fontId;
1484 (fontId - 1u) < mFontIdCache.Count())
1486 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1488 if(!font.mVectorFontId)
1490 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1493 mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
1495 // Vector metrics are in EMs, convert to pixels
1496 const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
1497 array[i].width *= scale;
1498 array[i].height *= scale;
1499 array[i].xBearing *= scale;
1500 array[i].yBearing *= scale;
1501 array[i].advance *= scale;
1515 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
1517 const FontId index = fontId - 1u;
1520 (index < mFontIdCache.Count()))
1522 data.isColorBitmap = false;
1523 data.isColorEmoji = false;
1525 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1527 switch(fontIdCacheItem.type)
1529 case FontDescription::FACE_FONT:
1531 // For the software italics.
1532 bool isShearRequired = false;
1534 const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1535 FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1539 #ifdef FREETYPE_BITMAP_SUPPORT
1540 // Check to see if this is fixed size bitmap
1541 if(fontFaceCacheItem.mIsFixedSizeBitmap)
1543 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1548 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1549 // i.e. with the SNum-3R font.
1550 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1551 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
1553 if(FT_Err_Ok == error)
1555 if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
1557 // Does the software bold.
1558 FT_GlyphSlot_Embolden(ftFace->glyph);
1561 if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
1563 // Will do the software italic.
1564 isShearRequired = true;
1568 error = FT_Get_Glyph(ftFace->glyph, &glyph);
1570 // Convert to bitmap if necessary
1571 if(FT_Err_Ok == error)
1573 if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
1575 int offsetX = 0, offsetY = 0;
1576 bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
1578 // Create a bitmap for the outline
1581 // Retrieve the horizontal and vertical distance from the current pen position to the
1582 // left and top border of the glyph bitmap for a normal glyph before applying the outline.
1583 if(FT_Err_Ok == error)
1585 FT_Glyph normalGlyph;
1586 error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);
1588 error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
1589 if(FT_Err_Ok == error)
1591 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);
1593 offsetX = bitmapGlyph->left;
1594 offsetY = bitmapGlyph->top;
1597 // Created FT_Glyph object must be released with FT_Done_Glyph
1598 FT_Done_Glyph(normalGlyph);
1601 // Now apply the outline
1605 error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
1607 if(FT_Err_Ok == error)
1609 FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
1610 error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
1612 if(FT_Err_Ok == error)
1614 FT_Stroker_Done(stroker);
1618 DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
1623 DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
1627 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1628 if(FT_Err_Ok == error)
1630 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
1634 // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
1635 data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
1636 data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
1639 ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
1643 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
1648 ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
1651 data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1653 // Created FT_Glyph object must be released with FT_Done_Glyph
1654 FT_Done_Glyph(glyph);
1659 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
1663 case FontDescription::BITMAP_FONT:
1665 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1667 unsigned int index = 0u;
1668 for(auto& item : bitmapFontCacheItem.font.glyphs)
1670 if(item.utf32 == glyphIndex)
1672 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1675 pixelBuffer = LoadImageFromFile(item.url);
1678 data.width = pixelBuffer.GetWidth();
1679 data.height = pixelBuffer.GetHeight();
1681 data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1683 ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer());
1685 // Sets the pixel format.
1686 data.format = pixelBuffer.GetPixelFormat();
1695 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
1701 if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count()))
1703 // It's an embedded item.
1704 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1706 data.width = item.width;
1707 data.height = item.height;
1708 if(0u != item.pixelBufferId)
1710 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId - 1u].pixelBuffer;
1713 ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer());
1715 // Sets the pixel format.
1716 data.format = pixelBuffer.GetPixelFormat();
1721 // Creates the output buffer
1722 const unsigned int bufferSize = data.width * data.height * 4u;
1723 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1725 memset(data.buffer, 0u, bufferSize);
1727 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1733 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
1735 TextAbstraction::FontClient::GlyphBufferData data;
1737 CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
1739 return PixelData::New(data.buffer,
1740 data.width * data.height * Pixel::GetBytesPerPixel(data.format),
1744 PixelData::DELETE_ARRAY);
1747 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
1752 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1754 (fontId - 1u < mFontIdCache.Count()))
1756 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1757 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1759 if(!font.mVectorFontId)
1761 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1764 mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1769 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
1771 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1772 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize);
1774 // First look into the cache if there is an ellipsis glyph for the requested point size.
1775 for(const auto& item : mEllipsisCache)
1777 if(item.requestedPointSize == requestedPointSize)
1779 // Use the glyph in the cache.
1780 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1781 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1786 // No glyph has been found. Create one.
1787 mEllipsisCache.PushBack(EllipsisItem());
1788 EllipsisItem& item = *(mEllipsisCache.End() - 1u);
1790 item.requestedPointSize = requestedPointSize;
1792 // Find a font for the ellipsis glyph.
1793 item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1797 // Set the character index to access the glyph inside the font.
1798 item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].id].mFreeTypeFace,
1799 ELLIPSIS_CHARACTER);
1801 GetBitmapMetrics(&item.glyph, 1u, true);
1803 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1804 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1808 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
1810 FT_Error error = -1;
1812 const FontId index = fontId - 1u;
1815 (index < mFontIdCache.Count()))
1817 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1819 switch(fontIdCacheItem.type)
1821 case FontDescription::FACE_FONT:
1823 #ifdef FREETYPE_BITMAP_SUPPORT
1824 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1825 FT_Face ftFace = item.mFreeTypeFace;
1827 // Check to see if this is fixed size bitmap
1828 if(item.mHasColorTables)
1830 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1835 case FontDescription::BITMAP_FONT:
1837 error = FT_Err_Ok; // Will return true;
1842 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
1847 return FT_Err_Ok == error;
1850 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId)
1852 FT_Face fontFace = nullptr;
1854 const FontId index = fontId - 1u;
1856 (index < mFontIdCache.Count()))
1858 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1860 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1862 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1868 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId)
1870 const FontId index = fontId - 1u;
1872 (index < mFontIdCache.Count()))
1874 return mFontIdCache[index].type;
1876 return FontDescription::INVALID;
1879 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1881 // nullptr as first parameter means the current configuration is used.
1882 return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1885 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
1887 EmbeddedItem embeddedItem;
1889 embeddedItem.pixelBufferId = 0u;
1890 embeddedItem.width = description.width;
1891 embeddedItem.height = description.height;
1893 pixelFormat = Pixel::A8;
1895 if(!description.url.empty())
1897 // Check if the url is in the cache.
1898 PixelBufferId index = 0u;
1900 for(const auto& cacheItem : mPixelBufferCache)
1903 if(cacheItem.url == description.url)
1905 // The url is in the pixel buffer cache.
1906 // Set the index +1 to the vector.
1907 embeddedItem.pixelBufferId = index;
1912 Devel::PixelBuffer pixelBuffer;
1913 if(0u == embeddedItem.pixelBufferId)
1915 // The pixel buffer is not in the cache. Create one and cache it.
1917 // Load the image from the url.
1918 pixelBuffer = LoadImageFromFile(description.url);
1920 // Create the cache item.
1921 PixelBufferCacheItem pixelBufferCacheItem;
1922 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1923 pixelBufferCacheItem.url = description.url;
1925 // Store the cache item in the cache.
1926 mPixelBufferCache.push_back(std::move(pixelBufferCacheItem));
1928 // Set the pixel buffer id to the embedded item.
1929 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1933 // Retrieve the pixel buffer from the cache to set the pixel format.
1934 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1939 // Set the size of the embedded item if it has not been set.
1940 if(0u == embeddedItem.width)
1942 embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1945 if(0u == embeddedItem.height)
1947 embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1950 // Set the pixel format.
1951 pixelFormat = pixelBuffer.GetPixelFormat();
1955 // Find if the same embeddedItem has already been created.
1956 unsigned int index = 0u;
1957 for(const auto& item : mEmbeddedItemCache)
1960 if((item.pixelBufferId == embeddedItem.pixelBufferId) &&
1961 (item.width == embeddedItem.width) &&
1962 (item.height == embeddedItem.height))
1968 // Cache the embedded item.
1969 mEmbeddedItemCache.PushBack(embeddedItem);
1971 return mEmbeddedItemCache.Count();
1975 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
1977 mIsAtlasLimitationEnabled = enabled;
1980 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
1982 return mIsAtlasLimitationEnabled;
1985 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
1987 return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1990 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
1992 return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
1995 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
1997 return mCurrentMaximumBlockSizeFitInAtlas;
2000 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
2002 bool isChanged = false;
2003 const Size& maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
2004 const uint16_t& padding = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
2006 if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
2008 mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
2015 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
2017 return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
2021 void FontClient::Plugin::InitSystemFonts()
2023 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2025 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
2029 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont);
2031 // Reserve some space to avoid reallocations.
2032 mSystemFonts.reserve(fontSet->nfont);
2034 for(int i = 0u; i < fontSet->nfont; ++i)
2036 FcPattern* fontPattern = fontSet->fonts[i];
2040 // Skip fonts with no path
2041 if(GetFcString(fontPattern, FC_FILE, path))
2043 mSystemFonts.push_back(FontDescription());
2044 FontDescription& fontDescription = mSystemFonts.back();
2046 fontDescription.path = std::move(path);
2051 GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
2052 GetFcInt(fontPattern, FC_WIDTH, width);
2053 GetFcInt(fontPattern, FC_WEIGHT, weight);
2054 GetFcInt(fontPattern, FC_SLANT, slant);
2055 fontDescription.width = IntToWidthType(width);
2056 fontDescription.weight = IntToWeightType(weight);
2057 fontDescription.slant = IntToSlantType(slant);
2059 FONT_LOG_DESCRIPTION(fontDescription, "");
2063 // Destroys the font set created.
2064 FcFontSetDestroy(fontSet);
2068 bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
2070 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2072 FcResult result = FcResultMatch;
2073 FcPattern* match = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
2075 const bool matched = nullptr != match;
2076 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " pattern matched : %s\n", (matched ? "true" : "false"));
2083 GetFcString(match, FC_FILE, fontDescription.path);
2084 GetFcString(match, FC_FAMILY, fontDescription.family);
2085 GetFcInt(match, FC_WIDTH, width);
2086 GetFcInt(match, FC_WEIGHT, weight);
2087 GetFcInt(match, FC_SLANT, slant);
2088 fontDescription.width = IntToWidthType(width);
2089 fontDescription.weight = IntToWeightType(weight);
2090 fontDescription.slant = IntToSlantType(slant);
2092 // Retrieve the character set and increase the reference counter.
2093 FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
2094 *characterSet = FcCharSetCopy(*characterSet);
2096 // destroyed the matched pattern
2097 FcPatternDestroy(match);
2098 FONT_LOG_DESCRIPTION(fontDescription, "");
2103 FcPattern* FontClient::Plugin::CreateFontFamilyPattern(const FontDescription& fontDescription) const
2105 // create the cached font family lookup pattern
2106 // a pattern holds a set of names, each name refers to a property of the font
2107 FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2109 if(!fontFamilyPattern)
2114 // add a property to the pattern for the font family
2115 FcPatternAddString(fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fontDescription.family.c_str()));
2117 // add a property to the pattern for local setting.
2118 const char* locale = setlocale(LC_MESSAGES, nullptr);
2119 if(locale != nullptr)
2121 FcPatternAddString(fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale));
2124 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
2128 width = DEFAULT_FONT_WIDTH;
2131 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
2135 weight = DEFAULT_FONT_WEIGHT;
2138 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
2142 slant = DEFAULT_FONT_SLANT;
2145 FcPatternAddInteger(fontFamilyPattern, FC_WIDTH, width);
2146 FcPatternAddInteger(fontFamilyPattern, FC_WEIGHT, weight);
2147 FcPatternAddInteger(fontFamilyPattern, FC_SLANT, slant);
2149 // modify the config, with the mFontFamilyPatterm
2150 FcConfigSubstitute(nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern);
2152 // provide default values for unspecified properties in the font pattern
2153 // e.g. patterns without a specified style or weight are set to Medium
2154 FcDefaultSubstitute(fontFamilyPattern);
2156 return fontFamilyPattern;
2159 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
2161 FcFontSet* fontset = nullptr;
2163 // create a new pattern.
2164 // a pattern holds a set of names, each name refers to a property of the font
2165 FcPattern* pattern = FcPatternCreate();
2167 if(nullptr != pattern)
2169 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
2170 FcObjectSet* objectSet = FcObjectSetCreate();
2172 if(nullptr != objectSet)
2174 // build an object set from a list of property names
2175 FcObjectSetAdd(objectSet, FC_FILE);
2176 FcObjectSetAdd(objectSet, FC_FAMILY);
2177 FcObjectSetAdd(objectSet, FC_WIDTH);
2178 FcObjectSetAdd(objectSet, FC_WEIGHT);
2179 FcObjectSetAdd(objectSet, FC_SLANT);
2181 // get a list of fonts
2182 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
2183 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.
2185 // clear up the object set
2186 FcObjectSetDestroy(objectSet);
2189 // clear up the pattern
2190 FcPatternDestroy(pattern);
2196 bool FontClient::Plugin::GetFcString(const FcPattern* const pattern,
2197 const char* const n,
2198 std::string& string)
2200 FcChar8* file = nullptr;
2201 const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
2203 if(FcResultMatch == retVal)
2205 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
2206 string.assign(reinterpret_cast<const char*>(file));
2214 bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
2216 const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
2218 if(FcResultMatch == retVal)
2226 FontId FontClient::Plugin::CreateFont(const FontPath& path,
2227 PointSize26Dot6 requestedPointSize,
2228 FaceIndex faceIndex,
2229 bool cacheDescription)
2231 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2232 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
2233 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2237 // Create & cache new font face
2239 int error = FT_New_Face(mFreeTypeLibrary,
2244 if(FT_Err_Ok == error)
2246 // Check if a font is scalable.
2247 const bool isScalable = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
2248 const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
2249 const bool hasColorTables = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
2250 FontId fontFaceId = 0u;
2252 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " isScalable : [%s]\n", (isScalable ? "true" : "false"));
2253 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
2254 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
2256 // Check to see if the font contains fixed sizes?
2257 if(!isScalable && hasFixedSizedBitmaps)
2259 PointSize26Dot6 actualPointSize = 0u;
2260 int fixedSizeIndex = 0;
2261 for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
2263 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2264 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
2266 if(fixedSize >= requestedPointSize)
2268 actualPointSize = fixedSize;
2273 if(0u == actualPointSize)
2275 // The requested point size is bigger than the bigest fixed size.
2276 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2277 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2280 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
2282 // Tell Freetype to use this size
2283 error = FT_Select_Size(ftFace, fixedSizeIndex);
2284 if(FT_Err_Ok != error)
2286 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
2290 const float fixedWidth = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
2291 const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
2293 // Indicate that the font is a fixed sized bitmap
2294 FontMetrics metrics(fixedHeight, // The ascender in pixels.
2296 fixedHeight, // The height in pixels.
2300 // Create the FreeType font face item to cache.
2301 FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
2303 // Set the index to the font's id cache.
2304 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2306 // Create the font id item to cache.
2307 FontIdCacheItem fontIdCacheItem;
2308 fontIdCacheItem.type = FontDescription::FACE_FONT;
2310 // Set the index to the FreeType font face cache.
2311 fontIdCacheItem.id = mFontFaceCache.size();
2312 fontFaceId = fontIdCacheItem.id + 1u;
2315 mFontFaceCache.push_back(fontFaceCacheItem);
2316 mFontIdCache.PushBack(fontIdCacheItem);
2318 // Set the font id to be returned.
2319 id = mFontIdCache.Count();
2324 if(mIsAtlasLimitationEnabled)
2326 //There is limitation on block size to fit in predefined atlas size.
2327 //If the block size cannot fit into atlas size, then the system cannot draw block.
2328 //This is workaround to avoid issue in advance
2329 //Decrementing point-size until arriving to maximum allowed block size.
2330 auto requestedPointSizeBackup = requestedPointSize;
2331 const Size& maxSizeFitInAtlas = GetCurrentMaximumBlockSizeFitInAtlas();
2332 error = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
2334 if(requestedPointSize != requestedPointSizeBackup)
2336 DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
2341 error = FT_Set_Char_Size(ftFace,
2348 if(FT_Err_Ok == error)
2350 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2352 FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
2353 static_cast<float>(ftMetrics.descender) * FROM_266,
2354 static_cast<float>(ftMetrics.height) * FROM_266,
2355 static_cast<float>(ftFace->underline_position) * FROM_266,
2356 static_cast<float>(ftFace->underline_thickness) * FROM_266);
2358 // Create the FreeType font face item to cache.
2359 FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics);
2361 // Set the index to the font's id cache.
2362 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2364 // Create the font id item to cache.
2365 FontIdCacheItem fontIdCacheItem;
2366 fontIdCacheItem.type = FontDescription::FACE_FONT;
2368 // Set the index to the FreeType font face cache.
2369 fontIdCacheItem.id = mFontFaceCache.size();
2370 fontFaceId = fontIdCacheItem.id + 1u;
2373 mFontFaceCache.push_back(fontFaceCacheItem);
2374 mFontIdCache.PushBack(fontIdCacheItem);
2376 // Set the font id to be returned.
2377 id = mFontIdCache.Count();
2381 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
2385 if(0u != fontFaceId)
2387 if(cacheDescription)
2389 CacheFontPath(ftFace, fontFaceId, requestedPointSize, path);
2395 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str());
2398 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
2402 void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer)
2404 // Set the input dimensions.
2405 const ImageDimensions inputDimensions(srcWidth, srcHeight);
2407 // Set the output dimensions.
2408 // If the output dimension is not given, the input dimension is set
2409 // and won't be downscaling.
2410 data.width = (data.width == 0) ? srcWidth : data.width;
2411 data.height = (data.height == 0) ? srcHeight : data.height;
2412 const ImageDimensions desiredDimensions(data.width, data.height);
2414 // Creates the output buffer
2415 const unsigned int bufferSize = data.width * data.height * 4u;
2416 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2418 if(inputDimensions == desiredDimensions)
2420 // There isn't downscaling.
2421 memcpy(data.buffer, srcBuffer, bufferSize);
2425 Dali::Internal::Platform::LanczosSample4BPP(srcBuffer,
2432 void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired)
2434 if(srcBitmap.width * srcBitmap.rows > 0)
2436 switch(srcBitmap.pixel_mode)
2438 case FT_PIXEL_MODE_GRAY:
2440 if(srcBitmap.pitch == static_cast<int>(srcBitmap.width))
2442 uint8_t* pixelsIn = srcBitmap.buffer;
2443 unsigned int width = srcBitmap.width;
2444 unsigned height = srcBitmap.rows;
2446 std::unique_ptr<uint8_t, void (*)(void*)> pixelsOutPtr(nullptr, free);
2451 * Glyphs' bitmaps with no slant retrieved from FreeType:
2461 * Expected glyphs' bitmaps with italic slant:
2462 * ____________ ______
2469 * ------------ ------
2471 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2481 * 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.
2483 unsigned int widthOut = 0u;
2484 unsigned int heightOut = 0u;
2485 uint8_t* pixelsOut = nullptr;
2487 Dali::Internal::Platform::HorizontalShear(pixelsIn,
2491 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2498 pixelsIn = pixelsOut;
2499 pixelsOutPtr.reset(pixelsOut);
2502 const unsigned int bufferSize = width * height;
2503 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2505 data.height = height;
2506 data.format = Pixel::L8; // Sets the pixel format.
2507 memcpy(data.buffer, pixelsIn, bufferSize);
2512 #ifdef FREETYPE_BITMAP_SUPPORT
2513 case FT_PIXEL_MODE_BGRA:
2515 if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
2517 ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer);
2519 // Sets the pixel format.
2520 data.format = Pixel::BGRA8888;
2527 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n");
2534 bool FontClient::Plugin::FindFont(const FontPath& path,
2535 PointSize26Dot6 requestedPointSize,
2536 FaceIndex faceIndex,
2537 FontId& fontId) const
2539 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2540 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
2541 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2542 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size());
2545 for(const auto& cacheItem : mFontFaceCache)
2547 if(cacheItem.mRequestedPointSize == requestedPointSize &&
2548 cacheItem.mFaceIndex == faceIndex &&
2549 cacheItem.mPath == path)
2551 fontId = cacheItem.mFontId + 1u;
2553 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font found, id : %d\n", fontId);
2558 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font not found\n");
2562 bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
2563 FontDescriptionId& validatedFontId)
2565 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2566 FONT_LOG_DESCRIPTION(fontDescription, "");
2567 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
2569 validatedFontId = 0u;
2571 for(const auto& item : mValidatedFontCache)
2573 if(!fontDescription.family.empty() &&
2574 (fontDescription.family == item.fontDescription.family) &&
2575 (fontDescription.width == item.fontDescription.width) &&
2576 (fontDescription.weight == item.fontDescription.weight) &&
2577 (fontDescription.slant == item.fontDescription.slant))
2579 validatedFontId = item.index;
2581 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId);
2586 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font not found\n");
2590 bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription,
2591 FontList*& fontList,
2592 CharacterSetList*& characterSetList)
2594 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2595 FONT_LOG_DESCRIPTION(fontDescription, "");
2596 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size());
2600 for(const auto& item : mFallbackCache)
2602 if(!fontDescription.family.empty() &&
2603 (fontDescription.family == item.fontDescription.family) &&
2604 (fontDescription.width == item.fontDescription.width) &&
2605 (fontDescription.weight == item.fontDescription.weight) &&
2606 (fontDescription.slant == item.fontDescription.slant))
2608 fontList = item.fallbackFonts;
2609 characterSetList = item.characterSets;
2611 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fallback font list found.\n");
2616 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fallback font list not found.\n");
2620 bool FontClient::Plugin::FindFont(FontDescriptionId validatedFontId,
2621 PointSize26Dot6 requestedPointSize,
2624 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
2625 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
2626 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2630 for(const auto& item : mFontDescriptionSizeCache)
2632 if((validatedFontId == item.validatedFontId) &&
2633 (requestedPointSize == item.requestedPointSize))
2635 fontId = item.fontId;
2637 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font found, id : %d\n", fontId);
2642 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font not found.\n");
2646 bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const
2650 for(const auto& item : mBitmapFontCache)
2652 if(bitmapFont == item.font.name)
2654 fontId = item.id + 1u;
2662 bool FontClient::Plugin::IsScalable(const FontPath& path)
2664 bool isScalable = false;
2667 int error = FT_New_Face(mFreeTypeLibrary,
2671 if(FT_Err_Ok != error)
2673 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
2677 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2683 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription)
2685 // Create a font pattern.
2686 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2688 FcResult result = FcResultMatch;
2690 // match the pattern
2691 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2692 bool isScalable = false;
2696 // Get the path to the font file name.
2698 GetFcString(match, FC_FILE, path);
2699 isScalable = IsScalable(path);
2703 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2706 // Destroys the created patterns.
2707 FcPatternDestroy(match);
2708 FcPatternDestroy(fontFamilyPattern);
2713 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes)
2715 // Empty the caller container
2719 int error = FT_New_Face(mFreeTypeLibrary,
2723 if(FT_Err_Ok != error)
2725 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
2728 // Fetch the number of fixed sizes available
2729 if(ftFace->num_fixed_sizes && ftFace->available_sizes)
2731 for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
2733 sizes.PushBack(ftFace->available_sizes[i].size);
2738 void FontClient::Plugin::GetFixedSizes(const FontDescription& fontDescription,
2739 Vector<PointSize26Dot6>& sizes)
2741 // Create a font pattern.
2742 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2744 FcResult result = FcResultMatch;
2746 // match the pattern
2747 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2751 // Get the path to the font file name.
2753 GetFcString(match, FC_FILE, path);
2754 GetFixedSizes(path, sizes);
2758 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2761 // Destroys the created patterns.
2762 FcPatternDestroy(match);
2763 FcPatternDestroy(fontFamilyPattern);
2766 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
2768 bool hasItalicStyle = false;
2770 const FontId index = fontId - 1u;
2773 (index < mFontIdCache.Count()))
2775 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2777 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
2779 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2781 hasItalicStyle = 0u != (font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC);
2786 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
2789 return hasItalicStyle;
2792 void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path)
2794 FontDescription description;
2795 description.path = path;
2796 description.family = std::move(FontFamily(ftFace->family_name));
2797 description.weight = FontWeight::NONE;
2798 description.width = FontWidth::NONE;
2799 description.slant = FontSlant::NONE;
2801 // Note FreeType doesn't give too much info to build a proper font style.
2802 if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
2804 description.slant = FontSlant::ITALIC;
2806 if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
2808 description.weight = FontWeight::BOLD;
2811 FontDescriptionId validatedFontId = 0u;
2812 if(!FindValidatedFont(description,
2815 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2817 FcResult result = FcResultMatch;
2818 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2820 FcCharSet* characterSet = nullptr;
2821 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2823 const FontId fontFaceId = id - 1u;
2824 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
2826 // Destroys the created patterns.
2827 FcPatternDestroy(match);
2828 FcPatternDestroy(pattern);
2830 // Add the path to the cache.
2831 description.type = FontDescription::FACE_FONT;
2832 mFontDescriptionCache.push_back(description);
2834 // Set the index to the vector of paths to font file names.
2835 validatedFontId = mFontDescriptionCache.size();
2837 // Increase the reference counter and add the character set to the cache.
2838 mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
2840 // Cache the index and the font's description.
2841 mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
2844 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2845 mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
2851 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription(const FontDescription& description)
2853 FcCharSet* characterSet = nullptr;
2855 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2857 if(nullptr != pattern)
2859 FcResult result = FcResultMatch;
2860 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2862 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2864 // Destroys the created patterns.
2865 FcPatternDestroy(match);
2866 FcPatternDestroy(pattern);
2869 return characterSet;
2872 void FontClient::Plugin::ClearFallbackCache(std::vector<FallbackCacheItem>& fallbackCache)
2874 for(auto& item : fallbackCache)
2876 if(nullptr != item.fallbackFonts)
2878 delete item.fallbackFonts;
2881 if(nullptr != item.characterSets)
2883 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2884 DestroyCharacterSets(*item.characterSets);
2885 delete item.characterSets;
2890 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2892 for(auto& item : mFontFaceCache)
2894 FcCharSetDestroy(item.mCharacterSet);
2895 item.mCharacterSet = nullptr;
2899 } // namespace Internal
2901 } // namespace TextAbstraction