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);
157 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
158 : fontDescription{std::move(font)},
159 fallbackFonts{fallbackFonts},
160 characterSets{characterSets}
164 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
165 FontDescriptionId index)
166 : fontDescription{fontDescription},
171 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
172 FontDescriptionId index)
173 : fontDescription{std::move(fontDescription)},
178 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem(FontDescriptionId validatedFontId,
179 PointSize26Dot6 requestedPointSize,
181 : validatedFontId(validatedFontId),
182 requestedPointSize(requestedPointSize),
187 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace,
188 const FontPath& path,
189 PointSize26Dot6 requestedPointSize,
191 const FontMetrics& metrics)
192 : mFreeTypeFace(ftFace),
194 mRequestedPointSize(requestedPointSize),
197 mCharacterSet(nullptr),
199 mFixedWidthPixels(0.f),
200 mFixedHeightPixels(0.f),
203 mIsFixedSizeBitmap(false),
204 mHasColorTables(false)
208 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace,
209 const FontPath& path,
210 PointSize26Dot6 requestedPointSize,
212 const FontMetrics& metrics,
217 : mFreeTypeFace(ftFace),
219 mRequestedPointSize(requestedPointSize),
222 mCharacterSet(nullptr),
223 mFixedSizeIndex(fixedSizeIndex),
224 mFixedWidthPixels(fixedWidth),
225 mFixedHeightPixels(fixedHeight),
228 mIsFixedSizeBitmap(true),
229 mHasColorTables(hasColorTables)
233 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
234 unsigned int verticalDpi)
235 : mFreeTypeLibrary(nullptr),
236 mDpiHorizontal(horizontalDpi),
237 mDpiVertical(verticalDpi),
238 mDefaultFontDescription(),
243 mValidatedFontCache(),
244 mFontDescriptionCache(),
245 mCharacterSetCache(),
246 mFontDescriptionSizeCache(),
247 mVectorFontCache(nullptr),
249 mEmbeddedItemCache(),
250 mDefaultFontDescriptionCached(false)
252 int error = FT_Init_FreeType(&mFreeTypeLibrary);
253 if(FT_Err_Ok != error)
255 DALI_LOG_INFO(gLogFilter, Debug::General, "FreeType Init error: %d\n", error);
258 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
259 mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
263 FontClient::Plugin::~Plugin()
265 ClearFallbackCache(mFallbackCache);
267 // Free the resources allocated by the FcCharSet objects.
268 DestroyCharacterSets(mDefaultFontCharacterSets);
269 DestroyCharacterSets(mCharacterSetCache);
270 ClearCharacterSetFromFontFaceCache();
272 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
273 delete mVectorFontCache;
275 FT_Done_FreeType(mFreeTypeLibrary);
278 void FontClient::Plugin::ClearCache()
280 mDefaultFontDescription = FontDescription();
282 mSystemFonts.clear();
283 mDefaultFonts.clear();
285 DestroyCharacterSets(mDefaultFontCharacterSets);
286 mDefaultFontCharacterSets.Clear();
288 ClearFallbackCache(mFallbackCache);
289 mFallbackCache.clear();
291 mFontIdCache.Clear();
293 ClearCharacterSetFromFontFaceCache();
294 mFontFaceCache.clear();
296 mValidatedFontCache.clear();
297 mFontDescriptionCache.clear();
299 DestroyCharacterSets(mCharacterSetCache);
300 mCharacterSetCache.Clear();
302 mFontDescriptionSizeCache.clear();
304 mEllipsisCache.Clear();
305 mPixelBufferCache.clear();
306 mEmbeddedItemCache.Clear();
307 mBitmapFontCache.clear();
309 mDefaultFontDescriptionCached = false;
312 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
313 unsigned int verticalDpi)
315 mDpiHorizontal = horizontalDpi;
316 mDpiVertical = verticalDpi;
319 void FontClient::Plugin::ResetSystemDefaults()
321 mDefaultFontDescriptionCached = false;
324 void FontClient::Plugin::SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
326 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n");
327 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
328 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
329 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
330 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
334 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
336 FcResult result = FcResultMatch;
338 // Match the pattern.
339 FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
341 false /* don't trim */,
343 &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
345 if(nullptr != fontSet)
347 DALI_LOG_INFO(gLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont);
348 // Reserve some space to avoid reallocations.
349 fontList.reserve(fontSet->nfont);
351 for(int i = 0u; i < fontSet->nfont; ++i)
353 FcPattern* fontPattern = fontSet->fonts[i];
357 // Skip fonts with no path
358 if(GetFcString(fontPattern, FC_FILE, path))
360 // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
361 FcCharSet* characterSet = nullptr;
362 FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
364 // Increase the reference counter of the character set.
365 characterSetList.PushBack(FcCharSetCopy(characterSet));
367 fontList.push_back(FontDescription());
368 FontDescription& newFontDescription = fontList.back();
370 newFontDescription.path = std::move(path);
375 GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
376 GetFcInt(fontPattern, FC_WIDTH, width);
377 GetFcInt(fontPattern, FC_WEIGHT, weight);
378 GetFcInt(fontPattern, FC_SLANT, slant);
379 newFontDescription.width = IntToWidthType(width);
380 newFontDescription.weight = IntToWeightType(weight);
381 newFontDescription.slant = IntToSlantType(slant);
383 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", newFontDescription.family.c_str());
384 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", newFontDescription.path.c_str());
385 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[newFontDescription.width]);
386 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[newFontDescription.weight]);
387 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant]);
391 // Destroys the font set created by FcFontSort.
392 FcFontSetDestroy(fontSet);
396 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " No fonts found.\n");
399 // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
400 FcPatternDestroy(fontFamilyPattern);
402 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n");
405 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts)
407 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n");
409 if(mDefaultFonts.empty())
411 FontDescription fontDescription;
412 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
413 fontDescription.width = IntToWidthType(DEFAULT_FONT_WIDTH);
414 fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT);
415 fontDescription.slant = IntToSlantType(DEFAULT_FONT_SLANT);
416 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
419 defaultFonts = mDefaultFonts;
421 DALI_LOG_INFO(gLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size());
422 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n");
425 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
427 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
429 if(!mDefaultFontDescriptionCached)
431 // Clear any font config stored info in the caches.
433 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
434 DestroyCharacterSets(mDefaultFontCharacterSets);
435 DestroyCharacterSets(mCharacterSetCache);
436 mDefaultFontCharacterSets.Clear();
437 mCharacterSetCache.Clear();
439 for(auto& item : mFallbackCache)
441 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
442 DestroyCharacterSets(*item.characterSets);
444 delete item.characterSets;
445 item.characterSets = nullptr;
448 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
449 ClearCharacterSetFromFontFaceCache();
451 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
452 FcInitReinitialize();
454 FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
456 if(nullptr != matchPattern)
458 FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
459 FcDefaultSubstitute(matchPattern);
461 FcCharSet* characterSet = nullptr;
462 MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
463 // Decrease the reference counter of the character set as it's not stored.
464 FcCharSetDestroy(characterSet);
466 // Destroys the pattern created.
467 FcPatternDestroy(matchPattern);
470 // Create again the character sets as they are not valid after FcInitReinitialize()
472 for(const auto& description : mDefaultFonts)
474 mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
477 for(const auto& description : mFontDescriptionCache)
479 mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
482 for(auto& item : mFallbackCache)
484 if(nullptr != item.fallbackFonts)
486 if(nullptr == item.characterSets)
488 item.characterSets = new CharacterSetList;
491 for(const auto& description : *(item.fallbackFonts))
493 item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
498 mDefaultFontDescriptionCached = true;
501 fontDescription.path = mDefaultFontDescription.path;
502 fontDescription.family = mDefaultFontDescription.family;
503 fontDescription.width = mDefaultFontDescription.width;
504 fontDescription.weight = mDefaultFontDescription.weight;
505 fontDescription.slant = mDefaultFontDescription.slant;
507 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
508 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
509 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
510 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
511 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
512 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
515 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts)
517 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
519 if(mSystemFonts.empty())
524 systemFonts = mSystemFonts;
525 DALI_LOG_INFO(gLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size());
526 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
529 void FontClient::Plugin::GetDescription(FontId id,
530 FontDescription& fontDescription) const
532 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
533 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
534 const FontId index = id - 1u;
536 if((id > 0u) && (index < mFontIdCache.Count()))
538 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
539 switch(fontIdCacheItem.type)
541 case FontDescription::FACE_FONT:
543 for(const auto& item : mFontDescriptionSizeCache)
545 if(item.fontId == fontIdCacheItem.id)
547 fontDescription = *(mFontDescriptionCache.begin() + item.validatedFontId - 1u);
549 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
550 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
551 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
552 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
553 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
554 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
560 case FontDescription::BITMAP_FONT:
562 fontDescription.type = FontDescription::BITMAP_FONT;
563 fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
568 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
569 fontDescription.type = FontDescription::INVALID;
570 fontDescription.family.clear();
575 DALI_LOG_INFO(gLogFilter, Debug::General, " No description found for the font ID %d\n", id);
576 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
579 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id)
581 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
582 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
583 const FontId index = id - 1u;
586 (index < mFontIdCache.Count()))
588 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
590 switch(fontIdCacheItem.type)
592 case FontDescription::FACE_FONT:
594 DALI_LOG_INFO(gLogFilter, Debug::General, " point size : %d\n", (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize);
595 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
596 return (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize;
598 case FontDescription::BITMAP_FONT:
600 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
604 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
610 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid font ID %d\n", id);
613 DALI_LOG_INFO(gLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE);
614 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
615 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
618 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character)
620 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
621 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
622 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", character);
624 if((fontId < 1u) || (fontId > mFontIdCache.Count()))
626 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n", mFontIdCache.Count());
627 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
633 bool isSupported = false;
635 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
637 switch(fontIdCacheItem.type)
639 case FontDescription::FACE_FONT:
641 if(fontIdCacheItem.id < mFontFaceCache.size())
643 FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
645 if(nullptr == cacheItem.mCharacterSet)
647 // Create again the character set.
648 // It can be null if the ResetSystemDefaults() method has been called.
650 FontDescription description;
651 description.path = cacheItem.mPath;
652 description.family = std::move(FontFamily(cacheItem.mFreeTypeFace->family_name));
653 description.weight = FontWeight::NONE;
654 description.width = FontWidth::NONE;
655 description.slant = FontSlant::NONE;
657 // Note FreeType doesn't give too much info to build a proper font style.
658 if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
660 description.slant = FontSlant::ITALIC;
662 if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
664 description.weight = FontWeight::BOLD;
667 cacheItem.mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
670 isSupported = FcCharSetHasChar(cacheItem.mCharacterSet, character);
674 case FontDescription::BITMAP_FONT:
676 const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
678 for(const auto& glyph : bitmapFont.glyphs)
680 if(glyph.utf32 == character)
690 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
694 DALI_LOG_INFO(gLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false"));
695 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
699 FontId FontClient::Plugin::FindFontForCharacter(const FontList& fontList,
700 const CharacterSetList& characterSetList,
702 PointSize26Dot6 requestedPointSize,
705 DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
707 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n");
708 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", character);
709 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
710 DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
713 bool foundColor = false;
715 DALI_LOG_INFO(gLogFilter, Debug::General, " number of fonts : %d\n", fontList.size());
717 // Traverse the list of fonts.
718 // Check for each font if supports the character.
719 for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
721 const FontDescription& description = fontList[index];
722 const FcCharSet* const characterSet = characterSetList[index];
724 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", description.family.c_str());
725 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str());
726 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width]);
727 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight]);
728 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant]);
730 bool foundInRanges = false;
731 if(nullptr != characterSet)
733 foundInRanges = FcCharSetHasChar(characterSet, character);
738 fontId = GetFontId(description,
742 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " font id : %d\n", fontId);
747 (fontId - 1u < mFontIdCache.Count()))
749 const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
751 foundColor = item.mHasColorTables;
754 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " foundColor : %s\n", (foundColor ? "true" : "false"));
757 // Keep going unless we prefer a different (color) font.
758 if(!preferColor || foundColor)
765 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
766 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n");
770 FontId FontClient::Plugin::FindDefaultFont(Character charcode,
771 PointSize26Dot6 requestedPointSize,
774 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n");
775 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", charcode);
776 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
777 DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
781 // Create the list of default fonts if it has not been created.
782 if(mDefaultFonts.empty())
784 FontDescription fontDescription;
785 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
786 fontDescription.width = IntToWidthType(DEFAULT_FONT_WIDTH);
787 fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT);
788 fontDescription.slant = IntToSlantType(DEFAULT_FONT_SLANT);
790 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
792 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size());
794 // Traverse the list of default fonts.
795 // Check for each default font if supports the character.
796 fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
798 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
799 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n");
804 FontId FontClient::Plugin::FindFallbackFont(Character charcode,
805 const FontDescription& preferredFontDescription,
806 PointSize26Dot6 requestedPointSize,
809 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n");
810 DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", charcode);
811 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
812 DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
814 // The font id to be returned.
817 FontDescription fontDescription;
819 // Fill the font description with the preferred font description and complete with the defaults.
820 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
821 fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? IntToWeightType(DEFAULT_FONT_WEIGHT) : preferredFontDescription.weight);
822 fontDescription.width = ((FontWidth::NONE == preferredFontDescription.width) ? IntToWidthType(DEFAULT_FONT_WIDTH) : preferredFontDescription.width);
823 fontDescription.slant = ((FontSlant::NONE == preferredFontDescription.slant) ? IntToSlantType(DEFAULT_FONT_SLANT) : preferredFontDescription.slant);
825 DALI_LOG_INFO(gLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n");
826 DALI_LOG_INFO(gLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
827 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
828 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
829 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
831 // Check first if the font's description has been queried before.
832 FontList* fontList = nullptr;
833 CharacterSetList* characterSetList = nullptr;
835 if(!FindFallbackFontList(fontDescription, fontList, characterSetList))
837 fontList = new FontList;
838 characterSetList = new CharacterSetList;
840 SetFontList(fontDescription, *fontList, *characterSetList);
842 FontDescription appleColorEmoji;
843 appleColorEmoji.family = "Apple Color Emoji";
844 appleColorEmoji.width = fontDescription.width;
845 appleColorEmoji.weight = fontDescription.weight;
846 appleColorEmoji.slant = fontDescription.slant;
847 FontList emojiFontList;
848 CharacterSetList emojiCharSetList;
849 SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
851 std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
852 emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
853 *fontList = std::move(emojiFontList);
854 *characterSetList = std::move(emojiCharSetList);
857 // Add the font-list to the cache.
858 mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
861 if(fontList && characterSetList)
863 fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
866 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
867 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
871 FontId FontClient::Plugin::GetFontId(const FontPath& path,
872 PointSize26Dot6 requestedPointSize,
874 bool cacheDescription)
876 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n");
877 DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str());
878 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
882 if(nullptr != mFreeTypeLibrary)
885 if(FindFont(path, requestedPointSize, faceIndex, foundId))
891 id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
895 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
896 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n");
901 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
902 PointSize26Dot6 requestedPointSize,
905 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n");
906 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
907 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
908 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
909 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
910 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
911 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
913 // This method uses three vectors which caches:
914 // * The bitmap font cache
915 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
916 // * The path to font file names.
917 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
919 // 1) Checks if the font description matches with a previously loaded bitmap font.
920 // Returns if a font is found.
921 // 2) Checks in the cache if the font's description has been validated before.
922 // If it was it gets an index to the vector with paths to font file names. Otherwise,
923 // retrieves using font config a path to a font file name which matches with the
924 // font's description. The path is stored in the cache.
926 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
927 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
928 // the GetFontId() method with the path to the font file name and the point size to
931 // The font id to be returned.
934 // Check first if the font description matches with a previously loaded bitmap font.
935 if(FindBitmapFont(fontDescription.family, fontId))
940 // Check if the font's description have been validated before.
941 FontDescriptionId validatedFontId = 0u;
943 if(!FindValidatedFont(fontDescription,
946 // Use font config to validate the font's description.
947 ValidateFont(fontDescription,
951 FontId fontFaceId = 0u;
952 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
953 if(!FindFont(validatedFontId, requestedPointSize, fontFaceId))
955 // Retrieve the font file name path.
956 const FontDescription& description = *(mFontDescriptionCache.begin() + validatedFontId - 1u);
958 // Retrieve the font id. Do not cache the description as it has been already cached.
959 fontId = GetFontId(description.path,
964 fontFaceId = mFontIdCache[fontId - 1u].id;
965 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(mCharacterSetCache[validatedFontId - 1u]);
967 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
968 mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
974 fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
977 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId);
978 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n");
983 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont)
985 for(const auto& item : mBitmapFontCache)
987 if(bitmapFont.name == item.font.name)
993 BitmapFontCacheItem bitmapFontCacheItem;
994 bitmapFontCacheItem.font = bitmapFont;
995 bitmapFontCacheItem.id = mFontIdCache.Count();
997 // Resize the vector with the pixel buffers.
998 bitmapFontCacheItem.pixelBuffers.resize(bitmapFont.glyphs.size());
1000 // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
1001 unsigned int index = 0u;
1002 for(auto& glyph : bitmapFontCacheItem.font.glyphs)
1004 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1006 if(EqualsZero(glyph.ascender) && EqualsZero(glyph.descender))
1009 pixelBuffer = LoadImageFromFile(glyph.url);
1013 glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1017 bitmapFontCacheItem.font.ascender = std::max(glyph.ascender, bitmapFontCacheItem.font.ascender);
1018 bitmapFontCacheItem.font.descender = std::min(glyph.descender, bitmapFontCacheItem.font.descender);
1023 FontIdCacheItem fontIdCacheItem;
1024 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1025 fontIdCacheItem.id = mBitmapFontCache.size();
1027 mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
1028 mFontIdCache.PushBack(fontIdCacheItem);
1030 return bitmapFontCacheItem.id + 1u;
1033 void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
1034 FontDescriptionId& validatedFontId)
1036 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n");
1037 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
1038 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
1039 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
1040 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
1041 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
1043 // Create a font pattern.
1044 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
1046 FontDescription description;
1048 FcCharSet* characterSet = nullptr;
1049 bool matched = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
1050 FcPatternDestroy(fontFamilyPattern);
1052 if(matched && (nullptr != characterSet))
1054 // Add the path to the cache.
1055 description.type = FontDescription::FACE_FONT;
1056 mFontDescriptionCache.push_back(description);
1058 // Set the index to the vector of paths to font file names.
1059 validatedFontId = mFontDescriptionCache.size();
1061 DALI_LOG_INFO(gLogFilter, Debug::General, " matched description; family : [%s]\n", description.family.c_str());
1062 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str());
1063 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width]);
1064 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight]);
1065 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant]);
1066 DALI_LOG_INFO(gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
1068 // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1069 mCharacterSetCache.PushBack(characterSet);
1071 // Cache the index and the matched font's description.
1072 FontDescriptionCacheItem item(description,
1075 mValidatedFontCache.push_back(std::move(item));
1077 if((fontDescription.family != description.family) ||
1078 (fontDescription.width != description.width) ||
1079 (fontDescription.weight != description.weight) ||
1080 (fontDescription.slant != description.slant))
1082 // Cache the given font's description if it's different than the matched.
1083 FontDescriptionCacheItem item(fontDescription,
1086 mValidatedFontCache.push_back(std::move(item));
1091 DALI_LOG_INFO(gLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str());
1094 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n");
1097 void FontClient::Plugin::GetFontMetrics(FontId fontId,
1098 FontMetrics& metrics)
1100 const FontId index = fontId - 1u;
1103 (index < mFontIdCache.Count()))
1105 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1107 switch(fontIdCacheItem.type)
1109 case FontDescription::FACE_FONT:
1111 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1113 metrics = font.mMetrics;
1115 // Adjust the metrics if the fixed-size font should be down-scaled
1116 if(font.mIsFixedSizeBitmap)
1118 const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1120 if(desiredFixedSize > 0.f)
1122 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1124 metrics.ascender = metrics.ascender * scaleFactor;
1125 metrics.descender = metrics.descender * scaleFactor;
1126 metrics.height = metrics.height * scaleFactor;
1127 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1128 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1133 case FontDescription::BITMAP_FONT:
1135 const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1137 metrics.ascender = bitmapFontCacheItem.font.ascender;
1138 metrics.descender = bitmapFontCacheItem.font.descender;
1139 metrics.height = metrics.ascender - metrics.descender;
1140 metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1141 metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1146 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1152 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
1156 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId fontId,
1159 GlyphIndex glyphIndex = 0u;
1160 const FontId index = fontId - 1u;
1163 (index < mFontIdCache.Count()))
1165 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1167 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1169 FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1171 glyphIndex = FT_Get_Char_Index(ftFace, charcode);
1178 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
1183 if(VECTOR_GLYPH == type)
1185 return GetVectorMetrics(array, size, horizontal);
1188 return GetBitmapMetrics(array, size, horizontal);
1191 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
1197 for(unsigned int i = 0; i < size; ++i)
1199 GlyphInfo& glyph = array[i];
1201 FontId index = glyph.fontId - 1u;
1203 if((glyph.fontId > 0u) &&
1204 (index < mFontIdCache.Count()))
1206 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1208 switch(fontIdCacheItem.type)
1210 case FontDescription::FACE_FONT:
1212 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1214 FT_Face ftFace = font.mFreeTypeFace;
1216 #ifdef FREETYPE_BITMAP_SUPPORT
1217 // Check to see if we should be loading a Fixed Size bitmap?
1218 if(font.mIsFixedSizeBitmap)
1220 FT_Select_Size(ftFace, font.mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
1221 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
1222 if(FT_Err_Ok == error)
1224 glyph.width = font.mFixedWidthPixels;
1225 glyph.height = font.mFixedHeightPixels;
1226 glyph.advance = font.mFixedWidthPixels;
1227 glyph.xBearing = 0.0f;
1228 glyph.yBearing = font.mFixedHeightPixels;
1230 // Adjust the metrics if the fixed-size font should be down-scaled
1231 const float desiredFixedSize = static_cast<float>(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1233 if(desiredFixedSize > 0.f)
1235 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1237 glyph.width = glyph.width * scaleFactor;
1238 glyph.height = glyph.height * scaleFactor;
1239 glyph.advance = glyph.advance * scaleFactor;
1240 glyph.xBearing = glyph.xBearing * scaleFactor;
1241 glyph.yBearing = glyph.yBearing * scaleFactor;
1243 glyph.scaleFactor = scaleFactor;
1248 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
1255 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1256 // i.e. with the SNum-3R font.
1257 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1258 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);
1260 // Keep the width of the glyph before doing the software emboldening.
1261 // It will be used to calculate a scale factor to be applied to the
1262 // advance as Harfbuzz doesn't apply any SW emboldening to calculate
1263 // the advance of the glyph.
1264 const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1266 if(FT_Err_Ok == error)
1268 const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
1269 if(isEmboldeningRequired)
1271 // Does the software bold.
1272 FT_GlyphSlot_Embolden(ftFace->glyph);
1275 glyph.width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
1276 glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
1279 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
1280 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
1284 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
1285 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
1288 if(isEmboldeningRequired && !Dali::EqualsZero(width))
1290 // If the glyph is emboldened by software, the advance is multiplied by a
1291 // scale factor to make it slightly bigger.
1292 glyph.advance *= (glyph.width / width);
1295 // Use the bounding box of the bitmap to correct the metrics.
1296 // For some fonts i.e the SNum-3R the metrics need to be corrected,
1297 // otherwise the glyphs 'dance' up and down depending on the
1298 // font's point size.
1301 error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);
1304 FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
1306 const float descender = glyph.height - glyph.yBearing;
1307 glyph.height = (bbox.yMax - bbox.yMin) * FROM_266;
1308 glyph.yBearing = glyph.height - round(descender);
1310 // Created FT_Glyph object must be released with FT_Done_Glyph
1311 FT_Done_Glyph(ftGlyph);
1320 case FontDescription::BITMAP_FONT:
1322 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1324 unsigned int index = 0u;
1325 for(auto& item : bitmapFontCacheItem.font.glyphs)
1327 if(item.utf32 == glyph.index)
1329 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1332 pixelBuffer = LoadImageFromFile(item.url);
1335 glyph.width = static_cast<float>(pixelBuffer.GetWidth());
1336 glyph.height = static_cast<float>(pixelBuffer.GetHeight());
1337 glyph.xBearing = 0.f;
1338 glyph.yBearing = glyph.height + item.descender;
1339 glyph.advance = glyph.width;
1340 glyph.scaleFactor = 1.f;
1351 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1357 // Check if it's an embedded image.
1358 if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count()))
1360 const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1362 glyph.width = static_cast<float>(item.width);
1363 glyph.height = static_cast<float>(item.height);
1364 glyph.xBearing = 0.f;
1365 glyph.yBearing = glyph.height;
1366 glyph.advance = glyph.width;
1367 glyph.scaleFactor = 1.f;
1379 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
1383 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1386 for(unsigned int i = 0u; i < size; ++i)
1388 FontId fontId = array[i].fontId;
1391 (fontId - 1u) < mFontIdCache.Count())
1393 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1395 if(!font.mVectorFontId)
1397 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1400 mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
1402 // Vector metrics are in EMs, convert to pixels
1403 const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
1404 array[i].width *= scale;
1405 array[i].height *= scale;
1406 array[i].xBearing *= scale;
1407 array[i].yBearing *= scale;
1408 array[i].advance *= scale;
1422 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
1424 const FontId index = fontId - 1u;
1427 (index < mFontIdCache.Count()))
1429 data.isColorBitmap = false;
1430 data.isColorEmoji = false;
1432 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1434 switch(fontIdCacheItem.type)
1436 case FontDescription::FACE_FONT:
1438 // For the software italics.
1439 bool isShearRequired = false;
1441 const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1442 FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1446 #ifdef FREETYPE_BITMAP_SUPPORT
1447 // Check to see if this is fixed size bitmap
1448 if(fontFaceCacheItem.mIsFixedSizeBitmap)
1450 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1455 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1456 // i.e. with the SNum-3R font.
1457 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1458 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
1460 if(FT_Err_Ok == error)
1462 if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
1464 // Does the software bold.
1465 FT_GlyphSlot_Embolden(ftFace->glyph);
1468 if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
1470 // Will do the software italic.
1471 isShearRequired = true;
1475 error = FT_Get_Glyph(ftFace->glyph, &glyph);
1477 // Convert to bitmap if necessary
1478 if(FT_Err_Ok == error)
1480 if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
1482 int offsetX = 0, offsetY = 0;
1483 bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
1485 // Create a bitmap for the outline
1488 // Retrieve the horizontal and vertical distance from the current pen position to the
1489 // left and top border of the glyph bitmap for a normal glyph before applying the outline.
1490 if(FT_Err_Ok == error)
1492 FT_Glyph normalGlyph;
1493 error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);
1495 error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
1496 if(FT_Err_Ok == error)
1498 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);
1500 offsetX = bitmapGlyph->left;
1501 offsetY = bitmapGlyph->top;
1504 // Created FT_Glyph object must be released with FT_Done_Glyph
1505 FT_Done_Glyph(normalGlyph);
1508 // Now apply the outline
1512 error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
1514 if(FT_Err_Ok == error)
1516 FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
1517 error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
1519 if(FT_Err_Ok == error)
1521 FT_Stroker_Done(stroker);
1525 DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
1530 DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
1534 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
1535 if(FT_Err_Ok == error)
1537 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
1541 // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
1542 data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
1543 data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
1546 ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
1550 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
1555 ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
1558 data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1560 // Created FT_Glyph object must be released with FT_Done_Glyph
1561 FT_Done_Glyph(glyph);
1566 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
1570 case FontDescription::BITMAP_FONT:
1572 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1574 unsigned int index = 0u;
1575 for(auto& item : bitmapFontCacheItem.font.glyphs)
1577 if(item.utf32 == glyphIndex)
1579 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1582 pixelBuffer = LoadImageFromFile(item.url);
1585 data.width = pixelBuffer.GetWidth();
1586 data.height = pixelBuffer.GetHeight();
1588 data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1590 ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer());
1592 // Sets the pixel format.
1593 data.format = pixelBuffer.GetPixelFormat();
1602 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1608 if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count()))
1610 // It's an embedded item.
1611 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1613 data.width = item.width;
1614 data.height = item.height;
1615 if(0u != item.pixelBufferId)
1617 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId - 1u].pixelBuffer;
1620 ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer());
1622 // Sets the pixel format.
1623 data.format = pixelBuffer.GetPixelFormat();
1628 // Creates the output buffer
1629 const unsigned int bufferSize = data.width * data.height * 4u;
1630 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1632 memset(data.buffer, 0u, bufferSize);
1634 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1640 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
1642 TextAbstraction::FontClient::GlyphBufferData data;
1644 CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
1646 return PixelData::New(data.buffer,
1647 data.width * data.height * Pixel::GetBytesPerPixel(data.format),
1651 PixelData::DELETE_ARRAY);
1654 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
1659 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1661 (fontId - 1u < mFontIdCache.Count()))
1663 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1664 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1666 if(!font.mVectorFontId)
1668 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1671 mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1676 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
1678 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n");
1679 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize);
1681 // First look into the cache if there is an ellipsis glyph for the requested point size.
1682 for(const auto& item : mEllipsisCache)
1684 if(item.requestedPointSize == requestedPointSize)
1686 // Use the glyph in the cache.
1688 DALI_LOG_INFO(gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1689 DALI_LOG_INFO(gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1690 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n");
1696 // No glyph has been found. Create one.
1697 mEllipsisCache.PushBack(EllipsisItem());
1698 EllipsisItem& item = *(mEllipsisCache.End() - 1u);
1700 item.requestedPointSize = requestedPointSize;
1702 // Find a font for the ellipsis glyph.
1703 item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1707 // Set the character index to access the glyph inside the font.
1708 item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].id].mFreeTypeFace,
1709 ELLIPSIS_CHARACTER);
1711 GetBitmapMetrics(&item.glyph, 1u, true);
1713 DALI_LOG_INFO(gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1714 DALI_LOG_INFO(gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1715 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n");
1720 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
1722 FT_Error error = -1;
1724 const FontId index = fontId - 1u;
1727 (index < mFontIdCache.Count()))
1729 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1731 switch(fontIdCacheItem.type)
1733 case FontDescription::FACE_FONT:
1735 #ifdef FREETYPE_BITMAP_SUPPORT
1736 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1737 FT_Face ftFace = item.mFreeTypeFace;
1739 // Check to see if this is fixed size bitmap
1740 if(item.mHasColorTables)
1742 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
1747 case FontDescription::BITMAP_FONT:
1749 error = FT_Err_Ok; // Will return true;
1754 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1759 return FT_Err_Ok == error;
1762 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId)
1764 FT_Face fontFace = nullptr;
1766 const FontId index = fontId - 1u;
1768 (index < mFontIdCache.Count()))
1770 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1772 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
1774 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1780 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId)
1782 const FontId index = fontId - 1u;
1784 (index < mFontIdCache.Count()))
1786 return mFontIdCache[index].type;
1788 return FontDescription::INVALID;
1791 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1793 // nullptr as first parameter means the current configuration is used.
1794 return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1797 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
1799 EmbeddedItem embeddedItem;
1801 embeddedItem.pixelBufferId = 0u;
1802 embeddedItem.width = description.width;
1803 embeddedItem.height = description.height;
1805 pixelFormat = Pixel::A8;
1807 if(!description.url.empty())
1809 // Check if the url is in the cache.
1810 PixelBufferId index = 0u;
1812 for(const auto& cacheItem : mPixelBufferCache)
1815 if(cacheItem.url == description.url)
1817 // The url is in the pixel buffer cache.
1818 // Set the index +1 to the vector.
1819 embeddedItem.pixelBufferId = index;
1824 Devel::PixelBuffer pixelBuffer;
1825 if(0u == embeddedItem.pixelBufferId)
1827 // The pixel buffer is not in the cache. Create one and cache it.
1829 // Load the image from the url.
1830 pixelBuffer = LoadImageFromFile(description.url);
1832 // Create the cache item.
1833 PixelBufferCacheItem pixelBufferCacheItem;
1834 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1835 pixelBufferCacheItem.url = description.url;
1837 // Store the cache item in the cache.
1838 mPixelBufferCache.push_back(std::move(pixelBufferCacheItem));
1840 // Set the pixel buffer id to the embedded item.
1841 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1845 // Retrieve the pixel buffer from the cache to set the pixel format.
1846 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1851 // Set the size of the embedded item if it has not been set.
1852 if(0u == embeddedItem.width)
1854 embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1857 if(0u == embeddedItem.height)
1859 embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1862 // Set the pixel format.
1863 pixelFormat = pixelBuffer.GetPixelFormat();
1867 // Find if the same embeddedItem has already been created.
1868 unsigned int index = 0u;
1869 for(const auto& item : mEmbeddedItemCache)
1872 if((item.pixelBufferId == embeddedItem.pixelBufferId) &&
1873 (item.width == embeddedItem.width) &&
1874 (item.height == embeddedItem.height))
1880 // Cache the embedded item.
1881 mEmbeddedItemCache.PushBack(embeddedItem);
1883 return mEmbeddedItemCache.Count();
1886 void FontClient::Plugin::InitSystemFonts()
1888 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n");
1890 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1894 DALI_LOG_INFO(gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont);
1896 // Reserve some space to avoid reallocations.
1897 mSystemFonts.reserve(fontSet->nfont);
1899 for(int i = 0u; i < fontSet->nfont; ++i)
1901 FcPattern* fontPattern = fontSet->fonts[i];
1905 // Skip fonts with no path
1906 if(GetFcString(fontPattern, FC_FILE, path))
1908 mSystemFonts.push_back(FontDescription());
1909 FontDescription& fontDescription = mSystemFonts.back();
1911 fontDescription.path = std::move(path);
1916 GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
1917 GetFcInt(fontPattern, FC_WIDTH, width);
1918 GetFcInt(fontPattern, FC_WEIGHT, weight);
1919 GetFcInt(fontPattern, FC_SLANT, slant);
1920 fontDescription.width = IntToWidthType(width);
1921 fontDescription.weight = IntToWeightType(weight);
1922 fontDescription.slant = IntToSlantType(slant);
1924 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str());
1925 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
1926 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
1927 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
1928 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
1932 // Destroys the font set created.
1933 FcFontSetDestroy(fontSet);
1935 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n");
1938 bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
1940 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n");
1942 FcResult result = FcResultMatch;
1943 FcPattern* match = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1945 const bool matched = nullptr != match;
1946 DALI_LOG_INFO(gLogFilter, Debug::General, " pattern matched : %s\n", (matched ? "true" : "false"));
1953 GetFcString(match, FC_FILE, fontDescription.path);
1954 GetFcString(match, FC_FAMILY, fontDescription.family);
1955 GetFcInt(match, FC_WIDTH, width);
1956 GetFcInt(match, FC_WEIGHT, weight);
1957 GetFcInt(match, FC_SLANT, slant);
1958 fontDescription.width = IntToWidthType(width);
1959 fontDescription.weight = IntToWeightType(weight);
1960 fontDescription.slant = IntToSlantType(slant);
1962 // Retrieve the character set and increase the reference counter.
1963 FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
1964 *characterSet = FcCharSetCopy(*characterSet);
1966 // destroyed the matched pattern
1967 FcPatternDestroy(match);
1969 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
1970 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
1971 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
1972 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
1973 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
1976 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n");
1980 FcPattern* FontClient::Plugin::CreateFontFamilyPattern(const FontDescription& fontDescription) const
1982 // create the cached font family lookup pattern
1983 // a pattern holds a set of names, each name refers to a property of the font
1984 FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1986 if(!fontFamilyPattern)
1991 // add a property to the pattern for the font family
1992 FcPatternAddString(fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fontDescription.family.c_str()));
1994 // add a property to the pattern for local setting.
1995 const char* locale = setlocale(LC_MESSAGES, nullptr);
1996 if(locale != nullptr)
1998 FcPatternAddString(fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale));
2001 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
2005 width = DEFAULT_FONT_WIDTH;
2008 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
2012 weight = DEFAULT_FONT_WEIGHT;
2015 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
2019 slant = DEFAULT_FONT_SLANT;
2022 FcPatternAddInteger(fontFamilyPattern, FC_WIDTH, width);
2023 FcPatternAddInteger(fontFamilyPattern, FC_WEIGHT, weight);
2024 FcPatternAddInteger(fontFamilyPattern, FC_SLANT, slant);
2026 // modify the config, with the mFontFamilyPatterm
2027 FcConfigSubstitute(nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern);
2029 // provide default values for unspecified properties in the font pattern
2030 // e.g. patterns without a specified style or weight are set to Medium
2031 FcDefaultSubstitute(fontFamilyPattern);
2033 return fontFamilyPattern;
2036 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
2038 FcFontSet* fontset = nullptr;
2040 // create a new pattern.
2041 // a pattern holds a set of names, each name refers to a property of the font
2042 FcPattern* pattern = FcPatternCreate();
2044 if(nullptr != pattern)
2046 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
2047 FcObjectSet* objectSet = FcObjectSetCreate();
2049 if(nullptr != objectSet)
2051 // build an object set from a list of property names
2052 FcObjectSetAdd(objectSet, FC_FILE);
2053 FcObjectSetAdd(objectSet, FC_FAMILY);
2054 FcObjectSetAdd(objectSet, FC_WIDTH);
2055 FcObjectSetAdd(objectSet, FC_WEIGHT);
2056 FcObjectSetAdd(objectSet, FC_SLANT);
2058 // get a list of fonts
2059 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
2060 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.
2062 // clear up the object set
2063 FcObjectSetDestroy(objectSet);
2066 // clear up the pattern
2067 FcPatternDestroy(pattern);
2073 bool FontClient::Plugin::GetFcString(const FcPattern* const pattern,
2074 const char* const n,
2075 std::string& string)
2077 FcChar8* file = nullptr;
2078 const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
2080 if(FcResultMatch == retVal)
2082 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
2083 string.assign(reinterpret_cast<const char*>(file));
2091 bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
2093 const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
2095 if(FcResultMatch == retVal)
2103 FontId FontClient::Plugin::CreateFont(const FontPath& path,
2104 PointSize26Dot6 requestedPointSize,
2105 FaceIndex faceIndex,
2106 bool cacheDescription)
2108 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n");
2109 DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str());
2110 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2114 // Create & cache new font face
2116 int error = FT_New_Face(mFreeTypeLibrary,
2121 if(FT_Err_Ok == error)
2123 // Check if a font is scalable.
2124 const bool isScalable = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
2125 const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
2126 const bool hasColorTables = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
2127 FontId fontFaceId = 0u;
2129 DALI_LOG_INFO(gLogFilter, Debug::General, " isScalable : [%s]\n", (isScalable ? "true" : "false"));
2130 DALI_LOG_INFO(gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
2131 DALI_LOG_INFO(gLogFilter, Debug::General, " hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
2133 // Check to see if the font contains fixed sizes?
2134 if(!isScalable && hasFixedSizedBitmaps)
2136 PointSize26Dot6 actualPointSize = 0u;
2137 int fixedSizeIndex = 0;
2138 for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
2140 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2141 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
2143 if(fixedSize >= requestedPointSize)
2145 actualPointSize = fixedSize;
2150 if(0u == actualPointSize)
2152 // The requested point size is bigger than the bigest fixed size.
2153 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2154 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2157 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
2159 // Tell Freetype to use this size
2160 error = FT_Select_Size(ftFace, fixedSizeIndex);
2161 if(FT_Err_Ok != error)
2163 DALI_LOG_INFO(gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
2167 const float fixedWidth = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
2168 const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
2170 // Indicate that the font is a fixed sized bitmap
2171 FontMetrics metrics(fixedHeight, // The ascender in pixels.
2173 fixedHeight, // The height in pixels.
2177 // Create the FreeType font face item to cache.
2178 FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
2180 // Set the index to the font's id cache.
2181 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2183 // Create the font id item to cache.
2184 FontIdCacheItem fontIdCacheItem;
2185 fontIdCacheItem.type = FontDescription::FACE_FONT;
2187 // Set the index to the FreeType font face cache.
2188 fontIdCacheItem.id = mFontFaceCache.size();
2189 fontFaceId = fontIdCacheItem.id + 1u;
2192 mFontFaceCache.push_back(fontFaceCacheItem);
2193 mFontIdCache.PushBack(fontIdCacheItem);
2195 // Set the font id to be returned.
2196 id = mFontIdCache.Count();
2201 error = FT_Set_Char_Size(ftFace,
2207 if(FT_Err_Ok == error)
2209 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2211 FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
2212 static_cast<float>(ftMetrics.descender) * FROM_266,
2213 static_cast<float>(ftMetrics.height) * FROM_266,
2214 static_cast<float>(ftFace->underline_position) * FROM_266,
2215 static_cast<float>(ftFace->underline_thickness) * FROM_266);
2217 // Create the FreeType font face item to cache.
2218 FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics);
2220 // Set the index to the font's id cache.
2221 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2223 // Create the font id item to cache.
2224 FontIdCacheItem fontIdCacheItem;
2225 fontIdCacheItem.type = FontDescription::FACE_FONT;
2227 // Set the index to the FreeType font face cache.
2228 fontIdCacheItem.id = mFontFaceCache.size();
2229 fontFaceId = fontIdCacheItem.id + 1u;
2232 mFontFaceCache.push_back(fontFaceCacheItem);
2233 mFontIdCache.PushBack(fontIdCacheItem);
2235 // Set the font id to be returned.
2236 id = mFontIdCache.Count();
2240 DALI_LOG_INFO(gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
2244 if(0u != fontFaceId)
2246 if(cacheDescription)
2248 CacheFontPath(ftFace, fontFaceId, requestedPointSize, path);
2254 DALI_LOG_INFO(gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str());
2257 DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id);
2258 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n");
2263 void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer)
2265 // Set the input dimensions.
2266 const ImageDimensions inputDimensions(srcWidth, srcHeight);
2268 // Set the output dimensions.
2269 // If the output dimension is not given, the input dimension is set
2270 // and won't be downscaling.
2271 data.width = (data.width == 0) ? srcWidth : data.width;
2272 data.height = (data.height == 0) ? srcHeight : data.height;
2273 const ImageDimensions desiredDimensions(data.width, data.height);
2275 // Creates the output buffer
2276 const unsigned int bufferSize = data.width * data.height * 4u;
2277 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2279 if(inputDimensions == desiredDimensions)
2281 // There isn't downscaling.
2282 memcpy(data.buffer, srcBuffer, bufferSize);
2286 Dali::Internal::Platform::LanczosSample4BPP(srcBuffer,
2293 void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired)
2295 if(srcBitmap.width * srcBitmap.rows > 0)
2297 switch(srcBitmap.pixel_mode)
2299 case FT_PIXEL_MODE_GRAY:
2301 if(srcBitmap.pitch == static_cast<int>(srcBitmap.width))
2303 uint8_t* pixelsIn = srcBitmap.buffer;
2304 unsigned int width = srcBitmap.width;
2305 unsigned height = srcBitmap.rows;
2307 std::unique_ptr<uint8_t, void (*)(void*)> pixelsOutPtr(nullptr, free);
2312 * Glyphs' bitmaps with no slant retrieved from FreeType:
2322 * Expected glyphs' bitmaps with italic slant:
2323 * ____________ ______
2330 * ------------ ------
2332 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2342 * 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.
2344 unsigned int widthOut = 0u;
2345 unsigned int heightOut = 0u;
2346 uint8_t* pixelsOut = nullptr;
2348 Dali::Internal::Platform::HorizontalShear(pixelsIn,
2352 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2359 pixelsIn = pixelsOut;
2360 pixelsOutPtr.reset(pixelsOut);
2363 const unsigned int bufferSize = width * height;
2364 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2366 data.height = height;
2367 data.format = Pixel::L8; // Sets the pixel format.
2368 memcpy(data.buffer, pixelsIn, bufferSize);
2373 #ifdef FREETYPE_BITMAP_SUPPORT
2374 case FT_PIXEL_MODE_BGRA:
2376 if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
2378 ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer);
2380 // Sets the pixel format.
2381 data.format = Pixel::BGRA8888;
2388 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n");
2395 bool FontClient::Plugin::FindFont(const FontPath& path,
2396 PointSize26Dot6 requestedPointSize,
2397 FaceIndex faceIndex,
2398 FontId& fontId) const
2400 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n");
2401 DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str());
2402 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2403 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size());
2406 for(const auto& cacheItem : mFontFaceCache)
2408 if(cacheItem.mRequestedPointSize == requestedPointSize &&
2409 cacheItem.mFaceIndex == faceIndex &&
2410 cacheItem.mPath == path)
2412 fontId = cacheItem.mFontId + 1u;
2414 DALI_LOG_INFO(gLogFilter, Debug::General, " font found, id : %d\n", fontId);
2415 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2421 DALI_LOG_INFO(gLogFilter, Debug::General, " font not found\n");
2422 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2427 bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
2428 FontDescriptionId& validatedFontId)
2430 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n");
2431 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
2432 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
2433 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
2434 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2435 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2436 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
2438 validatedFontId = 0u;
2440 for(const auto& item : mValidatedFontCache)
2442 if(!fontDescription.family.empty() &&
2443 (fontDescription.family == item.fontDescription.family) &&
2444 (fontDescription.width == item.fontDescription.width) &&
2445 (fontDescription.weight == item.fontDescription.weight) &&
2446 (fontDescription.slant == item.fontDescription.slant))
2448 validatedFontId = item.index;
2450 DALI_LOG_INFO(gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId);
2451 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n");
2456 DALI_LOG_INFO(gLogFilter, Debug::General, " validated font not found\n");
2457 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n");
2461 bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription,
2462 FontList*& fontList,
2463 CharacterSetList*& characterSetList)
2465 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n");
2466 DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str());
2467 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str());
2468 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]);
2469 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]);
2470 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]);
2471 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size());
2475 for(const auto& item : mFallbackCache)
2477 if(!fontDescription.family.empty() &&
2478 (fontDescription.family == item.fontDescription.family) &&
2479 (fontDescription.width == item.fontDescription.width) &&
2480 (fontDescription.weight == item.fontDescription.weight) &&
2481 (fontDescription.slant == item.fontDescription.slant))
2483 fontList = item.fallbackFonts;
2484 characterSetList = item.characterSets;
2486 DALI_LOG_INFO(gLogFilter, Debug::General, " fallback font list found.\n");
2487 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n");
2492 DALI_LOG_INFO(gLogFilter, Debug::General, " fallback font list not found.\n");
2493 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n");
2497 bool FontClient::Plugin::FindFont(FontDescriptionId validatedFontId,
2498 PointSize26Dot6 requestedPointSize,
2501 DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n");
2502 DALI_LOG_INFO(gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId);
2503 DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
2507 for(const auto& item : mFontDescriptionSizeCache)
2509 if((validatedFontId == item.validatedFontId) &&
2510 (requestedPointSize == item.requestedPointSize))
2512 fontId = item.fontId;
2514 DALI_LOG_INFO(gLogFilter, Debug::General, " font found, id : %d\n", fontId);
2515 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2520 DALI_LOG_INFO(gLogFilter, Debug::General, " font not found.\n");
2521 DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n");
2525 bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const
2529 for(const auto& item : mBitmapFontCache)
2531 if(bitmapFont == item.font.name)
2533 fontId = item.id + 1u;
2541 bool FontClient::Plugin::IsScalable(const FontPath& path)
2543 bool isScalable = false;
2546 int error = FT_New_Face(mFreeTypeLibrary,
2550 if(FT_Err_Ok != error)
2552 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
2556 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2562 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription)
2564 // Create a font pattern.
2565 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2567 FcResult result = FcResultMatch;
2569 // match the pattern
2570 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2571 bool isScalable = false;
2575 // Get the path to the font file name.
2577 GetFcString(match, FC_FILE, path);
2578 isScalable = IsScalable(path);
2582 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2585 // Destroys the created patterns.
2586 FcPatternDestroy(match);
2587 FcPatternDestroy(fontFamilyPattern);
2592 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes)
2594 // Empty the caller container
2598 int error = FT_New_Face(mFreeTypeLibrary,
2602 if(FT_Err_Ok != error)
2604 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
2607 // Fetch the number of fixed sizes available
2608 if(ftFace->num_fixed_sizes && ftFace->available_sizes)
2610 for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
2612 sizes.PushBack(ftFace->available_sizes[i].size);
2617 void FontClient::Plugin::GetFixedSizes(const FontDescription& fontDescription,
2618 Vector<PointSize26Dot6>& sizes)
2620 // Create a font pattern.
2621 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2623 FcResult result = FcResultMatch;
2625 // match the pattern
2626 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2630 // Get the path to the font file name.
2632 GetFcString(match, FC_FILE, path);
2633 GetFixedSizes(path, sizes);
2637 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
2640 // Destroys the created patterns.
2641 FcPatternDestroy(match);
2642 FcPatternDestroy(fontFamilyPattern);
2645 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
2647 bool hasItalicStyle = false;
2649 const FontId index = fontId - 1u;
2652 (index < mFontIdCache.Count()))
2654 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2656 if(FontDescription::FACE_FONT == fontIdCacheItem.type)
2658 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2660 hasItalicStyle = 0u != (font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC);
2665 DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId);
2668 return hasItalicStyle;
2671 void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path)
2673 FontDescription description;
2674 description.path = path;
2675 description.family = std::move(FontFamily(ftFace->family_name));
2676 description.weight = FontWeight::NONE;
2677 description.width = FontWidth::NONE;
2678 description.slant = FontSlant::NONE;
2680 // Note FreeType doesn't give too much info to build a proper font style.
2681 if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
2683 description.slant = FontSlant::ITALIC;
2685 if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
2687 description.weight = FontWeight::BOLD;
2690 FontDescriptionId validatedFontId = 0u;
2691 if(!FindValidatedFont(description,
2694 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2696 FcResult result = FcResultMatch;
2697 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2699 FcCharSet* characterSet = nullptr;
2700 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2702 const FontId fontFaceId = id - 1u;
2703 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
2705 // Destroys the created patterns.
2706 FcPatternDestroy(match);
2707 FcPatternDestroy(pattern);
2709 // Add the path to the cache.
2710 description.type = FontDescription::FACE_FONT;
2711 mFontDescriptionCache.push_back(description);
2713 // Set the index to the vector of paths to font file names.
2714 validatedFontId = mFontDescriptionCache.size();
2716 // Increase the reference counter and add the character set to the cache.
2717 mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
2719 // Cache the index and the font's description.
2720 mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
2723 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2724 mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId,
2730 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription(const FontDescription& description)
2732 FcCharSet* characterSet = nullptr;
2734 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2736 if(nullptr != pattern)
2738 FcResult result = FcResultMatch;
2739 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2741 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2743 // Destroys the created patterns.
2744 FcPatternDestroy(match);
2745 FcPatternDestroy(pattern);
2748 return characterSet;
2751 void FontClient::Plugin::ClearFallbackCache(std::vector<FallbackCacheItem>& fallbackCache)
2753 for(auto& item : fallbackCache)
2755 if(nullptr != item.fallbackFonts)
2757 delete item.fallbackFonts;
2760 if(nullptr != item.characterSets)
2762 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2763 DestroyCharacterSets(*item.characterSets);
2764 delete item.characterSets;
2769 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2771 for(auto& item : mFontFaceCache)
2773 FcCharSetDestroy(item.mCharacterSet);
2774 item.mCharacterSet = nullptr;
2778 } // namespace Internal
2780 } // namespace TextAbstraction