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 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT );
1245 if( FT_Err_Ok == error )
1247 glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1248 glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
1251 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
1252 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
1256 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
1257 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
1267 case FontDescription::BITMAP_FONT:
1269 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1271 unsigned int index = 0u;
1272 for( auto& item : bitmapFontCacheItem.font.glyphs )
1274 if( item.utf32 == glyph.index )
1276 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1279 pixelBuffer = LoadImageFromFile( item.url );
1282 glyph.width = static_cast< float >( pixelBuffer.GetWidth() );
1283 glyph.height = static_cast< float >( pixelBuffer.GetHeight() );
1284 glyph.xBearing = 0.f;
1285 glyph.yBearing = glyph.height + item.descender;
1286 glyph.advance = glyph.width;
1287 glyph.scaleFactor = 1.f;
1298 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1304 // Check if it's an embedded image.
1305 if( ( 0u == glyph.fontId ) && ( 0u != glyph.index ) && ( glyph.index <= mEmbeddedItemCache.Count() ) )
1307 const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1309 glyph.width = static_cast<float>( item.width );
1310 glyph.height = static_cast<float>( item.height );
1311 glyph.xBearing = 0.f;
1312 glyph.yBearing = glyph.height;
1313 glyph.advance = glyph.width;
1314 glyph.scaleFactor = 1.f;
1326 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
1330 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1331 bool success( true );
1333 for( unsigned int i = 0u; i < size; ++i )
1335 FontId fontId = array[i].fontId;
1337 if( ( fontId > 0u ) &&
1338 ( fontId - 1u ) < mFontIdCache.Count() )
1340 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1342 if( ! font.mVectorFontId )
1344 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1347 mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
1349 // Vector metrics are in EMs, convert to pixels
1350 const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
1351 array[i].width *= scale;
1352 array[i].height *= scale;
1353 array[i].xBearing *= scale;
1354 array[i].yBearing *= scale;
1355 array[i].advance *= scale;
1369 void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth )
1371 const FontId index = fontId - 1u;
1373 if( ( fontId > 0u ) &&
1374 ( index < mFontIdCache.Count() ) )
1376 data.isColorBitmap = false;
1377 data.isColorEmoji = false;
1379 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1381 switch( fontIdCacheItem.type )
1383 case FontDescription::FACE_FONT:
1385 // For the software italics.
1386 bool isShearRequired = false;
1388 const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1389 FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1393 #ifdef FREETYPE_BITMAP_SUPPORT
1394 // Check to see if this is fixed size bitmap
1395 if( fontFaceCacheItem.mIsFixedSizeBitmap )
1397 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1402 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT );
1404 if( FT_Err_Ok == error )
1406 if( isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) )
1408 // Does the software bold.
1409 FT_GlyphSlot_Embolden( ftFace->glyph );
1412 if( isItalicRequired && !( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) )
1414 // Will do the software italic.
1415 isShearRequired = true;
1419 error = FT_Get_Glyph( ftFace->glyph, &glyph );
1421 // Convert to bitmap if necessary
1422 if( FT_Err_Ok == error )
1424 if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
1426 // Check whether we should create a bitmap for the outline
1427 if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 )
1431 error = FT_Stroker_New( mFreeTypeLibrary, &stroker );
1433 if( FT_Err_Ok == error )
1435 FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 );
1436 error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 );
1438 if( FT_Err_Ok == error )
1440 FT_Stroker_Done( stroker );
1444 DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error );
1449 DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error );
1453 error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
1454 if( FT_Err_Ok == error )
1456 FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph );
1457 ConvertBitmap( data, bitmapGlyph->bitmap, isShearRequired );
1461 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error );
1466 ConvertBitmap( data, ftFace->glyph->bitmap, isShearRequired );
1469 data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1471 // Created FT_Glyph object must be released with FT_Done_Glyph
1472 FT_Done_Glyph( glyph );
1477 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error );
1481 case FontDescription::BITMAP_FONT:
1483 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1485 unsigned int index = 0u;
1486 for( auto& item : bitmapFontCacheItem.font.glyphs )
1488 if( item.utf32 == glyphIndex )
1490 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1493 pixelBuffer = LoadImageFromFile( item.url );
1496 data.width = pixelBuffer.GetWidth();
1497 data.height = pixelBuffer.GetHeight();
1499 data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1501 ConvertBitmap( data, data.width, data.height, pixelBuffer.GetBuffer() );
1503 // Sets the pixel format.
1504 data.format = pixelBuffer.GetPixelFormat();
1513 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1519 if( ( 0u != glyphIndex ) && ( glyphIndex <= mEmbeddedItemCache.Count() ) )
1521 // It's an embedded item.
1522 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1524 data.width = item.width;
1525 data.height = item.height;
1526 if( 0u != item.pixelBufferId )
1528 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId-1u].pixelBuffer;
1531 ConvertBitmap( data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer() );
1533 // Sets the pixel format.
1534 data.format = pixelBuffer.GetPixelFormat();
1539 // Creates the output buffer
1540 const unsigned int bufferSize = data.width * data.height * 4u;
1541 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1543 memset( data.buffer, 0u, bufferSize );
1545 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1551 PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
1553 TextAbstraction::FontClient::GlyphBufferData data;
1555 CreateBitmap( fontId, glyphIndex, false, false, data, outlineWidth );
1557 return PixelData::New( data.buffer,
1558 data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
1562 PixelData::DELETE_ARRAY );
1565 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
1570 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1571 if( ( fontId > 0u ) &&
1572 ( fontId - 1u < mFontIdCache.Count() ) )
1574 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1575 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1577 if( ! font.mVectorFontId )
1579 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1582 mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
1587 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
1589 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" );
1590 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize );
1592 // First look into the cache if there is an ellipsis glyph for the requested point size.
1593 for( const auto& item : mEllipsisCache )
1595 if( item.requestedPointSize != requestedPointSize )
1597 // Use the glyph in the cache.
1599 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1600 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1601 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1607 // No glyph has been found. Create one.
1608 mEllipsisCache.PushBack( EllipsisItem() );
1609 EllipsisItem& item = *( mEllipsisCache.End() - 1u );
1611 item.requestedPointSize = requestedPointSize;
1613 // Find a font for the ellipsis glyph.
1614 item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
1618 // Set the character index to access the glyph inside the font.
1619 item.glyph.index = FT_Get_Char_Index( mFontFaceCache[mFontIdCache[item.glyph.fontId-1u].id].mFreeTypeFace,
1620 ELLIPSIS_CHARACTER );
1622 GetBitmapMetrics( &item.glyph, 1u, true );
1624 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1625 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1626 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1631 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1633 FT_Error error = -1;
1635 const FontId index = fontId - 1u;
1637 if( ( fontId > 0u ) &&
1638 ( index < mFontIdCache.Count() ) )
1640 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1642 switch( fontIdCacheItem.type )
1644 case FontDescription::FACE_FONT:
1646 #ifdef FREETYPE_BITMAP_SUPPORT
1647 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1648 FT_Face ftFace = item.mFreeTypeFace;
1650 // Check to see if this is fixed size bitmap
1651 if( item.mHasColorTables )
1653 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1658 case FontDescription::BITMAP_FONT:
1660 error = FT_Err_Ok; // Will return true;
1665 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1670 return FT_Err_Ok == error;
1673 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId )
1675 FT_Face fontFace = nullptr;
1677 const FontId index = fontId - 1u;
1678 if( ( fontId > 0u ) &&
1679 ( index < mFontIdCache.Count() ) )
1681 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1683 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1685 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1691 FontDescription::Type FontClient::Plugin::GetFontType( FontId fontId )
1693 const FontId index = fontId - 1u;
1694 if( ( fontId > 0u ) &&
1695 ( index < mFontIdCache.Count() ) )
1697 return mFontIdCache[index].type;
1699 return FontDescription::INVALID;
1702 bool FontClient::Plugin::AddCustomFontDirectory( const FontPath& path )
1704 // nullptr as first parameter means the current configuration is used.
1705 return FcConfigAppFontAddDir( nullptr, reinterpret_cast<const FcChar8 *>( path.c_str() ) );
1708 GlyphIndex FontClient::Plugin::CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat )
1710 EmbeddedItem embeddedItem;
1712 embeddedItem.pixelBufferId = 0u;
1713 embeddedItem.width = description.width;
1714 embeddedItem.height = description.height;
1716 pixelFormat = Pixel::A8;
1718 if( !description.url.empty() )
1720 // Check if the url is in the cache.
1721 PixelBufferId index = 0u;
1723 for( const auto& cacheItem : mPixelBufferCache )
1726 if( cacheItem.url == description.url )
1728 // The url is in the pixel buffer cache.
1729 // Set the index +1 to the vector.
1730 embeddedItem.pixelBufferId = index;
1735 Devel::PixelBuffer pixelBuffer;
1736 if( 0u == embeddedItem.pixelBufferId )
1738 // The pixel buffer is not in the cache. Create one and cache it.
1740 // Load the image from the url.
1741 pixelBuffer = LoadImageFromFile( description.url );
1743 // Create the cache item.
1744 PixelBufferCacheItem pixelBufferCacheItem;
1745 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1746 pixelBufferCacheItem.url = description.url;
1748 // Store the cache item in the cache.
1749 mPixelBufferCache.push_back( std::move( pixelBufferCacheItem ) );
1751 // Set the pixel buffer id to the embedded item.
1752 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1756 // Retrieve the pixel buffer from the cache to set the pixel format.
1757 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId-1u].pixelBuffer;
1762 // Set the size of the embedded item if it has not been set.
1763 if( 0u == embeddedItem.width )
1765 embeddedItem.width = static_cast<unsigned int>( pixelBuffer.GetWidth() );
1768 if( 0u == embeddedItem.height )
1770 embeddedItem.height = static_cast<unsigned int>( pixelBuffer.GetHeight() );
1773 // Set the pixel format.
1774 pixelFormat = pixelBuffer.GetPixelFormat();
1778 // Find if the same embeddedItem has already been created.
1779 unsigned int index = 0u;
1780 for( const auto& item : mEmbeddedItemCache )
1783 if( ( item.pixelBufferId == embeddedItem.pixelBufferId ) &&
1784 ( item.width == embeddedItem.width ) &&
1785 ( item.height == embeddedItem.height ) )
1791 // Cache the embedded item.
1792 mEmbeddedItemCache.PushBack( embeddedItem );
1794 return mEmbeddedItemCache.Count();
1797 void FontClient::Plugin::InitSystemFonts()
1799 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1801 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1805 DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont );
1807 // Reserve some space to avoid reallocations.
1808 mSystemFonts.reserve( fontSet->nfont );
1810 for( int i = 0u; i < fontSet->nfont; ++i )
1812 FcPattern* fontPattern = fontSet->fonts[i];
1816 // Skip fonts with no path
1817 if( GetFcString( fontPattern, FC_FILE, path ) )
1819 mSystemFonts.push_back( FontDescription() );
1820 FontDescription& fontDescription = mSystemFonts.back();
1822 fontDescription.path = std::move( path );
1827 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1828 GetFcInt( fontPattern, FC_WIDTH, width );
1829 GetFcInt( fontPattern, FC_WEIGHT, weight );
1830 GetFcInt( fontPattern, FC_SLANT, slant );
1831 fontDescription.width = IntToWidthType( width );
1832 fontDescription.weight = IntToWeightType( weight );
1833 fontDescription.slant = IntToSlantType( slant );
1835 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str() );
1836 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1837 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1838 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1839 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1843 // Destroys the font set created.
1844 FcFontSetDestroy( fontSet );
1846 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1849 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1851 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1853 FcResult result = FcResultMatch;
1854 FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result ); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1856 const bool matched = nullptr != match;
1857 DALI_LOG_INFO( gLogFilter, Debug::General, " pattern matched : %s\n", ( matched ? "true" : "false" ) );
1864 GetFcString( match, FC_FILE, fontDescription.path );
1865 GetFcString( match, FC_FAMILY, fontDescription.family );
1866 GetFcInt( match, FC_WIDTH, width );
1867 GetFcInt( match, FC_WEIGHT, weight );
1868 GetFcInt( match, FC_SLANT, slant );
1869 fontDescription.width = IntToWidthType( width );
1870 fontDescription.weight = IntToWeightType( weight );
1871 fontDescription.slant = IntToSlantType( slant );
1873 // Retrieve the character set and increase the reference counter.
1874 FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1875 *characterSet = FcCharSetCopy( *characterSet );
1877 // destroyed the matched pattern
1878 FcPatternDestroy( match );
1880 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
1881 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1882 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1883 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1884 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1887 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1891 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1893 // create the cached font family lookup pattern
1894 // a pattern holds a set of names, each name refers to a property of the font
1895 FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1897 if( !fontFamilyPattern )
1902 // add a property to the pattern for the font family
1903 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1905 // add a property to the pattern for local setting.
1906 const char* locale = setlocale( LC_MESSAGES, nullptr );
1907 if( locale != nullptr)
1909 FcPatternAddString( fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>( locale ) );
1912 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1916 width = DEFAULT_FONT_WIDTH;
1919 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1923 weight = DEFAULT_FONT_WEIGHT;
1926 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1930 slant = DEFAULT_FONT_SLANT;
1933 FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1934 FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1935 FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1937 // modify the config, with the mFontFamilyPatterm
1938 FcConfigSubstitute( nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern );
1940 // provide default values for unspecified properties in the font pattern
1941 // e.g. patterns without a specified style or weight are set to Medium
1942 FcDefaultSubstitute( fontFamilyPattern );
1944 return fontFamilyPattern;
1947 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1949 FcFontSet* fontset = nullptr;
1951 // create a new pattern.
1952 // a pattern holds a set of names, each name refers to a property of the font
1953 FcPattern* pattern = FcPatternCreate();
1955 if( nullptr != pattern )
1957 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1958 FcObjectSet* objectSet = FcObjectSetCreate();
1960 if( nullptr != objectSet )
1962 // build an object set from a list of property names
1963 FcObjectSetAdd( objectSet, FC_FILE );
1964 FcObjectSetAdd( objectSet, FC_FAMILY );
1965 FcObjectSetAdd( objectSet, FC_WIDTH );
1966 FcObjectSetAdd( objectSet, FC_WEIGHT );
1967 FcObjectSetAdd( objectSet, FC_SLANT );
1969 // get a list of fonts
1970 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1971 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.
1973 // clear up the object set
1974 FcObjectSetDestroy( objectSet );
1977 // clear up the pattern
1978 FcPatternDestroy( pattern );
1984 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
1985 const char* const n,
1986 std::string& string )
1988 FcChar8* file = nullptr;
1989 const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
1991 if( FcResultMatch == retVal )
1993 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1994 string.assign( reinterpret_cast<const char*>( file ) );
2002 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
2004 const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
2006 if( FcResultMatch == retVal )
2014 FontId FontClient::Plugin::CreateFont( const FontPath& path,
2015 PointSize26Dot6 requestedPointSize,
2016 FaceIndex faceIndex,
2017 bool cacheDescription )
2019 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" );
2020 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2021 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2025 // Create & cache new font face
2027 int error = FT_New_Face( mFreeTypeLibrary,
2032 if( FT_Err_Ok == error )
2034 // Check if a font is scalable.
2035 const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) );
2036 const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes );
2037 const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) );
2038 FontId fontFaceId = 0u;
2040 DALI_LOG_INFO( gLogFilter, Debug::General, " isScalable : [%s]\n", ( isScalable ? "true" : "false" ) );
2041 DALI_LOG_INFO( gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) );
2042 DALI_LOG_INFO( gLogFilter, Debug::General, " hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) );
2044 // Check to see if the font contains fixed sizes?
2045 if( !isScalable && hasFixedSizedBitmaps )
2047 PointSize26Dot6 actualPointSize = 0u;
2048 int fixedSizeIndex = 0;
2049 for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex )
2051 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2052 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize );
2054 if( fixedSize >= requestedPointSize )
2056 actualPointSize = fixedSize;
2061 if( 0u == actualPointSize )
2063 // The requested point size is bigger than the bigest fixed size.
2064 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2065 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2068 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize );
2070 // Tell Freetype to use this size
2071 error = FT_Select_Size( ftFace, fixedSizeIndex );
2072 if ( FT_Err_Ok != error )
2074 DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error );
2078 const float fixedWidth = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].width );
2079 const float fixedHeight = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].height );
2081 // Indicate that the font is a fixed sized bitmap
2082 FontMetrics metrics( fixedHeight, // The ascender in pixels.
2084 fixedHeight, // The height in pixels.
2088 // Create the FreeType font face item to cache.
2089 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables );
2091 // Set the index to the font's id cache.
2092 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2094 // Create the font id item to cache.
2095 FontIdCacheItem fontIdCacheItem;
2096 fontIdCacheItem.type = FontDescription::FACE_FONT;
2098 // Set the index to the FreeType font face cache.
2099 fontIdCacheItem.id = mFontFaceCache.size();
2100 fontFaceId = fontIdCacheItem.id + 1u;
2103 mFontFaceCache.push_back( fontFaceCacheItem );
2104 mFontIdCache.PushBack( fontIdCacheItem );
2106 // Set the font id to be returned.
2107 id = mFontIdCache.Count();
2112 error = FT_Set_Char_Size( ftFace,
2118 if( FT_Err_Ok == error )
2121 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2123 FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266,
2124 static_cast< float >( ftMetrics.descender ) * FROM_266,
2125 static_cast< float >( ftMetrics.height ) * FROM_266,
2126 static_cast< float >( ftFace->underline_position ) * FROM_266,
2127 static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
2129 // Create the FreeType font face item to cache.
2130 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics );
2132 // Set the index to the font's id cache.
2133 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2135 // Create the font id item to cache.
2136 FontIdCacheItem fontIdCacheItem;
2137 fontIdCacheItem.type = FontDescription::FACE_FONT;
2139 // Set the index to the FreeType font face cache.
2140 fontIdCacheItem.id = mFontFaceCache.size();
2141 fontFaceId = fontIdCacheItem.id + 1u;
2144 mFontFaceCache.push_back( fontFaceCacheItem );
2145 mFontIdCache.PushBack( fontIdCacheItem );
2147 // Set the font id to be returned.
2148 id = mFontIdCache.Count();
2152 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize );
2156 if( 0u != fontFaceId )
2158 if( cacheDescription )
2160 CacheFontPath( ftFace, fontFaceId, requestedPointSize, path );
2166 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str() );
2169 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
2170 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" );
2175 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer )
2177 // Set the input dimensions.
2178 const ImageDimensions inputDimensions( srcWidth, srcHeight );
2180 // Set the output dimensions.
2181 // If the output dimension is not given, the input dimension is set
2182 // and won't be downscaling.
2183 data.width = ( data.width == 0 ) ? srcWidth : data.width;
2184 data.height = ( data.height == 0 ) ? srcHeight : data.height;
2185 const ImageDimensions desiredDimensions( data.width, data.height );
2187 // Creates the output buffer
2188 const unsigned int bufferSize = data.width * data.height * 4u;
2189 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2191 if( inputDimensions == desiredDimensions )
2193 // There isn't downscaling.
2194 memcpy( data.buffer, srcBuffer, bufferSize );
2198 Dali::Internal::Platform::LanczosSample4BPP( srcBuffer,
2201 desiredDimensions );
2205 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired )
2207 if( srcBitmap.width*srcBitmap.rows > 0 )
2209 switch( srcBitmap.pixel_mode )
2211 case FT_PIXEL_MODE_GRAY:
2213 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
2215 uint8_t* pixelsIn = srcBitmap.buffer;
2216 unsigned int width = srcBitmap.width;
2217 unsigned height = srcBitmap.rows;
2219 std::unique_ptr<uint8_t, void(*)(void*)> pixelsOutPtr( nullptr, free );
2221 if( isShearRequired )
2224 * Glyphs' bitmaps with no slant retrieved from FreeType:
2234 * Expected glyphs' bitmaps with italic slant:
2235 * ____________ ______
2242 * ------------ ------
2244 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2254 * 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.
2256 unsigned int widthOut = 0u;
2257 unsigned int heightOut = 0u;
2258 uint8_t* pixelsOut = nullptr;
2260 Dali::Internal::Platform::HorizontalShear( pixelsIn,
2264 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2271 pixelsIn = pixelsOut;
2272 pixelsOutPtr.reset( pixelsOut );
2275 const unsigned int bufferSize = width * height;
2276 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2278 data.height = height;
2279 data.format = Pixel::L8; // Sets the pixel format.
2280 memcpy( data.buffer, pixelsIn, bufferSize );
2285 #ifdef FREETYPE_BITMAP_SUPPORT
2286 case FT_PIXEL_MODE_BGRA:
2288 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
2290 ConvertBitmap( data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer );
2292 // Sets the pixel format.
2293 data.format = Pixel::BGRA8888;
2300 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" );
2307 bool FontClient::Plugin::FindFont( const FontPath& path,
2308 PointSize26Dot6 requestedPointSize,
2309 FaceIndex faceIndex,
2310 FontId& fontId ) const
2312 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2313 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2314 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2315 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size() );
2318 for( const auto& cacheItem : mFontFaceCache )
2320 if( cacheItem.mRequestedPointSize == requestedPointSize &&
2321 cacheItem.mFaceIndex == faceIndex &&
2322 cacheItem.mPath == path )
2324 fontId = cacheItem.mFontId + 1u;
2326 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2327 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2333 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found\n" );
2334 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2339 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
2340 FontDescriptionId& validatedFontId )
2342 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" );
2343 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2344 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2345 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2346 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2347 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2348 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size() );
2350 validatedFontId = 0u;
2352 for( const auto& item : mValidatedFontCache )
2354 if( !fontDescription.family.empty() &&
2355 ( fontDescription.family == item.fontDescription.family ) &&
2356 ( fontDescription.width == item.fontDescription.width ) &&
2357 ( fontDescription.weight == item.fontDescription.weight ) &&
2358 ( fontDescription.slant == item.fontDescription.slant ) )
2360 validatedFontId = item.index;
2362 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId );
2363 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2368 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font not found\n" );
2369 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2373 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
2374 FontList*& fontList,
2375 CharacterSetList*& characterSetList )
2377 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" );
2378 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2379 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2380 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2381 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2382 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2383 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size() );
2387 for( const auto& item : mFallbackCache )
2389 if( !fontDescription.family.empty() &&
2390 ( fontDescription.family == item.fontDescription.family ) &&
2391 ( fontDescription.width == item.fontDescription.width ) &&
2392 ( fontDescription.weight == item.fontDescription.weight ) &&
2393 ( fontDescription.slant == item.fontDescription.slant ) )
2395 fontList = item.fallbackFonts;
2396 characterSetList = item.characterSets;
2398 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list found.\n" );
2399 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2404 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list not found.\n" );
2405 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2409 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
2410 PointSize26Dot6 requestedPointSize,
2413 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2414 DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId );
2415 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2419 for( const auto& item : mFontDescriptionSizeCache )
2421 if( ( validatedFontId == item.validatedFontId ) &&
2422 ( requestedPointSize == item.requestedPointSize ) )
2424 fontId = item.fontId;
2426 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2427 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2432 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found.\n" );
2433 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2437 bool FontClient::Plugin::FindBitmapFont( const FontFamily& bitmapFont, FontId& fontId ) const
2441 for( const auto& item : mBitmapFontCache )
2443 if( bitmapFont == item.font.name )
2445 fontId = item.id + 1u;
2453 bool FontClient::Plugin::IsScalable( const FontPath& path )
2455 bool isScalable = false;
2458 int error = FT_New_Face( mFreeTypeLibrary,
2462 if( FT_Err_Ok != error )
2464 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() );
2468 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2474 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
2476 // Create a font pattern.
2477 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2479 FcResult result = FcResultMatch;
2481 // match the pattern
2482 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2483 bool isScalable = false;
2487 // Get the path to the font file name.
2489 GetFcString( match, FC_FILE, path );
2490 isScalable = IsScalable( path );
2494 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2497 // Destroys the created patterns.
2498 FcPatternDestroy( match );
2499 FcPatternDestroy( fontFamilyPattern );
2504 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
2506 // Empty the caller container
2510 int error = FT_New_Face( mFreeTypeLibrary,
2514 if( FT_Err_Ok != error )
2516 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() );
2519 // Fetch the number of fixed sizes available
2520 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
2522 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
2524 sizes.PushBack( ftFace->available_sizes[ i ].size );
2529 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
2530 Vector< PointSize26Dot6 >& sizes )
2532 // Create a font pattern.
2533 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2535 FcResult result = FcResultMatch;
2537 // match the pattern
2538 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2542 // Get the path to the font file name.
2544 GetFcString( match, FC_FILE, path );
2545 GetFixedSizes( path, sizes );
2549 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2552 // Destroys the created patterns.
2553 FcPatternDestroy( match );
2554 FcPatternDestroy( fontFamilyPattern );
2557 bool FontClient::Plugin::HasItalicStyle( FontId fontId ) const
2559 bool hasItalicStyle = false;
2561 const FontId index = fontId - 1u;
2563 if( ( fontId > 0 ) &&
2564 ( index < mFontIdCache.Count() ) )
2566 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2568 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
2570 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2572 hasItalicStyle = 0u != ( font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC );
2577 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
2580 return hasItalicStyle;
2583 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path )
2585 FontDescription description;
2586 description.path = path;
2587 description.family = std::move( FontFamily( ftFace->family_name ) );
2588 description.weight = FontWeight::NONE;
2589 description.width = FontWidth::NONE;
2590 description.slant = FontSlant::NONE;
2592 // Note FreeType doesn't give too much info to build a proper font style.
2593 if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
2595 description.slant = FontSlant::ITALIC;
2597 if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
2599 description.weight = FontWeight::BOLD;
2602 FontDescriptionId validatedFontId = 0u;
2603 if( !FindValidatedFont( description,
2606 // Set the index to the vector of paths to font file names.
2607 validatedFontId = mFontDescriptionCache.size();
2609 FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2611 FcResult result = FcResultMatch;
2612 FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2614 FcCharSet* characterSet = nullptr;
2615 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2617 const FontId fontFaceId = id - 1u;
2618 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( characterSet ); // Increases the reference counter.
2620 // Destroys the created patterns.
2621 FcPatternDestroy( match );
2622 FcPatternDestroy( pattern );
2624 // Add the path to the cache.
2625 description.type = FontDescription::FACE_FONT;
2626 mFontDescriptionCache.push_back( description );
2628 // Increase the reference counter and add the character set to the cache.
2629 mCharacterSetCache.PushBack( FcCharSetCopy( characterSet ) );
2631 // Cache the index and the font's description.
2632 mValidatedFontCache.push_back( std::move( FontDescriptionCacheItem( std::move( description ),
2633 validatedFontId) ) );
2635 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2636 mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
2642 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description )
2644 FcCharSet* characterSet = nullptr;
2646 FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2648 if( nullptr != pattern )
2650 FcResult result = FcResultMatch;
2651 FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2653 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2655 // Destroys the created patterns.
2656 FcPatternDestroy( match );
2657 FcPatternDestroy( pattern );
2660 return characterSet;
2663 void FontClient::Plugin::ClearFallbackCache( std::vector<FallbackCacheItem>& fallbackCache )
2665 for( auto& item : fallbackCache )
2667 if( nullptr != item.fallbackFonts )
2669 delete item.fallbackFonts;
2672 if( nullptr != item.characterSets )
2674 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2675 DestroyCharacterSets( *item.characterSets );
2676 delete item.characterSets;
2681 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2683 for( auto& item : mFontFaceCache )
2685 FcCharSetDestroy( item.mCharacterSet );
2686 item.mCharacterSet = nullptr;
2690 } // namespace Internal
2692 } // namespace TextAbstraction