2 * Copyright (c) 2018 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 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 // Created FT_Glyph object must be released with FT_Done_Glyph
1470 FT_Done_Glyph( glyph );
1475 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error );
1479 case FontDescription::BITMAP_FONT:
1481 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1483 unsigned int index = 0u;
1484 for( auto& item : bitmapFontCacheItem.font.glyphs )
1486 if( item.utf32 == glyphIndex )
1488 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1491 pixelBuffer = LoadImageFromFile( item.url );
1494 data.width = pixelBuffer.GetWidth();
1495 data.height = pixelBuffer.GetHeight();
1497 ConvertBitmap( data, data.width, data.height, pixelBuffer.GetBuffer() );
1499 // Sets the pixel format.
1500 data.format = pixelBuffer.GetPixelFormat();
1509 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1515 if( ( 0u != glyphIndex ) && ( glyphIndex <= mEmbeddedItemCache.Count() ) )
1517 // It's an embedded item.
1518 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1520 data.width = item.width;
1521 data.height = item.height;
1522 if( 0u != item.pixelBufferId )
1524 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId-1u].pixelBuffer;
1527 ConvertBitmap( data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer() );
1529 // Sets the pixel format.
1530 data.format = pixelBuffer.GetPixelFormat();
1535 // Creates the output buffer
1536 const unsigned int bufferSize = data.width * data.height * 4u;
1537 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1539 memset( data.buffer, 0u, bufferSize );
1541 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1547 PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
1549 TextAbstraction::FontClient::GlyphBufferData data;
1551 CreateBitmap( fontId, glyphIndex, false, false, data, outlineWidth );
1553 return PixelData::New( data.buffer,
1554 data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
1558 PixelData::DELETE_ARRAY );
1561 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
1566 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1567 if( ( fontId > 0u ) &&
1568 ( fontId - 1u < mFontIdCache.Count() ) )
1570 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1571 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1573 if( ! font.mVectorFontId )
1575 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1578 mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
1583 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
1585 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" );
1586 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize );
1588 // First look into the cache if there is an ellipsis glyph for the requested point size.
1589 for( const auto& item : mEllipsisCache )
1591 if( item.requestedPointSize != requestedPointSize )
1593 // Use the glyph in the cache.
1595 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1596 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1597 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1603 // No glyph has been found. Create one.
1604 mEllipsisCache.PushBack( EllipsisItem() );
1605 EllipsisItem& item = *( mEllipsisCache.End() - 1u );
1607 item.requestedPointSize = requestedPointSize;
1609 // Find a font for the ellipsis glyph.
1610 item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
1614 // Set the character index to access the glyph inside the font.
1615 item.glyph.index = FT_Get_Char_Index( mFontFaceCache[mFontIdCache[item.glyph.fontId-1u].id].mFreeTypeFace,
1616 ELLIPSIS_CHARACTER );
1618 GetBitmapMetrics( &item.glyph, 1u, true );
1620 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1621 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1622 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1627 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1629 FT_Error error = -1;
1631 const FontId index = fontId - 1u;
1633 #ifdef FREETYPE_BITMAP_SUPPORT
1634 if( ( fontId > 0u ) &&
1635 ( index < mFontIdCache.Count() ) )
1637 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1639 switch( fontIdCacheItem.type )
1641 case FontDescription::FACE_FONT:
1643 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1644 FT_Face ftFace = item.mFreeTypeFace;
1646 // Check to see if this is fixed size bitmap
1647 if( item.mHasColorTables )
1649 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1653 case FontDescription::BITMAP_FONT:
1655 error = FT_Err_Ok; // Will return true;
1660 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1666 return FT_Err_Ok == error;
1669 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId )
1671 FT_Face fontFace = nullptr;
1673 const FontId index = fontId - 1u;
1674 if( ( fontId > 0u ) &&
1675 ( index < mFontIdCache.Count() ) )
1677 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1679 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1681 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1687 FontDescription::Type FontClient::Plugin::GetFontType( FontId fontId )
1689 const FontId index = fontId - 1u;
1690 if( ( fontId > 0u ) &&
1691 ( index < mFontIdCache.Count() ) )
1693 return mFontIdCache[index].type;
1695 return FontDescription::INVALID;
1698 bool FontClient::Plugin::AddCustomFontDirectory( const FontPath& path )
1700 // nullptr as first parameter means the current configuration is used.
1701 return FcConfigAppFontAddDir( nullptr, reinterpret_cast<const FcChar8 *>( path.c_str() ) );
1704 GlyphIndex FontClient::Plugin::CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat )
1706 EmbeddedItem embeddedItem;
1708 embeddedItem.pixelBufferId = 0u;
1709 embeddedItem.width = description.width;
1710 embeddedItem.height = description.height;
1712 pixelFormat = Pixel::A8;
1714 if( !description.url.empty() )
1716 // Check if the url is in the cache.
1717 PixelBufferId index = 0u;
1719 for( const auto& cacheItem : mPixelBufferCache )
1722 if( cacheItem.url == description.url )
1724 // The url is in the pixel buffer cache.
1725 // Set the index +1 to the vector.
1726 embeddedItem.pixelBufferId = index;
1731 Devel::PixelBuffer pixelBuffer;
1732 if( 0u == embeddedItem.pixelBufferId )
1734 // The pixel buffer is not in the cache. Create one and cache it.
1736 // Load the image from the url.
1737 pixelBuffer = LoadImageFromFile( description.url );
1739 // Create the cache item.
1740 PixelBufferCacheItem pixelBufferCacheItem;
1741 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1742 pixelBufferCacheItem.url = description.url;
1744 // Store the cache item in the cache.
1745 mPixelBufferCache.push_back( std::move( pixelBufferCacheItem ) );
1747 // Set the pixel buffer id to the embedded item.
1748 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1752 // Retrieve the pixel buffer from the cache to set the pixel format.
1753 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId-1u].pixelBuffer;
1758 // Set the size of the embedded item if it has not been set.
1759 if( 0u == embeddedItem.width )
1761 embeddedItem.width = static_cast<unsigned int>( pixelBuffer.GetWidth() );
1764 if( 0u == embeddedItem.height )
1766 embeddedItem.height = static_cast<unsigned int>( pixelBuffer.GetHeight() );
1769 // Set the pixel format.
1770 pixelFormat = pixelBuffer.GetPixelFormat();
1774 // Find if the same embeddedItem has already been created.
1775 unsigned int index = 0u;
1776 for( const auto& item : mEmbeddedItemCache )
1779 if( ( item.pixelBufferId == embeddedItem.pixelBufferId ) &&
1780 ( item.width == embeddedItem.width ) &&
1781 ( item.height == embeddedItem.height ) )
1787 // Cache the embedded item.
1788 mEmbeddedItemCache.PushBack( embeddedItem );
1790 return mEmbeddedItemCache.Count();
1793 void FontClient::Plugin::InitSystemFonts()
1795 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1797 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1801 DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont );
1803 // Reserve some space to avoid reallocations.
1804 mSystemFonts.reserve( fontSet->nfont );
1806 for( int i = 0u; i < fontSet->nfont; ++i )
1808 FcPattern* fontPattern = fontSet->fonts[i];
1812 // Skip fonts with no path
1813 if( GetFcString( fontPattern, FC_FILE, path ) )
1815 mSystemFonts.push_back( FontDescription() );
1816 FontDescription& fontDescription = mSystemFonts.back();
1818 fontDescription.path = std::move( path );
1823 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1824 GetFcInt( fontPattern, FC_WIDTH, width );
1825 GetFcInt( fontPattern, FC_WEIGHT, weight );
1826 GetFcInt( fontPattern, FC_SLANT, slant );
1827 fontDescription.width = IntToWidthType( width );
1828 fontDescription.weight = IntToWeightType( weight );
1829 fontDescription.slant = IntToSlantType( slant );
1831 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str() );
1832 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1833 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1834 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1835 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1839 // Destroys the font set created.
1840 FcFontSetDestroy( fontSet );
1842 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1845 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1847 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1849 FcResult result = FcResultMatch;
1850 FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result ); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1852 const bool matched = nullptr != match;
1853 DALI_LOG_INFO( gLogFilter, Debug::General, " pattern matched : %s\n", ( matched ? "true" : "false" ) );
1860 GetFcString( match, FC_FILE, fontDescription.path );
1861 GetFcString( match, FC_FAMILY, fontDescription.family );
1862 GetFcInt( match, FC_WIDTH, width );
1863 GetFcInt( match, FC_WEIGHT, weight );
1864 GetFcInt( match, FC_SLANT, slant );
1865 fontDescription.width = IntToWidthType( width );
1866 fontDescription.weight = IntToWeightType( weight );
1867 fontDescription.slant = IntToSlantType( slant );
1869 // Retrieve the character set and increase the reference counter.
1870 FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1871 *characterSet = FcCharSetCopy( *characterSet );
1873 // destroyed the matched pattern
1874 FcPatternDestroy( match );
1876 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
1877 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1878 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1879 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1880 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1883 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1887 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1889 // create the cached font family lookup pattern
1890 // a pattern holds a set of names, each name refers to a property of the font
1891 FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
1893 if( !fontFamilyPattern )
1898 // add a property to the pattern for the font family
1899 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1901 // add a property to the pattern for local setting.
1902 const char* locale = setlocale( LC_MESSAGES, nullptr );
1903 if( locale != nullptr)
1905 FcPatternAddString( fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>( locale ) );
1908 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1912 width = DEFAULT_FONT_WIDTH;
1915 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1919 weight = DEFAULT_FONT_WEIGHT;
1922 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1926 slant = DEFAULT_FONT_SLANT;
1929 FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1930 FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1931 FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1933 // modify the config, with the mFontFamilyPatterm
1934 FcConfigSubstitute( nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern );
1936 // provide default values for unspecified properties in the font pattern
1937 // e.g. patterns without a specified style or weight are set to Medium
1938 FcDefaultSubstitute( fontFamilyPattern );
1940 return fontFamilyPattern;
1943 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1945 FcFontSet* fontset = nullptr;
1947 // create a new pattern.
1948 // a pattern holds a set of names, each name refers to a property of the font
1949 FcPattern* pattern = FcPatternCreate();
1951 if( nullptr != pattern )
1953 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1954 FcObjectSet* objectSet = FcObjectSetCreate();
1956 if( nullptr != objectSet )
1958 // build an object set from a list of property names
1959 FcObjectSetAdd( objectSet, FC_FILE );
1960 FcObjectSetAdd( objectSet, FC_FAMILY );
1961 FcObjectSetAdd( objectSet, FC_WIDTH );
1962 FcObjectSetAdd( objectSet, FC_WEIGHT );
1963 FcObjectSetAdd( objectSet, FC_SLANT );
1965 // get a list of fonts
1966 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1967 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.
1969 // clear up the object set
1970 FcObjectSetDestroy( objectSet );
1973 // clear up the pattern
1974 FcPatternDestroy( pattern );
1980 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
1981 const char* const n,
1982 std::string& string )
1984 FcChar8* file = nullptr;
1985 const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
1987 if( FcResultMatch == retVal )
1989 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1990 string.assign( reinterpret_cast<const char*>( file ) );
1998 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
2000 const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
2002 if( FcResultMatch == retVal )
2010 FontId FontClient::Plugin::CreateFont( const FontPath& path,
2011 PointSize26Dot6 requestedPointSize,
2012 FaceIndex faceIndex,
2013 bool cacheDescription )
2015 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" );
2016 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2017 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2021 // Create & cache new font face
2023 int error = FT_New_Face( mFreeTypeLibrary,
2028 if( FT_Err_Ok == error )
2030 // Check if a font is scalable.
2031 const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) );
2032 const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes );
2033 const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) );
2034 FontId fontFaceId = 0u;
2036 DALI_LOG_INFO( gLogFilter, Debug::General, " isScalable : [%s]\n", ( isScalable ? "true" : "false" ) );
2037 DALI_LOG_INFO( gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) );
2038 DALI_LOG_INFO( gLogFilter, Debug::General, " hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) );
2040 // Check to see if the font contains fixed sizes?
2041 if( !isScalable && hasFixedSizedBitmaps )
2043 PointSize26Dot6 actualPointSize = 0u;
2044 int fixedSizeIndex = 0;
2045 for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex )
2047 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
2048 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize );
2050 if( fixedSize >= requestedPointSize )
2052 actualPointSize = fixedSize;
2057 if( 0u == actualPointSize )
2059 // The requested point size is bigger than the bigest fixed size.
2060 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
2061 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
2064 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize );
2066 // Tell Freetype to use this size
2067 error = FT_Select_Size( ftFace, fixedSizeIndex );
2068 if ( FT_Err_Ok != error )
2070 DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error );
2074 const float fixedWidth = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].width );
2075 const float fixedHeight = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].height );
2077 // Indicate that the font is a fixed sized bitmap
2078 FontMetrics metrics( fixedHeight, // The ascender in pixels.
2080 fixedHeight, // The height in pixels.
2084 // Create the FreeType font face item to cache.
2085 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables );
2087 // Set the index to the font's id cache.
2088 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2090 // Create the font id item to cache.
2091 FontIdCacheItem fontIdCacheItem;
2092 fontIdCacheItem.type = FontDescription::FACE_FONT;
2094 // Set the index to the FreeType font face cache.
2095 fontIdCacheItem.id = mFontFaceCache.size();
2096 fontFaceId = fontIdCacheItem.id + 1u;
2099 mFontFaceCache.push_back( fontFaceCacheItem );
2100 mFontIdCache.PushBack( fontIdCacheItem );
2102 // Set the font id to be returned.
2103 id = mFontIdCache.Count();
2108 error = FT_Set_Char_Size( ftFace,
2114 if( FT_Err_Ok == error )
2117 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2119 FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266,
2120 static_cast< float >( ftMetrics.descender ) * FROM_266,
2121 static_cast< float >( ftMetrics.height ) * FROM_266,
2122 static_cast< float >( ftFace->underline_position ) * FROM_266,
2123 static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
2125 // Create the FreeType font face item to cache.
2126 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics );
2128 // Set the index to the font's id cache.
2129 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2131 // Create the font id item to cache.
2132 FontIdCacheItem fontIdCacheItem;
2133 fontIdCacheItem.type = FontDescription::FACE_FONT;
2135 // Set the index to the FreeType font face cache.
2136 fontIdCacheItem.id = mFontFaceCache.size();
2137 fontFaceId = fontIdCacheItem.id + 1u;
2140 mFontFaceCache.push_back( fontFaceCacheItem );
2141 mFontIdCache.PushBack( fontIdCacheItem );
2143 // Set the font id to be returned.
2144 id = mFontIdCache.Count();
2148 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize );
2152 if( 0u != fontFaceId )
2154 if( cacheDescription )
2156 CacheFontPath( ftFace, fontFaceId, requestedPointSize, path );
2162 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str() );
2165 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
2166 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" );
2171 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer )
2173 // Set the input dimensions.
2174 const ImageDimensions inputDimensions( srcWidth, srcHeight );
2176 // Set the output dimensions.
2177 // If the output dimension is not given, the input dimension is set
2178 // and won't be downscaling.
2179 data.width = ( data.width == 0 ) ? srcWidth : data.width;
2180 data.height = ( data.height == 0 ) ? srcHeight : data.height;
2181 const ImageDimensions desiredDimensions( data.width, data.height );
2183 // Creates the output buffer
2184 const unsigned int bufferSize = data.width * data.height * 4u;
2185 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2187 if( inputDimensions == desiredDimensions )
2189 // There isn't downscaling.
2190 memcpy( data.buffer, srcBuffer, bufferSize );
2194 Dali::Internal::Platform::LanczosSample4BPP( srcBuffer,
2197 desiredDimensions );
2201 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired )
2203 if( srcBitmap.width*srcBitmap.rows > 0 )
2205 switch( srcBitmap.pixel_mode )
2207 case FT_PIXEL_MODE_GRAY:
2209 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
2211 uint8_t* pixelsIn = srcBitmap.buffer;
2212 unsigned int width = srcBitmap.width;
2213 unsigned height = srcBitmap.rows;
2215 std::unique_ptr<uint8_t, void(*)(void*)> pixelsOutPtr( nullptr, free );
2217 if( isShearRequired )
2220 * Glyphs' bitmaps with no slant retrieved from FreeType:
2230 * Expected glyphs' bitmaps with italic slant:
2231 * ____________ ______
2238 * ------------ ------
2240 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2250 * 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.
2252 unsigned int widthOut = 0u;
2253 unsigned int heightOut = 0u;
2254 uint8_t* pixelsOut = nullptr;
2256 Dali::Internal::Platform::HorizontalShear( pixelsIn,
2260 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2267 pixelsIn = pixelsOut;
2268 pixelsOutPtr.reset( pixelsOut );
2271 const unsigned int bufferSize = width * height;
2272 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2274 data.height = height;
2275 data.format = Pixel::L8; // Sets the pixel format.
2276 memcpy( data.buffer, pixelsIn, bufferSize );
2281 #ifdef FREETYPE_BITMAP_SUPPORT
2282 case FT_PIXEL_MODE_BGRA:
2284 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
2286 ConvertBitmap( data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer );
2288 // Sets the pixel format.
2289 data.format = Pixel::BGRA8888;
2296 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" );
2303 bool FontClient::Plugin::FindFont( const FontPath& path,
2304 PointSize26Dot6 requestedPointSize,
2305 FaceIndex faceIndex,
2306 FontId& fontId ) const
2308 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2309 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2310 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2311 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size() );
2314 for( const auto& cacheItem : mFontFaceCache )
2316 if( cacheItem.mRequestedPointSize == requestedPointSize &&
2317 cacheItem.mFaceIndex == faceIndex &&
2318 cacheItem.mPath == path )
2320 fontId = cacheItem.mFontId + 1u;
2322 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2323 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2329 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found\n" );
2330 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2335 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
2336 FontDescriptionId& validatedFontId )
2338 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" );
2339 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2340 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2341 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2342 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2343 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2344 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size() );
2346 validatedFontId = 0u;
2348 for( const auto& item : mValidatedFontCache )
2350 if( !fontDescription.family.empty() &&
2351 ( fontDescription.family == item.fontDescription.family ) &&
2352 ( fontDescription.width == item.fontDescription.width ) &&
2353 ( fontDescription.weight == item.fontDescription.weight ) &&
2354 ( fontDescription.slant == item.fontDescription.slant ) )
2356 validatedFontId = item.index;
2358 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId );
2359 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2364 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font not found\n" );
2365 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2369 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
2370 FontList*& fontList,
2371 CharacterSetList*& characterSetList )
2373 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" );
2374 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2375 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2376 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2377 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2378 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2379 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size() );
2383 for( const auto& item : mFallbackCache )
2385 if( !fontDescription.family.empty() &&
2386 ( fontDescription.family == item.fontDescription.family ) &&
2387 ( fontDescription.width == item.fontDescription.width ) &&
2388 ( fontDescription.weight == item.fontDescription.weight ) &&
2389 ( fontDescription.slant == item.fontDescription.slant ) )
2391 fontList = item.fallbackFonts;
2392 characterSetList = item.characterSets;
2394 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list found.\n" );
2395 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2400 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list not found.\n" );
2401 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2405 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
2406 PointSize26Dot6 requestedPointSize,
2409 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2410 DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId );
2411 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2415 for( const auto& item : mFontDescriptionSizeCache )
2417 if( ( validatedFontId == item.validatedFontId ) &&
2418 ( requestedPointSize == item.requestedPointSize ) )
2420 fontId = item.fontId;
2422 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2423 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2428 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found.\n" );
2429 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2433 bool FontClient::Plugin::FindBitmapFont( const FontFamily& bitmapFont, FontId& fontId ) const
2437 for( const auto& item : mBitmapFontCache )
2439 if( bitmapFont == item.font.name )
2441 fontId = item.id + 1u;
2449 bool FontClient::Plugin::IsScalable( const FontPath& path )
2451 bool isScalable = false;
2454 int error = FT_New_Face( mFreeTypeLibrary,
2458 if( FT_Err_Ok != error )
2460 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() );
2464 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2470 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
2472 // Create a font pattern.
2473 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2475 FcResult result = FcResultMatch;
2477 // match the pattern
2478 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2479 bool isScalable = false;
2483 // Get the path to the font file name.
2485 GetFcString( match, FC_FILE, path );
2486 isScalable = IsScalable( path );
2490 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2493 // Destroys the created patterns.
2494 FcPatternDestroy( match );
2495 FcPatternDestroy( fontFamilyPattern );
2500 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
2502 // Empty the caller container
2506 int error = FT_New_Face( mFreeTypeLibrary,
2510 if( FT_Err_Ok != error )
2512 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() );
2515 // Fetch the number of fixed sizes available
2516 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
2518 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
2520 sizes.PushBack( ftFace->available_sizes[ i ].size );
2525 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
2526 Vector< PointSize26Dot6 >& sizes )
2528 // Create a font pattern.
2529 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2531 FcResult result = FcResultMatch;
2533 // match the pattern
2534 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result ); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
2538 // Get the path to the font file name.
2540 GetFcString( match, FC_FILE, path );
2541 GetFixedSizes( path, sizes );
2545 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2548 // Destroys the created patterns.
2549 FcPatternDestroy( match );
2550 FcPatternDestroy( fontFamilyPattern );
2553 bool FontClient::Plugin::HasItalicStyle( FontId fontId ) const
2555 bool hasItalicStyle = false;
2557 const FontId index = fontId - 1u;
2559 if( ( fontId > 0 ) &&
2560 ( index < mFontIdCache.Count() ) )
2562 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2564 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
2566 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2568 hasItalicStyle = 0u != ( font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC );
2573 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
2576 return hasItalicStyle;
2579 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path )
2581 FontDescription description;
2582 description.path = path;
2583 description.family = std::move( FontFamily( ftFace->family_name ) );
2584 description.weight = FontWeight::NONE;
2585 description.width = FontWidth::NONE;
2586 description.slant = FontSlant::NONE;
2588 // Note FreeType doesn't give too much info to build a proper font style.
2589 if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
2591 description.slant = FontSlant::ITALIC;
2593 if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
2595 description.weight = FontWeight::BOLD;
2598 FontDescriptionId validatedFontId = 0u;
2599 if( !FindValidatedFont( description,
2602 // Set the index to the vector of paths to font file names.
2603 validatedFontId = mFontDescriptionCache.size();
2605 FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2607 FcResult result = FcResultMatch;
2608 FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2610 FcCharSet* characterSet = nullptr;
2611 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2613 const FontId fontFaceId = id - 1u;
2614 mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy( characterSet ); // Increases the reference counter.
2616 // Destroys the created patterns.
2617 FcPatternDestroy( match );
2618 FcPatternDestroy( pattern );
2620 // Add the path to the cache.
2621 description.type = FontDescription::FACE_FONT;
2622 mFontDescriptionCache.push_back( description );
2624 // Increase the reference counter and add the character set to the cache.
2625 mCharacterSetCache.PushBack( FcCharSetCopy( characterSet ) );
2627 // Cache the index and the font's description.
2628 mValidatedFontCache.push_back( std::move( FontDescriptionCacheItem( std::move( description ),
2629 validatedFontId) ) );
2631 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2632 mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
2638 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description )
2640 FcCharSet* characterSet = nullptr;
2642 FcPattern* pattern = CreateFontFamilyPattern( description ); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2644 if( nullptr != pattern )
2646 FcResult result = FcResultMatch;
2647 FcPattern* match = FcFontMatch( nullptr, pattern, &result ); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2649 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2651 // Destroys the created patterns.
2652 FcPatternDestroy( match );
2653 FcPatternDestroy( pattern );
2656 return characterSet;
2659 void FontClient::Plugin::ClearFallbackCache( std::vector<FallbackCacheItem>& fallbackCache )
2661 for( auto& item : fallbackCache )
2663 if( nullptr != item.fallbackFonts )
2665 delete item.fallbackFonts;
2668 if( nullptr != item.characterSets )
2670 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2671 DestroyCharacterSets( *item.characterSets );
2672 delete item.characterSets;
2677 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2679 for( auto& item : mFontFaceCache )
2681 FcCharSetDestroy( item.mCharacterSet );
2682 item.mCharacterSet = nullptr;
2686 } // namespace Internal
2688 } // namespace TextAbstraction