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
67 const int FONT_WIDTH_TYPE_TO_INT[] = { 50, 63, 75, 87, 100, 113, 125, 150, 200 };
68 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / sizeof( int );
71 // ULTRA_LIGHT, EXTRA_LIGHT 40
73 // DEMI_LIGHT, SEMI_LIGHT 55
77 // DEMI_BOLD, SEMI_BOLD 180
79 // ULTRA_BOLD, EXTRA_BOLD 205
80 // BLACK, HEAVY, EXTRA_BLACK 210
81 const int FONT_WEIGHT_TYPE_TO_INT[] = { 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210 };
82 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof( FONT_WEIGHT_TYPE_TO_INT ) / sizeof( int );
87 const int FONT_SLANT_TYPE_TO_INT[] = { 0, 100, 110 };
88 const unsigned int NUM_FONT_SLANT_TYPE = sizeof( FONT_SLANT_TYPE_TO_INT ) / sizeof( int );
97 namespace TextAbstraction
104 * @brief Returns the FontWidth's enum index for the given width value.
106 * @param[in] width The width value.
108 * @return The FontWidth's enum index.
110 FontWidth::Type IntToWidthType( int width )
112 return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
116 * @brief Returns the FontWeight's enum index for the given weight value.
118 * @param[in] weight The weight value.
120 * @return The FontWeight's enum index.
122 FontWeight::Type IntToWeightType( int weight )
124 return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
128 * @brief Returns the FontSlant's enum index for the given slant value.
130 * @param[in] slant The slant value.
132 * @return The FontSlant's enum index.
134 FontSlant::Type IntToSlantType( int slant )
136 return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
139 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( const FontDescription& font, FontList* list )
140 : fontDescription( font ),
141 fallbackFonts( list )
145 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
146 FontDescriptionId index )
147 : fontDescription( fontDescription ),
152 FontClient::Plugin::FontIdCacheItem::FontIdCacheItem( FontDescriptionId validatedFontId,
153 PointSize26Dot6 requestedPointSize,
155 : validatedFontId( validatedFontId ),
156 requestedPointSize( requestedPointSize ),
161 FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace,
162 const FontPath& path,
163 PointSize26Dot6 requestedPointSize,
165 const FontMetrics& metrics )
166 : mFreeTypeFace( ftFace ),
168 mRequestedPointSize( requestedPointSize ),
171 mFixedWidthPixels( 0.0f ),
172 mFixedHeightPixels( 0.0f ),
174 mIsFixedSizeBitmap( false )
178 FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace,
179 const FontPath& path,
180 PointSize26Dot6 requestedPointSize,
182 const FontMetrics& metrics,
185 : mFreeTypeFace( ftFace ),
187 mRequestedPointSize( requestedPointSize ),
190 mFixedWidthPixels( fixedWidth ),
191 mFixedHeightPixels( fixedHeight ),
193 mIsFixedSizeBitmap( true )
197 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
198 unsigned int verticalDpi )
199 : mFreeTypeLibrary( NULL ),
200 mDpiHorizontal( horizontalDpi ),
201 mDpiVertical( verticalDpi ),
202 mDefaultFontDescription(),
206 mValidatedFontCache(),
207 mFontDescriptionCache( 1u ),
209 mVectorFontCache( NULL ),
211 mDefaultFontDescriptionCached( false )
213 int error = FT_Init_FreeType( &mFreeTypeLibrary );
214 if( FT_Err_Ok != error )
216 DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
219 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
220 mVectorFontCache = new VectorFontCache( mFreeTypeLibrary );
224 FontClient::Plugin::~Plugin()
226 for( std::vector<FallbackCacheItem>::iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end();
230 FallbackCacheItem& item = *it;
232 if( item.fallbackFonts )
234 delete item.fallbackFonts;
235 item.fallbackFonts = NULL;
239 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
240 delete mVectorFontCache;
243 FT_Done_FreeType( mFreeTypeLibrary );
246 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
247 unsigned int verticalDpi )
249 mDpiHorizontal = horizontalDpi;
250 mDpiVertical = verticalDpi;
253 void FontClient::Plugin::ResetSystemDefaults()
255 mDefaultFontDescriptionCached = false;
258 void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList )
260 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::SetFontList family(%s)\n", fontDescription.family.c_str() );
264 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
266 FcResult result = FcResultMatch;
268 // Match the pattern.
269 FcFontSet* fontSet = FcFontSort( NULL /* use default configure */,
271 false /* don't trim */,
275 if( NULL != fontSet )
277 // Reserve some space to avoid reallocations.
278 fontList.reserve( fontSet->nfont );
280 for( int i = 0u; i < fontSet->nfont; ++i )
282 FcPattern* fontPattern = fontSet->fonts[i];
286 // Skip fonts with no path
287 if( GetFcString( fontPattern, FC_FILE, path ) )
289 fontList.push_back( FontDescription() );
290 FontDescription& newFontDescription = fontList.back();
292 newFontDescription.path = path;
297 GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
298 GetFcInt( fontPattern, FC_WIDTH, width );
299 GetFcInt( fontPattern, FC_WEIGHT, weight );
300 GetFcInt( fontPattern, FC_SLANT, slant );
301 newFontDescription.width = IntToWidthType( width );
302 newFontDescription.weight = IntToWeightType( weight );
303 newFontDescription.slant = IntToSlantType( slant );
307 FcFontSetDestroy( fontSet );
310 FcPatternDestroy( fontFamilyPattern );
313 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
315 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultFonts mDefaultFonts(%s)\n", ( mDefaultFonts.empty()?"empty":"valid" ) );
317 if( mDefaultFonts.empty() )
319 FontDescription fontDescription;
320 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
321 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
322 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
323 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
324 SetFontList( fontDescription, mDefaultFonts );
327 defaultFonts = mDefaultFonts;
330 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
332 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultPlatformFontDescription\n");
334 if( !mDefaultFontDescriptionCached )
336 FcInitReinitialize(); // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
338 FcPattern* matchPattern = FcPatternCreate();
339 FcConfigSubstitute(NULL, matchPattern, FcMatchPattern);
340 FcDefaultSubstitute( matchPattern );
342 MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription );
343 FcPatternDestroy( matchPattern );
345 mDefaultFontDescriptionCached = true;
348 fontDescription.path = mDefaultFontDescription.path;
349 fontDescription.family = mDefaultFontDescription.family;
350 fontDescription.width = mDefaultFontDescription.width;
351 fontDescription.weight = mDefaultFontDescription.weight;
352 fontDescription.slant = mDefaultFontDescription.slant;
355 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
357 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetSystemFonts\n");
359 if( mSystemFonts.empty() )
364 systemFonts = mSystemFonts;
367 void FontClient::Plugin::GetDescription( FontId id,
368 FontDescription& fontDescription ) const
370 for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
371 endIt = mFontIdCache.end();
375 const FontIdCacheItem& item = *it;
377 if( item.fontId == id )
379 fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
384 DALI_LOG_ERROR( "FontClient::Plugin::GetDescription. No description found for the font ID %d\n", id );
387 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
389 const FontId index = id - 1u;
392 index < mFontCache.size() )
394 return ( *( mFontCache.begin() + index ) ).mRequestedPointSize;
398 DALI_LOG_ERROR( "FontClient::Plugin::GetPointSize. Invalid font ID %d\n", id );
401 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
404 FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
406 PointSize26Dot6 requestedPointSize,
409 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFontForCharacter\n");
412 bool foundColor(false);
414 // Traverse the list of fonts.
415 // Check for each default font if supports the character.
417 for( FontList::const_iterator it = fontList.begin(), endIt = fontList.end();
421 const FontDescription& description = *it;
423 FcPattern* pattern = CreateFontFamilyPattern( description );
425 FcResult result = FcResultMatch;
426 FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result );
428 FcCharSet* charSet = NULL;
429 FcPatternGetCharSet( match, FC_CHARSET, 0u, &charSet );
431 if( FcCharSetHasChar( charSet, charcode ) )
433 Vector< PointSize26Dot6 > fixedSizes;
434 GetFixedSizes( description,
437 PointSize26Dot6 actualPointSize = requestedPointSize;
439 const Vector< PointSize26Dot6 >::SizeType count = fixedSizes.Count();
443 // If the font is not scalable, pick the largest size <= requestedPointSize
444 actualPointSize = fixedSizes[0];
445 for( unsigned int i=1; i<count; ++i )
447 if( fixedSizes[i] <= requestedPointSize &&
448 fixedSizes[i] > actualPointSize )
450 actualPointSize = fixedSizes[i];
455 fontId = GetFontId( description,
462 BufferImage bitmap = CreateBitmap( fontId, GetGlyphIndex(fontId,charcode) );
464 Pixel::BGRA8888 == bitmap.GetPixelFormat() )
470 // Keep going unless we prefer a different (color) font
471 if( !preferColor || foundColor )
473 FcPatternDestroy( match );
474 FcPatternDestroy( pattern );
479 FcPatternDestroy( match );
480 FcPatternDestroy( pattern );
486 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
487 PointSize26Dot6 requestedPointSize,
490 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindDefaultFont DefaultFontsList(%s)\n", (mDefaultFonts.empty()?"empty":"created") );
494 // Create the list of default fonts if it has not been created.
495 if( mDefaultFonts.empty() )
497 FontDescription fontDescription;
498 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
499 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
500 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
501 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
502 SetFontList( fontDescription, mDefaultFonts );
505 // Traverse the list of default fonts.
506 // Check for each default font if supports the character.
507 fontId = FindFontForCharacter( mDefaultFonts, charcode, requestedPointSize, preferColor );
512 FontId FontClient::Plugin::FindFallbackFont( FontId preferredFont,
514 PointSize26Dot6 requestedPointSize,
517 // The font id to be returned.
520 FontDescription fontDescription;
521 GetDescription( preferredFont, fontDescription );
523 // Check first if the font's description has been queried before.
524 FontList* fontList( NULL );
526 if( !FindFallbackFontList( fontDescription, fontList ) )
528 fontList = new FontList;
529 SetFontList( fontDescription, *fontList );
531 // Add the font-list to the cache.
532 mFallbackCache.push_back( FallbackCacheItem(fontDescription, fontList) );
537 fontId = FindFontForCharacter( *fontList, charcode, requestedPointSize, preferColor );
543 FontId FontClient::Plugin::GetFontId( const FontPath& path,
544 PointSize26Dot6 requestedPointSize,
545 PointSize26Dot6 actualPointSize,
547 bool cacheDescription )
549 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId fontPatch:%s\n", path.c_str() );
553 if( NULL != mFreeTypeLibrary )
556 if( FindFont( path, requestedPointSize, faceIndex, foundId ) )
562 id = CreateFont( path, requestedPointSize, actualPointSize, faceIndex, cacheDescription );
569 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
570 PointSize26Dot6 requestedPointSize,
571 PointSize26Dot6 actualPointSize,
572 FaceIndex faceIndex )
574 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId font family(%s)\n", fontDescription.family.c_str() );
576 // This method uses three vectors which caches:
577 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
578 // * The path to font file names.
579 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
581 // 1) Checks in the cache if the font's description has been validated before.
582 // If it was it gets an index to the vector with paths to font file names. Otherwise,
583 // retrieves using font config a path to a font file name which matches with the
584 // font's description. The path is stored in the cache.
586 // 2) Checks in the cache if the pair 'font point size, index to the vector with paths to
587 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
588 // the GetFontId() method with the path to the font file name and the point size to
591 // The font id to be returned.
594 // Check first if the font's description have been validated before.
595 FontDescriptionId validatedFontId = 0u;
597 if( !FindValidatedFont( fontDescription,
600 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId Validating Font\n");
602 // Use font config to validate the font's description.
603 ValidateFont( fontDescription,
607 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
608 if( !FindFont( validatedFontId, requestedPointSize, fontId ) )
610 // Retrieve the font file name path.
611 const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
613 // Retrieve the font id. Do not cache the description as it has been already cached.
614 fontId = GetFontId( description.path,
620 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
621 mFontIdCache.push_back( FontIdCacheItem( validatedFontId,
629 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
630 FontDescriptionId& validatedFontId )
632 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont Validating Font family(%s) \n", fontDescription.family.c_str() );
634 // Create a font pattern.
635 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
637 FontDescription description;
639 bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description );
640 FcPatternDestroy( fontFamilyPattern );
644 // Set the index to the vector of paths to font file names.
645 validatedFontId = mFontDescriptionCache.size();
647 // Add the path to the cache.
648 mFontDescriptionCache.push_back( description );
650 // Cache the index and the matched font's description.
651 FontDescriptionCacheItem item( description,
654 mValidatedFontCache.push_back( item );
656 if( ( fontDescription.family != description.family ) ||
657 ( fontDescription.width != description.width ) ||
658 ( fontDescription.weight != description.weight ) ||
659 ( fontDescription.slant != description.slant ) )
661 // Cache the given font's description if it's different than the matched.
662 FontDescriptionCacheItem item( fontDescription,
665 mValidatedFontCache.push_back( item );
670 DALI_LOG_ERROR( "FontClient::Plugin::ValidateFont failed for font %s %d %d %d\n",
671 fontDescription.family.c_str(),
672 fontDescription.width,
673 fontDescription.weight,
674 fontDescription.slant );
677 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont validatedFontId(%u) font family(%s)\n", validatedFontId, fontDescription.family.c_str() );
680 void FontClient::Plugin::GetFontMetrics( FontId fontId,
681 FontMetrics& metrics )
683 if( ( fontId > 0 ) &&
684 ( fontId - 1u < mFontCache.size() ) )
686 const CacheItem& font = mFontCache[fontId-1];
688 metrics = font.mMetrics;
690 // Adjust the metrics if the fixed-size font should be down-scaled
691 if( font.mIsFixedSizeBitmap )
693 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
695 if( desiredFixedSize > 0.f )
697 const float scaleFactor = desiredFixedSize / static_cast<float>( font.mFixedHeightPixels );
699 metrics.ascender = floorf( metrics.ascender * scaleFactor );
700 metrics.descender = floorf( metrics.descender * scaleFactor );
701 metrics.height = floorf( metrics.height * scaleFactor );
702 metrics.underlinePosition = floorf( metrics.underlinePosition * scaleFactor );
703 metrics.underlineThickness = floorf( metrics.underlineThickness * scaleFactor );
709 DALI_LOG_ERROR( "Invalid font ID %d\n", fontId );
713 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
716 GlyphIndex index( 0 );
719 fontId-1 < mFontCache.size() )
721 FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
723 index = FT_Get_Char_Index( ftFace, charcode );
729 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
734 if( VECTOR_GLYPH == type )
736 return GetVectorMetrics( array, size, horizontal );
739 return GetBitmapMetrics( array, size, horizontal );
742 bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
746 bool success( true );
748 for( unsigned int i=0; i<size; ++i )
750 GlyphInfo& glyph = array[i];
752 FontId fontId = glyph.fontId;
755 fontId-1 < mFontCache.size() )
757 const CacheItem& font = mFontCache[fontId-1];
759 FT_Face ftFace = font.mFreeTypeFace;
761 #ifdef FREETYPE_BITMAP_SUPPORT
762 // Check to see if we should be loading a Fixed Size bitmap?
763 if ( font.mIsFixedSizeBitmap )
765 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR );
766 if ( FT_Err_Ok == error )
768 glyph.width = font.mFixedWidthPixels;
769 glyph.height = font.mFixedHeightPixels;
770 glyph.advance = font.mFixedWidthPixels;
771 glyph.xBearing = 0.0f;
772 glyph.yBearing = font.mFixedHeightPixels;
774 // Adjust the metrics if the fixed-size font should be down-scaled
775 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
777 if( desiredFixedSize > 0.f )
779 const float scaleFactor = desiredFixedSize / static_cast<float>( font.mFixedHeightPixels );
781 glyph.width = floorf( glyph.width * scaleFactor );
782 glyph.height = floorf( glyph.height * scaleFactor );
783 glyph.advance = floorf( glyph.advance * scaleFactor );
784 glyph.xBearing = floorf( glyph.xBearing * scaleFactor );
785 glyph.yBearing = floorf( glyph.yBearing * scaleFactor );
787 glyph.scaleFactor = scaleFactor;
792 DALI_LOG_ERROR( "FreeType Bitmap Load_Glyph error %d\n", error );
799 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_DEFAULT );
801 if( FT_Err_Ok == error )
803 glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
804 glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
807 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
808 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
812 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
813 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
831 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
835 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
836 bool success( true );
838 for( unsigned int i=0; i<size; ++i )
840 FontId fontId = array[i].fontId;
843 fontId-1 < mFontCache.size() )
845 CacheItem& font = mFontCache[fontId-1];
847 if( ! font.mVectorFontId )
849 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
852 mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
854 // Vector metrics are in EMs, convert to pixels
855 const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
856 array[i].width *= scale;
857 array[i].height *= scale;
858 array[i].xBearing *= scale;
859 array[i].yBearing *= scale;
860 array[i].advance *= scale;
874 BufferImage FontClient::Plugin::CreateBitmap( FontId fontId,
875 GlyphIndex glyphIndex )
880 fontId-1 < mFontCache.size() )
882 FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
886 #ifdef FREETYPE_BITMAP_SUPPORT
887 // Check to see if this is fixed size bitmap
888 if ( mFontCache[fontId-1].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( bitmap, bitmapGlyph->bitmap );
915 DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error );
920 ConvertBitmap( bitmap, 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 );
936 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
941 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
943 fontId-1 < mFontCache.size() )
945 CacheItem& font = mFontCache[fontId-1];
947 if( ! font.mVectorFontId )
949 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
952 mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
957 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
959 // First look into the cache if there is an ellipsis glyph for the requested point size.
960 for( Vector<EllipsisItem>::ConstIterator it = mEllipsisCache.Begin(),
961 endIt = mEllipsisCache.End();
965 const EllipsisItem& item = *it;
967 if( fabsf( item.requestedPointSize - requestedPointSize ) < Math::MACHINE_EPSILON_1000 )
969 // Use the glyph in the cache.
974 // No glyph has been found. Create one.
975 mEllipsisCache.PushBack( EllipsisItem() );
976 EllipsisItem& item = *( mEllipsisCache.End() - 1u );
978 item.requestedPointSize = requestedPointSize;
980 // Find a font for the ellipsis glyph.
981 item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
985 // Set the character index to access the glyph inside the font.
986 item.glyph.index = FT_Get_Char_Index( mFontCache[item.glyph.fontId-1].mFreeTypeFace,
987 ELLIPSIS_CHARACTER );
989 GetBitmapMetrics( &item.glyph, 1u, true );
994 void FontClient::Plugin::InitSystemFonts()
996 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::InitSystemFonts \n");
998 FcFontSet* fontSet = GetFcFontSet();
1002 // Reserve some space to avoid reallocations.
1003 mSystemFonts.reserve( fontSet->nfont );
1005 for( int i = 0u; i < fontSet->nfont; ++i )
1007 FcPattern* fontPattern = fontSet->fonts[i];
1011 // Skip fonts with no path
1012 if( GetFcString( fontPattern, FC_FILE, path ) )
1014 mSystemFonts.push_back( FontDescription() );
1015 FontDescription& fontDescription = mSystemFonts.back();
1017 fontDescription.path = path;
1022 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1023 GetFcInt( fontPattern, FC_WIDTH, width );
1024 GetFcInt( fontPattern, FC_WEIGHT, weight );
1025 GetFcInt( fontPattern, FC_SLANT, slant );
1026 fontDescription.width = IntToWidthType( width );
1027 fontDescription.weight = IntToWeightType( weight );
1028 fontDescription.slant = IntToSlantType( slant );
1029 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::InitSystemFonts font family(%s)\n", fontDescription.family.c_str() );
1034 FcFontSetDestroy( fontSet );
1038 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription )
1040 FcResult result = FcResultMatch;
1041 FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result );
1050 GetFcString( match, FC_FILE, fontDescription.path );
1051 GetFcString( match, FC_FAMILY, fontDescription.family );
1052 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::MatchFontDescriptionToPattern matched:%s \n", fontDescription.family.c_str());
1053 GetFcInt( match, FC_WIDTH, width );
1054 GetFcInt( match, FC_WEIGHT, weight );
1055 GetFcInt( match, FC_SLANT, slant );
1056 fontDescription.width = IntToWidthType( width );
1057 fontDescription.weight = IntToWeightType( weight );
1058 fontDescription.slant = IntToSlantType( slant );
1059 // destroyed the matched pattern
1060 FcPatternDestroy( match );
1067 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription )
1069 // create the cached font family lookup pattern
1070 // a pattern holds a set of names, each name refers to a property of the font
1071 FcPattern* fontFamilyPattern = FcPatternCreate();
1073 // add a property to the pattern for the font family
1074 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1076 FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, FONT_WIDTH_TYPE_TO_INT[fontDescription.width] );
1077 FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight] );
1078 FcPatternAddInteger( fontFamilyPattern, FC_SLANT, FONT_SLANT_TYPE_TO_INT[fontDescription.slant] );
1080 // Add a property of the pattern, to say we want to match TrueType fonts
1081 FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
1083 // modify the config, with the mFontFamilyPatterm
1084 FcConfigSubstitute( NULL /* use default configure */, fontFamilyPattern, FcMatchPattern );
1086 // provide default values for unspecified properties in the font pattern
1087 // e.g. patterns without a specified style or weight are set to Medium
1088 FcDefaultSubstitute( fontFamilyPattern );
1090 return fontFamilyPattern;
1093 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1095 // create a new pattern.
1096 // a pattern holds a set of names, each name refers to a property of the font
1097 FcPattern* pattern = FcPatternCreate();
1099 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1100 FcObjectSet* objectSet = FcObjectSetCreate();
1102 // build an object set from a list of property names
1103 FcObjectSetAdd( objectSet, FC_FILE );
1104 FcObjectSetAdd( objectSet, FC_FAMILY );
1105 FcObjectSetAdd( objectSet, FC_WIDTH );
1106 FcObjectSetAdd( objectSet, FC_WEIGHT );
1107 FcObjectSetAdd( objectSet, FC_SLANT );
1109 // get a list of fonts
1110 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1111 FcFontSet* fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
1113 // clear up the object set
1116 FcObjectSetDestroy( objectSet );
1118 // clear up the pattern
1121 FcPatternDestroy( pattern );
1127 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
1128 const char* const n,
1129 std::string& string )
1131 FcChar8* file = NULL;
1132 const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
1134 if( FcResultMatch == retVal )
1136 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1137 string.assign( reinterpret_cast<const char*>( file ) );
1145 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
1147 const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
1149 if( FcResultMatch == retVal )
1157 FontId FontClient::Plugin::CreateFont( const FontPath& path,
1158 PointSize26Dot6 requestedPointSize,
1159 PointSize26Dot6 actualPointSize,
1160 FaceIndex faceIndex,
1161 bool cacheDescription )
1165 // Create & cache new font face
1167 int error = FT_New_Face( mFreeTypeLibrary,
1172 if( FT_Err_Ok == error )
1174 // Check to see if the font contains fixed sizes?
1175 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1177 // Ensure this size is available
1178 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1180 if ( static_cast<FT_Pos>( actualPointSize ) == ftFace->available_sizes[ i ].size )
1182 // Tell Freetype to use this size
1183 error = FT_Select_Size( ftFace, i );
1184 if ( FT_Err_Ok != error )
1186 DALI_LOG_ERROR( "FreeType Select_Size error: %d\n", error );
1190 float fixedWidth = static_cast< float >( ftFace->available_sizes[ i ].width );
1191 float fixedHeight = static_cast< float >( ftFace->available_sizes[ i ].height );
1193 // Indicate that the font is a fixed sized bitmap
1194 FontMetrics metrics( fixedHeight, // The ascender in pixels.
1196 fixedHeight, // The height in pixels.
1200 mFontCache.push_back( CacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedWidth, fixedHeight ) );
1201 id = mFontCache.size();
1203 if( cacheDescription )
1205 CacheFontPath( ftFace, id, requestedPointSize, path );
1213 // Can't find this size
1214 std::stringstream sizes;
1215 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1221 sizes << ftFace->available_sizes[ i ].size;
1223 DALI_LOG_ERROR( "FreeType Font: %s, does not contain Bitmaps of size: %d. Available sizes are: %s\n",
1224 path.c_str(), actualPointSize, sizes.str().c_str() );
1228 error = FT_Set_Char_Size( ftFace,
1234 if( FT_Err_Ok == error )
1237 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1239 FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266,
1240 static_cast< float >( ftMetrics.descender ) * FROM_266,
1241 static_cast< float >( ftMetrics.height ) * FROM_266,
1242 static_cast< float >( ftFace->underline_position ) * FROM_266,
1243 static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
1245 mFontCache.push_back( CacheItem( ftFace, path, requestedPointSize, faceIndex, metrics ) );
1246 id = mFontCache.size();
1248 if( cacheDescription )
1250 CacheFontPath( ftFace, id, requestedPointSize, path );
1255 DALI_LOG_ERROR( "FreeType Set_Char_Size error: %d for pointSize %d\n", error, actualPointSize );
1261 DALI_LOG_ERROR( "FreeType New_Face error: %d for %s\n", error, path.c_str() );
1267 void FontClient::Plugin::ConvertBitmap( BufferImage& destBitmap,
1268 FT_Bitmap srcBitmap )
1270 if( srcBitmap.width*srcBitmap.rows > 0 )
1272 switch( srcBitmap.pixel_mode )
1274 case FT_PIXEL_MODE_GRAY:
1276 if( srcBitmap.pitch == static_cast< int >( srcBitmap.width ) )
1278 destBitmap = BufferImage::New( srcBitmap.width, srcBitmap.rows, Pixel::L8 );
1280 PixelBuffer* destBuffer = destBitmap.GetBuffer();
1283 memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows );
1287 DALI_LOG_ERROR( "GetBuffer returns null\n" );
1293 #ifdef FREETYPE_BITMAP_SUPPORT
1294 case FT_PIXEL_MODE_BGRA:
1296 if ( srcBitmap.pitch == static_cast< int >( srcBitmap.width << 2 ) )
1298 destBitmap = BufferImage::New( srcBitmap.width, srcBitmap.rows, Pixel::BGRA8888 );
1300 PixelBuffer* destBuffer = destBitmap.GetBuffer();
1303 memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows*4 );
1307 DALI_LOG_ERROR( "GetBuffer returns null\n" );
1315 DALI_LOG_ERROR( "FontClient Unable to create Bitmap of this PixelType\n" );
1322 bool FontClient::Plugin::FindFont( const FontPath& path,
1323 PointSize26Dot6 requestedPointSize,
1324 FaceIndex faceIndex,
1325 FontId& fontId ) const
1328 for( std::vector<CacheItem>::const_iterator it = mFontCache.begin(),
1329 endIt = mFontCache.end();
1333 const CacheItem& cacheItem = *it;
1335 if( cacheItem.mRequestedPointSize == requestedPointSize &&
1336 cacheItem.mFaceIndex == faceIndex &&
1337 cacheItem.mPath == path )
1347 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
1348 FontDescriptionId& validatedFontId )
1350 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont fontDescription family(%s)\n", fontDescription.family.c_str() );
1352 validatedFontId = 0u;
1354 for( std::vector<FontDescriptionCacheItem>::const_iterator it = mValidatedFontCache.begin(),
1355 endIt = mValidatedFontCache.end();
1359 const FontDescriptionCacheItem& item = *it;
1361 if( !fontDescription.family.empty() &&
1362 ( fontDescription.family == item.fontDescription.family ) &&
1363 ( fontDescription.width == item.fontDescription.width ) &&
1364 ( fontDescription.weight == item.fontDescription.weight ) &&
1365 ( fontDescription.slant == item.fontDescription.slant ) )
1367 validatedFontId = item.index;
1369 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont validated font family(%s) font id (%u) \n", fontDescription.family.c_str(), validatedFontId );
1375 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont NOT VALIDATED return false\n" );
1380 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
1381 FontList*& fontList )
1383 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList fontDescription family(%s)\n", fontDescription.family.c_str() );
1387 for( std::vector<FallbackCacheItem>::const_iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end();
1391 const FallbackCacheItem& item = *it;
1393 if( !fontDescription.family.empty() &&
1394 ( fontDescription.family == item.fontDescription.family ) &&
1395 ( fontDescription.width == item.fontDescription.width ) &&
1396 ( fontDescription.weight == item.fontDescription.weight ) &&
1397 ( fontDescription.slant == item.fontDescription.slant ) )
1399 fontList = item.fallbackFonts;
1401 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList font family(%s) font-list (%p) \n", fontDescription.family.c_str(), fontList );
1407 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList NOT FOUND return false\n" );
1412 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
1413 PointSize26Dot6 requestedPointSize,
1418 for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
1419 endIt = mFontIdCache.end();
1423 const FontIdCacheItem& item = *it;
1425 if( ( validatedFontId == item.validatedFontId ) &&
1426 ( requestedPointSize == item.requestedPointSize ) )
1428 fontId = item.fontId;
1436 bool FontClient::Plugin::IsScalable( const FontPath& path )
1439 int error = FT_New_Face( mFreeTypeLibrary,
1443 if( FT_Err_Ok != error )
1445 DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() );
1447 return ( ftFace->num_fixed_sizes == 0 );
1450 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
1452 // Create a font pattern.
1453 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1455 FcResult result = FcResultMatch;
1457 // match the pattern
1458 FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
1459 bool isScalable = true;
1463 // Get the path to the font file name.
1465 GetFcString( match, FC_FILE, path );
1466 isScalable = IsScalable( path );
1470 DALI_LOG_ERROR( "FreeType Cannot check font: %s %d %d %d\n",
1471 fontDescription.family.c_str(),
1472 fontDescription.width,
1473 fontDescription.weight,
1474 fontDescription.slant );
1476 FcPatternDestroy( fontFamilyPattern );
1477 FcPatternDestroy( match );
1481 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
1483 // Empty the caller container
1487 int error = FT_New_Face( mFreeTypeLibrary,
1491 if( FT_Err_Ok != error )
1493 DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() );
1496 // Fetch the number of fixed sizes available
1497 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1499 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1501 sizes.PushBack( ftFace->available_sizes[ i ].size );
1506 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
1507 Vector< PointSize26Dot6 >& sizes )
1509 // Create a font pattern.
1510 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1512 FcResult result = FcResultMatch;
1514 // match the pattern
1515 FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
1519 // Get the path to the font file name.
1521 GetFcString( match, FC_FILE, path );
1522 GetFixedSizes( path, sizes );
1526 DALI_LOG_ERROR( "FreeType Cannot check font: %s %d %d %d\n",
1527 fontDescription.family.c_str(),
1528 fontDescription.width,
1529 fontDescription.weight,
1530 fontDescription.slant );
1532 FcPatternDestroy( match );
1533 FcPatternDestroy( fontFamilyPattern );
1536 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path )
1538 FontDescription description;
1539 description.path = path;
1540 description.family = FontFamily( ftFace->family_name );
1541 description.weight = FontWeight::NORMAL;
1542 description.width = FontWidth::NORMAL;
1543 description.slant = FontSlant::NORMAL;
1545 // Note FreeType doesn't give too much info to build a proper font style.
1546 if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
1548 description.slant = FontSlant::ITALIC;
1550 if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
1552 description.weight = FontWeight::BOLD;
1555 FontDescriptionId validatedFontId = 0u;
1556 if( !FindValidatedFont( description,
1559 // Set the index to the vector of paths to font file names.
1560 validatedFontId = mFontDescriptionCache.size();
1562 // Add the path to the cache.
1563 mFontDescriptionCache.push_back( description );
1565 // Cache the index and the font's description.
1566 FontDescriptionCacheItem item( description,
1569 mValidatedFontCache.push_back( item );
1571 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
1572 mFontIdCache.push_back( FontIdCacheItem( validatedFontId,
1578 } // namespace Internal
1580 } // namespace TextAbstraction