2 * Copyright (c) 2019 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/public-api/common/dali-vector.h>
25 #include <dali/public-api/common/vector-wrapper.h>
26 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/platform-abstraction.h>
28 #include <dali/internal/text/text-abstraction/font-client-helper.h>
29 #include <dali/internal/imaging/common/image-operations.h>
30 #include <dali/internal/adaptor/common/adaptor-impl.h>
31 #include <dali/devel-api/adaptor-framework/image-loading.h>
34 #include <fontconfig/fontconfig.h>
39 #if defined(DEBUG_ENABLED)
40 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
44 * Conversion from Fractional26.6 to float
46 const float FROM_266 = 1.0f / 64.0f;
47 const float POINTS_PER_INCH = 72.f;
49 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
50 const int DEFAULT_FONT_WIDTH = 100; // normal
51 const int DEFAULT_FONT_WEIGHT = 80; // normal
52 const int DEFAULT_FONT_SLANT = 0; // normal
54 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
56 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
58 // NONE -1 --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
68 const int FONT_WIDTH_TYPE_TO_INT[] = { -1, 50, 63, 75, 87, 100, 113, 125, 150, 200 };
69 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / sizeof( int );
71 // NONE -1 --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
73 // ULTRA_LIGHT, EXTRA_LIGHT 40
75 // DEMI_LIGHT, SEMI_LIGHT 55
79 // DEMI_BOLD, SEMI_BOLD 180
81 // ULTRA_BOLD, EXTRA_BOLD 205
82 // BLACK, HEAVY, EXTRA_BLACK 210
83 const int FONT_WEIGHT_TYPE_TO_INT[] = { -1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210 };
84 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof( FONT_WEIGHT_TYPE_TO_INT ) / sizeof( int );
86 // NONE -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
90 const int FONT_SLANT_TYPE_TO_INT[] = { -1, 0, 100, 110 };
91 const unsigned int NUM_FONT_SLANT_TYPE = sizeof( FONT_SLANT_TYPE_TO_INT ) / sizeof( int );
100 namespace TextAbstraction
107 * @brief Returns the FontWidth's enum index for the given width value.
109 * @param[in] width The width value.
111 * @return The FontWidth's enum index.
113 FontWidth::Type IntToWidthType( int width )
115 return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
119 * @brief Returns the FontWeight's enum index for the given weight value.
121 * @param[in] weight The weight value.
123 * @return The FontWeight's enum index.
125 FontWeight::Type IntToWeightType( int weight )
127 return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
131 * @brief Returns the FontSlant's enum index for the given slant value.
133 * @param[in] slant The slant value.
135 * @return The FontSlant's enum index.
137 FontSlant::Type IntToSlantType( int slant )
139 return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
143 * @brief Free the resources allocated by the FcCharSet objects.
145 * @param[in] characterSets The vector of character sets.
147 void DestroyCharacterSets( CharacterSetList& characterSets )
149 for( auto& item : characterSets )
151 FcCharSetDestroy( item );
155 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets )
156 : fontDescription{ std::move( font ) },
157 fallbackFonts{ fallbackFonts },
158 characterSets{ characterSets }
162 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
163 FontDescriptionId index )
164 : fontDescription{ fontDescription },
169 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( FontDescription&& fontDescription,
170 FontDescriptionId index )
171 : fontDescription{ std::move( fontDescription ) },
176 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem( FontDescriptionId validatedFontId,
177 PointSize26Dot6 requestedPointSize,
179 : validatedFontId( validatedFontId ),
180 requestedPointSize( requestedPointSize ),
185 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
186 const FontPath& path,
187 PointSize26Dot6 requestedPointSize,
189 const FontMetrics& metrics )
190 : mFreeTypeFace( ftFace ),
192 mRequestedPointSize( requestedPointSize ),
195 mCharacterSet( nullptr ),
196 mFixedSizeIndex( 0 ),
197 mFixedWidthPixels( 0.f ),
198 mFixedHeightPixels( 0.f ),
201 mIsFixedSizeBitmap( false ),
202 mHasColorTables( false )
206 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
207 const FontPath& path,
208 PointSize26Dot6 requestedPointSize,
210 const FontMetrics& metrics,
214 bool hasColorTables )
215 : mFreeTypeFace( ftFace ),
217 mRequestedPointSize( requestedPointSize ),
220 mCharacterSet( nullptr ),
221 mFixedSizeIndex( fixedSizeIndex ),
222 mFixedWidthPixels( fixedWidth ),
223 mFixedHeightPixels( fixedHeight ),
226 mIsFixedSizeBitmap( true ),
227 mHasColorTables( hasColorTables )
231 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
232 unsigned int verticalDpi )
233 : mFreeTypeLibrary( nullptr ),
234 mDpiHorizontal( horizontalDpi ),
235 mDpiVertical( verticalDpi ),
236 mDefaultFontDescription(),
241 mValidatedFontCache(),
242 mFontDescriptionCache(),
243 mCharacterSetCache(),
244 mFontDescriptionSizeCache(),
245 mVectorFontCache( nullptr ),
247 mEmbeddedItemCache(),
248 mDefaultFontDescriptionCached( false )
250 int error = FT_Init_FreeType( &mFreeTypeLibrary );
251 if( FT_Err_Ok != error )
253 DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Init error: %d\n", error );
256 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
257 mVectorFontCache = new VectorFontCache( mFreeTypeLibrary );
261 FontClient::Plugin::~Plugin()
263 ClearFallbackCache( mFallbackCache );
265 // Free the resources allocated by the FcCharSet objects.
266 DestroyCharacterSets( mDefaultFontCharacterSets );
267 DestroyCharacterSets( mCharacterSetCache );
268 ClearCharacterSetFromFontFaceCache();
270 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
271 delete mVectorFontCache;
273 FT_Done_FreeType( mFreeTypeLibrary );
276 void FontClient::Plugin::ClearCache()
278 mDefaultFontDescription = FontDescription();
280 mSystemFonts.clear();
281 mDefaultFonts.clear();
283 DestroyCharacterSets( mDefaultFontCharacterSets );
284 mDefaultFontCharacterSets.Clear();
286 ClearFallbackCache( mFallbackCache );
287 mFallbackCache.clear();
289 mFontIdCache.Clear();
291 ClearCharacterSetFromFontFaceCache();
292 mFontFaceCache.clear();
294 mValidatedFontCache.clear();
295 mFontDescriptionCache.clear();
297 DestroyCharacterSets( mCharacterSetCache );
298 mCharacterSetCache.Clear();
300 mFontDescriptionSizeCache.clear();
302 mEllipsisCache.Clear();
303 mPixelBufferCache.clear();
304 mEmbeddedItemCache.Clear();
305 mBitmapFontCache.clear();
307 mDefaultFontDescriptionCached = false;
310 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
311 unsigned int verticalDpi )
313 mDpiHorizontal = horizontalDpi;
314 mDpiVertical = verticalDpi;
317 void FontClient::Plugin::ResetSystemDefaults()
319 mDefaultFontDescriptionCached = false;
322 void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList )
324 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n" );
325 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
326 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
327 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
328 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
332 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
334 FcResult result = FcResultMatch;
336 // Match the pattern.
337 FcFontSet* fontSet = FcFontSort( nullptr /* use default configure */,
339 false /* don't trim */,
341 &result ); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
343 if( nullptr != fontSet )
345 DALI_LOG_INFO( gLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont );
346 // Reserve some space to avoid reallocations.
347 fontList.reserve( fontSet->nfont );
349 for( int i = 0u; i < fontSet->nfont; ++i )
351 FcPattern* fontPattern = fontSet->fonts[i];
355 // Skip fonts with no path
356 if( GetFcString( fontPattern, FC_FILE, path ) )
358 // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
359 FcCharSet* characterSet = nullptr;
360 FcPatternGetCharSet( fontPattern, FC_CHARSET, 0u, &characterSet );
362 // Increase the reference counter of the character set.
363 characterSetList.PushBack( FcCharSetCopy( characterSet ) );
365 fontList.push_back( FontDescription() );
366 FontDescription& newFontDescription = fontList.back();
368 newFontDescription.path = std::move( path );
373 GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
374 GetFcInt( fontPattern, FC_WIDTH, width );
375 GetFcInt( fontPattern, FC_WEIGHT, weight );
376 GetFcInt( fontPattern, FC_SLANT, slant );
377 newFontDescription.width = IntToWidthType( width );
378 newFontDescription.weight = IntToWeightType( weight );
379 newFontDescription.slant = IntToSlantType( slant );
381 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", newFontDescription.family.c_str() );
382 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", newFontDescription.path.c_str() );
383 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[newFontDescription.width] );
384 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[newFontDescription.weight] );
385 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant] );
389 // Destroys the font set created by FcFontSort.
390 FcFontSetDestroy( fontSet );
394 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " No fonts found.\n" );
397 // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
398 FcPatternDestroy( fontFamilyPattern );
400 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n" );
403 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
405 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n" );
407 if( mDefaultFonts.empty() )
409 FontDescription fontDescription;
410 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
411 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
412 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
413 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
414 SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
417 defaultFonts = mDefaultFonts;
419 DALI_LOG_INFO( gLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size() );
420 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n" );
423 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
425 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
427 if( !mDefaultFontDescriptionCached )
429 // Clear any font config stored info in the caches.
431 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
432 DestroyCharacterSets( mDefaultFontCharacterSets );
433 DestroyCharacterSets( mCharacterSetCache );
434 mDefaultFontCharacterSets.Clear();
435 mCharacterSetCache.Clear();
437 for( auto& item : mFallbackCache )
439 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
440 DestroyCharacterSets( *item.characterSets );
442 delete item.characterSets;
443 item.characterSets = nullptr;
446 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
447 ClearCharacterSetFromFontFaceCache();
449 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
450 FcInitReinitialize();
452 FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
454 if( nullptr != matchPattern )
456 FcConfigSubstitute( nullptr, matchPattern, FcMatchPattern );
457 FcDefaultSubstitute( matchPattern );
459 FcCharSet* characterSet = nullptr;
460 MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription, &characterSet );
461 // Decrease the reference counter of the character set as it's not stored.
462 FcCharSetDestroy( characterSet );
464 // Destroys the pattern created.
465 FcPatternDestroy( matchPattern );
468 // Create again the character sets as they are not valid after FcInitReinitialize()
470 for( const auto& description : mDefaultFonts )
472 mDefaultFontCharacterSets.PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
475 for( const auto& description : mFontDescriptionCache )
477 mCharacterSetCache.PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
480 for( auto& item : mFallbackCache )
482 if( nullptr != item.fallbackFonts )
484 if( nullptr == item.characterSets )
486 item.characterSets = new CharacterSetList;
489 for( const auto& description : *( item.fallbackFonts ) )
491 item.characterSets->PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
496 mDefaultFontDescriptionCached = true;
499 fontDescription.path = mDefaultFontDescription.path;
500 fontDescription.family = mDefaultFontDescription.family;
501 fontDescription.width = mDefaultFontDescription.width;
502 fontDescription.weight = mDefaultFontDescription.weight;
503 fontDescription.slant = mDefaultFontDescription.slant;
505 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
506 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
507 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
508 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
509 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
510 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
513 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
515 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
517 if( mSystemFonts.empty() )
522 systemFonts = mSystemFonts;
523 DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size() );
524 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
527 void FontClient::Plugin::GetDescription( FontId id,
528 FontDescription& fontDescription ) const
530 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
531 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
532 const FontId index = id - 1u;
534 if( ( id > 0u ) && ( index < mFontIdCache.Count() ) )
536 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
537 switch( fontIdCacheItem.type )
539 case FontDescription::FACE_FONT:
541 for( const auto& item : mFontDescriptionSizeCache )
543 if( item.fontId == fontIdCacheItem.id )
545 fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId - 1u );
547 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
548 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
549 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
550 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
551 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
552 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
558 case FontDescription::BITMAP_FONT:
560 fontDescription.type = FontDescription::BITMAP_FONT;
561 fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
566 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
567 fontDescription.type = FontDescription::INVALID;
568 fontDescription.family.clear();
573 DALI_LOG_INFO( gLogFilter, Debug::General, " No description found for the font ID %d\n", id );
574 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
577 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
579 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
580 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
581 const FontId index = id - 1u;
584 ( index < mFontIdCache.Count() ) )
586 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
588 switch( fontIdCacheItem.type )
590 case FontDescription::FACE_FONT:
592 DALI_LOG_INFO( gLogFilter, Debug::General, " point size : %d\n", ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize );
593 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
594 return ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize;
596 case FontDescription::BITMAP_FONT:
598 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
602 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
608 DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font ID %d\n", id );
611 DALI_LOG_INFO( gLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE );
612 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
613 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
616 bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character character )
618 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
619 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
620 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character );
622 if( ( fontId < 1u ) || ( fontId > mFontIdCache.Count() ) )
624 DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n",mFontIdCache.Count());
625 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
631 bool isSupported = false;
633 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
635 switch( fontIdCacheItem.type )
637 case FontDescription::FACE_FONT:
639 if( fontIdCacheItem.id < mFontFaceCache.size() )
641 FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
643 if( nullptr == cacheItem.mCharacterSet )
645 // Create again the character set.
646 // It can be null if the ResetSystemDefaults() method has been called.
648 FontDescription description;
649 description.path = cacheItem.mPath;
650 description.family = std::move( FontFamily( cacheItem.mFreeTypeFace->family_name ) );
651 description.weight = FontWeight::NONE;
652 description.width = FontWidth::NONE;
653 description.slant = FontSlant::NONE;
655 // Note FreeType doesn't give too much info to build a proper font style.
656 if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC )
658 description.slant = FontSlant::ITALIC;
660 if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD )
662 description.weight = FontWeight::BOLD;
665 cacheItem.mCharacterSet = FcCharSetCopy( CreateCharacterSetFromDescription( description ) );
668 isSupported = FcCharSetHasChar( cacheItem.mCharacterSet, character );
672 case FontDescription::BITMAP_FONT:
674 const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
676 for( const auto& glyph : bitmapFont.glyphs )
678 if( glyph.utf32 == character )
688 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
692 DALI_LOG_INFO( gLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false") );
693 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
697 FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
698 const CharacterSetList& characterSetList,
700 PointSize26Dot6 requestedPointSize,
703 DALI_ASSERT_DEBUG( ( fontList.size() == characterSetList.Count() ) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets." );
705 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n" );
706 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character );
707 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
708 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
711 bool foundColor = false;
713 DALI_LOG_INFO( gLogFilter, Debug::General, " number of fonts : %d\n", fontList.size() );
715 // Traverse the list of fonts.
716 // Check for each font if supports the character.
717 for( unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index )
719 const FontDescription& description = fontList[index];
720 const FcCharSet* const characterSet = characterSetList[index];
722 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", description.family.c_str() );
723 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str() );
724 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width] );
725 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight] );
726 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant] );
728 bool foundInRanges = false;
729 if( nullptr != characterSet )
731 foundInRanges = FcCharSetHasChar( characterSet, character );
736 fontId = GetFontId( description,
740 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " font id : %d\n", fontId );
744 if( ( fontId > 0 ) &&
745 ( fontId - 1u < mFontIdCache.Count() ) )
747 const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
749 foundColor = item.mHasColorTables;
752 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " foundColor : %s\n", ( foundColor ? "true" : "false" ) );
755 // Keep going unless we prefer a different (color) font.
756 if( !preferColor || foundColor )
763 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
764 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n" );
768 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
769 PointSize26Dot6 requestedPointSize,
772 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n" );
773 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", charcode );
774 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
775 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
779 // Create the list of default fonts if it has not been created.
780 if( mDefaultFonts.empty() )
782 FontDescription fontDescription;
783 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
784 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
785 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
786 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
788 SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
790 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size() );
793 // Traverse the list of default fonts.
794 // Check for each default font if supports the character.
795 fontId = FindFontForCharacter( mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor );
797 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
798 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n" );
803 FontId FontClient::Plugin::FindFallbackFont( Character charcode,
804 const FontDescription& preferredFontDescription,
805 PointSize26Dot6 requestedPointSize,
808 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n" );
809 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", charcode );
810 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
811 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
813 // The font id to be returned.
816 FontDescription fontDescription;
818 // Fill the font description with the preferred font description and complete with the defaults.
819 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
820 fontDescription.weight = ( ( FontWeight::NONE == preferredFontDescription.weight ) ? IntToWeightType( DEFAULT_FONT_WEIGHT ) : preferredFontDescription.weight );
821 fontDescription.width = ( ( FontWidth::NONE == preferredFontDescription.width ) ? IntToWidthType( DEFAULT_FONT_WIDTH ) : preferredFontDescription.width );
822 fontDescription.slant = ( ( FontSlant::NONE == preferredFontDescription.slant ) ? IntToSlantType( DEFAULT_FONT_SLANT ) : preferredFontDescription.slant );
824 DALI_LOG_INFO( gLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n" );
825 DALI_LOG_INFO( gLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str() );
826 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight] );
827 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width] );
828 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant] );
830 // Check first if the font's description has been queried before.
831 FontList* fontList = nullptr;
832 CharacterSetList* characterSetList = nullptr;
834 if( !FindFallbackFontList( fontDescription, fontList, characterSetList ) )
836 fontList = new FontList;
837 characterSetList = new CharacterSetList;
839 SetFontList( fontDescription, *fontList, *characterSetList );
841 // Add the font-list to the cache.
842 mFallbackCache.push_back( std::move( FallbackCacheItem( std::move( fontDescription ), fontList, characterSetList ) ) );
845 if( fontList && characterSetList )
847 fontId = FindFontForCharacter( *fontList, *characterSetList, charcode, requestedPointSize, preferColor );
850 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
851 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
855 FontId FontClient::Plugin::GetFontId( const FontPath& path,
856 PointSize26Dot6 requestedPointSize,
858 bool cacheDescription )
860 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
861 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
862 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
866 if( nullptr != mFreeTypeLibrary )
869 if( FindFont( path, requestedPointSize, faceIndex, foundId ) )
875 id = CreateFont( path, requestedPointSize, faceIndex, cacheDescription );
879 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
880 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
885 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
886 PointSize26Dot6 requestedPointSize,
887 FaceIndex faceIndex )
889 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
890 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
891 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
892 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
893 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
894 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
895 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
897 // This method uses three vectors which caches:
898 // * The bitmap font cache
899 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
900 // * The path to font file names.
901 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
903 // 1) Checks if the font description matches with a previously loaded bitmap font.
904 // Returns if a font is found.
905 // 2) Checks in the cache if the font's description has been validated before.
906 // If it was it gets an index to the vector with paths to font file names. Otherwise,
907 // retrieves using font config a path to a font file name which matches with the
908 // font's description. The path is stored in the cache.
910 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
911 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
912 // the GetFontId() method with the path to the font file name and the point size to
915 // The font id to be returned.
918 // Check first if the font description matches with a previously loaded bitmap font.
919 if( FindBitmapFont( fontDescription.family, fontId ) )
924 // Check if the font's description have been validated before.
925 FontDescriptionId validatedFontId = 0u;
927 if( !FindValidatedFont( fontDescription,
930 // Use font config to validate the font's description.
931 ValidateFont( fontDescription,
935 FontId fontFaceId = 0u;
936 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
937 if( !FindFont( validatedFontId, requestedPointSize, fontFaceId ) )
939 // Retrieve the font file name path.
940 const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId - 1u );
942 // Retrieve the font id. Do not cache the description as it has been already cached.
943 fontId = GetFontId( description.path,
948 fontFaceId = mFontIdCache[fontId-1u].id;
949 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( mCharacterSetCache[validatedFontId - 1u] );
951 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
952 mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
958 fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
961 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
962 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
967 FontId FontClient::Plugin::GetFontId( const BitmapFont& bitmapFont )
969 for( const auto& item : mBitmapFontCache )
971 if( bitmapFont.name == item.font.name )
977 BitmapFontCacheItem bitmapFontCacheItem;
978 bitmapFontCacheItem.font = bitmapFont;
979 bitmapFontCacheItem.id = mFontIdCache.Count();
981 // Resize the vector with the pixel buffers.
982 bitmapFontCacheItem.pixelBuffers.resize( bitmapFont.glyphs.size() );
984 // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
985 unsigned int index = 0u;
986 for( auto& glyph : bitmapFontCacheItem.font.glyphs )
988 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
990 if( EqualsZero( glyph.ascender ) && EqualsZero( glyph.descender ) )
993 pixelBuffer = LoadImageFromFile( glyph.url );
997 glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1001 bitmapFontCacheItem.font.ascender = std::max( glyph.ascender, bitmapFontCacheItem.font.ascender );
1002 bitmapFontCacheItem.font.descender = std::min( glyph.descender, bitmapFontCacheItem.font.descender );
1007 FontIdCacheItem fontIdCacheItem;
1008 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1009 fontIdCacheItem.id = mBitmapFontCache.size();
1011 mBitmapFontCache.push_back( std::move( bitmapFontCacheItem ) );
1012 mFontIdCache.PushBack( fontIdCacheItem );
1014 return bitmapFontCacheItem.id + 1u;
1017 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
1018 FontDescriptionId& validatedFontId )
1020 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n" );
1021 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
1022 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1023 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1024 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1025 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1027 // Create a font pattern.
1028 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1030 FontDescription description;
1032 FcCharSet* characterSet = nullptr;
1033 bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description, &characterSet );
1034 FcPatternDestroy( fontFamilyPattern );
1036 if( matched && ( nullptr != characterSet ) )
1038 // Add the path to the cache.
1039 description.type = FontDescription::FACE_FONT;
1040 mFontDescriptionCache.push_back( description );
1042 // Set the index to the vector of paths to font file names.
1043 validatedFontId = mFontDescriptionCache.size();
1045 DALI_LOG_INFO( gLogFilter, Debug::General, " matched description; family : [%s]\n", description.family.c_str() );
1046 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str() );
1047 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width] );
1048 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight] );
1049 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant] );
1050 DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId );
1052 // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1053 mCharacterSetCache.PushBack( characterSet );
1055 // Cache the index and the matched font's description.
1056 FontDescriptionCacheItem item( description,
1059 mValidatedFontCache.push_back( std::move( item ) );
1061 if( ( fontDescription.family != description.family ) ||
1062 ( fontDescription.width != description.width ) ||
1063 ( fontDescription.weight != description.weight ) ||
1064 ( fontDescription.slant != description.slant ) )
1066 // Cache the given font's description if it's different than the matched.
1067 FontDescriptionCacheItem item( fontDescription,
1070 mValidatedFontCache.push_back( std::move( item ) );
1075 DALI_LOG_INFO( gLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str() );
1078 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n" );
1081 void FontClient::Plugin::GetFontMetrics( FontId fontId,
1082 FontMetrics& metrics )
1084 const FontId index = fontId - 1u;
1086 if( ( fontId > 0 ) &&
1087 ( index < mFontIdCache.Count() ) )
1089 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1091 switch( fontIdCacheItem.type )
1093 case FontDescription::FACE_FONT:
1095 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1097 metrics = font.mMetrics;
1099 // Adjust the metrics if the fixed-size font should be down-scaled
1100 if( font.mIsFixedSizeBitmap )
1102 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1104 if( desiredFixedSize > 0.f )
1106 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1108 metrics.ascender = metrics.ascender * scaleFactor;
1109 metrics.descender = metrics.descender * scaleFactor;
1110 metrics.height = metrics.height * scaleFactor;
1111 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1112 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1117 case FontDescription::BITMAP_FONT:
1119 const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1121 metrics.ascender = bitmapFontCacheItem.font.ascender;
1122 metrics.descender = bitmapFontCacheItem.font.descender;
1123 metrics.height = metrics.ascender - metrics.descender;
1124 metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1125 metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1130 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1136 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
1140 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
1141 Character charcode )
1143 GlyphIndex glyphIndex = 0u;
1144 const FontId index = fontId - 1u;
1146 if( ( fontId > 0u ) &&
1147 ( index < mFontIdCache.Count() ) )
1149 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1151 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1153 FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1155 glyphIndex = FT_Get_Char_Index( ftFace, charcode );
1162 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
1167 if( VECTOR_GLYPH == type )
1169 return GetVectorMetrics( array, size, horizontal );
1172 return GetBitmapMetrics( array, size, horizontal );
1175 bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
1179 bool success( true );
1181 for( unsigned int i=0; i<size; ++i )
1183 GlyphInfo& glyph = array[i];
1185 FontId index = glyph.fontId - 1u;
1187 if( ( glyph.fontId > 0u ) &&
1188 ( index < mFontIdCache.Count() ) )
1190 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1192 switch( fontIdCacheItem.type )
1194 case FontDescription::FACE_FONT:
1196 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1198 FT_Face ftFace = font.mFreeTypeFace;
1200 #ifdef FREETYPE_BITMAP_SUPPORT
1201 // Check to see if we should be loading a Fixed Size bitmap?
1202 if( font.mIsFixedSizeBitmap )
1204 FT_Select_Size( ftFace, font.mFixedSizeIndex ); ///< @todo: needs to be investigated why it's needed to select the size again.
1205 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR );
1206 if ( FT_Err_Ok == error )
1208 glyph.width = font.mFixedWidthPixels;
1209 glyph.height = font.mFixedHeightPixels;
1210 glyph.advance = font.mFixedWidthPixels;
1211 glyph.xBearing = 0.0f;
1212 glyph.yBearing = font.mFixedHeightPixels;
1214 // Adjust the metrics if the fixed-size font should be down-scaled
1215 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1217 if( desiredFixedSize > 0.f )
1219 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1221 glyph.width = glyph.width * scaleFactor ;
1222 glyph.height = glyph.height * scaleFactor;
1223 glyph.advance = glyph.advance * scaleFactor;
1224 glyph.xBearing = glyph.xBearing * scaleFactor;
1225 glyph.yBearing = glyph.yBearing * scaleFactor;
1227 glyph.scaleFactor = scaleFactor;
1232 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error );
1239 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1240 // i.e. with the SNum-3R font.
1241 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1242 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT );
1244 // Keep the width of the glyph before doing the software emboldening.
1245 // It will be used to calculate a scale factor to be applied to the
1246 // advance as Harfbuzz doesn't apply any SW emboldening to calculate
1247 // the advance of the glyph.
1248 const float width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1250 if( FT_Err_Ok == error )
1252 const bool isEmboldeningRequired = glyph.isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD );
1253 if( isEmboldeningRequired )
1255 // Does the software bold.
1256 FT_GlyphSlot_Embolden( ftFace->glyph );
1259 glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1260 glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266;
1263 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
1264 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
1268 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
1269 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
1272 if( isEmboldeningRequired && !Dali::EqualsZero( width ) )
1274 // If the glyph is emboldened by software, the advance is multiplied by a
1275 // scale factor to make it slightly bigger.
1276 glyph.advance *= ( glyph.width / width );
1279 // Use the bounding box of the bitmap to correct the metrics.
1280 // For some fonts i.e the SNum-3R the metrics need to be corrected,
1281 // otherwise the glyphs 'dance' up and down depending on the
1282 // font's point size.
1285 error = FT_Get_Glyph( ftFace->glyph, &ftGlyph );
1288 FT_Glyph_Get_CBox( ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox );
1290 const float descender = glyph.height - glyph.yBearing;
1291 glyph.height = ( bbox.yMax - bbox.yMin) * FROM_266;
1292 glyph.yBearing = glyph.height - std::round( descender );
1294 // Created FT_Glyph object must be released with FT_Done_Glyph
1295 FT_Done_Glyph( ftGlyph );
1304 case FontDescription::BITMAP_FONT:
1306 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1308 unsigned int index = 0u;
1309 for( auto& item : bitmapFontCacheItem.font.glyphs )
1311 if( item.utf32 == glyph.index )
1313 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1316 pixelBuffer = LoadImageFromFile( item.url );
1319 glyph.width = static_cast< float >( pixelBuffer.GetWidth() );
1320 glyph.height = static_cast< float >( pixelBuffer.GetHeight() );
1321 glyph.xBearing = 0.f;
1322 glyph.yBearing = glyph.height + item.descender;
1323 glyph.advance = glyph.width;
1324 glyph.scaleFactor = 1.f;
1335 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1341 // Check if it's an embedded image.
1342 if( ( 0u == glyph.fontId ) && ( 0u != glyph.index ) && ( glyph.index <= mEmbeddedItemCache.Count() ) )
1344 const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1346 glyph.width = static_cast<float>( item.width );
1347 glyph.height = static_cast<float>( item.height );
1348 glyph.xBearing = 0.f;
1349 glyph.yBearing = glyph.height;
1350 glyph.advance = glyph.width;
1351 glyph.scaleFactor = 1.f;
1363 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
1367 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1368 bool success( true );
1370 for( unsigned int i = 0u; i < size; ++i )
1372 FontId fontId = array[i].fontId;
1374 if( ( fontId > 0u ) &&
1375 ( fontId - 1u ) < mFontIdCache.Count() )
1377 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1379 if( ! font.mVectorFontId )
1381 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1384 mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
1386 // Vector metrics are in EMs, convert to pixels
1387 const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
1388 array[i].width *= scale;
1389 array[i].height *= scale;
1390 array[i].xBearing *= scale;
1391 array[i].yBearing *= scale;
1392 array[i].advance *= scale;
1406 void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth )
1408 const FontId index = fontId - 1u;
1410 if( ( fontId > 0u ) &&
1411 ( index < mFontIdCache.Count() ) )
1413 data.isColorBitmap = false;
1414 data.isColorEmoji = false;
1416 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1418 switch( fontIdCacheItem.type )
1420 case FontDescription::FACE_FONT:
1422 // For the software italics.
1423 bool isShearRequired = false;
1425 const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1426 FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1430 #ifdef FREETYPE_BITMAP_SUPPORT
1431 // Check to see if this is fixed size bitmap
1432 if( fontFaceCacheItem.mIsFixedSizeBitmap )
1434 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1439 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1440 // i.e. with the SNum-3R font.
1441 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1442 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT );
1444 if( FT_Err_Ok == error )
1446 if( isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) )
1448 // Does the software bold.
1449 FT_GlyphSlot_Embolden( ftFace->glyph );
1452 if( isItalicRequired && !( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) )
1454 // Will do the software italic.
1455 isShearRequired = true;
1459 error = FT_Get_Glyph( ftFace->glyph, &glyph );
1461 // Convert to bitmap if necessary
1462 if( FT_Err_Ok == error )
1464 if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
1466 // Check whether we should create a bitmap for the outline
1467 if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 )
1471 error = FT_Stroker_New( mFreeTypeLibrary, &stroker );
1473 if( FT_Err_Ok == error )
1475 FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 );
1476 error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 );
1478 if( FT_Err_Ok == error )
1480 FT_Stroker_Done( stroker );
1484 DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error );
1489 DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error );
1493 error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
1494 if( FT_Err_Ok == error )
1496 FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph );
1497 ConvertBitmap( data, bitmapGlyph->bitmap, isShearRequired );
1501 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error );
1506 ConvertBitmap( data, ftFace->glyph->bitmap, isShearRequired );
1509 data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1511 // Created FT_Glyph object must be released with FT_Done_Glyph
1512 FT_Done_Glyph( glyph );
1517 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error );
1521 case FontDescription::BITMAP_FONT:
1523 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1525 unsigned int index = 0u;
1526 for( auto& item : bitmapFontCacheItem.font.glyphs )
1528 if( item.utf32 == glyphIndex )
1530 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1533 pixelBuffer = LoadImageFromFile( item.url );
1536 data.width = pixelBuffer.GetWidth();
1537 data.height = pixelBuffer.GetHeight();
1539 data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1541 ConvertBitmap( data, data.width, data.height, pixelBuffer.GetBuffer() );
1543 // Sets the pixel format.
1544 data.format = pixelBuffer.GetPixelFormat();
1553 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1559 if( ( 0u != glyphIndex ) && ( glyphIndex <= mEmbeddedItemCache.Count() ) )
1561 // It's an embedded item.
1562 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1564 data.width = item.width;
1565 data.height = item.height;
1566 if( 0u != item.pixelBufferId )
1568 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId-1u].pixelBuffer;
1571 ConvertBitmap( data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer() );
1573 // Sets the pixel format.
1574 data.format = pixelBuffer.GetPixelFormat();
1579 // Creates the output buffer
1580 const unsigned int bufferSize = data.width * data.height * 4u;
1581 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1583 memset( data.buffer, 0u, bufferSize );
1585 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1591 PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
1593 TextAbstraction::FontClient::GlyphBufferData data;
1595 CreateBitmap( fontId, glyphIndex, false, false, data, outlineWidth );
1597 return PixelData::New( data.buffer,
1598 data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
1602 PixelData::DELETE_ARRAY );
1605 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
1610 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1611 if( ( fontId > 0u ) &&
1612 ( fontId - 1u < mFontIdCache.Count() ) )
1614 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1615 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1617 if( ! font.mVectorFontId )
1619 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1622 mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
1627 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
1629 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" );
1630 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize );
1632 // First look into the cache if there is an ellipsis glyph for the requested point size.
1633 for( const auto& item : mEllipsisCache )
1635 if( item.requestedPointSize != requestedPointSize )
1637 // Use the glyph in the cache.
1639 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1640 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1641 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1647 // No glyph has been found. Create one.
1648 mEllipsisCache.PushBack( EllipsisItem() );
1649 EllipsisItem& item = *( mEllipsisCache.End() - 1u );
1651 item.requestedPointSize = requestedPointSize;
1653 // Find a font for the ellipsis glyph.
1654 item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
1658 // Set the character index to access the glyph inside the font.
1659 item.glyph.index = FT_Get_Char_Index( mFontFaceCache[mFontIdCache[item.glyph.fontId-1u].id].mFreeTypeFace,
1660 ELLIPSIS_CHARACTER );
1662 GetBitmapMetrics( &item.glyph, 1u, true );
1664 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1665 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1666 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1671 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1673 FT_Error error = -1;
1675 const FontId index = fontId - 1u;
1677 if( ( fontId > 0u ) &&
1678 ( index < mFontIdCache.Count() ) )
1680 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1682 switch( fontIdCacheItem.type )
1684 case FontDescription::FACE_FONT:
1686 #ifdef FREETYPE_BITMAP_SUPPORT
1687 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1688 FT_Face ftFace = item.mFreeTypeFace;
1690 // Check to see if this is fixed size bitmap
1691 if( item.mHasColorTables )
1693 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1698 case FontDescription::BITMAP_FONT:
1700 error = FT_Err_Ok; // Will return true;
1705 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1710 return FT_Err_Ok == error;
1713 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId )
1715 FT_Face fontFace = nullptr;
1717 const FontId index = fontId - 1u;
1718 if( ( fontId > 0u ) &&
1719 ( index < mFontIdCache.Count() ) )
1721 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1723 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1725 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1731 FontDescription::Type FontClient::Plugin::GetFontType( FontId fontId )
1733 const FontId index = fontId - 1u;
1734 if( ( fontId > 0u ) &&
1735 ( index < mFontIdCache.Count() ) )
1737 return mFontIdCache[index].type;
1739 return FontDescription::INVALID;
1742 bool FontClient::Plugin::AddCustomFontDirectory( const FontPath& path )
1744 // nullptr as first parameter means the current configuration is used.
1745 return FcConfigAppFontAddDir( nullptr, reinterpret_cast<const FcChar8 *>( path.c_str() ) );
1748 GlyphIndex FontClient::Plugin::CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat )
1750 EmbeddedItem embeddedItem;
1752 embeddedItem.pixelBufferId = 0u;
1753 embeddedItem.width = description.width;
1754 embeddedItem.height = description.height;
1756 pixelFormat = Pixel::A8;
1758 if( !description.url.empty() )
1760 // Check if the url is in the cache.
1761 PixelBufferId index = 0u;
1763 for( const auto& cacheItem : mPixelBufferCache )
1766 if( cacheItem.url == description.url )
1768 // The url is in the pixel buffer cache.
1769 // Set the index +1 to the vector.
1770 embeddedItem.pixelBufferId = index;
1775 Devel::PixelBuffer pixelBuffer;
1776 if( 0u == embeddedItem.pixelBufferId )
1778 // The pixel buffer is not in the cache. Create one and cache it.
1780 // Load the image from the url.
1781 pixelBuffer = LoadImageFromFile( description.url );
1783 // Create the cache item.
1784 PixelBufferCacheItem pixelBufferCacheItem;
1785 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1786 pixelBufferCacheItem.url = description.url;
1788 // Store the cache item in the cache.
1789 mPixelBufferCache.push_back( std::move( pixelBufferCacheItem ) );
1791 // Set the pixel buffer id to the embedded item.
1792 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1796 // Retrieve the pixel buffer from the cache to set the pixel format.
1797 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId-1u].pixelBuffer;
1802 // Set the size of the embedded item if it has not been set.
1803 if( 0u == embeddedItem.width )
1805 embeddedItem.width = static_cast<unsigned int>( pixelBuffer.GetWidth() );
1808 if( 0u == embeddedItem.height )
1810 embeddedItem.height = static_cast<unsigned int>( pixelBuffer.GetHeight() );
1813 // Set the pixel format.
1814 pixelFormat = pixelBuffer.GetPixelFormat();
1818 // Find if the same embeddedItem has already been created.
1819 unsigned int index = 0u;
1820 for( const auto& item : mEmbeddedItemCache )
1823 if( ( item.pixelBufferId == embeddedItem.pixelBufferId ) &&
1824 ( item.width == embeddedItem.width ) &&
1825 ( item.height == embeddedItem.height ) )
1831 // Cache the embedded item.
1832 mEmbeddedItemCache.PushBack( embeddedItem );
1834 return mEmbeddedItemCache.Count();
1837 void FontClient::Plugin::InitSystemFonts()
1839 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1841 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1845 DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont );
1847 // Reserve some space to avoid reallocations.
1848 mSystemFonts.reserve( fontSet->nfont );
1850 for( int i = 0u; i < fontSet->nfont; ++i )
1852 FcPattern* fontPattern = fontSet->fonts[i];
1856 // Skip fonts with no path
1857 if( GetFcString( fontPattern, FC_FILE, path ) )
1859 mSystemFonts.push_back( FontDescription() );
1860 FontDescription& fontDescription = mSystemFonts.back();
1862 fontDescription.path = std::move( path );
1867 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1868 GetFcInt( fontPattern, FC_WIDTH, width );
1869 GetFcInt( fontPattern, FC_WEIGHT, weight );
1870 GetFcInt( fontPattern, FC_SLANT, slant );
1871 fontDescription.width = IntToWidthType( width );
1872 fontDescription.weight = IntToWeightType( weight );
1873 fontDescription.slant = IntToSlantType( slant );
1875 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str() );
1876 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1877 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1878 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1879 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1883 // Destroys the font set created.
1884 FcFontSetDestroy( fontSet );
1886 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1889 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1891 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1893 FcResult result = FcResultMatch;
1894 FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result ); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1896 const bool matched = nullptr != match;
1897 DALI_LOG_INFO( gLogFilter, Debug::General, " pattern matched : %s\n", ( matched ? "true" : "false" ) );
1904 GetFcString( match, FC_FILE, fontDescription.path );
1905 GetFcString( match, FC_FAMILY, fontDescription.family );
1906 GetFcInt( match, FC_WIDTH, width );
1907 GetFcInt( match, FC_WEIGHT, weight );
1908 GetFcInt( match, FC_SLANT, slant );
1909 fontDescription.width = IntToWidthType( width );
1910 fontDescription.weight = IntToWeightType( weight );
1911 fontDescription.slant = IntToSlantType( slant );
1913 // Retrieve the character set and increase the reference counter.
1914 FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1915 *characterSet = FcCharSetCopy( *characterSet );
1917 // destroyed the matched pattern
1918 FcPatternDestroy( match );
1920 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
1921 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1922 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1923 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1924 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1927 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1931 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1933 // create the cached font family lookup pattern
1934 // a pattern holds a set of names, each name refers to a property of the font
1935 FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1937 if( !fontFamilyPattern )
1942 // add a property to the pattern for the font family
1943 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1945 // add a property to the pattern for local setting.
1946 const char* locale = setlocale( LC_MESSAGES, nullptr );
1947 if( locale != nullptr)
1949 FcPatternAddString( fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>( locale ) );
1952 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1956 width = DEFAULT_FONT_WIDTH;
1959 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1963 weight = DEFAULT_FONT_WEIGHT;
1966 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1970 slant = DEFAULT_FONT_SLANT;
1973 FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1974 FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1975 FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1977 // modify the config, with the mFontFamilyPatterm
1978 FcConfigSubstitute( nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern );
1980 // provide default values for unspecified properties in the font pattern
1981 // e.g. patterns without a specified style or weight are set to Medium
1982 FcDefaultSubstitute( fontFamilyPattern );
1984 return fontFamilyPattern;
1987 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1989 FcFontSet* fontset = nullptr;
1991 // create a new pattern.
1992 // a pattern holds a set of names, each name refers to a property of the font
1993 FcPattern* pattern = FcPatternCreate();
1995 if( nullptr != pattern )
1997 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1998 FcObjectSet* objectSet = FcObjectSetCreate();
2000 if( nullptr != objectSet )
2002 // build an object set from a list of property names
2003 FcObjectSetAdd( objectSet, FC_FILE );
2004 FcObjectSetAdd( objectSet, FC_FAMILY );
2005 FcObjectSetAdd( objectSet, FC_WIDTH );
2006 FcObjectSetAdd( objectSet, FC_WEIGHT );
2007 FcObjectSetAdd( objectSet, FC_SLANT );
2009 // get a list of fonts
2010 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
2011 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.
2013 // clear up the object set
2014 FcObjectSetDestroy( objectSet );
2017 // clear up the pattern
2018 FcPatternDestroy( pattern );
2024 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
2025 const char* const n,
2026 std::string& string )
2028 FcChar8* file = nullptr;
2029 const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
2031 if( FcResultMatch == retVal )
2033 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
2034 string.assign( reinterpret_cast<const char*>( file ) );
2042 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
2044 const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
2046 if( FcResultMatch == retVal )
2054 FontId FontClient::Plugin::CreateFont( const FontPath& path,
2055 PointSize26Dot6 requestedPointSize,
2056 FaceIndex faceIndex,
2057 bool cacheDescription )
2059 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" );
2060 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2061 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2065 // Create & cache new font face
2067 int error = FT_New_Face( mFreeTypeLibrary,
2072 if( FT_Err_Ok == error )
2074 // Check if a font is scalable.
2075 const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) );
2076 const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes );
2077 const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) );
2078 FontId fontFaceId = 0u;
2080 DALI_LOG_INFO( gLogFilter, Debug::General, " isScalable : [%s]\n", ( isScalable ? "true" : "false" ) );
2081 DALI_LOG_INFO( gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) );
2082 DALI_LOG_INFO( gLogFilter, Debug::General, " hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) );
2084 // Check to see if the font contains fixed sizes?
2085 if( !isScalable && hasFixedSizedBitmaps )
2087 PointSize26Dot6 actualPointSize = 0u;
2088 int fixedSizeIndex = 0;
2089 for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex )
2091 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2092 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize );
2094 if( fixedSize >= requestedPointSize )
2096 actualPointSize = fixedSize;
2101 if( 0u == actualPointSize )
2103 // The requested point size is bigger than the bigest fixed size.
2104 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2105 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2108 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize );
2110 // Tell Freetype to use this size
2111 error = FT_Select_Size( ftFace, fixedSizeIndex );
2112 if ( FT_Err_Ok != error )
2114 DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error );
2118 const float fixedWidth = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].width );
2119 const float fixedHeight = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].height );
2121 // Indicate that the font is a fixed sized bitmap
2122 FontMetrics metrics( fixedHeight, // The ascender in pixels.
2124 fixedHeight, // The height in pixels.
2128 // Create the FreeType font face item to cache.
2129 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables );
2131 // Set the index to the font's id cache.
2132 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2134 // Create the font id item to cache.
2135 FontIdCacheItem fontIdCacheItem;
2136 fontIdCacheItem.type = FontDescription::FACE_FONT;
2138 // Set the index to the FreeType font face cache.
2139 fontIdCacheItem.id = mFontFaceCache.size();
2140 fontFaceId = fontIdCacheItem.id + 1u;
2143 mFontFaceCache.push_back( fontFaceCacheItem );
2144 mFontIdCache.PushBack( fontIdCacheItem );
2146 // Set the font id to be returned.
2147 id = mFontIdCache.Count();
2152 error = FT_Set_Char_Size( ftFace,
2158 if( FT_Err_Ok == error )
2161 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2163 FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266,
2164 static_cast< float >( ftMetrics.descender ) * FROM_266,
2165 static_cast< float >( ftMetrics.height ) * FROM_266,
2166 static_cast< float >( ftFace->underline_position ) * FROM_266,
2167 static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
2169 // Create the FreeType font face item to cache.
2170 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics );
2172 // Set the index to the font's id cache.
2173 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2175 // Create the font id item to cache.
2176 FontIdCacheItem fontIdCacheItem;
2177 fontIdCacheItem.type = FontDescription::FACE_FONT;
2179 // Set the index to the FreeType font face cache.
2180 fontIdCacheItem.id = mFontFaceCache.size();
2181 fontFaceId = fontIdCacheItem.id + 1u;
2184 mFontFaceCache.push_back( fontFaceCacheItem );
2185 mFontIdCache.PushBack( fontIdCacheItem );
2187 // Set the font id to be returned.
2188 id = mFontIdCache.Count();
2192 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize );
2196 if( 0u != fontFaceId )
2198 if( cacheDescription )
2200 CacheFontPath( ftFace, fontFaceId, requestedPointSize, path );
2206 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str() );
2209 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
2210 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" );
2215 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer )
2217 // Set the input dimensions.
2218 const ImageDimensions inputDimensions( srcWidth, srcHeight );
2220 // Set the output dimensions.
2221 // If the output dimension is not given, the input dimension is set
2222 // and won't be downscaling.
2223 data.width = ( data.width == 0 ) ? srcWidth : data.width;
2224 data.height = ( data.height == 0 ) ? srcHeight : data.height;
2225 const ImageDimensions desiredDimensions( data.width, data.height );
2227 // Creates the output buffer
2228 const unsigned int bufferSize = data.width * data.height * 4u;
2229 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2231 if( inputDimensions == desiredDimensions )
2233 // There isn't downscaling.
2234 memcpy( data.buffer, srcBuffer, bufferSize );
2238 Dali::Internal::Platform::LanczosSample4BPP( srcBuffer,
2241 desiredDimensions );
2245 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired )
2247 if( srcBitmap.width*srcBitmap.rows > 0 )
2249 switch( srcBitmap.pixel_mode )
2251 case FT_PIXEL_MODE_GRAY:
2253 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
2255 uint8_t* pixelsIn = srcBitmap.buffer;
2256 unsigned int width = srcBitmap.width;
2257 unsigned height = srcBitmap.rows;
2259 std::unique_ptr<uint8_t, void(*)(void*)> pixelsOutPtr( nullptr, free );
2261 if( isShearRequired )
2264 * Glyphs' bitmaps with no slant retrieved from FreeType:
2274 * Expected glyphs' bitmaps with italic slant:
2275 * ____________ ______
2282 * ------------ ------
2284 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2294 * 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.
2296 unsigned int widthOut = 0u;
2297 unsigned int heightOut = 0u;
2298 uint8_t* pixelsOut = nullptr;
2300 Dali::Internal::Platform::HorizontalShear( pixelsIn,
2304 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2311 pixelsIn = pixelsOut;
2312 pixelsOutPtr.reset( pixelsOut );
2315 const unsigned int bufferSize = width * height;
2316 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2318 data.height = height;
2319 data.format = Pixel::L8; // Sets the pixel format.
2320 memcpy( data.buffer, pixelsIn, bufferSize );
2325 #ifdef FREETYPE_BITMAP_SUPPORT
2326 case FT_PIXEL_MODE_BGRA:
2328 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
2330 ConvertBitmap( data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer );
2332 // Sets the pixel format.
2333 data.format = Pixel::BGRA8888;
2340 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" );
2347 bool FontClient::Plugin::FindFont( const FontPath& path,
2348 PointSize26Dot6 requestedPointSize,
2349 FaceIndex faceIndex,
2350 FontId& fontId ) const
2352 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2353 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2354 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2355 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size() );
2358 for( const auto& cacheItem : mFontFaceCache )
2360 if( cacheItem.mRequestedPointSize == requestedPointSize &&
2361 cacheItem.mFaceIndex == faceIndex &&
2362 cacheItem.mPath == path )
2364 fontId = cacheItem.mFontId + 1u;
2366 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2367 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2373 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found\n" );
2374 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2379 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
2380 FontDescriptionId& validatedFontId )
2382 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" );
2383 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2384 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2385 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2386 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2387 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2388 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size() );
2390 validatedFontId = 0u;
2392 for( const auto& item : mValidatedFontCache )
2394 if( !fontDescription.family.empty() &&
2395 ( fontDescription.family == item.fontDescription.family ) &&
2396 ( fontDescription.width == item.fontDescription.width ) &&
2397 ( fontDescription.weight == item.fontDescription.weight ) &&
2398 ( fontDescription.slant == item.fontDescription.slant ) )
2400 validatedFontId = item.index;
2402 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId );
2403 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2408 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font not found\n" );
2409 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2413 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
2414 FontList*& fontList,
2415 CharacterSetList*& characterSetList )
2417 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" );
2418 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2419 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2420 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2421 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2422 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2423 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size() );
2427 for( const auto& item : mFallbackCache )
2429 if( !fontDescription.family.empty() &&
2430 ( fontDescription.family == item.fontDescription.family ) &&
2431 ( fontDescription.width == item.fontDescription.width ) &&
2432 ( fontDescription.weight == item.fontDescription.weight ) &&
2433 ( fontDescription.slant == item.fontDescription.slant ) )
2435 fontList = item.fallbackFonts;
2436 characterSetList = item.characterSets;
2438 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list found.\n" );
2439 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2444 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list not found.\n" );
2445 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2449 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
2450 PointSize26Dot6 requestedPointSize,
2453 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2454 DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId );
2455 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2459 for( const auto& item : mFontDescriptionSizeCache )
2461 if( ( validatedFontId == item.validatedFontId ) &&
2462 ( requestedPointSize == item.requestedPointSize ) )
2464 fontId = item.fontId;
2466 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2467 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2472 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found.\n" );
2473 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2477 bool FontClient::Plugin::FindBitmapFont( const FontFamily& bitmapFont, FontId& fontId ) const
2481 for( const auto& item : mBitmapFontCache )
2483 if( bitmapFont == item.font.name )
2485 fontId = item.id + 1u;
2493 bool FontClient::Plugin::IsScalable( const FontPath& path )
2495 bool isScalable = false;
2498 int error = FT_New_Face( mFreeTypeLibrary,
2502 if( FT_Err_Ok != error )
2504 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() );
2508 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2514 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
2516 // Create a font pattern.
2517 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2519 FcResult result = FcResultMatch;
2521 // match the pattern
2522 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2523 bool isScalable = false;
2527 // Get the path to the font file name.
2529 GetFcString( match, FC_FILE, path );
2530 isScalable = IsScalable( path );
2534 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2537 // Destroys the created patterns.
2538 FcPatternDestroy( match );
2539 FcPatternDestroy( fontFamilyPattern );
2544 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
2546 // Empty the caller container
2550 int error = FT_New_Face( mFreeTypeLibrary,
2554 if( FT_Err_Ok != error )
2556 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() );
2559 // Fetch the number of fixed sizes available
2560 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
2562 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
2564 sizes.PushBack( ftFace->available_sizes[ i ].size );
2569 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
2570 Vector< PointSize26Dot6 >& sizes )
2572 // Create a font pattern.
2573 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2575 FcResult result = FcResultMatch;
2577 // match the pattern
2578 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2582 // Get the path to the font file name.
2584 GetFcString( match, FC_FILE, path );
2585 GetFixedSizes( path, sizes );
2589 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2592 // Destroys the created patterns.
2593 FcPatternDestroy( match );
2594 FcPatternDestroy( fontFamilyPattern );
2597 bool FontClient::Plugin::HasItalicStyle( FontId fontId ) const
2599 bool hasItalicStyle = false;
2601 const FontId index = fontId - 1u;
2603 if( ( fontId > 0 ) &&
2604 ( index < mFontIdCache.Count() ) )
2606 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2608 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
2610 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2612 hasItalicStyle = 0u != ( font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC );
2617 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
2620 return hasItalicStyle;
2623 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path )
2625 FontDescription description;
2626 description.path = path;
2627 description.family = std::move( FontFamily( ftFace->family_name ) );
2628 description.weight = FontWeight::NONE;
2629 description.width = FontWidth::NONE;
2630 description.slant = FontSlant::NONE;
2632 // Note FreeType doesn't give too much info to build a proper font style.
2633 if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
2635 description.slant = FontSlant::ITALIC;
2637 if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
2639 description.weight = FontWeight::BOLD;
2642 FontDescriptionId validatedFontId = 0u;
2643 if( !FindValidatedFont( description,
2646 FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2648 FcResult result = FcResultMatch;
2649 FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2651 FcCharSet* characterSet = nullptr;
2652 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2654 const FontId fontFaceId = id - 1u;
2655 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( characterSet ); // Increases the reference counter.
2657 // Destroys the created patterns.
2658 FcPatternDestroy( match );
2659 FcPatternDestroy( pattern );
2661 // Add the path to the cache.
2662 description.type = FontDescription::FACE_FONT;
2663 mFontDescriptionCache.push_back( description );
2665 // Set the index to the vector of paths to font file names.
2666 validatedFontId = mFontDescriptionCache.size();
2668 // Increase the reference counter and add the character set to the cache.
2669 mCharacterSetCache.PushBack( FcCharSetCopy( characterSet ) );
2671 // Cache the index and the font's description.
2672 mValidatedFontCache.push_back( std::move( FontDescriptionCacheItem( std::move( description ),
2673 validatedFontId) ) );
2675 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2676 mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
2682 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description )
2684 FcCharSet* characterSet = nullptr;
2686 FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2688 if( nullptr != pattern )
2690 FcResult result = FcResultMatch;
2691 FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2693 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2695 // Destroys the created patterns.
2696 FcPatternDestroy( match );
2697 FcPatternDestroy( pattern );
2700 return characterSet;
2703 void FontClient::Plugin::ClearFallbackCache( std::vector<FallbackCacheItem>& fallbackCache )
2705 for( auto& item : fallbackCache )
2707 if( nullptr != item.fallbackFonts )
2709 delete item.fallbackFonts;
2712 if( nullptr != item.characterSets )
2714 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2715 DestroyCharacterSets( *item.characterSets );
2716 delete item.characterSets;
2721 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2723 for( auto& item : mFontFaceCache )
2725 FcCharSetDestroy( item.mCharacterSet );
2726 item.mCharacterSet = nullptr;
2730 } // namespace Internal
2732 } // namespace TextAbstraction