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 );
102 namespace TextAbstraction
109 * @brief Returns the FontWidth's enum index for the given width value.
111 * @param[in] width The width value.
113 * @return The FontWidth's enum index.
115 FontWidth::Type IntToWidthType( int width )
117 return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
121 * @brief Returns the FontWeight's enum index for the given weight value.
123 * @param[in] weight The weight value.
125 * @return The FontWeight's enum index.
127 FontWeight::Type IntToWeightType( int weight )
129 return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
133 * @brief Returns the FontSlant's enum index for the given slant value.
135 * @param[in] slant The slant value.
137 * @return The FontSlant's enum index.
139 FontSlant::Type IntToSlantType( int slant )
141 return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
145 * @brief Free the resources allocated by the FcCharSet objects.
147 * @param[in] characterSets The vector of character sets.
149 void DestroyCharacterSets( CharacterSetList& characterSets )
151 for( auto& item : characterSets )
153 FcCharSetDestroy( item );
157 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets )
158 : fontDescription{ std::move( font ) },
159 fallbackFonts{ fallbackFonts },
160 characterSets{ characterSets }
164 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
165 FontDescriptionId index )
166 : fontDescription{ fontDescription },
171 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( FontDescription&& fontDescription,
172 FontDescriptionId index )
173 : fontDescription{ std::move( fontDescription ) },
178 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem( FontDescriptionId validatedFontId,
179 PointSize26Dot6 requestedPointSize,
181 : validatedFontId( validatedFontId ),
182 requestedPointSize( requestedPointSize ),
187 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
188 const FontPath& path,
189 PointSize26Dot6 requestedPointSize,
191 const FontMetrics& metrics )
192 : mFreeTypeFace( ftFace ),
194 mRequestedPointSize( requestedPointSize ),
197 mCharacterSet( nullptr ),
198 mFixedSizeIndex( 0 ),
199 mFixedWidthPixels( 0.f ),
200 mFixedHeightPixels( 0.f ),
203 mIsFixedSizeBitmap( false ),
204 mHasColorTables( false )
208 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
209 const FontPath& path,
210 PointSize26Dot6 requestedPointSize,
212 const FontMetrics& metrics,
216 bool hasColorTables )
217 : mFreeTypeFace( ftFace ),
219 mRequestedPointSize( requestedPointSize ),
222 mCharacterSet( nullptr ),
223 mFixedSizeIndex( fixedSizeIndex ),
224 mFixedWidthPixels( fixedWidth ),
225 mFixedHeightPixels( fixedHeight ),
228 mIsFixedSizeBitmap( true ),
229 mHasColorTables( hasColorTables )
233 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
234 unsigned int verticalDpi )
235 : mFreeTypeLibrary( nullptr ),
236 mDpiHorizontal( horizontalDpi ),
237 mDpiVertical( verticalDpi ),
238 mDefaultFontDescription(),
243 mValidatedFontCache(),
244 mFontDescriptionCache( 1u ),
245 mCharacterSetCache(),
246 mFontDescriptionSizeCache(),
247 mVectorFontCache( nullptr ),
249 mEmbeddedItemCache(),
250 mDefaultFontDescriptionCached( false )
252 mCharacterSetCache.Resize( 1u );
254 int error = FT_Init_FreeType( &mFreeTypeLibrary );
255 if( FT_Err_Ok != error )
257 DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Init error: %d\n", error );
260 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
261 mVectorFontCache = new VectorFontCache( mFreeTypeLibrary );
266 FontClient::Plugin::~Plugin()
268 ClearFallbackCache( mFallbackCache );
270 // Free the resources allocated by the FcCharSet objects.
271 DestroyCharacterSets( mDefaultFontCharacterSets );
272 DestroyCharacterSets( mCharacterSetCache );
273 ClearCharacterSetFromFontFaceCache();
275 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
276 delete mVectorFontCache;
278 FT_Done_FreeType( mFreeTypeLibrary );
281 void FontClient::Plugin::ClearCache()
283 mDefaultFontDescription = FontDescription();
285 mSystemFonts.clear();
286 mDefaultFonts.clear();
288 DestroyCharacterSets( mDefaultFontCharacterSets );
289 mDefaultFontCharacterSets.Clear();
291 ClearFallbackCache( mFallbackCache );
292 mFallbackCache.clear();
294 mFontIdCache.Clear();
296 ClearCharacterSetFromFontFaceCache();
297 mFontFaceCache.clear();
299 mValidatedFontCache.clear();
300 mFontDescriptionCache.clear();
301 mFontDescriptionCache.resize( 1u );
303 DestroyCharacterSets( mCharacterSetCache );
304 mCharacterSetCache.Clear();
305 mCharacterSetCache.Resize( 1u );
307 mFontDescriptionSizeCache.clear();
309 mEllipsisCache.Clear();
310 mPixelBufferCache.clear();
311 mEmbeddedItemCache.Clear();
312 mBitmapFontCache.clear();
314 mDefaultFontDescriptionCached = false;
317 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
318 unsigned int verticalDpi )
320 mDpiHorizontal = horizontalDpi;
321 mDpiVertical = verticalDpi;
324 void FontClient::Plugin::ResetSystemDefaults()
326 mDefaultFontDescriptionCached = false;
329 void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList )
331 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n" );
332 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
333 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
334 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
335 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
339 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
341 FcResult result = FcResultMatch;
343 // Match the pattern.
344 FcFontSet* fontSet = FcFontSort( nullptr /* use default configure */,
346 false /* don't trim */,
348 &result ); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
350 if( nullptr != fontSet )
352 DALI_LOG_INFO( gLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont );
353 // Reserve some space to avoid reallocations.
354 fontList.reserve( fontSet->nfont );
356 for( int i = 0u; i < fontSet->nfont; ++i )
358 FcPattern* fontPattern = fontSet->fonts[i];
362 // Skip fonts with no path
363 if( GetFcString( fontPattern, FC_FILE, path ) )
365 // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
366 FcCharSet* characterSet = nullptr;
367 FcPatternGetCharSet( fontPattern, FC_CHARSET, 0u, &characterSet );
369 // Increase the reference counter of the character set.
370 characterSetList.PushBack( FcCharSetCopy( characterSet ) );
372 fontList.push_back( FontDescription() );
373 FontDescription& newFontDescription = fontList.back();
375 newFontDescription.path = std::move( path );
380 GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
381 GetFcInt( fontPattern, FC_WIDTH, width );
382 GetFcInt( fontPattern, FC_WEIGHT, weight );
383 GetFcInt( fontPattern, FC_SLANT, slant );
384 newFontDescription.width = IntToWidthType( width );
385 newFontDescription.weight = IntToWeightType( weight );
386 newFontDescription.slant = IntToSlantType( slant );
388 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", newFontDescription.family.c_str() );
389 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", newFontDescription.path.c_str() );
390 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[newFontDescription.width] );
391 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[newFontDescription.weight] );
392 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant] );
396 // Destroys the font set created by FcFontSort.
397 FcFontSetDestroy( fontSet );
401 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " No fonts found.\n" );
404 // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
405 FcPatternDestroy( fontFamilyPattern );
407 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n" );
410 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
412 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n" );
414 if( mDefaultFonts.empty() )
416 FontDescription fontDescription;
417 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
418 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
419 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
420 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
421 SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
424 defaultFonts = mDefaultFonts;
426 DALI_LOG_INFO( gLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size() );
427 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n" );
430 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
432 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
434 if( !mDefaultFontDescriptionCached )
436 // Clear any font config stored info in the caches.
438 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
439 DestroyCharacterSets( mDefaultFontCharacterSets );
440 DestroyCharacterSets( mCharacterSetCache );
441 mDefaultFontCharacterSets.Clear();
442 mCharacterSetCache.Clear();
444 for( auto& item : mFallbackCache )
446 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
447 DestroyCharacterSets( *item.characterSets );
449 delete item.characterSets;
450 item.characterSets = nullptr;
453 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
454 ClearCharacterSetFromFontFaceCache();
456 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
457 FcInitReinitialize();
459 FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
461 if( nullptr != matchPattern )
463 FcConfigSubstitute( nullptr, matchPattern, FcMatchPattern );
464 FcDefaultSubstitute( matchPattern );
466 FcCharSet* characterSet = nullptr;
467 MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription, &characterSet );
468 // Decrease the reference counter of the character set as it's not stored.
469 FcCharSetDestroy( characterSet );
471 // Destroys the pattern created.
472 FcPatternDestroy( matchPattern );
475 // Create again the character sets as they are not valid after FcInitReinitialize()
477 for( const auto& description : mDefaultFonts )
479 mDefaultFontCharacterSets.PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
482 for( const auto& description : mFontDescriptionCache )
484 mCharacterSetCache.PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
487 for( auto& item : mFallbackCache )
489 if( nullptr != item.fallbackFonts )
491 if( nullptr == item.characterSets )
493 item.characterSets = new CharacterSetList;
496 for( const auto& description : *( item.fallbackFonts ) )
498 item.characterSets->PushBack( FcCharSetCopy( CreateCharacterSetFromDescription( description ) ) );
503 mDefaultFontDescriptionCached = true;
506 fontDescription.path = mDefaultFontDescription.path;
507 fontDescription.family = mDefaultFontDescription.family;
508 fontDescription.width = mDefaultFontDescription.width;
509 fontDescription.weight = mDefaultFontDescription.weight;
510 fontDescription.slant = mDefaultFontDescription.slant;
512 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
513 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
514 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
515 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
516 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
517 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
520 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
522 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
524 if( mSystemFonts.empty() )
529 systemFonts = mSystemFonts;
530 DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size() );
531 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
534 void FontClient::Plugin::GetDescription( FontId id,
535 FontDescription& fontDescription ) const
537 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
538 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
539 const FontId index = id - 1u;
541 if( ( id > 0u ) && ( index < mFontIdCache.Count() ) )
543 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
544 switch( fontIdCacheItem.type )
546 case FontDescription::FACE_FONT:
548 for( const auto& item : mFontDescriptionSizeCache )
550 if( item.fontId == fontIdCacheItem.id )
552 fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
554 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
555 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
556 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
557 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
558 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
559 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
565 case FontDescription::BITMAP_FONT:
567 fontDescription.type = FontDescription::BITMAP_FONT;
568 fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
573 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
574 fontDescription.type = FontDescription::INVALID;
575 fontDescription.family.clear();
580 DALI_LOG_INFO( gLogFilter, Debug::General, " No description found for the font ID %d\n", id );
581 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
584 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
586 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
587 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
588 const FontId index = id - 1u;
591 ( index < mFontIdCache.Count() ) )
593 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
595 switch( fontIdCacheItem.type )
597 case FontDescription::FACE_FONT:
599 DALI_LOG_INFO( gLogFilter, Debug::General, " point size : %d\n", ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize );
600 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
601 return ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize;
603 case FontDescription::BITMAP_FONT:
605 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
609 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
615 DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font ID %d\n", id );
618 DALI_LOG_INFO( gLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE );
619 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
620 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
623 bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character character )
625 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
626 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
627 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character );
629 if( ( fontId < 1u ) || ( fontId > mFontIdCache.Count() ) )
631 DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n",mFontIdCache.Count());
632 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
638 bool isSupported = false;
640 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
642 switch( fontIdCacheItem.type )
644 case FontDescription::FACE_FONT:
646 if( fontIdCacheItem.id < mFontFaceCache.size() )
648 FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
650 if( nullptr == cacheItem.mCharacterSet )
652 // Create again the character set.
653 // It can be null if the ResetSystemDefaults() method has been called.
655 FontDescription description;
656 description.path = cacheItem.mPath;
657 description.family = std::move( FontFamily( cacheItem.mFreeTypeFace->family_name ) );
658 description.weight = FontWeight::NONE;
659 description.width = FontWidth::NONE;
660 description.slant = FontSlant::NONE;
662 // Note FreeType doesn't give too much info to build a proper font style.
663 if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC )
665 description.slant = FontSlant::ITALIC;
667 if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD )
669 description.weight = FontWeight::BOLD;
672 cacheItem.mCharacterSet = FcCharSetCopy( CreateCharacterSetFromDescription( description ) );
675 isSupported = FcCharSetHasChar( cacheItem.mCharacterSet, character );
679 case FontDescription::BITMAP_FONT:
681 const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
683 for( const auto& glyph : bitmapFont.glyphs )
685 if( glyph.utf32 == character )
695 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
699 DALI_LOG_INFO( gLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false") );
700 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
704 FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
705 const CharacterSetList& characterSetList,
707 PointSize26Dot6 requestedPointSize,
710 DALI_ASSERT_DEBUG( ( fontList.size() == characterSetList.Count() ) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets." );
712 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n" );
713 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character );
714 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
715 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
718 bool foundColor = false;
720 DALI_LOG_INFO( gLogFilter, Debug::General, " number of fonts : %d\n", fontList.size() );
722 // Traverse the list of fonts.
723 // Check for each font if supports the character.
724 for( unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index )
726 const FontDescription& description = fontList[index];
727 const FcCharSet* const characterSet = characterSetList[index];
729 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", description.family.c_str() );
730 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str() );
731 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width] );
732 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight] );
733 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant] );
735 bool foundInRanges = false;
736 if( nullptr != characterSet )
738 foundInRanges = FcCharSetHasChar( characterSet, character );
743 fontId = GetFontId( description,
747 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " font id : %d\n", fontId );
751 if( ( fontId > 0 ) &&
752 ( fontId - 1u < mFontIdCache.Count() ) )
754 const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
756 foundColor = item.mHasColorTables;
759 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " foundColor : %s\n", ( foundColor ? "true" : "false" ) );
762 // Keep going unless we prefer a different (color) font.
763 if( !preferColor || foundColor )
770 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
771 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n" );
775 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
776 PointSize26Dot6 requestedPointSize,
779 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n" );
780 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", charcode );
781 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
782 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
786 // Create the list of default fonts if it has not been created.
787 if( mDefaultFonts.empty() )
789 FontDescription fontDescription;
790 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
791 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
792 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
793 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
795 SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
797 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size() );
800 // Traverse the list of default fonts.
801 // Check for each default font if supports the character.
802 fontId = FindFontForCharacter( mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor );
804 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
805 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n" );
810 FontId FontClient::Plugin::FindFallbackFont( Character charcode,
811 const FontDescription& preferredFontDescription,
812 PointSize26Dot6 requestedPointSize,
815 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n" );
816 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", charcode );
817 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
818 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
820 // The font id to be returned.
823 FontDescription fontDescription;
825 // Fill the font description with the preferred font description and complete with the defaults.
826 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
827 fontDescription.weight = ( ( FontWeight::NONE == preferredFontDescription.weight ) ? IntToWeightType( DEFAULT_FONT_WEIGHT ) : preferredFontDescription.weight );
828 fontDescription.width = ( ( FontWidth::NONE == preferredFontDescription.width ) ? IntToWidthType( DEFAULT_FONT_WIDTH ) : preferredFontDescription.width );
829 fontDescription.slant = ( ( FontSlant::NONE == preferredFontDescription.slant ) ? IntToSlantType( DEFAULT_FONT_SLANT ) : preferredFontDescription.slant );
831 DALI_LOG_INFO( gLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n" );
832 DALI_LOG_INFO( gLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str() );
833 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight] );
834 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width] );
835 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant] );
837 // Check first if the font's description has been queried before.
838 FontList* fontList = nullptr;
839 CharacterSetList* characterSetList = nullptr;
841 if( !FindFallbackFontList( fontDescription, fontList, characterSetList ) )
843 fontList = new FontList;
844 characterSetList = new CharacterSetList;
846 SetFontList( fontDescription, *fontList, *characterSetList );
848 // Add the font-list to the cache.
849 mFallbackCache.push_back( std::move( FallbackCacheItem( std::move( fontDescription ), fontList, characterSetList ) ) );
852 if( fontList && characterSetList )
854 fontId = FindFontForCharacter( *fontList, *characterSetList, charcode, requestedPointSize, preferColor );
857 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
858 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
862 FontId FontClient::Plugin::GetFontId( const FontPath& path,
863 PointSize26Dot6 requestedPointSize,
865 bool cacheDescription )
867 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
868 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
869 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
873 if( nullptr != mFreeTypeLibrary )
876 if( FindFont( path, requestedPointSize, faceIndex, foundId ) )
882 id = CreateFont( path, requestedPointSize, faceIndex, cacheDescription );
886 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
887 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
892 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
893 PointSize26Dot6 requestedPointSize,
894 FaceIndex faceIndex )
896 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
897 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
898 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
899 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
900 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
901 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
902 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
904 // This method uses three vectors which caches:
905 // * The bitmap font cache
906 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
907 // * The path to font file names.
908 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
910 // 1) Checks if the font description matches with a previously loaded bitmap font.
911 // Returns if a font is found.
912 // 2) Checks in the cache if the font's description has been validated before.
913 // If it was it gets an index to the vector with paths to font file names. Otherwise,
914 // retrieves using font config a path to a font file name which matches with the
915 // font's description. The path is stored in the cache.
917 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
918 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
919 // the GetFontId() method with the path to the font file name and the point size to
922 // The font id to be returned.
925 // Check first if the font description matches with a previously loaded bitmap font.
926 if( FindBitmapFont( fontDescription.family, fontId ) )
931 // Check if the font's description have been validated before.
932 FontDescriptionId validatedFontId = 0u;
934 if( !FindValidatedFont( fontDescription,
937 // Use font config to validate the font's description.
938 ValidateFont( fontDescription,
942 FontId fontFaceId = 0u;
943 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
944 if( !FindFont( validatedFontId, requestedPointSize, fontFaceId ) )
946 // Retrieve the font file name path.
947 const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
949 // Retrieve the font id. Do not cache the description as it has been already cached.
950 fontId = GetFontId( description.path,
955 fontFaceId = mFontIdCache[fontId-1u].id;
956 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( mCharacterSetCache[validatedFontId] );
958 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
959 mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
965 fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
968 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
969 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
974 FontId FontClient::Plugin::GetFontId( const BitmapFont& bitmapFont )
976 for( const auto& item : mBitmapFontCache )
978 if( bitmapFont.name == item.font.name )
984 BitmapFontCacheItem bitmapFontCacheItem;
985 bitmapFontCacheItem.font = bitmapFont;
986 bitmapFontCacheItem.id = mFontIdCache.Count();
988 // Resize the vector with the pixel buffers.
989 bitmapFontCacheItem.pixelBuffers.resize( bitmapFont.glyphs.size() );
991 // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
992 unsigned int index = 0u;
993 for( auto& glyph : bitmapFontCacheItem.font.glyphs )
995 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
997 if( EqualsZero( glyph.ascender ) && EqualsZero( glyph.descender ) )
1000 pixelBuffer = LoadImageFromFile( glyph.url );
1004 glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
1008 bitmapFontCacheItem.font.ascender = std::max( glyph.ascender, bitmapFontCacheItem.font.ascender );
1009 bitmapFontCacheItem.font.descender = std::min( glyph.descender, bitmapFontCacheItem.font.descender );
1014 FontIdCacheItem fontIdCacheItem;
1015 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1016 fontIdCacheItem.id = mBitmapFontCache.size();
1018 mBitmapFontCache.push_back( std::move( bitmapFontCacheItem ) );
1019 mFontIdCache.PushBack( fontIdCacheItem );
1021 return bitmapFontCacheItem.id + 1u;
1024 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
1025 FontDescriptionId& validatedFontId )
1027 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n" );
1028 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
1029 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1030 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1031 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1032 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1034 // Create a font pattern.
1035 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1037 FontDescription description;
1039 FcCharSet* characterSet = nullptr;
1040 bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description, &characterSet );
1041 FcPatternDestroy( fontFamilyPattern );
1043 if( matched && ( nullptr != characterSet ) )
1045 // Set the index to the vector of paths to font file names.
1046 validatedFontId = mFontDescriptionCache.size();
1048 DALI_LOG_INFO( gLogFilter, Debug::General, " matched description; family : [%s]\n", description.family.c_str() );
1049 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str() );
1050 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width] );
1051 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight] );
1052 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant] );
1053 DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId );
1055 // Add the path to the cache.
1056 description.type = FontDescription::FACE_FONT;
1057 mFontDescriptionCache.push_back( description );
1059 // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1060 mCharacterSetCache.PushBack( characterSet );
1062 // Cache the index and the matched font's description.
1063 FontDescriptionCacheItem item( description,
1066 mValidatedFontCache.push_back( std::move( item ) );
1068 if( ( fontDescription.family != description.family ) ||
1069 ( fontDescription.width != description.width ) ||
1070 ( fontDescription.weight != description.weight ) ||
1071 ( fontDescription.slant != description.slant ) )
1073 // Cache the given font's description if it's different than the matched.
1074 FontDescriptionCacheItem item( fontDescription,
1077 mValidatedFontCache.push_back( std::move( item ) );
1082 DALI_LOG_INFO( gLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str() );
1085 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n" );
1088 void FontClient::Plugin::GetFontMetrics( FontId fontId,
1089 FontMetrics& metrics )
1091 const FontId index = fontId - 1u;
1093 if( ( fontId > 0 ) &&
1094 ( index < mFontIdCache.Count() ) )
1096 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1098 switch( fontIdCacheItem.type )
1100 case FontDescription::FACE_FONT:
1102 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1104 metrics = font.mMetrics;
1106 // Adjust the metrics if the fixed-size font should be down-scaled
1107 if( font.mIsFixedSizeBitmap )
1109 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1111 if( desiredFixedSize > 0.f )
1113 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1115 metrics.ascender = metrics.ascender * scaleFactor;
1116 metrics.descender = metrics.descender * scaleFactor;
1117 metrics.height = metrics.height * scaleFactor;
1118 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1119 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1124 case FontDescription::BITMAP_FONT:
1126 const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1128 metrics.ascender = bitmapFontCacheItem.font.ascender;
1129 metrics.descender = bitmapFontCacheItem.font.descender;
1130 metrics.height = metrics.ascender - metrics.descender;
1131 metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1132 metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1137 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1143 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
1147 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
1148 Character charcode )
1150 GlyphIndex glyphIndex = 0u;
1151 const FontId index = fontId - 1u;
1153 if( ( fontId > 0u ) &&
1154 ( index < mFontIdCache.Count() ) )
1156 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1158 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1160 FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1162 glyphIndex = FT_Get_Char_Index( ftFace, charcode );
1169 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
1174 if( VECTOR_GLYPH == type )
1176 return GetVectorMetrics( array, size, horizontal );
1179 return GetBitmapMetrics( array, size, horizontal );
1182 bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
1186 bool success( true );
1188 for( unsigned int i=0; i<size; ++i )
1190 GlyphInfo& glyph = array[i];
1192 FontId index = glyph.fontId - 1u;
1194 if( ( glyph.fontId > 0u ) &&
1195 ( index < mFontIdCache.Count() ) )
1197 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1199 switch( fontIdCacheItem.type )
1201 case FontDescription::FACE_FONT:
1203 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1205 FT_Face ftFace = font.mFreeTypeFace;
1207 #ifdef FREETYPE_BITMAP_SUPPORT
1208 // Check to see if we should be loading a Fixed Size bitmap?
1209 if( font.mIsFixedSizeBitmap )
1211 FT_Select_Size( ftFace, font.mFixedSizeIndex ); ///< @todo: needs to be investigated why it's needed to select the size again.
1212 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR );
1213 if ( FT_Err_Ok == error )
1215 glyph.width = font.mFixedWidthPixels;
1216 glyph.height = font.mFixedHeightPixels;
1217 glyph.advance = font.mFixedWidthPixels;
1218 glyph.xBearing = 0.0f;
1219 glyph.yBearing = font.mFixedHeightPixels;
1221 // Adjust the metrics if the fixed-size font should be down-scaled
1222 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1224 if( desiredFixedSize > 0.f )
1226 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1228 glyph.width = glyph.width * scaleFactor ;
1229 glyph.height = glyph.height * scaleFactor;
1230 glyph.advance = glyph.advance * scaleFactor;
1231 glyph.xBearing = glyph.xBearing * scaleFactor;
1232 glyph.yBearing = glyph.yBearing * scaleFactor;
1234 glyph.scaleFactor = scaleFactor;
1239 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error );
1246 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT );
1248 if( FT_Err_Ok == error )
1250 glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1251 glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
1254 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
1255 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
1259 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
1260 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
1270 case FontDescription::BITMAP_FONT:
1272 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1274 unsigned int index = 0u;
1275 for( auto& item : bitmapFontCacheItem.font.glyphs )
1277 if( item.utf32 == glyph.index )
1279 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1282 pixelBuffer = LoadImageFromFile( item.url );
1285 glyph.width = static_cast< float >( pixelBuffer.GetWidth() );
1286 glyph.height = static_cast< float >( pixelBuffer.GetHeight() );
1287 glyph.xBearing = 0.f;
1288 glyph.yBearing = glyph.height + item.descender;
1289 glyph.advance = glyph.width;
1290 glyph.scaleFactor = 1.f;
1301 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1307 // Check if it's an embedded image.
1308 if( ( 0u == glyph.fontId ) && ( 0u != glyph.index ) && ( glyph.index <= mEmbeddedItemCache.Count() ) )
1310 const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1312 glyph.width = static_cast<float>( item.width );
1313 glyph.height = static_cast<float>( item.height );
1314 glyph.xBearing = 0.f;
1315 glyph.yBearing = glyph.height;
1316 glyph.advance = glyph.width;
1317 glyph.scaleFactor = 1.f;
1329 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
1333 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1334 bool success( true );
1336 for( unsigned int i = 0u; i < size; ++i )
1338 FontId fontId = array[i].fontId;
1340 if( ( fontId > 0u ) &&
1341 ( fontId - 1u ) < mFontIdCache.Count() )
1343 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1345 if( ! font.mVectorFontId )
1347 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1350 mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
1352 // Vector metrics are in EMs, convert to pixels
1353 const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
1354 array[i].width *= scale;
1355 array[i].height *= scale;
1356 array[i].xBearing *= scale;
1357 array[i].yBearing *= scale;
1358 array[i].advance *= scale;
1372 void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth )
1374 const FontId index = fontId - 1u;
1376 if( ( fontId > 0u ) &&
1377 ( index < mFontIdCache.Count() ) )
1379 data.isColorBitmap = false;
1380 data.isColorEmoji = false;
1382 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1384 switch( fontIdCacheItem.type )
1386 case FontDescription::FACE_FONT:
1388 // For the software italics.
1389 bool isShearRequired = false;
1391 const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1392 FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1396 #ifdef FREETYPE_BITMAP_SUPPORT
1397 // Check to see if this is fixed size bitmap
1398 if( fontFaceCacheItem.mIsFixedSizeBitmap )
1400 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1405 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT );
1407 if( FT_Err_Ok == error )
1409 if( isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) )
1411 // Does the software bold.
1412 FT_GlyphSlot_Embolden( ftFace->glyph );
1415 if( isItalicRequired && !( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) )
1417 // Will do the software italic.
1418 isShearRequired = true;
1422 error = FT_Get_Glyph( ftFace->glyph, &glyph );
1424 // Convert to bitmap if necessary
1425 if ( FT_Err_Ok == error )
1427 if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
1429 // Check whether we should create a bitmap for the outline
1430 if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 )
1434 error = FT_Stroker_New( mFreeTypeLibrary, &stroker );
1436 if( FT_Err_Ok == error )
1438 FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 );
1439 error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 );
1441 if( FT_Err_Ok == error )
1443 FT_Stroker_Done( stroker );
1447 DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error );
1452 DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error );
1456 error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
1457 if( FT_Err_Ok == error )
1459 FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph );
1460 ConvertBitmap( data, bitmapGlyph->bitmap, isShearRequired );
1464 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error );
1469 ConvertBitmap( data, ftFace->glyph->bitmap, isShearRequired );
1472 data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap;
1474 // Created FT_Glyph object must be released with FT_Done_Glyph
1475 FT_Done_Glyph( glyph );
1480 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error );
1484 case FontDescription::BITMAP_FONT:
1486 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1488 unsigned int index = 0u;
1489 for( auto& item : bitmapFontCacheItem.font.glyphs )
1491 if( item.utf32 == glyphIndex )
1493 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1496 pixelBuffer = LoadImageFromFile( item.url );
1499 data.width = pixelBuffer.GetWidth();
1500 data.height = pixelBuffer.GetHeight();
1502 data.isColorBitmap = bitmapFontCacheItem.font.isColorFont;
1504 ConvertBitmap( data, data.width, data.height, pixelBuffer.GetBuffer() );
1506 // Sets the pixel format.
1507 data.format = pixelBuffer.GetPixelFormat();
1516 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1522 if( ( 0u != glyphIndex ) && ( glyphIndex <= mEmbeddedItemCache.Count() ) )
1524 // It's an embedded item.
1525 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1527 data.width = item.width;
1528 data.height = item.height;
1529 if( 0u != item.pixelBufferId )
1531 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId-1u].pixelBuffer;
1534 ConvertBitmap( data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer() );
1536 // Sets the pixel format.
1537 data.format = pixelBuffer.GetPixelFormat();
1542 // Creates the output buffer
1543 const unsigned int bufferSize = data.width * data.height * 4u;
1544 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1546 memset( data.buffer, 0u, bufferSize );
1548 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1554 PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
1556 TextAbstraction::FontClient::GlyphBufferData data;
1558 CreateBitmap( fontId, glyphIndex, false, false, data, outlineWidth );
1560 return PixelData::New( data.buffer,
1561 data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
1565 PixelData::DELETE_ARRAY );
1568 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
1573 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1574 if( ( fontId > 0u ) &&
1575 ( fontId - 1u < mFontIdCache.Count() ) )
1577 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1578 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1580 if( ! font.mVectorFontId )
1582 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1585 mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
1590 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
1592 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" );
1593 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize );
1595 // First look into the cache if there is an ellipsis glyph for the requested point size.
1596 for( const auto& item : mEllipsisCache )
1598 if( item.requestedPointSize != requestedPointSize )
1600 // Use the glyph in the cache.
1602 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1603 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1604 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1610 // No glyph has been found. Create one.
1611 mEllipsisCache.PushBack( EllipsisItem() );
1612 EllipsisItem& item = *( mEllipsisCache.End() - 1u );
1614 item.requestedPointSize = requestedPointSize;
1616 // Find a font for the ellipsis glyph.
1617 item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
1621 // Set the character index to access the glyph inside the font.
1622 item.glyph.index = FT_Get_Char_Index( mFontFaceCache[mFontIdCache[item.glyph.fontId-1u].id].mFreeTypeFace,
1623 ELLIPSIS_CHARACTER );
1625 GetBitmapMetrics( &item.glyph, 1u, true );
1627 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1628 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1629 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1634 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1636 FT_Error error = -1;
1638 const FontId index = fontId - 1u;
1640 if( ( fontId > 0u ) &&
1641 ( index < mFontIdCache.Count() ) )
1643 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1645 switch( fontIdCacheItem.type )
1647 case FontDescription::FACE_FONT:
1649 #ifdef FREETYPE_BITMAP_SUPPORT
1650 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1651 FT_Face ftFace = item.mFreeTypeFace;
1653 // Check to see if this is fixed size bitmap
1654 if( item.mHasColorTables )
1656 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1661 case FontDescription::BITMAP_FONT:
1663 error = FT_Err_Ok; // Will return true;
1668 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1673 return FT_Err_Ok == error;
1676 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId )
1678 FT_Face fontFace = nullptr;
1680 const FontId index = fontId - 1u;
1681 if( ( fontId > 0u ) &&
1682 ( index < mFontIdCache.Count() ) )
1684 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1686 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1688 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1694 FontDescription::Type FontClient::Plugin::GetFontType( FontId fontId )
1696 const FontId index = fontId - 1u;
1697 if( ( fontId > 0u ) &&
1698 ( index < mFontIdCache.Count() ) )
1700 return mFontIdCache[index].type;
1702 return FontDescription::INVALID;
1705 bool FontClient::Plugin::AddCustomFontDirectory( const FontPath& path )
1707 // nullptr as first parameter means the current configuration is used.
1708 return FcConfigAppFontAddDir( nullptr, reinterpret_cast<const FcChar8 *>( path.c_str() ) );
1711 GlyphIndex FontClient::Plugin::CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat )
1713 EmbeddedItem embeddedItem;
1715 embeddedItem.pixelBufferId = 0u;
1716 embeddedItem.width = description.width;
1717 embeddedItem.height = description.height;
1719 pixelFormat = Pixel::A8;
1721 if( !description.url.empty() )
1723 // Check if the url is in the cache.
1724 PixelBufferId index = 0u;
1726 for( const auto& cacheItem : mPixelBufferCache )
1729 if( cacheItem.url == description.url )
1731 // The url is in the pixel buffer cache.
1732 // Set the index +1 to the vector.
1733 embeddedItem.pixelBufferId = index;
1738 Devel::PixelBuffer pixelBuffer;
1739 if( 0u == embeddedItem.pixelBufferId )
1741 // The pixel buffer is not in the cache. Create one and cache it.
1743 // Load the image from the url.
1744 pixelBuffer = LoadImageFromFile( description.url );
1746 // Create the cache item.
1747 PixelBufferCacheItem pixelBufferCacheItem;
1748 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1749 pixelBufferCacheItem.url = description.url;
1751 // Store the cache item in the cache.
1752 mPixelBufferCache.push_back( std::move( pixelBufferCacheItem ) );
1754 // Set the pixel buffer id to the embedded item.
1755 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1759 // Retrieve the pixel buffer from the cache to set the pixel format.
1760 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId-1u].pixelBuffer;
1765 // Set the size of the embedded item if it has not been set.
1766 if( 0u == embeddedItem.width )
1768 embeddedItem.width = static_cast<unsigned int>( pixelBuffer.GetWidth() );
1771 if( 0u == embeddedItem.height )
1773 embeddedItem.height = static_cast<unsigned int>( pixelBuffer.GetHeight() );
1776 // Set the pixel format.
1777 pixelFormat = pixelBuffer.GetPixelFormat();
1781 // Find if the same embeddedItem has already been created.
1782 unsigned int index = 0u;
1783 for( const auto& item : mEmbeddedItemCache )
1786 if( ( item.pixelBufferId == embeddedItem.pixelBufferId ) &&
1787 ( item.width == embeddedItem.width ) &&
1788 ( item.height == embeddedItem.height ) )
1794 // Cache the embedded item.
1795 mEmbeddedItemCache.PushBack( embeddedItem );
1797 return mEmbeddedItemCache.Count();
1800 void FontClient::Plugin::InitSystemFonts()
1802 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1804 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1808 DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont );
1810 // Reserve some space to avoid reallocations.
1811 mSystemFonts.reserve( fontSet->nfont );
1813 for( int i = 0u; i < fontSet->nfont; ++i )
1815 FcPattern* fontPattern = fontSet->fonts[i];
1819 // Skip fonts with no path
1820 if( GetFcString( fontPattern, FC_FILE, path ) )
1822 mSystemFonts.push_back( FontDescription() );
1823 FontDescription& fontDescription = mSystemFonts.back();
1825 fontDescription.path = std::move( path );
1830 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1831 GetFcInt( fontPattern, FC_WIDTH, width );
1832 GetFcInt( fontPattern, FC_WEIGHT, weight );
1833 GetFcInt( fontPattern, FC_SLANT, slant );
1834 fontDescription.width = IntToWidthType( width );
1835 fontDescription.weight = IntToWeightType( weight );
1836 fontDescription.slant = IntToSlantType( slant );
1838 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str() );
1839 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1840 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1841 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1842 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1846 // Destroys the font set created.
1847 FcFontSetDestroy( fontSet );
1849 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1852 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1854 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1856 FcResult result = FcResultMatch;
1857 FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result ); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1859 const bool matched = nullptr != match;
1860 DALI_LOG_INFO( gLogFilter, Debug::General, " pattern matched : %s\n", ( matched ? "true" : "false" ) );
1867 GetFcString( match, FC_FILE, fontDescription.path );
1868 GetFcString( match, FC_FAMILY, fontDescription.family );
1869 GetFcInt( match, FC_WIDTH, width );
1870 GetFcInt( match, FC_WEIGHT, weight );
1871 GetFcInt( match, FC_SLANT, slant );
1872 fontDescription.width = IntToWidthType( width );
1873 fontDescription.weight = IntToWeightType( weight );
1874 fontDescription.slant = IntToSlantType( slant );
1876 // Retrieve the character set and increase the reference counter.
1877 FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1878 *characterSet = FcCharSetCopy( *characterSet );
1880 // destroyed the matched pattern
1881 FcPatternDestroy( match );
1883 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
1884 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1885 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1886 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1887 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1890 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1894 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1896 // create the cached font family lookup pattern
1897 // a pattern holds a set of names, each name refers to a property of the font
1898 FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1900 if( !fontFamilyPattern )
1905 // add a property to the pattern for the font family
1906 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1908 // add a property to the pattern for local setting.
1909 const char* locale = setlocale( LC_MESSAGES, nullptr );
1910 if( locale != nullptr)
1912 FcPatternAddString( fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>( locale ) );
1915 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1919 width = DEFAULT_FONT_WIDTH;
1922 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1926 weight = DEFAULT_FONT_WEIGHT;
1929 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1933 slant = DEFAULT_FONT_SLANT;
1936 FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1937 FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1938 FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1940 // modify the config, with the mFontFamilyPatterm
1941 FcConfigSubstitute( nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern );
1943 // provide default values for unspecified properties in the font pattern
1944 // e.g. patterns without a specified style or weight are set to Medium
1945 FcDefaultSubstitute( fontFamilyPattern );
1947 return fontFamilyPattern;
1950 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1952 FcFontSet* fontset = nullptr;
1954 // create a new pattern.
1955 // a pattern holds a set of names, each name refers to a property of the font
1956 FcPattern* pattern = FcPatternCreate();
1958 if( nullptr != pattern )
1960 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1961 FcObjectSet* objectSet = FcObjectSetCreate();
1963 if( nullptr != objectSet )
1965 // build an object set from a list of property names
1966 FcObjectSetAdd( objectSet, FC_FILE );
1967 FcObjectSetAdd( objectSet, FC_FAMILY );
1968 FcObjectSetAdd( objectSet, FC_WIDTH );
1969 FcObjectSetAdd( objectSet, FC_WEIGHT );
1970 FcObjectSetAdd( objectSet, FC_SLANT );
1972 // get a list of fonts
1973 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1974 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.
1976 // clear up the object set
1977 FcObjectSetDestroy( objectSet );
1980 // clear up the pattern
1981 FcPatternDestroy( pattern );
1987 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
1988 const char* const n,
1989 std::string& string )
1991 FcChar8* file = nullptr;
1992 const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
1994 if( FcResultMatch == retVal )
1996 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1997 string.assign( reinterpret_cast<const char*>( file ) );
2005 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
2007 const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
2009 if( FcResultMatch == retVal )
2017 FontId FontClient::Plugin::CreateFont( const FontPath& path,
2018 PointSize26Dot6 requestedPointSize,
2019 FaceIndex faceIndex,
2020 bool cacheDescription )
2022 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" );
2023 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2024 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2028 // Create & cache new font face
2030 int error = FT_New_Face( mFreeTypeLibrary,
2035 if( FT_Err_Ok == error )
2037 // Check if a font is scalable.
2038 const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) );
2039 const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes );
2040 const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) );
2041 FontId fontFaceId = 0u;
2043 DALI_LOG_INFO( gLogFilter, Debug::General, " isScalable : [%s]\n", ( isScalable ? "true" : "false" ) );
2044 DALI_LOG_INFO( gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) );
2045 DALI_LOG_INFO( gLogFilter, Debug::General, " hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) );
2047 // Check to see if the font contains fixed sizes?
2048 if( !isScalable && hasFixedSizedBitmaps )
2050 PointSize26Dot6 actualPointSize = 0u;
2051 int fixedSizeIndex = 0;
2052 for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex )
2054 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2055 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize );
2057 if( fixedSize >= requestedPointSize )
2059 actualPointSize = fixedSize;
2064 if( 0u == actualPointSize )
2066 // The requested point size is bigger than the bigest fixed size.
2067 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2068 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2071 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize );
2073 // Tell Freetype to use this size
2074 error = FT_Select_Size( ftFace, fixedSizeIndex );
2075 if ( FT_Err_Ok != error )
2077 DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error );
2081 const float fixedWidth = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].width );
2082 const float fixedHeight = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].height );
2084 // Indicate that the font is a fixed sized bitmap
2085 FontMetrics metrics( fixedHeight, // The ascender in pixels.
2087 fixedHeight, // The height in pixels.
2091 // Create the FreeType font face item to cache.
2092 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables );
2094 // Set the index to the font's id cache.
2095 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2097 // Create the font id item to cache.
2098 FontIdCacheItem fontIdCacheItem;
2099 fontIdCacheItem.type = FontDescription::FACE_FONT;
2101 // Set the index to the FreeType font face cache.
2102 fontIdCacheItem.id = mFontFaceCache.size();
2103 fontFaceId = fontIdCacheItem.id + 1u;
2106 mFontFaceCache.push_back( fontFaceCacheItem );
2107 mFontIdCache.PushBack( fontIdCacheItem );
2109 // Set the font id to be returned.
2110 id = mFontIdCache.Count();
2115 error = FT_Set_Char_Size( ftFace,
2121 if( FT_Err_Ok == error )
2124 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2126 FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266,
2127 static_cast< float >( ftMetrics.descender ) * FROM_266,
2128 static_cast< float >( ftMetrics.height ) * FROM_266,
2129 static_cast< float >( ftFace->underline_position ) * FROM_266,
2130 static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
2132 // Create the FreeType font face item to cache.
2133 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics );
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();
2155 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize );
2159 if( 0u != fontFaceId )
2161 if( cacheDescription )
2163 CacheFontPath( ftFace, fontFaceId, requestedPointSize, path );
2169 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str() );
2172 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
2173 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" );
2178 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer )
2180 // Set the input dimensions.
2181 const ImageDimensions inputDimensions( srcWidth, srcHeight );
2183 // Set the output dimensions.
2184 // If the output dimension is not given, the input dimension is set
2185 // and won't be downscaling.
2186 data.width = ( data.width == 0 ) ? srcWidth : data.width;
2187 data.height = ( data.height == 0 ) ? srcHeight : data.height;
2188 const ImageDimensions desiredDimensions( data.width, data.height );
2190 // Creates the output buffer
2191 const unsigned int bufferSize = data.width * data.height * 4u;
2192 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2194 if( inputDimensions == desiredDimensions )
2196 // There isn't downscaling.
2197 memcpy( data.buffer, srcBuffer, bufferSize );
2201 Dali::Internal::Platform::LanczosSample4BPP( srcBuffer,
2204 desiredDimensions );
2208 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired )
2210 if( srcBitmap.width*srcBitmap.rows > 0 )
2212 switch( srcBitmap.pixel_mode )
2214 case FT_PIXEL_MODE_GRAY:
2216 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
2218 uint8_t* pixelsIn = srcBitmap.buffer;
2219 unsigned int width = srcBitmap.width;
2220 unsigned height = srcBitmap.rows;
2222 std::unique_ptr<uint8_t, void(*)(void*)> pixelsOutPtr( nullptr, free );
2224 if( isShearRequired )
2227 * Glyphs' bitmaps with no slant retrieved from FreeType:
2237 * Expected glyphs' bitmaps with italic slant:
2238 * ____________ ______
2245 * ------------ ------
2247 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2257 * 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.
2259 unsigned int widthOut = 0u;
2260 unsigned int heightOut = 0u;
2261 uint8_t* pixelsOut = nullptr;
2263 Dali::Internal::Platform::HorizontalShear( pixelsIn,
2267 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2274 pixelsIn = pixelsOut;
2275 pixelsOutPtr.reset( pixelsOut );
2278 const unsigned int bufferSize = width * height;
2279 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2281 data.height = height;
2282 data.format = Pixel::L8; // Sets the pixel format.
2283 memcpy( data.buffer, pixelsIn, bufferSize );
2288 #ifdef FREETYPE_BITMAP_SUPPORT
2289 case FT_PIXEL_MODE_BGRA:
2291 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
2293 ConvertBitmap( data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer );
2295 // Sets the pixel format.
2296 data.format = Pixel::BGRA8888;
2303 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" );
2310 bool FontClient::Plugin::FindFont( const FontPath& path,
2311 PointSize26Dot6 requestedPointSize,
2312 FaceIndex faceIndex,
2313 FontId& fontId ) const
2315 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2316 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2317 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2318 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size() );
2321 for( const auto& cacheItem : mFontFaceCache )
2323 if( cacheItem.mRequestedPointSize == requestedPointSize &&
2324 cacheItem.mFaceIndex == faceIndex &&
2325 cacheItem.mPath == path )
2327 fontId = cacheItem.mFontId + 1u;
2329 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2330 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2336 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found\n" );
2337 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2342 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
2343 FontDescriptionId& validatedFontId )
2345 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" );
2346 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2347 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2348 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2349 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2350 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2351 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size() );
2353 validatedFontId = 0u;
2355 for( const auto& item : mValidatedFontCache )
2357 if( !fontDescription.family.empty() &&
2358 ( fontDescription.family == item.fontDescription.family ) &&
2359 ( fontDescription.width == item.fontDescription.width ) &&
2360 ( fontDescription.weight == item.fontDescription.weight ) &&
2361 ( fontDescription.slant == item.fontDescription.slant ) )
2363 validatedFontId = item.index;
2365 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId );
2366 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2371 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font not found\n" );
2372 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2376 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
2377 FontList*& fontList,
2378 CharacterSetList*& characterSetList )
2380 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" );
2381 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2382 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2383 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2384 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2385 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2386 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size() );
2390 for( const auto& item : mFallbackCache )
2392 if( !fontDescription.family.empty() &&
2393 ( fontDescription.family == item.fontDescription.family ) &&
2394 ( fontDescription.width == item.fontDescription.width ) &&
2395 ( fontDescription.weight == item.fontDescription.weight ) &&
2396 ( fontDescription.slant == item.fontDescription.slant ) )
2398 fontList = item.fallbackFonts;
2399 characterSetList = item.characterSets;
2401 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list found.\n" );
2402 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2407 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list not found.\n" );
2408 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2412 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
2413 PointSize26Dot6 requestedPointSize,
2416 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2417 DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId );
2418 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2422 for( const auto& item : mFontDescriptionSizeCache )
2424 if( ( validatedFontId == item.validatedFontId ) &&
2425 ( requestedPointSize == item.requestedPointSize ) )
2427 fontId = item.fontId;
2429 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2430 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2435 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found.\n" );
2436 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2440 bool FontClient::Plugin::FindBitmapFont( const FontFamily& bitmapFont, FontId& fontId ) const
2444 for( const auto& item : mBitmapFontCache )
2446 if( bitmapFont == item.font.name )
2448 fontId = item.id + 1u;
2456 bool FontClient::Plugin::IsScalable( const FontPath& path )
2458 bool isScalable = false;
2461 int error = FT_New_Face( mFreeTypeLibrary,
2465 if( FT_Err_Ok != error )
2467 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() );
2471 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2477 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
2479 // Create a font pattern.
2480 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2482 FcResult result = FcResultMatch;
2484 // match the pattern
2485 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2486 bool isScalable = false;
2490 // Get the path to the font file name.
2492 GetFcString( match, FC_FILE, path );
2493 isScalable = IsScalable( path );
2497 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2500 // Destroys the created patterns.
2501 FcPatternDestroy( match );
2502 FcPatternDestroy( fontFamilyPattern );
2507 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
2509 // Empty the caller container
2513 int error = FT_New_Face( mFreeTypeLibrary,
2517 if( FT_Err_Ok != error )
2519 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() );
2522 // Fetch the number of fixed sizes available
2523 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
2525 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
2527 sizes.PushBack( ftFace->available_sizes[ i ].size );
2532 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
2533 Vector< PointSize26Dot6 >& sizes )
2535 // Create a font pattern.
2536 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2538 FcResult result = FcResultMatch;
2540 // match the pattern
2541 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2545 // Get the path to the font file name.
2547 GetFcString( match, FC_FILE, path );
2548 GetFixedSizes( path, sizes );
2552 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2555 // Destroys the created patterns.
2556 FcPatternDestroy( match );
2557 FcPatternDestroy( fontFamilyPattern );
2560 bool FontClient::Plugin::HasItalicStyle( FontId fontId ) const
2562 bool hasItalicStyle = false;
2564 const FontId index = fontId - 1u;
2566 if( ( fontId > 0 ) &&
2567 ( index < mFontIdCache.Count() ) )
2569 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2571 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
2573 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2575 hasItalicStyle = 0u != ( font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC );
2580 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
2583 return hasItalicStyle;
2586 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path )
2588 FontDescription description;
2589 description.path = path;
2590 description.family = std::move( FontFamily( ftFace->family_name ) );
2591 description.weight = FontWeight::NONE;
2592 description.width = FontWidth::NONE;
2593 description.slant = FontSlant::NONE;
2595 // Note FreeType doesn't give too much info to build a proper font style.
2596 if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
2598 description.slant = FontSlant::ITALIC;
2600 if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
2602 description.weight = FontWeight::BOLD;
2605 FontDescriptionId validatedFontId = 0u;
2606 if( !FindValidatedFont( description,
2609 // Set the index to the vector of paths to font file names.
2610 validatedFontId = mFontDescriptionCache.size();
2612 FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2614 FcResult result = FcResultMatch;
2615 FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2617 FcCharSet* characterSet = nullptr;
2618 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2620 const FontId fontFaceId = id - 1u;
2621 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( characterSet ); // Increases the reference counter.
2623 // Destroys the created patterns.
2624 FcPatternDestroy( match );
2625 FcPatternDestroy( pattern );
2627 // Add the path to the cache.
2628 description.type = FontDescription::FACE_FONT;
2629 mFontDescriptionCache.push_back( description );
2631 // Increase the reference counter and add the character set to the cache.
2632 mCharacterSetCache.PushBack( FcCharSetCopy( characterSet ) );
2634 // Cache the index and the font's description.
2635 mValidatedFontCache.push_back( std::move( FontDescriptionCacheItem( std::move( description ),
2636 validatedFontId) ) );
2638 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2639 mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
2645 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description )
2647 FcCharSet* characterSet = nullptr;
2649 FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2651 if( nullptr != pattern )
2653 FcResult result = FcResultMatch;
2654 FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2656 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2658 // Destroys the created patterns.
2659 FcPatternDestroy( match );
2660 FcPatternDestroy( pattern );
2663 return characterSet;
2666 void FontClient::Plugin::ClearFallbackCache( std::vector<FallbackCacheItem>& fallbackCache )
2668 for( auto& item : fallbackCache )
2670 if( nullptr != item.fallbackFonts )
2672 delete item.fallbackFonts;
2675 if( nullptr != item.characterSets )
2677 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2678 DestroyCharacterSets( *item.characterSets );
2679 delete item.characterSets;
2684 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2686 for( auto& item : mFontFaceCache )
2688 FcCharSetDestroy( item.mCharacterSet );
2689 item.mCharacterSet = nullptr;
2693 } // namespace Internal
2695 } // namespace TextAbstraction