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( 1u ),
243 mCharacterSetCache(),
244 mFontDescriptionSizeCache(),
245 mVectorFontCache( nullptr ),
247 mEmbeddedItemCache(),
248 mDefaultFontDescriptionCached( false )
250 mCharacterSetCache.Resize( 1u );
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();
298 mFontDescriptionCache.resize( 1u );
300 DestroyCharacterSets( mCharacterSetCache );
301 mCharacterSetCache.Clear();
302 mCharacterSetCache.Resize( 1u );
304 mFontDescriptionSizeCache.clear();
306 mEllipsisCache.Clear();
307 mPixelBufferCache.clear();
308 mEmbeddedItemCache.Clear();
309 mBitmapFontCache.clear();
311 mDefaultFontDescriptionCached = false;
314 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
315 unsigned int verticalDpi )
317 mDpiHorizontal = horizontalDpi;
318 mDpiVertical = verticalDpi;
321 void FontClient::Plugin::ResetSystemDefaults()
323 mDefaultFontDescriptionCached = false;
326 void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList )
328 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n" );
329 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
330 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
331 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
332 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
336 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
338 FcResult result = FcResultMatch;
340 // Match the pattern.
341 FcFontSet* fontSet = FcFontSort( nullptr /* use default configure */,
343 false /* don't trim */,
345 &result ); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
347 if( nullptr != fontSet )
349 DALI_LOG_INFO( gLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont );
350 // Reserve some space to avoid reallocations.
351 fontList.reserve( fontSet->nfont );
353 for( int i = 0u; i < fontSet->nfont; ++i )
355 FcPattern* fontPattern = fontSet->fonts[i];
359 // Skip fonts with no path
360 if( GetFcString( fontPattern, FC_FILE, path ) )
362 // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
363 FcCharSet* characterSet = nullptr;
364 FcPatternGetCharSet( fontPattern, FC_CHARSET, 0u, &characterSet );
366 // Increase the reference counter of the character set.
367 characterSetList.PushBack( FcCharSetCopy( characterSet ) );
369 fontList.push_back( FontDescription() );
370 FontDescription& newFontDescription = fontList.back();
372 newFontDescription.path = std::move( path );
377 GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
378 GetFcInt( fontPattern, FC_WIDTH, width );
379 GetFcInt( fontPattern, FC_WEIGHT, weight );
380 GetFcInt( fontPattern, FC_SLANT, slant );
381 newFontDescription.width = IntToWidthType( width );
382 newFontDescription.weight = IntToWeightType( weight );
383 newFontDescription.slant = IntToSlantType( slant );
385 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", newFontDescription.family.c_str() );
386 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", newFontDescription.path.c_str() );
387 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[newFontDescription.width] );
388 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[newFontDescription.weight] );
389 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant] );
393 // Destroys the font set created by FcFontSort.
394 FcFontSetDestroy( fontSet );
398 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " No fonts found.\n" );
401 // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
402 FcPatternDestroy( fontFamilyPattern );
404 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n" );
407 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
409 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n" );
411 if( mDefaultFonts.empty() )
413 FontDescription fontDescription;
414 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
415 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
416 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
417 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
418 SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
421 defaultFonts = mDefaultFonts;
423 DALI_LOG_INFO( gLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size() );
424 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n" );
427 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
429 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
431 if( !mDefaultFontDescriptionCached )
433 // Clear any font config stored info in the caches.
435 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
436 DestroyCharacterSets( mDefaultFontCharacterSets );
437 DestroyCharacterSets( mCharacterSetCache );
438 mDefaultFontCharacterSets.Clear();
439 mCharacterSetCache.Clear();
441 for( auto& item : mFallbackCache )
443 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
444 DestroyCharacterSets( *item.characterSets );
446 delete item.characterSets;
447 item.characterSets = nullptr;
450 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
451 ClearCharacterSetFromFontFaceCache();
453 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
454 FcInitReinitialize();
456 FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
458 if( nullptr != matchPattern )
460 FcConfigSubstitute( nullptr, matchPattern, FcMatchPattern );
461 FcDefaultSubstitute( matchPattern );
463 FcCharSet* characterSet = nullptr;
464 MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription, &characterSet );
465 // Decrease the reference counter of the character set as it's not stored.
466 FcCharSetDestroy( characterSet );
468 // Destroys the pattern created.
469 FcPatternDestroy( matchPattern );
472 // Create again the character sets as they are not valid after FcInitReinitialize()
474 for( const auto& description : mDefaultFonts )
476 mDefaultFontCharacterSets.PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
479 for( const auto& description : mFontDescriptionCache )
481 mCharacterSetCache.PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
484 for( auto& item : mFallbackCache )
486 if( nullptr != item.fallbackFonts )
488 if( nullptr == item.characterSets )
490 item.characterSets = new CharacterSetList;
493 for( const auto& description : *( item.fallbackFonts ) )
495 item.characterSets->PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
500 mDefaultFontDescriptionCached = true;
503 fontDescription.path = mDefaultFontDescription.path;
504 fontDescription.family = mDefaultFontDescription.family;
505 fontDescription.width = mDefaultFontDescription.width;
506 fontDescription.weight = mDefaultFontDescription.weight;
507 fontDescription.slant = mDefaultFontDescription.slant;
509 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
510 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
511 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
512 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
513 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
514 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
517 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
519 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
521 if( mSystemFonts.empty() )
526 systemFonts = mSystemFonts;
527 DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size() );
528 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
531 void FontClient::Plugin::GetDescription( FontId id,
532 FontDescription& fontDescription ) const
534 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
535 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
536 const FontId index = id - 1u;
538 if( ( id > 0u ) && ( index < mFontIdCache.Count() ) )
540 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
541 switch( fontIdCacheItem.type )
543 case FontDescription::FACE_FONT:
545 for( const auto& item : mFontDescriptionSizeCache )
547 if( item.fontId == fontIdCacheItem.id )
549 fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
551 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
552 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
553 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
554 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
555 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
556 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
562 case FontDescription::BITMAP_FONT:
564 fontDescription.type = FontDescription::BITMAP_FONT;
565 fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
570 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
571 fontDescription.type = FontDescription::INVALID;
572 fontDescription.family.clear();
577 DALI_LOG_INFO( gLogFilter, Debug::General, " No description found for the font ID %d\n", id );
578 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
581 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
583 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
584 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
585 const FontId index = id - 1u;
588 ( index < mFontIdCache.Count() ) )
590 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
592 switch( fontIdCacheItem.type )
594 case FontDescription::FACE_FONT:
596 DALI_LOG_INFO( gLogFilter, Debug::General, " point size : %d\n", ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize );
597 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
598 return ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize;
600 case FontDescription::BITMAP_FONT:
602 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
606 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
612 DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font ID %d\n", id );
615 DALI_LOG_INFO( gLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE );
616 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
617 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
620 bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character character )
622 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
623 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
624 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character );
626 if( ( fontId < 1u ) || ( fontId > mFontIdCache.Count() ) )
628 DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n",mFontIdCache.Count());
629 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
635 bool isSupported = false;
637 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
639 switch( fontIdCacheItem.type )
641 case FontDescription::FACE_FONT:
643 if( fontIdCacheItem.id < mFontFaceCache.size() )
645 FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
647 if( nullptr == cacheItem.mCharacterSet )
649 // Create again the character set.
650 // It can be null if the ResetSystemDefaults() method has been called.
652 FontDescription description;
653 description.path = cacheItem.mPath;
654 description.family = std::move( FontFamily( cacheItem.mFreeTypeFace->family_name ) );
655 description.weight = FontWeight::NONE;
656 description.width = FontWidth::NONE;
657 description.slant = FontSlant::NONE;
659 // Note FreeType doesn't give too much info to build a proper font style.
660 if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC )
662 description.slant = FontSlant::ITALIC;
664 if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD )
666 description.weight = FontWeight::BOLD;
669 cacheItem.mCharacterSet = FcCharSetCopy( CreateCharacterSetFromDescription( description ) );
672 isSupported = FcCharSetHasChar( cacheItem.mCharacterSet, character );
676 case FontDescription::BITMAP_FONT:
678 const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
680 for( const auto& glyph : bitmapFont.glyphs )
682 if( glyph.utf32 == character )
692 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
696 DALI_LOG_INFO( gLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false") );
697 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
701 FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
702 const CharacterSetList& characterSetList,
704 PointSize26Dot6 requestedPointSize,
707 DALI_ASSERT_DEBUG( ( fontList.size() == characterSetList.Count() ) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets." );
709 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n" );
710 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character );
711 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
712 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
715 bool foundColor = false;
717 DALI_LOG_INFO( gLogFilter, Debug::General, " number of fonts : %d\n", fontList.size() );
719 // Traverse the list of fonts.
720 // Check for each font if supports the character.
721 for( unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index )
723 const FontDescription& description = fontList[index];
724 const FcCharSet* const characterSet = characterSetList[index];
726 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", description.family.c_str() );
727 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str() );
728 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width] );
729 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight] );
730 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant] );
732 bool foundInRanges = false;
733 if( nullptr != characterSet )
735 foundInRanges = FcCharSetHasChar( characterSet, character );
740 fontId = GetFontId( description,
744 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " font id : %d\n", fontId );
748 if( ( fontId > 0 ) &&
749 ( fontId - 1u < mFontIdCache.Count() ) )
751 const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
753 foundColor = item.mHasColorTables;
756 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " foundColor : %s\n", ( foundColor ? "true" : "false" ) );
759 // Keep going unless we prefer a different (color) font.
760 if( !preferColor || foundColor )
767 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
768 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n" );
772 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
773 PointSize26Dot6 requestedPointSize,
776 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n" );
777 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", charcode );
778 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
779 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
783 // Create the list of default fonts if it has not been created.
784 if( mDefaultFonts.empty() )
786 FontDescription fontDescription;
787 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
788 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
789 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
790 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
792 SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
794 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size() );
797 // Traverse the list of default fonts.
798 // Check for each default font if supports the character.
799 fontId = FindFontForCharacter( mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor );
801 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
802 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n" );
807 FontId FontClient::Plugin::FindFallbackFont( Character charcode,
808 const FontDescription& preferredFontDescription,
809 PointSize26Dot6 requestedPointSize,
812 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n" );
813 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", charcode );
814 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
815 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
817 // The font id to be returned.
820 FontDescription fontDescription;
822 // Fill the font description with the preferred font description and complete with the defaults.
823 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
824 fontDescription.weight = ( ( FontWeight::NONE == preferredFontDescription.weight ) ? IntToWeightType( DEFAULT_FONT_WEIGHT ) : preferredFontDescription.weight );
825 fontDescription.width = ( ( FontWidth::NONE == preferredFontDescription.width ) ? IntToWidthType( DEFAULT_FONT_WIDTH ) : preferredFontDescription.width );
826 fontDescription.slant = ( ( FontSlant::NONE == preferredFontDescription.slant ) ? IntToSlantType( DEFAULT_FONT_SLANT ) : preferredFontDescription.slant );
828 DALI_LOG_INFO( gLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n" );
829 DALI_LOG_INFO( gLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str() );
830 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight] );
831 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width] );
832 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant] );
834 // Check first if the font's description has been queried before.
835 FontList* fontList = nullptr;
836 CharacterSetList* characterSetList = nullptr;
838 if( !FindFallbackFontList( fontDescription, fontList, characterSetList ) )
840 fontList = new FontList;
841 characterSetList = new CharacterSetList;
843 SetFontList( fontDescription, *fontList, *characterSetList );
845 // Add the font-list to the cache.
846 mFallbackCache.push_back( std::move( FallbackCacheItem( std::move( fontDescription ), fontList, characterSetList ) ) );
849 if( fontList && characterSetList )
851 fontId = FindFontForCharacter( *fontList, *characterSetList, charcode, requestedPointSize, preferColor );
854 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
855 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
859 FontId FontClient::Plugin::GetFontId( const FontPath& path,
860 PointSize26Dot6 requestedPointSize,
862 bool cacheDescription )
864 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
865 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
866 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
870 if( nullptr != mFreeTypeLibrary )
873 if( FindFont( path, requestedPointSize, faceIndex, foundId ) )
879 id = CreateFont( path, requestedPointSize, faceIndex, cacheDescription );
883 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
884 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
889 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
890 PointSize26Dot6 requestedPointSize,
891 FaceIndex faceIndex )
893 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
894 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
895 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
896 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
897 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
898 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
899 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
901 // This method uses three vectors which caches:
902 // * The bitmap font cache
903 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
904 // * The path to font file names.
905 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
907 // 1) Checks if the font description matches with a previously loaded bitmap font.
908 // Returns if a font is found.
909 // 2) Checks in the cache if the font's description has been validated before.
910 // If it was it gets an index to the vector with paths to font file names. Otherwise,
911 // retrieves using font config a path to a font file name which matches with the
912 // font's description. The path is stored in the cache.
914 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
915 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
916 // the GetFontId() method with the path to the font file name and the point size to
919 // The font id to be returned.
922 // Check first if the font description matches with a previously loaded bitmap font.
923 if( FindBitmapFont( fontDescription.family, fontId ) )
928 // Check if the font's description have been validated before.
929 FontDescriptionId validatedFontId = 0u;
931 if( !FindValidatedFont( fontDescription,
934 // Use font config to validate the font's description.
935 ValidateFont( fontDescription,
939 FontId fontFaceId = 0u;
940 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
941 if( !FindFont( validatedFontId, requestedPointSize, fontFaceId ) )
943 // Retrieve the font file name path.
944 const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
946 // Retrieve the font id. Do not cache the description as it has been already cached.
947 fontId = GetFontId( description.path,
952 fontFaceId = mFontIdCache[fontId-1u].id;
953 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( mCharacterSetCache[validatedFontId] );
955 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
956 mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
962 fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
965 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
966 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
971 FontId FontClient::Plugin::GetFontId( const BitmapFont& bitmapFont )
973 for( const auto& item : mBitmapFontCache )
975 if( bitmapFont.name == item.font.name )
981 BitmapFontCacheItem bitmapFontCacheItem;
982 bitmapFontCacheItem.font = bitmapFont;
983 bitmapFontCacheItem.id = mFontIdCache.Count();
985 // Resize the vector with the pixel buffers.
986 bitmapFontCacheItem.pixelBuffers.resize( bitmapFont.glyphs.size() );
988 // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
989 unsigned int index = 0u;
990 for( auto& glyph : bitmapFontCacheItem.font.glyphs )
992 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
994 if( EqualsZero( glyph.ascender ) && EqualsZero( glyph.descender ) )
997 pixelBuffer = LoadImageFromFile( glyph.url );
1001 glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1005 bitmapFontCacheItem.font.ascender = std::max( glyph.ascender, bitmapFontCacheItem.font.ascender );
1006 bitmapFontCacheItem.font.descender = std::min( glyph.descender, bitmapFontCacheItem.font.descender );
1011 FontIdCacheItem fontIdCacheItem;
1012 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1013 fontIdCacheItem.id = mBitmapFontCache.size();
1015 mBitmapFontCache.push_back( std::move( bitmapFontCacheItem ) );
1016 mFontIdCache.PushBack( fontIdCacheItem );
1018 return bitmapFontCacheItem.id + 1u;
1021 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
1022 FontDescriptionId& validatedFontId )
1024 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n" );
1025 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
1026 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1027 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1028 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1029 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1031 // Create a font pattern.
1032 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1034 FontDescription description;
1036 FcCharSet* characterSet = nullptr;
1037 bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description, &characterSet );
1038 FcPatternDestroy( fontFamilyPattern );
1040 if( matched && ( nullptr != characterSet ) )
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 // Add the path to the cache.
1053 description.type = FontDescription::FACE_FONT;
1054 mFontDescriptionCache.push_back( description );
1056 // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1057 mCharacterSetCache.PushBack( characterSet );
1059 // Cache the index and the matched font's description.
1060 FontDescriptionCacheItem item( description,
1063 mValidatedFontCache.push_back( std::move( item ) );
1065 if( ( fontDescription.family != description.family ) ||
1066 ( fontDescription.width != description.width ) ||
1067 ( fontDescription.weight != description.weight ) ||
1068 ( fontDescription.slant != description.slant ) )
1070 // Cache the given font's description if it's different than the matched.
1071 FontDescriptionCacheItem item( fontDescription,
1074 mValidatedFontCache.push_back( std::move( item ) );
1079 DALI_LOG_INFO( gLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str() );
1082 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n" );
1085 void FontClient::Plugin::GetFontMetrics( FontId fontId,
1086 FontMetrics& metrics )
1088 const FontId index = fontId - 1u;
1090 if( ( fontId > 0 ) &&
1091 ( index < mFontIdCache.Count() ) )
1093 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1095 switch( fontIdCacheItem.type )
1097 case FontDescription::FACE_FONT:
1099 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1101 metrics = font.mMetrics;
1103 // Adjust the metrics if the fixed-size font should be down-scaled
1104 if( font.mIsFixedSizeBitmap )
1106 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1108 if( desiredFixedSize > 0.f )
1110 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1112 metrics.ascender = metrics.ascender * scaleFactor;
1113 metrics.descender = metrics.descender * scaleFactor;
1114 metrics.height = metrics.height * scaleFactor;
1115 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1116 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1121 case FontDescription::BITMAP_FONT:
1123 const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1125 metrics.ascender = bitmapFontCacheItem.font.ascender;
1126 metrics.descender = bitmapFontCacheItem.font.descender;
1127 metrics.height = metrics.ascender - metrics.descender;
1128 metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1129 metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1134 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1140 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
1144 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
1145 Character charcode )
1147 GlyphIndex glyphIndex = 0u;
1148 const FontId index = fontId - 1u;
1150 if( ( fontId > 0u ) &&
1151 ( index < mFontIdCache.Count() ) )
1153 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1155 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1157 FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1159 glyphIndex = FT_Get_Char_Index( ftFace, charcode );
1166 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
1171 if( VECTOR_GLYPH == type )
1173 return GetVectorMetrics( array, size, horizontal );
1176 return GetBitmapMetrics( array, size, horizontal );
1179 bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
1183 bool success( true );
1185 for( unsigned int i=0; i<size; ++i )
1187 GlyphInfo& glyph = array[i];
1189 FontId index = glyph.fontId - 1u;
1191 if( ( glyph.fontId > 0u ) &&
1192 ( index < mFontIdCache.Count() ) )
1194 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1196 switch( fontIdCacheItem.type )
1198 case FontDescription::FACE_FONT:
1200 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1202 FT_Face ftFace = font.mFreeTypeFace;
1204 #ifdef FREETYPE_BITMAP_SUPPORT
1205 // Check to see if we should be loading a Fixed Size bitmap?
1206 if( font.mIsFixedSizeBitmap )
1208 FT_Select_Size( ftFace, font.mFixedSizeIndex ); ///< @todo: needs to be investigated why it's needed to select the size again.
1209 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR );
1210 if ( FT_Err_Ok == error )
1212 glyph.width = font.mFixedWidthPixels;
1213 glyph.height = font.mFixedHeightPixels;
1214 glyph.advance = font.mFixedWidthPixels;
1215 glyph.xBearing = 0.0f;
1216 glyph.yBearing = font.mFixedHeightPixels;
1218 // Adjust the metrics if the fixed-size font should be down-scaled
1219 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1221 if( desiredFixedSize > 0.f )
1223 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1225 glyph.width = glyph.width * scaleFactor ;
1226 glyph.height = glyph.height * scaleFactor;
1227 glyph.advance = glyph.advance * scaleFactor;
1228 glyph.xBearing = glyph.xBearing * scaleFactor;
1229 glyph.yBearing = glyph.yBearing * scaleFactor;
1231 glyph.scaleFactor = scaleFactor;
1236 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error );
1243 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1244 // i.e. with the SNum-3R font.
1245 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1246 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT );
1248 // Keep the width of the glyph before doing the software emboldening.
1249 // It will be used to calculate a scale factor to be applied to the
1250 // advance as Harfbuzz doesn't apply any SW emboldening to calculate
1251 // the advance of the glyph.
1252 const float width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1254 if( FT_Err_Ok == error )
1256 const bool isEmboldeningRequired = glyph.isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD );
1257 if( isEmboldeningRequired )
1259 // Does the software bold.
1260 FT_GlyphSlot_Embolden( ftFace->glyph );
1263 glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1264 glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266;
1267 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
1268 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
1272 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
1273 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
1276 if( isEmboldeningRequired && !Dali::EqualsZero( width ) )
1278 // If the glyph is emboldened by software, the advance is multiplied by a
1279 // scale factor to make it slightly bigger.
1280 glyph.advance *= ( glyph.width / width );
1283 // Use the bounding box of the bitmap to correct the metrics.
1284 // For some fonts i.e the SNum-3R the metrics need to be corrected,
1285 // otherwise the glyphs 'dance' up and down depending on the
1286 // font's point size.
1289 error = FT_Get_Glyph( ftFace->glyph, &ftGlyph );
1292 FT_Glyph_Get_CBox( ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox );
1294 const float descender = glyph.height - glyph.yBearing;
1295 glyph.height = ( bbox.yMax - bbox.yMin) * FROM_266;
1296 glyph.yBearing = glyph.height - std::round( descender );
1298 // Created FT_Glyph object must be released with FT_Done_Glyph
1299 FT_Done_Glyph( ftGlyph );
1308 case FontDescription::BITMAP_FONT:
1310 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1312 unsigned int index = 0u;
1313 for( auto& item : bitmapFontCacheItem.font.glyphs )
1315 if( item.utf32 == glyph.index )
1317 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1320 pixelBuffer = LoadImageFromFile( item.url );
1323 glyph.width = static_cast< float >( pixelBuffer.GetWidth() );
1324 glyph.height = static_cast< float >( pixelBuffer.GetHeight() );
1325 glyph.xBearing = 0.f;
1326 glyph.yBearing = glyph.height + item.descender;
1327 glyph.advance = glyph.width;
1328 glyph.scaleFactor = 1.f;
1339 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1345 // Check if it's an embedded image.
1346 if( ( 0u == glyph.fontId ) && ( 0u != glyph.index ) && ( glyph.index <= mEmbeddedItemCache.Count() ) )
1348 const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1350 glyph.width = static_cast<float>( item.width );
1351 glyph.height = static_cast<float>( item.height );
1352 glyph.xBearing = 0.f;
1353 glyph.yBearing = glyph.height;
1354 glyph.advance = glyph.width;
1355 glyph.scaleFactor = 1.f;
1367 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
1371 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1372 bool success( true );
1374 for( unsigned int i = 0u; i < size; ++i )
1376 FontId fontId = array[i].fontId;
1378 if( ( fontId > 0u ) &&
1379 ( fontId - 1u ) < mFontIdCache.Count() )
1381 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1383 if( ! font.mVectorFontId )
1385 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1388 mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
1390 // Vector metrics are in EMs, convert to pixels
1391 const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
1392 array[i].width *= scale;
1393 array[i].height *= scale;
1394 array[i].xBearing *= scale;
1395 array[i].yBearing *= scale;
1396 array[i].advance *= scale;
1410 void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth )
1412 const FontId index = fontId - 1u;
1414 if( ( fontId > 0u ) &&
1415 ( index < mFontIdCache.Count() ) )
1417 data.isColorBitmap = false;
1418 data.isColorEmoji = false;
1420 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1422 switch( fontIdCacheItem.type )
1424 case FontDescription::FACE_FONT:
1426 // For the software italics.
1427 bool isShearRequired = false;
1429 const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1430 FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1434 #ifdef FREETYPE_BITMAP_SUPPORT
1435 // Check to see if this is fixed size bitmap
1436 if( fontFaceCacheItem.mIsFixedSizeBitmap )
1438 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1443 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
1444 // i.e. with the SNum-3R font.
1445 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
1446 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT );
1448 if( FT_Err_Ok == error )
1450 if( isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) )
1452 // Does the software bold.
1453 FT_GlyphSlot_Embolden( ftFace->glyph );
1456 if( isItalicRequired && !( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) )
1458 // Will do the software italic.
1459 isShearRequired = true;
1463 error = FT_Get_Glyph( ftFace->glyph, &glyph );
1465 // Convert to bitmap if necessary
1466 if( FT_Err_Ok == error )
1468 if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
1470 // Check whether we should create a bitmap for the outline
1471 if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 )
1475 error = FT_Stroker_New( mFreeTypeLibrary, &stroker );
1477 if( FT_Err_Ok == error )
1479 FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 );
1480 error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 );
1482 if( FT_Err_Ok == error )
1484 FT_Stroker_Done( stroker );
1488 DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error );
1493 DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error );
1497 error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
1498 if( FT_Err_Ok == error )
1500 FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph );
1501 ConvertBitmap( data, bitmapGlyph->bitmap, isShearRequired );
1505 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error );
1510 ConvertBitmap( data, ftFace->glyph->bitmap, isShearRequired );
1513 data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1515 // Created FT_Glyph object must be released with FT_Done_Glyph
1516 FT_Done_Glyph( glyph );
1521 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error );
1525 case FontDescription::BITMAP_FONT:
1527 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1529 unsigned int index = 0u;
1530 for( auto& item : bitmapFontCacheItem.font.glyphs )
1532 if( item.utf32 == glyphIndex )
1534 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1537 pixelBuffer = LoadImageFromFile( item.url );
1540 data.width = pixelBuffer.GetWidth();
1541 data.height = pixelBuffer.GetHeight();
1543 data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1545 ConvertBitmap( data, data.width, data.height, pixelBuffer.GetBuffer() );
1547 // Sets the pixel format.
1548 data.format = pixelBuffer.GetPixelFormat();
1557 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1563 if( ( 0u != glyphIndex ) && ( glyphIndex <= mEmbeddedItemCache.Count() ) )
1565 // It's an embedded item.
1566 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1568 data.width = item.width;
1569 data.height = item.height;
1570 if( 0u != item.pixelBufferId )
1572 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId-1u].pixelBuffer;
1575 ConvertBitmap( data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer() );
1577 // Sets the pixel format.
1578 data.format = pixelBuffer.GetPixelFormat();
1583 // Creates the output buffer
1584 const unsigned int bufferSize = data.width * data.height * 4u;
1585 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1587 memset( data.buffer, 0u, bufferSize );
1589 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1595 PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
1597 TextAbstraction::FontClient::GlyphBufferData data;
1599 CreateBitmap( fontId, glyphIndex, false, false, data, outlineWidth );
1601 return PixelData::New( data.buffer,
1602 data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
1606 PixelData::DELETE_ARRAY );
1609 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
1614 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1615 if( ( fontId > 0u ) &&
1616 ( fontId - 1u < mFontIdCache.Count() ) )
1618 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1619 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1621 if( ! font.mVectorFontId )
1623 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1626 mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
1631 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
1633 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" );
1634 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize );
1636 // First look into the cache if there is an ellipsis glyph for the requested point size.
1637 for( const auto& item : mEllipsisCache )
1639 if( item.requestedPointSize != requestedPointSize )
1641 // Use the glyph in the cache.
1643 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1644 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1645 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1651 // No glyph has been found. Create one.
1652 mEllipsisCache.PushBack( EllipsisItem() );
1653 EllipsisItem& item = *( mEllipsisCache.End() - 1u );
1655 item.requestedPointSize = requestedPointSize;
1657 // Find a font for the ellipsis glyph.
1658 item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
1662 // Set the character index to access the glyph inside the font.
1663 item.glyph.index = FT_Get_Char_Index( mFontFaceCache[mFontIdCache[item.glyph.fontId-1u].id].mFreeTypeFace,
1664 ELLIPSIS_CHARACTER );
1666 GetBitmapMetrics( &item.glyph, 1u, true );
1668 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1669 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1670 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1675 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1677 FT_Error error = -1;
1679 const FontId index = fontId - 1u;
1681 if( ( fontId > 0u ) &&
1682 ( index < mFontIdCache.Count() ) )
1684 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1686 switch( fontIdCacheItem.type )
1688 case FontDescription::FACE_FONT:
1690 #ifdef FREETYPE_BITMAP_SUPPORT
1691 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1692 FT_Face ftFace = item.mFreeTypeFace;
1694 // Check to see if this is fixed size bitmap
1695 if( item.mHasColorTables )
1697 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1702 case FontDescription::BITMAP_FONT:
1704 error = FT_Err_Ok; // Will return true;
1709 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1714 return FT_Err_Ok == error;
1717 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId )
1719 FT_Face fontFace = nullptr;
1721 const FontId index = fontId - 1u;
1722 if( ( fontId > 0u ) &&
1723 ( index < mFontIdCache.Count() ) )
1725 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1727 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1729 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1735 FontDescription::Type FontClient::Plugin::GetFontType( FontId fontId )
1737 const FontId index = fontId - 1u;
1738 if( ( fontId > 0u ) &&
1739 ( index < mFontIdCache.Count() ) )
1741 return mFontIdCache[index].type;
1743 return FontDescription::INVALID;
1746 bool FontClient::Plugin::AddCustomFontDirectory( const FontPath& path )
1748 // nullptr as first parameter means the current configuration is used.
1749 return FcConfigAppFontAddDir( nullptr, reinterpret_cast<const FcChar8 *>( path.c_str() ) );
1752 GlyphIndex FontClient::Plugin::CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat )
1754 EmbeddedItem embeddedItem;
1756 embeddedItem.pixelBufferId = 0u;
1757 embeddedItem.width = description.width;
1758 embeddedItem.height = description.height;
1760 pixelFormat = Pixel::A8;
1762 if( !description.url.empty() )
1764 // Check if the url is in the cache.
1765 PixelBufferId index = 0u;
1767 for( const auto& cacheItem : mPixelBufferCache )
1770 if( cacheItem.url == description.url )
1772 // The url is in the pixel buffer cache.
1773 // Set the index +1 to the vector.
1774 embeddedItem.pixelBufferId = index;
1779 Devel::PixelBuffer pixelBuffer;
1780 if( 0u == embeddedItem.pixelBufferId )
1782 // The pixel buffer is not in the cache. Create one and cache it.
1784 // Load the image from the url.
1785 pixelBuffer = LoadImageFromFile( description.url );
1787 // Create the cache item.
1788 PixelBufferCacheItem pixelBufferCacheItem;
1789 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1790 pixelBufferCacheItem.url = description.url;
1792 // Store the cache item in the cache.
1793 mPixelBufferCache.push_back( std::move( pixelBufferCacheItem ) );
1795 // Set the pixel buffer id to the embedded item.
1796 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1800 // Retrieve the pixel buffer from the cache to set the pixel format.
1801 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId-1u].pixelBuffer;
1806 // Set the size of the embedded item if it has not been set.
1807 if( 0u == embeddedItem.width )
1809 embeddedItem.width = static_cast<unsigned int>( pixelBuffer.GetWidth() );
1812 if( 0u == embeddedItem.height )
1814 embeddedItem.height = static_cast<unsigned int>( pixelBuffer.GetHeight() );
1817 // Set the pixel format.
1818 pixelFormat = pixelBuffer.GetPixelFormat();
1822 // Find if the same embeddedItem has already been created.
1823 unsigned int index = 0u;
1824 for( const auto& item : mEmbeddedItemCache )
1827 if( ( item.pixelBufferId == embeddedItem.pixelBufferId ) &&
1828 ( item.width == embeddedItem.width ) &&
1829 ( item.height == embeddedItem.height ) )
1835 // Cache the embedded item.
1836 mEmbeddedItemCache.PushBack( embeddedItem );
1838 return mEmbeddedItemCache.Count();
1841 void FontClient::Plugin::InitSystemFonts()
1843 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1845 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1849 DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont );
1851 // Reserve some space to avoid reallocations.
1852 mSystemFonts.reserve( fontSet->nfont );
1854 for( int i = 0u; i < fontSet->nfont; ++i )
1856 FcPattern* fontPattern = fontSet->fonts[i];
1860 // Skip fonts with no path
1861 if( GetFcString( fontPattern, FC_FILE, path ) )
1863 mSystemFonts.push_back( FontDescription() );
1864 FontDescription& fontDescription = mSystemFonts.back();
1866 fontDescription.path = std::move( path );
1871 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1872 GetFcInt( fontPattern, FC_WIDTH, width );
1873 GetFcInt( fontPattern, FC_WEIGHT, weight );
1874 GetFcInt( fontPattern, FC_SLANT, slant );
1875 fontDescription.width = IntToWidthType( width );
1876 fontDescription.weight = IntToWeightType( weight );
1877 fontDescription.slant = IntToSlantType( slant );
1879 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str() );
1880 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1881 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1882 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1883 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1887 // Destroys the font set created.
1888 FcFontSetDestroy( fontSet );
1890 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1893 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1895 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1897 FcResult result = FcResultMatch;
1898 FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result ); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1900 const bool matched = nullptr != match;
1901 DALI_LOG_INFO( gLogFilter, Debug::General, " pattern matched : %s\n", ( matched ? "true" : "false" ) );
1908 GetFcString( match, FC_FILE, fontDescription.path );
1909 GetFcString( match, FC_FAMILY, fontDescription.family );
1910 GetFcInt( match, FC_WIDTH, width );
1911 GetFcInt( match, FC_WEIGHT, weight );
1912 GetFcInt( match, FC_SLANT, slant );
1913 fontDescription.width = IntToWidthType( width );
1914 fontDescription.weight = IntToWeightType( weight );
1915 fontDescription.slant = IntToSlantType( slant );
1917 // Retrieve the character set and increase the reference counter.
1918 FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1919 *characterSet = FcCharSetCopy( *characterSet );
1921 // destroyed the matched pattern
1922 FcPatternDestroy( match );
1924 DALI_LOG_INFO( gLogFilter, Debug::General, " 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] );
1931 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1935 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1937 // create the cached font family lookup pattern
1938 // a pattern holds a set of names, each name refers to a property of the font
1939 FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1941 if( !fontFamilyPattern )
1946 // add a property to the pattern for the font family
1947 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1949 // add a property to the pattern for local setting.
1950 const char* locale = setlocale( LC_MESSAGES, nullptr );
1951 if( locale != nullptr)
1953 FcPatternAddString( fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>( locale ) );
1956 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1960 width = DEFAULT_FONT_WIDTH;
1963 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1967 weight = DEFAULT_FONT_WEIGHT;
1970 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1974 slant = DEFAULT_FONT_SLANT;
1977 FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1978 FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1979 FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1981 // modify the config, with the mFontFamilyPatterm
1982 FcConfigSubstitute( nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern );
1984 // provide default values for unspecified properties in the font pattern
1985 // e.g. patterns without a specified style or weight are set to Medium
1986 FcDefaultSubstitute( fontFamilyPattern );
1988 return fontFamilyPattern;
1991 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1993 FcFontSet* fontset = nullptr;
1995 // create a new pattern.
1996 // a pattern holds a set of names, each name refers to a property of the font
1997 FcPattern* pattern = FcPatternCreate();
1999 if( nullptr != pattern )
2001 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
2002 FcObjectSet* objectSet = FcObjectSetCreate();
2004 if( nullptr != objectSet )
2006 // build an object set from a list of property names
2007 FcObjectSetAdd( objectSet, FC_FILE );
2008 FcObjectSetAdd( objectSet, FC_FAMILY );
2009 FcObjectSetAdd( objectSet, FC_WIDTH );
2010 FcObjectSetAdd( objectSet, FC_WEIGHT );
2011 FcObjectSetAdd( objectSet, FC_SLANT );
2013 // get a list of fonts
2014 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
2015 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.
2017 // clear up the object set
2018 FcObjectSetDestroy( objectSet );
2021 // clear up the pattern
2022 FcPatternDestroy( pattern );
2028 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
2029 const char* const n,
2030 std::string& string )
2032 FcChar8* file = nullptr;
2033 const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
2035 if( FcResultMatch == retVal )
2037 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
2038 string.assign( reinterpret_cast<const char*>( file ) );
2046 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
2048 const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
2050 if( FcResultMatch == retVal )
2058 FontId FontClient::Plugin::CreateFont( const FontPath& path,
2059 PointSize26Dot6 requestedPointSize,
2060 FaceIndex faceIndex,
2061 bool cacheDescription )
2063 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" );
2064 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2065 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2069 // Create & cache new font face
2071 int error = FT_New_Face( mFreeTypeLibrary,
2076 if( FT_Err_Ok == error )
2078 // Check if a font is scalable.
2079 const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) );
2080 const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes );
2081 const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) );
2082 FontId fontFaceId = 0u;
2084 DALI_LOG_INFO( gLogFilter, Debug::General, " isScalable : [%s]\n", ( isScalable ? "true" : "false" ) );
2085 DALI_LOG_INFO( gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) );
2086 DALI_LOG_INFO( gLogFilter, Debug::General, " hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) );
2088 // Check to see if the font contains fixed sizes?
2089 if( !isScalable && hasFixedSizedBitmaps )
2091 PointSize26Dot6 actualPointSize = 0u;
2092 int fixedSizeIndex = 0;
2093 for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex )
2095 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2096 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize );
2098 if( fixedSize >= requestedPointSize )
2100 actualPointSize = fixedSize;
2105 if( 0u == actualPointSize )
2107 // The requested point size is bigger than the bigest fixed size.
2108 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2109 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2112 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize );
2114 // Tell Freetype to use this size
2115 error = FT_Select_Size( ftFace, fixedSizeIndex );
2116 if ( FT_Err_Ok != error )
2118 DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error );
2122 const float fixedWidth = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].width );
2123 const float fixedHeight = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].height );
2125 // Indicate that the font is a fixed sized bitmap
2126 FontMetrics metrics( fixedHeight, // The ascender in pixels.
2128 fixedHeight, // The height in pixels.
2132 // Create the FreeType font face item to cache.
2133 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables );
2135 // Set the index to the font's id cache.
2136 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2138 // Create the font id item to cache.
2139 FontIdCacheItem fontIdCacheItem;
2140 fontIdCacheItem.type = FontDescription::FACE_FONT;
2142 // Set the index to the FreeType font face cache.
2143 fontIdCacheItem.id = mFontFaceCache.size();
2144 fontFaceId = fontIdCacheItem.id + 1u;
2147 mFontFaceCache.push_back( fontFaceCacheItem );
2148 mFontIdCache.PushBack( fontIdCacheItem );
2150 // Set the font id to be returned.
2151 id = mFontIdCache.Count();
2156 error = FT_Set_Char_Size( ftFace,
2162 if( FT_Err_Ok == error )
2165 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2167 FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266,
2168 static_cast< float >( ftMetrics.descender ) * FROM_266,
2169 static_cast< float >( ftMetrics.height ) * FROM_266,
2170 static_cast< float >( ftFace->underline_position ) * FROM_266,
2171 static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
2173 // Create the FreeType font face item to cache.
2174 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics );
2176 // Set the index to the font's id cache.
2177 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2179 // Create the font id item to cache.
2180 FontIdCacheItem fontIdCacheItem;
2181 fontIdCacheItem.type = FontDescription::FACE_FONT;
2183 // Set the index to the FreeType font face cache.
2184 fontIdCacheItem.id = mFontFaceCache.size();
2185 fontFaceId = fontIdCacheItem.id + 1u;
2188 mFontFaceCache.push_back( fontFaceCacheItem );
2189 mFontIdCache.PushBack( fontIdCacheItem );
2191 // Set the font id to be returned.
2192 id = mFontIdCache.Count();
2196 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize );
2200 if( 0u != fontFaceId )
2202 if( cacheDescription )
2204 CacheFontPath( ftFace, fontFaceId, requestedPointSize, path );
2210 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str() );
2213 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
2214 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" );
2219 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer )
2221 // Set the input dimensions.
2222 const ImageDimensions inputDimensions( srcWidth, srcHeight );
2224 // Set the output dimensions.
2225 // If the output dimension is not given, the input dimension is set
2226 // and won't be downscaling.
2227 data.width = ( data.width == 0 ) ? srcWidth : data.width;
2228 data.height = ( data.height == 0 ) ? srcHeight : data.height;
2229 const ImageDimensions desiredDimensions( data.width, data.height );
2231 // Creates the output buffer
2232 const unsigned int bufferSize = data.width * data.height * 4u;
2233 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2235 if( inputDimensions == desiredDimensions )
2237 // There isn't downscaling.
2238 memcpy( data.buffer, srcBuffer, bufferSize );
2242 Dali::Internal::Platform::LanczosSample4BPP( srcBuffer,
2245 desiredDimensions );
2249 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired )
2251 if( srcBitmap.width*srcBitmap.rows > 0 )
2253 switch( srcBitmap.pixel_mode )
2255 case FT_PIXEL_MODE_GRAY:
2257 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
2259 uint8_t* pixelsIn = srcBitmap.buffer;
2260 unsigned int width = srcBitmap.width;
2261 unsigned height = srcBitmap.rows;
2263 std::unique_ptr<uint8_t, void(*)(void*)> pixelsOutPtr( nullptr, free );
2265 if( isShearRequired )
2268 * Glyphs' bitmaps with no slant retrieved from FreeType:
2278 * Expected glyphs' bitmaps with italic slant:
2279 * ____________ ______
2286 * ------------ ------
2288 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2298 * 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.
2300 unsigned int widthOut = 0u;
2301 unsigned int heightOut = 0u;
2302 uint8_t* pixelsOut = nullptr;
2304 Dali::Internal::Platform::HorizontalShear( pixelsIn,
2308 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2315 pixelsIn = pixelsOut;
2316 pixelsOutPtr.reset( pixelsOut );
2319 const unsigned int bufferSize = width * height;
2320 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2322 data.height = height;
2323 data.format = Pixel::L8; // Sets the pixel format.
2324 memcpy( data.buffer, pixelsIn, bufferSize );
2329 #ifdef FREETYPE_BITMAP_SUPPORT
2330 case FT_PIXEL_MODE_BGRA:
2332 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
2334 ConvertBitmap( data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer );
2336 // Sets the pixel format.
2337 data.format = Pixel::BGRA8888;
2344 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" );
2351 bool FontClient::Plugin::FindFont( const FontPath& path,
2352 PointSize26Dot6 requestedPointSize,
2353 FaceIndex faceIndex,
2354 FontId& fontId ) const
2356 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2357 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2358 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2359 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size() );
2362 for( const auto& cacheItem : mFontFaceCache )
2364 if( cacheItem.mRequestedPointSize == requestedPointSize &&
2365 cacheItem.mFaceIndex == faceIndex &&
2366 cacheItem.mPath == path )
2368 fontId = cacheItem.mFontId + 1u;
2370 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2371 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2377 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found\n" );
2378 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2383 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
2384 FontDescriptionId& validatedFontId )
2386 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" );
2387 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2388 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2389 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2390 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2391 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2392 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size() );
2394 validatedFontId = 0u;
2396 for( const auto& item : mValidatedFontCache )
2398 if( !fontDescription.family.empty() &&
2399 ( fontDescription.family == item.fontDescription.family ) &&
2400 ( fontDescription.width == item.fontDescription.width ) &&
2401 ( fontDescription.weight == item.fontDescription.weight ) &&
2402 ( fontDescription.slant == item.fontDescription.slant ) )
2404 validatedFontId = item.index;
2406 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId );
2407 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2412 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font not found\n" );
2413 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2417 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
2418 FontList*& fontList,
2419 CharacterSetList*& characterSetList )
2421 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" );
2422 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2423 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2424 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2425 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2426 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2427 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size() );
2431 for( const auto& item : mFallbackCache )
2433 if( !fontDescription.family.empty() &&
2434 ( fontDescription.family == item.fontDescription.family ) &&
2435 ( fontDescription.width == item.fontDescription.width ) &&
2436 ( fontDescription.weight == item.fontDescription.weight ) &&
2437 ( fontDescription.slant == item.fontDescription.slant ) )
2439 fontList = item.fallbackFonts;
2440 characterSetList = item.characterSets;
2442 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list found.\n" );
2443 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2448 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list not found.\n" );
2449 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2453 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
2454 PointSize26Dot6 requestedPointSize,
2457 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2458 DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId );
2459 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2463 for( const auto& item : mFontDescriptionSizeCache )
2465 if( ( validatedFontId == item.validatedFontId ) &&
2466 ( requestedPointSize == item.requestedPointSize ) )
2468 fontId = item.fontId;
2470 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2471 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2476 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found.\n" );
2477 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2481 bool FontClient::Plugin::FindBitmapFont( const FontFamily& bitmapFont, FontId& fontId ) const
2485 for( const auto& item : mBitmapFontCache )
2487 if( bitmapFont == item.font.name )
2489 fontId = item.id + 1u;
2497 bool FontClient::Plugin::IsScalable( const FontPath& path )
2499 bool isScalable = false;
2502 int error = FT_New_Face( mFreeTypeLibrary,
2506 if( FT_Err_Ok != error )
2508 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() );
2512 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2518 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
2520 // Create a font pattern.
2521 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2523 FcResult result = FcResultMatch;
2525 // match the pattern
2526 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2527 bool isScalable = false;
2531 // Get the path to the font file name.
2533 GetFcString( match, FC_FILE, path );
2534 isScalable = IsScalable( path );
2538 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2541 // Destroys the created patterns.
2542 FcPatternDestroy( match );
2543 FcPatternDestroy( fontFamilyPattern );
2548 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
2550 // Empty the caller container
2554 int error = FT_New_Face( mFreeTypeLibrary,
2558 if( FT_Err_Ok != error )
2560 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() );
2563 // Fetch the number of fixed sizes available
2564 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
2566 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
2568 sizes.PushBack( ftFace->available_sizes[ i ].size );
2573 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
2574 Vector< PointSize26Dot6 >& sizes )
2576 // Create a font pattern.
2577 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2579 FcResult result = FcResultMatch;
2581 // match the pattern
2582 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2586 // Get the path to the font file name.
2588 GetFcString( match, FC_FILE, path );
2589 GetFixedSizes( path, sizes );
2593 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2596 // Destroys the created patterns.
2597 FcPatternDestroy( match );
2598 FcPatternDestroy( fontFamilyPattern );
2601 bool FontClient::Plugin::HasItalicStyle( FontId fontId ) const
2603 bool hasItalicStyle = false;
2605 const FontId index = fontId - 1u;
2607 if( ( fontId > 0 ) &&
2608 ( index < mFontIdCache.Count() ) )
2610 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2612 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
2614 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2616 hasItalicStyle = 0u != ( font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC );
2621 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
2624 return hasItalicStyle;
2627 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path )
2629 FontDescription description;
2630 description.path = path;
2631 description.family = std::move( FontFamily( ftFace->family_name ) );
2632 description.weight = FontWeight::NONE;
2633 description.width = FontWidth::NONE;
2634 description.slant = FontSlant::NONE;
2636 // Note FreeType doesn't give too much info to build a proper font style.
2637 if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
2639 description.slant = FontSlant::ITALIC;
2641 if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
2643 description.weight = FontWeight::BOLD;
2646 FontDescriptionId validatedFontId = 0u;
2647 if( !FindValidatedFont( description,
2650 // Set the index to the vector of paths to font file names.
2651 validatedFontId = mFontDescriptionCache.size();
2653 FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2655 FcResult result = FcResultMatch;
2656 FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2658 FcCharSet* characterSet = nullptr;
2659 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2661 const FontId fontFaceId = id - 1u;
2662 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( characterSet ); // Increases the reference counter.
2664 // Destroys the created patterns.
2665 FcPatternDestroy( match );
2666 FcPatternDestroy( pattern );
2668 // Add the path to the cache.
2669 description.type = FontDescription::FACE_FONT;
2670 mFontDescriptionCache.push_back( description );
2672 // Increase the reference counter and add the character set to the cache.
2673 mCharacterSetCache.PushBack( FcCharSetCopy( characterSet ) );
2675 // Cache the index and the font's description.
2676 mValidatedFontCache.push_back( std::move( FontDescriptionCacheItem( std::move( description ),
2677 validatedFontId) ) );
2679 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2680 mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
2686 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description )
2688 FcCharSet* characterSet = nullptr;
2690 FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2692 if( nullptr != pattern )
2694 FcResult result = FcResultMatch;
2695 FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2697 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2699 // Destroys the created patterns.
2700 FcPatternDestroy( match );
2701 FcPatternDestroy( pattern );
2704 return characterSet;
2707 void FontClient::Plugin::ClearFallbackCache( std::vector<FallbackCacheItem>& fallbackCache )
2709 for( auto& item : fallbackCache )
2711 if( nullptr != item.fallbackFonts )
2713 delete item.fallbackFonts;
2716 if( nullptr != item.characterSets )
2718 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2719 DestroyCharacterSets( *item.characterSets );
2720 delete item.characterSets;
2725 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2727 for( auto& item : mFontFaceCache )
2729 FcCharSetDestroy( item.mCharacterSet );
2730 item.mCharacterSet = nullptr;
2734 } // namespace Internal
2736 } // namespace TextAbstraction