2 * Copyright (c) 2015 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-abstraction/font-client-plugin-impl.h>
22 #include <dali/devel-api/text-abstraction/font-list.h>
23 #include <dali/public-api/common/dali-vector.h>
24 #include <dali/public-api/common/vector-wrapper.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/integration-api/platform-abstraction.h>
27 #include <dali/internal/text-abstraction/font-client-helper.h>
28 #include <adaptor-impl.h>
31 #include <fontconfig/fontconfig.h>
36 #if defined(DEBUG_ENABLED)
37 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
41 * Conversion from Fractional26.6 to float
43 const float FROM_266 = 1.0f / 64.0f;
44 const float POINTS_PER_INCH = 72.f;
46 const std::string FONT_FORMAT( "TrueType" );
47 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
48 const int DEFAULT_FONT_WIDTH = 100; // normal
49 const int DEFAULT_FONT_WEIGHT = 80; // normal
50 const int DEFAULT_FONT_SLANT = 0; // normal
52 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
54 const bool FONT_FIXED_SIZE_BITMAP( true );
56 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
58 // NONE -1 --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
68 const int FONT_WIDTH_TYPE_TO_INT[] = { -1, 50, 63, 75, 87, 100, 113, 125, 150, 200 };
69 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / sizeof( int );
71 // NONE -1 --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
73 // ULTRA_LIGHT, EXTRA_LIGHT 40
75 // DEMI_LIGHT, SEMI_LIGHT 55
79 // DEMI_BOLD, SEMI_BOLD 180
81 // ULTRA_BOLD, EXTRA_BOLD 205
82 // BLACK, HEAVY, EXTRA_BLACK 210
83 const int FONT_WEIGHT_TYPE_TO_INT[] = { -1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210 };
84 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof( FONT_WEIGHT_TYPE_TO_INT ) / sizeof( int );
86 // NONE -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
90 const int FONT_SLANT_TYPE_TO_INT[] = { -1, 0, 100, 110 };
91 const unsigned int NUM_FONT_SLANT_TYPE = sizeof( FONT_SLANT_TYPE_TO_INT ) / sizeof( int );
100 namespace TextAbstraction
107 * @brief Returns the FontWidth's enum index for the given width value.
109 * @param[in] width The width value.
111 * @return The FontWidth's enum index.
113 FontWidth::Type IntToWidthType( int width )
115 return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
119 * @brief Returns the FontWeight's enum index for the given weight value.
121 * @param[in] weight The weight value.
123 * @return The FontWeight's enum index.
125 FontWeight::Type IntToWeightType( int weight )
127 return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
131 * @brief Returns the FontSlant's enum index for the given slant value.
133 * @param[in] slant The slant value.
135 * @return The FontSlant's enum index.
137 FontSlant::Type IntToSlantType( int slant )
139 return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
142 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( const FontDescription& font, FontList* list )
143 : fontDescription( font ),
144 fallbackFonts( list )
148 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
149 FontDescriptionId index )
150 : fontDescription( fontDescription ),
155 FontClient::Plugin::FontIdCacheItem::FontIdCacheItem( FontDescriptionId validatedFontId,
156 PointSize26Dot6 requestedPointSize,
158 : validatedFontId( validatedFontId ),
159 requestedPointSize( requestedPointSize ),
164 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
165 const FontPath& path,
166 PointSize26Dot6 requestedPointSize,
168 const FontMetrics& metrics )
169 : mFreeTypeFace( ftFace ),
171 mRequestedPointSize( requestedPointSize ),
174 mFixedWidthPixels( 0.0f ),
175 mFixedHeightPixels( 0.0f ),
177 mIsFixedSizeBitmap( false )
181 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
182 const FontPath& path,
183 PointSize26Dot6 requestedPointSize,
185 const FontMetrics& metrics,
188 : mFreeTypeFace( ftFace ),
190 mRequestedPointSize( requestedPointSize ),
193 mFixedWidthPixels( fixedWidth ),
194 mFixedHeightPixels( fixedHeight ),
196 mIsFixedSizeBitmap( true )
200 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
201 unsigned int verticalDpi )
202 : mFreeTypeLibrary( NULL ),
203 mDpiHorizontal( horizontalDpi ),
204 mDpiVertical( verticalDpi ),
205 mDefaultFontDescription(),
209 mValidatedFontCache(),
210 mFontDescriptionCache( 1u ),
212 mVectorFontCache( NULL ),
214 mDefaultFontDescriptionCached( false )
216 int error = FT_Init_FreeType( &mFreeTypeLibrary );
217 if( FT_Err_Ok != error )
219 DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
222 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
223 mVectorFontCache = new VectorFontCache( mFreeTypeLibrary );
227 FontClient::Plugin::~Plugin()
229 for( std::vector<FallbackCacheItem>::iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end();
233 FallbackCacheItem& item = *it;
235 if( item.fallbackFonts )
237 delete item.fallbackFonts;
238 item.fallbackFonts = NULL;
242 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
243 delete mVectorFontCache;
246 FT_Done_FreeType( mFreeTypeLibrary );
249 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
250 unsigned int verticalDpi )
252 mDpiHorizontal = horizontalDpi;
253 mDpiVertical = verticalDpi;
256 void FontClient::Plugin::ResetSystemDefaults()
258 mDefaultFontDescriptionCached = false;
261 void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList )
263 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::SetFontList family(%s)\n", fontDescription.family.c_str() );
267 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
269 FcResult result = FcResultMatch;
271 // Match the pattern.
272 FcFontSet* fontSet = FcFontSort( NULL /* use default configure */,
274 false /* don't trim */,
278 if( NULL != fontSet )
280 // Reserve some space to avoid reallocations.
281 fontList.reserve( fontSet->nfont );
283 for( int i = 0u; i < fontSet->nfont; ++i )
285 FcPattern* fontPattern = fontSet->fonts[i];
289 // Skip fonts with no path
290 if( GetFcString( fontPattern, FC_FILE, path ) )
292 fontList.push_back( FontDescription() );
293 FontDescription& newFontDescription = fontList.back();
295 newFontDescription.path = path;
300 GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
301 GetFcInt( fontPattern, FC_WIDTH, width );
302 GetFcInt( fontPattern, FC_WEIGHT, weight );
303 GetFcInt( fontPattern, FC_SLANT, slant );
304 newFontDescription.width = IntToWidthType( width );
305 newFontDescription.weight = IntToWeightType( weight );
306 newFontDescription.slant = IntToSlantType( slant );
310 FcFontSetDestroy( fontSet );
313 FcPatternDestroy( fontFamilyPattern );
316 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
318 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultFonts mDefaultFonts(%s)\n", ( mDefaultFonts.empty()?"empty":"valid" ) );
320 if( mDefaultFonts.empty() )
322 FontDescription fontDescription;
323 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
324 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
325 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
326 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
327 SetFontList( fontDescription, mDefaultFonts );
330 defaultFonts = mDefaultFonts;
333 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
335 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultPlatformFontDescription\n");
337 if( !mDefaultFontDescriptionCached )
339 FcInitReinitialize(); // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
341 FcPattern* matchPattern = FcPatternCreate();
342 FcConfigSubstitute(NULL, matchPattern, FcMatchPattern);
343 FcDefaultSubstitute( matchPattern );
345 MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription );
346 FcPatternDestroy( matchPattern );
348 mDefaultFontDescriptionCached = true;
351 fontDescription.path = mDefaultFontDescription.path;
352 fontDescription.family = mDefaultFontDescription.family;
353 fontDescription.width = mDefaultFontDescription.width;
354 fontDescription.weight = mDefaultFontDescription.weight;
355 fontDescription.slant = mDefaultFontDescription.slant;
358 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
360 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetSystemFonts\n");
362 if( mSystemFonts.empty() )
367 systemFonts = mSystemFonts;
370 void FontClient::Plugin::GetDescription( FontId id,
371 FontDescription& fontDescription ) const
373 for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
374 endIt = mFontIdCache.end();
378 const FontIdCacheItem& item = *it;
380 if( item.fontId == id )
382 fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
387 DALI_LOG_ERROR( "FontClient::Plugin::GetDescription. No description found for the font ID %d\n", id );
390 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
392 const FontId index = id - 1u;
395 index < mFontCache.size() )
397 return ( *( mFontCache.begin() + index ) ).mRequestedPointSize;
401 DALI_LOG_ERROR( "FontClient::Plugin::GetPointSize. Invalid font ID %d\n", id );
404 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
407 FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
409 PointSize26Dot6 requestedPointSize,
412 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFontForCharacter\n");
415 bool foundColor = false;
417 // Traverse the list of fonts.
418 // Check for each font if supports the character.
419 for( FontList::const_iterator it = fontList.begin(), endIt = fontList.end();
423 const FontDescription& description = *it;
425 FcPattern* pattern = CreateFontFamilyPattern( description );
427 FcResult result = FcResultMatch;
428 FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result );
430 FcCharSet* charSet = NULL;
431 FcPatternGetCharSet( match, FC_CHARSET, 0u, &charSet );
433 if( FcCharSetHasChar( charSet, charcode ) )
435 Vector< PointSize26Dot6 > fixedSizes;
436 GetFixedSizes( description,
439 PointSize26Dot6 actualPointSize = requestedPointSize;
441 const Vector< PointSize26Dot6 >::SizeType count = fixedSizes.Count();
445 // If the font is not scalable, pick the largest size <= requestedPointSize
446 actualPointSize = fixedSizes[0];
447 for( unsigned int i=1; i<count; ++i )
449 if( fixedSizes[i] <= requestedPointSize &&
450 fixedSizes[i] > actualPointSize )
452 actualPointSize = fixedSizes[i];
457 fontId = GetFontId( description,
464 foundColor = IsColorGlyph( fontId, GetGlyphIndex( fontId, charcode ) );
467 // Keep going unless we prefer a different (color) font.
468 if( !preferColor || foundColor )
470 FcPatternDestroy( match );
471 FcPatternDestroy( pattern );
476 FcPatternDestroy( match );
477 FcPatternDestroy( pattern );
483 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
484 PointSize26Dot6 requestedPointSize,
487 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindDefaultFont DefaultFontsList(%s)\n", (mDefaultFonts.empty()?"empty":"created") );
491 // Create the list of default fonts if it has not been created.
492 if( mDefaultFonts.empty() )
494 FontDescription fontDescription;
495 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
496 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
497 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
498 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
500 SetFontList( fontDescription, mDefaultFonts );
503 // Traverse the list of default fonts.
504 // Check for each default font if supports the character.
505 fontId = FindFontForCharacter( mDefaultFonts, charcode, requestedPointSize, preferColor );
510 FontId FontClient::Plugin::FindFallbackFont( Character charcode,
511 const FontDescription& preferredFontDescription,
512 PointSize26Dot6 requestedPointSize,
515 // The font id to be returned.
518 FontDescription fontDescription;
520 // Fill the font description with the preferred font description and complete with the defaults.
521 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
522 fontDescription.weight = ( ( FontWeight::NONE == preferredFontDescription.weight ) ? IntToWeightType( DEFAULT_FONT_WEIGHT ) : preferredFontDescription.weight );
523 fontDescription.width = ( ( FontWidth::NONE == preferredFontDescription.width ) ? IntToWidthType( DEFAULT_FONT_WIDTH ) : preferredFontDescription.width );
524 fontDescription.slant = ( ( FontSlant::NONE == preferredFontDescription.slant ) ? IntToSlantType( DEFAULT_FONT_SLANT ) : preferredFontDescription.slant );
526 // Check first if the font's description has been queried before.
527 FontList* fontList( NULL );
529 if( !FindFallbackFontList( fontDescription, fontList ) )
531 fontList = new FontList;
532 SetFontList( fontDescription, *fontList );
534 // Add the font-list to the cache.
535 mFallbackCache.push_back( FallbackCacheItem( fontDescription, fontList ) );
540 fontId = FindFontForCharacter( *fontList, charcode, requestedPointSize, preferColor );
546 FontId FontClient::Plugin::GetFontId( const FontPath& path,
547 PointSize26Dot6 requestedPointSize,
548 PointSize26Dot6 actualPointSize,
550 bool cacheDescription )
552 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId fontPatch:%s\n", path.c_str() );
556 if( NULL != mFreeTypeLibrary )
559 if( FindFont( path, requestedPointSize, faceIndex, foundId ) )
565 id = CreateFont( path, requestedPointSize, actualPointSize, faceIndex, cacheDescription );
572 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
573 PointSize26Dot6 requestedPointSize,
574 PointSize26Dot6 actualPointSize,
575 FaceIndex faceIndex )
577 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId font family(%s)\n", fontDescription.family.c_str() );
579 // This method uses three vectors which caches:
580 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
581 // * The path to font file names.
582 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
584 // 1) Checks in the cache if the font's description has been validated before.
585 // If it was it gets an index to the vector with paths to font file names. Otherwise,
586 // retrieves using font config a path to a font file name which matches with the
587 // font's description. The path is stored in the cache.
589 // 2) Checks in the cache if the pair 'font point size, index to the vector with paths to
590 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
591 // the GetFontId() method with the path to the font file name and the point size to
594 // The font id to be returned.
597 // Check first if the font's description have been validated before.
598 FontDescriptionId validatedFontId = 0u;
600 if( !FindValidatedFont( fontDescription,
603 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId Validating Font\n");
605 // Use font config to validate the font's description.
606 ValidateFont( fontDescription,
610 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
611 if( !FindFont( validatedFontId, requestedPointSize, fontId ) )
613 // Retrieve the font file name path.
614 const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
616 // Retrieve the font id. Do not cache the description as it has been already cached.
617 fontId = GetFontId( description.path,
623 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
624 mFontIdCache.push_back( FontIdCacheItem( validatedFontId,
632 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
633 FontDescriptionId& validatedFontId )
635 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont Validating Font family(%s) \n", fontDescription.family.c_str() );
637 // Create a font pattern.
638 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
640 FontDescription description;
642 bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description );
643 FcPatternDestroy( fontFamilyPattern );
647 // Set the index to the vector of paths to font file names.
648 validatedFontId = mFontDescriptionCache.size();
650 // Add the path to the cache.
651 mFontDescriptionCache.push_back( description );
653 // Cache the index and the matched font's description.
654 FontDescriptionCacheItem item( description,
657 mValidatedFontCache.push_back( item );
659 if( ( fontDescription.family != description.family ) ||
660 ( fontDescription.width != description.width ) ||
661 ( fontDescription.weight != description.weight ) ||
662 ( fontDescription.slant != description.slant ) )
664 // Cache the given font's description if it's different than the matched.
665 FontDescriptionCacheItem item( fontDescription,
668 mValidatedFontCache.push_back( item );
673 DALI_LOG_ERROR( "FontClient::Plugin::ValidateFont failed for font %s %d %d %d\n",
674 fontDescription.family.c_str(),
675 fontDescription.width,
676 fontDescription.weight,
677 fontDescription.slant );
680 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont validatedFontId(%u) font family(%s)\n", validatedFontId, fontDescription.family.c_str() );
683 void FontClient::Plugin::GetFontMetrics( FontId fontId,
684 FontMetrics& metrics )
686 if( ( fontId > 0 ) &&
687 ( fontId - 1u < mFontCache.size() ) )
689 const FontFaceCacheItem& font = mFontCache[fontId-1];
691 metrics = font.mMetrics;
693 // Adjust the metrics if the fixed-size font should be down-scaled
694 if( font.mIsFixedSizeBitmap )
696 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
698 if( desiredFixedSize > 0.f )
700 const float scaleFactor = desiredFixedSize / static_cast<float>( font.mFixedHeightPixels );
702 metrics.ascender = floorf( metrics.ascender * scaleFactor );
703 metrics.descender = floorf( metrics.descender * scaleFactor );
704 metrics.height = floorf( metrics.height * scaleFactor );
705 metrics.underlinePosition = floorf( metrics.underlinePosition * scaleFactor );
706 metrics.underlineThickness = floorf( metrics.underlineThickness * scaleFactor );
712 DALI_LOG_ERROR( "Invalid font ID %d\n", fontId );
716 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
719 GlyphIndex index( 0 );
722 fontId-1 < mFontCache.size() )
724 FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
726 index = FT_Get_Char_Index( ftFace, charcode );
732 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
737 if( VECTOR_GLYPH == type )
739 return GetVectorMetrics( array, size, horizontal );
742 return GetBitmapMetrics( array, size, horizontal );
745 bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
749 bool success( true );
751 for( unsigned int i=0; i<size; ++i )
753 GlyphInfo& glyph = array[i];
755 FontId fontId = glyph.fontId;
758 fontId-1 < mFontCache.size() )
760 const FontFaceCacheItem& font = mFontCache[fontId-1];
762 FT_Face ftFace = font.mFreeTypeFace;
764 #ifdef FREETYPE_BITMAP_SUPPORT
765 // Check to see if we should be loading a Fixed Size bitmap?
766 if ( font.mIsFixedSizeBitmap )
768 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR );
769 if ( FT_Err_Ok == error )
771 glyph.width = font.mFixedWidthPixels;
772 glyph.height = font.mFixedHeightPixels;
773 glyph.advance = font.mFixedWidthPixels;
774 glyph.xBearing = 0.0f;
775 glyph.yBearing = font.mFixedHeightPixels;
777 // Adjust the metrics if the fixed-size font should be down-scaled
778 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
780 if( desiredFixedSize > 0.f )
782 const float scaleFactor = desiredFixedSize / static_cast<float>( font.mFixedHeightPixels );
784 glyph.width = floorf( glyph.width * scaleFactor );
785 glyph.height = floorf( glyph.height * scaleFactor );
786 glyph.advance = floorf( glyph.advance * scaleFactor );
787 glyph.xBearing = floorf( glyph.xBearing * scaleFactor );
788 glyph.yBearing = floorf( glyph.yBearing * scaleFactor );
790 glyph.scaleFactor = scaleFactor;
795 DALI_LOG_ERROR( "FreeType Bitmap Load_Glyph error %d\n", error );
802 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_DEFAULT );
804 if( FT_Err_Ok == error )
806 glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
807 glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
810 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
811 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
815 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
816 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
834 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
838 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
839 bool success( true );
841 for( unsigned int i=0; i<size; ++i )
843 FontId fontId = array[i].fontId;
846 fontId-1 < mFontCache.size() )
848 FontFaceCacheItem& font = mFontCache[fontId-1];
850 if( ! font.mVectorFontId )
852 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
855 mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
857 // Vector metrics are in EMs, convert to pixels
858 const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
859 array[i].width *= scale;
860 array[i].height *= scale;
861 array[i].xBearing *= scale;
862 array[i].yBearing *= scale;
863 array[i].advance *= scale;
877 void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data )
879 if( ( fontId > 0u ) &&
880 ( fontId - 1u < mFontCache.size() ) )
882 FT_Face ftFace = mFontCache[fontId - 1u].mFreeTypeFace;
886 #ifdef FREETYPE_BITMAP_SUPPORT
887 // Check to see if this is fixed size bitmap
888 if ( mFontCache[fontId - 1u].mIsFixedSizeBitmap )
890 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
895 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_DEFAULT );
897 if( FT_Err_Ok == error )
900 error = FT_Get_Glyph( ftFace->glyph, &glyph );
902 // Convert to bitmap if necessary
903 if ( FT_Err_Ok == error )
905 if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
907 error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
908 if ( FT_Err_Ok == error )
910 FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph;
911 ConvertBitmap( data, bitmapGlyph->bitmap );
915 DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error );
920 ConvertBitmap( data, ftFace->glyph->bitmap );
923 // Created FT_Glyph object must be released with FT_Done_Glyph
924 FT_Done_Glyph( glyph );
929 DALI_LOG_ERROR( "FT_Load_Glyph Failed with error: %d\n", error );
934 PixelData FontClient::Plugin::CreateBitmap( FontId fontId,
935 GlyphIndex glyphIndex )
937 TextAbstraction::FontClient::GlyphBufferData data;
939 CreateBitmap( fontId, glyphIndex, data );
941 return PixelData::New( data.buffer,
942 data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
946 PixelData::DELETE_ARRAY );
949 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
954 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
956 fontId-1 < mFontCache.size() )
958 FontFaceCacheItem& font = mFontCache[fontId-1];
960 if( ! font.mVectorFontId )
962 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
965 mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
970 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
972 // First look into the cache if there is an ellipsis glyph for the requested point size.
973 for( Vector<EllipsisItem>::ConstIterator it = mEllipsisCache.Begin(),
974 endIt = mEllipsisCache.End();
978 const EllipsisItem& item = *it;
980 if( fabsf( item.requestedPointSize - requestedPointSize ) < Math::MACHINE_EPSILON_1000 )
982 // Use the glyph in the cache.
987 // No glyph has been found. Create one.
988 mEllipsisCache.PushBack( EllipsisItem() );
989 EllipsisItem& item = *( mEllipsisCache.End() - 1u );
991 item.requestedPointSize = requestedPointSize;
993 // Find a font for the ellipsis glyph.
994 item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
998 // Set the character index to access the glyph inside the font.
999 item.glyph.index = FT_Get_Char_Index( mFontCache[item.glyph.fontId-1].mFreeTypeFace,
1000 ELLIPSIS_CHARACTER );
1002 GetBitmapMetrics( &item.glyph, 1u, true );
1007 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1009 FT_Error error = -1;
1011 #ifdef FREETYPE_BITMAP_SUPPORT
1012 if( ( fontId > 0u ) &&
1013 ( fontId - 1u < mFontCache.size() ) )
1015 const FontFaceCacheItem& item = mFontCache[fontId - 1u];
1016 FT_Face ftFace = item.mFreeTypeFace;
1018 // Check to see if this is fixed size bitmap
1019 if( item.mIsFixedSizeBitmap )
1021 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1026 return FT_Err_Ok == error;
1029 void FontClient::Plugin::InitSystemFonts()
1031 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::InitSystemFonts \n");
1033 FcFontSet* fontSet = GetFcFontSet();
1037 // Reserve some space to avoid reallocations.
1038 mSystemFonts.reserve( fontSet->nfont );
1040 for( int i = 0u; i < fontSet->nfont; ++i )
1042 FcPattern* fontPattern = fontSet->fonts[i];
1046 // Skip fonts with no path
1047 if( GetFcString( fontPattern, FC_FILE, path ) )
1049 mSystemFonts.push_back( FontDescription() );
1050 FontDescription& fontDescription = mSystemFonts.back();
1052 fontDescription.path = path;
1057 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1058 GetFcInt( fontPattern, FC_WIDTH, width );
1059 GetFcInt( fontPattern, FC_WEIGHT, weight );
1060 GetFcInt( fontPattern, FC_SLANT, slant );
1061 fontDescription.width = IntToWidthType( width );
1062 fontDescription.weight = IntToWeightType( weight );
1063 fontDescription.slant = IntToSlantType( slant );
1064 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::InitSystemFonts font family(%s)\n", fontDescription.family.c_str() );
1069 FcFontSetDestroy( fontSet );
1073 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription )
1075 FcResult result = FcResultMatch;
1076 FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result );
1085 GetFcString( match, FC_FILE, fontDescription.path );
1086 GetFcString( match, FC_FAMILY, fontDescription.family );
1087 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::MatchFontDescriptionToPattern matched:%s \n", fontDescription.family.c_str());
1088 GetFcInt( match, FC_WIDTH, width );
1089 GetFcInt( match, FC_WEIGHT, weight );
1090 GetFcInt( match, FC_SLANT, slant );
1091 fontDescription.width = IntToWidthType( width );
1092 fontDescription.weight = IntToWeightType( weight );
1093 fontDescription.slant = IntToSlantType( slant );
1095 // destroyed the matched pattern
1096 FcPatternDestroy( match );
1103 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription )
1105 // create the cached font family lookup pattern
1106 // a pattern holds a set of names, each name refers to a property of the font
1107 FcPattern* fontFamilyPattern = FcPatternCreate();
1109 // add a property to the pattern for the font family
1110 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1112 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1116 width = DEFAULT_FONT_WIDTH;
1119 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1123 weight = DEFAULT_FONT_WEIGHT;
1126 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1130 slant = DEFAULT_FONT_SLANT;
1133 FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1134 FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1135 FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1137 // Add a property of the pattern, to say we want to match TrueType fonts
1138 FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
1140 // modify the config, with the mFontFamilyPatterm
1141 FcConfigSubstitute( NULL /* use default configure */, fontFamilyPattern, FcMatchPattern );
1143 // provide default values for unspecified properties in the font pattern
1144 // e.g. patterns without a specified style or weight are set to Medium
1145 FcDefaultSubstitute( fontFamilyPattern );
1147 return fontFamilyPattern;
1150 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1152 // create a new pattern.
1153 // a pattern holds a set of names, each name refers to a property of the font
1154 FcPattern* pattern = FcPatternCreate();
1156 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1157 FcObjectSet* objectSet = FcObjectSetCreate();
1159 // build an object set from a list of property names
1160 FcObjectSetAdd( objectSet, FC_FILE );
1161 FcObjectSetAdd( objectSet, FC_FAMILY );
1162 FcObjectSetAdd( objectSet, FC_WIDTH );
1163 FcObjectSetAdd( objectSet, FC_WEIGHT );
1164 FcObjectSetAdd( objectSet, FC_SLANT );
1166 // get a list of fonts
1167 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1168 FcFontSet* fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
1170 // clear up the object set
1173 FcObjectSetDestroy( objectSet );
1175 // clear up the pattern
1178 FcPatternDestroy( pattern );
1184 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
1185 const char* const n,
1186 std::string& string )
1188 FcChar8* file = NULL;
1189 const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
1191 if( FcResultMatch == retVal )
1193 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1194 string.assign( reinterpret_cast<const char*>( file ) );
1202 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
1204 const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
1206 if( FcResultMatch == retVal )
1214 FontId FontClient::Plugin::CreateFont( const FontPath& path,
1215 PointSize26Dot6 requestedPointSize,
1216 PointSize26Dot6 actualPointSize,
1217 FaceIndex faceIndex,
1218 bool cacheDescription )
1222 // Create & cache new font face
1224 int error = FT_New_Face( mFreeTypeLibrary,
1229 if( FT_Err_Ok == error )
1231 // Check to see if the font contains fixed sizes?
1232 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1234 // Ensure this size is available
1235 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1237 if ( static_cast<FT_Pos>( actualPointSize ) == ftFace->available_sizes[ i ].size )
1239 // Tell Freetype to use this size
1240 error = FT_Select_Size( ftFace, i );
1241 if ( FT_Err_Ok != error )
1243 DALI_LOG_ERROR( "FreeType Select_Size error: %d\n", error );
1247 float fixedWidth = static_cast< float >( ftFace->available_sizes[ i ].width );
1248 float fixedHeight = static_cast< float >( ftFace->available_sizes[ i ].height );
1250 // Indicate that the font is a fixed sized bitmap
1251 FontMetrics metrics( fixedHeight, // The ascender in pixels.
1253 fixedHeight, // The height in pixels.
1257 mFontCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedWidth, fixedHeight ) );
1258 id = mFontCache.size();
1260 if( cacheDescription )
1262 CacheFontPath( ftFace, id, requestedPointSize, path );
1270 // Can't find this size
1271 std::stringstream sizes;
1272 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1278 sizes << ftFace->available_sizes[ i ].size;
1280 DALI_LOG_ERROR( "FreeType Font: %s, does not contain Bitmaps of size: %d. Available sizes are: %s\n",
1281 path.c_str(), actualPointSize, sizes.str().c_str() );
1285 error = FT_Set_Char_Size( ftFace,
1291 if( FT_Err_Ok == error )
1294 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1296 FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266,
1297 static_cast< float >( ftMetrics.descender ) * FROM_266,
1298 static_cast< float >( ftMetrics.height ) * FROM_266,
1299 static_cast< float >( ftFace->underline_position ) * FROM_266,
1300 static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
1302 mFontCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics ) );
1303 id = mFontCache.size();
1305 if( cacheDescription )
1307 CacheFontPath( ftFace, id, requestedPointSize, path );
1312 DALI_LOG_ERROR( "FreeType Set_Char_Size error: %d for pointSize %d\n", error, actualPointSize );
1318 DALI_LOG_ERROR( "FreeType New_Face error: %d for %s\n", error, path.c_str() );
1324 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap )
1326 if( srcBitmap.width*srcBitmap.rows > 0 )
1328 switch( srcBitmap.pixel_mode )
1330 case FT_PIXEL_MODE_GRAY:
1332 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
1334 const unsigned int bufferSize = srcBitmap.width * srcBitmap.rows;
1335 data.buffer = new unsigned char[bufferSize];
1336 data.width = srcBitmap.width;
1337 data.height = srcBitmap.rows;
1338 data.format = Pixel::L8;
1339 memcpy( data.buffer, srcBitmap.buffer, bufferSize );
1344 #ifdef FREETYPE_BITMAP_SUPPORT
1345 case FT_PIXEL_MODE_BGRA:
1347 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
1349 const unsigned int bufferSize = srcBitmap.width * srcBitmap.rows * 4u;
1350 data.buffer = new unsigned char[bufferSize];
1351 data.width = srcBitmap.width;
1352 data.height = srcBitmap.rows;
1353 data.format = Pixel::BGRA8888;
1354 memcpy( data.buffer, srcBitmap.buffer, bufferSize );
1361 DALI_LOG_ERROR( "FontClient Unable to create Bitmap of this PixelType\n" );
1368 bool FontClient::Plugin::FindFont( const FontPath& path,
1369 PointSize26Dot6 requestedPointSize,
1370 FaceIndex faceIndex,
1371 FontId& fontId ) const
1374 for( std::vector<FontFaceCacheItem>::const_iterator it = mFontCache.begin(),
1375 endIt = mFontCache.end();
1379 const FontFaceCacheItem& cacheItem = *it;
1381 if( cacheItem.mRequestedPointSize == requestedPointSize &&
1382 cacheItem.mFaceIndex == faceIndex &&
1383 cacheItem.mPath == path )
1393 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
1394 FontDescriptionId& validatedFontId )
1396 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont fontDescription family(%s)\n", fontDescription.family.c_str() );
1398 validatedFontId = 0u;
1400 for( std::vector<FontDescriptionCacheItem>::const_iterator it = mValidatedFontCache.begin(),
1401 endIt = mValidatedFontCache.end();
1405 const FontDescriptionCacheItem& item = *it;
1407 if( !fontDescription.family.empty() &&
1408 ( fontDescription.family == item.fontDescription.family ) &&
1409 ( fontDescription.width == item.fontDescription.width ) &&
1410 ( fontDescription.weight == item.fontDescription.weight ) &&
1411 ( fontDescription.slant == item.fontDescription.slant ) )
1413 validatedFontId = item.index;
1415 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont validated font family(%s) font id (%u) \n", fontDescription.family.c_str(), validatedFontId );
1421 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont NOT VALIDATED return false\n" );
1426 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
1427 FontList*& fontList )
1429 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList fontDescription family(%s)\n", fontDescription.family.c_str() );
1433 for( std::vector<FallbackCacheItem>::const_iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end();
1437 const FallbackCacheItem& item = *it;
1439 if( !fontDescription.family.empty() &&
1440 ( fontDescription.family == item.fontDescription.family ) &&
1441 ( fontDescription.width == item.fontDescription.width ) &&
1442 ( fontDescription.weight == item.fontDescription.weight ) &&
1443 ( fontDescription.slant == item.fontDescription.slant ) )
1445 fontList = item.fallbackFonts;
1447 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList font family(%s) font-list (%p) \n", fontDescription.family.c_str(), fontList );
1453 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList NOT FOUND return false\n" );
1458 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
1459 PointSize26Dot6 requestedPointSize,
1464 for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
1465 endIt = mFontIdCache.end();
1469 const FontIdCacheItem& item = *it;
1471 if( ( validatedFontId == item.validatedFontId ) &&
1472 ( requestedPointSize == item.requestedPointSize ) )
1474 fontId = item.fontId;
1482 bool FontClient::Plugin::IsScalable( const FontPath& path )
1485 int error = FT_New_Face( mFreeTypeLibrary,
1489 if( FT_Err_Ok != error )
1491 DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() );
1493 return ( ftFace->num_fixed_sizes == 0 );
1496 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
1498 // Create a font pattern.
1499 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1501 FcResult result = FcResultMatch;
1503 // match the pattern
1504 FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
1505 bool isScalable = true;
1509 // Get the path to the font file name.
1511 GetFcString( match, FC_FILE, path );
1512 isScalable = IsScalable( path );
1516 DALI_LOG_ERROR( "FreeType Cannot check font: %s %d %d %d\n",
1517 fontDescription.family.c_str(),
1518 fontDescription.width,
1519 fontDescription.weight,
1520 fontDescription.slant );
1522 FcPatternDestroy( fontFamilyPattern );
1523 FcPatternDestroy( match );
1527 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
1529 // Empty the caller container
1533 int error = FT_New_Face( mFreeTypeLibrary,
1537 if( FT_Err_Ok != error )
1539 DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() );
1542 // Fetch the number of fixed sizes available
1543 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1545 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1547 sizes.PushBack( ftFace->available_sizes[ i ].size );
1552 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
1553 Vector< PointSize26Dot6 >& sizes )
1555 // Create a font pattern.
1556 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1558 FcResult result = FcResultMatch;
1560 // match the pattern
1561 FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
1565 // Get the path to the font file name.
1567 GetFcString( match, FC_FILE, path );
1568 GetFixedSizes( path, sizes );
1572 DALI_LOG_ERROR( "FreeType Cannot check font: %s %d %d %d\n",
1573 fontDescription.family.c_str(),
1574 fontDescription.width,
1575 fontDescription.weight,
1576 fontDescription.slant );
1578 FcPatternDestroy( match );
1579 FcPatternDestroy( fontFamilyPattern );
1582 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path )
1584 FontDescription description;
1585 description.path = path;
1586 description.family = FontFamily( ftFace->family_name );
1587 description.weight = FontWeight::NONE;
1588 description.width = FontWidth::NONE;
1589 description.slant = FontSlant::NONE;
1591 // Note FreeType doesn't give too much info to build a proper font style.
1592 if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
1594 description.slant = FontSlant::ITALIC;
1596 if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
1598 description.weight = FontWeight::BOLD;
1601 FontDescriptionId validatedFontId = 0u;
1602 if( !FindValidatedFont( description,
1605 // Set the index to the vector of paths to font file names.
1606 validatedFontId = mFontDescriptionCache.size();
1608 // Add the path to the cache.
1609 mFontDescriptionCache.push_back( description );
1611 // Cache the index and the font's description.
1612 FontDescriptionCacheItem item( description,
1615 mValidatedFontCache.push_back( item );
1617 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
1618 mFontIdCache.push_back( FontIdCacheItem( validatedFontId,
1624 } // namespace Internal
1626 } // namespace TextAbstraction