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;
45 const std::string FONT_FORMAT( "TrueType" );
46 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
47 const int DEFAULT_FONT_WIDTH = 100; // normal
48 const int DEFAULT_FONT_WEIGHT = 80; // normal
49 const int DEFAULT_FONT_SLANT = 0; // normal
51 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
53 const bool FONT_FIXED_SIZE_BITMAP( true );
55 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
66 const int FONT_WIDTH_TYPE_TO_INT[] = { 50, 63, 75, 87, 100, 113, 125, 150, 200 };
67 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / sizeof( int );
70 // ULTRA_LIGHT, EXTRA_LIGHT 40
72 // DEMI_LIGHT, SEMI_LIGHT 55
76 // DEMI_BOLD, SEMI_BOLD 180
78 // ULTRA_BOLD, EXTRA_BOLD 205
79 // BLACK, HEAVY, EXTRA_BLACK 210
80 const int FONT_WEIGHT_TYPE_TO_INT[] = { 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210 };
81 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof( FONT_WEIGHT_TYPE_TO_INT ) / sizeof( int );
86 const int FONT_SLANT_TYPE_TO_INT[] = { 0, 100, 110 };
87 const unsigned int NUM_FONT_SLANT_TYPE = sizeof( FONT_SLANT_TYPE_TO_INT ) / sizeof( int );
96 namespace TextAbstraction
103 * @brief Returns the FontWidth's enum index for the given width value.
105 * @param[in] width The width value.
107 * @return The FontWidth's enum index.
109 FontWidth::Type IntToWidthType( int width )
111 return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
115 * @brief Returns the FontWeight's enum index for the given weight value.
117 * @param[in] weight The weight value.
119 * @return The FontWeight's enum index.
121 FontWeight::Type IntToWeightType( int weight )
123 return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
127 * @brief Returns the FontSlant's enum index for the given slant value.
129 * @param[in] slant The slant value.
131 * @return The FontSlant's enum index.
133 FontSlant::Type IntToSlantType( int slant )
135 return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
138 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( const FontDescription& font, FontList* list )
139 : fontDescription( font ),
140 fallbackFonts( list )
144 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
145 FontDescriptionId index )
146 : fontDescription( fontDescription ),
151 FontClient::Plugin::FontIdCacheItem::FontIdCacheItem( FontDescriptionId validatedFontId,
152 PointSize26Dot6 pointSize,
154 : validatedFontId( validatedFontId ),
155 pointSize( pointSize ),
160 FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace,
161 const FontPath& path,
162 PointSize26Dot6 pointSize,
164 const FontMetrics& metrics )
165 : mFreeTypeFace( ftFace ),
167 mPointSize( pointSize ),
170 mFixedWidthPixels( 0.0f ),
171 mFixedHeightPixels( 0.0f ),
173 mIsFixedSizeBitmap( false )
177 FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace,
178 const FontPath& path,
179 PointSize26Dot6 pointSize,
181 const FontMetrics& metrics,
184 : mFreeTypeFace( ftFace ),
186 mPointSize( pointSize ),
189 mFixedWidthPixels( fixedWidth ),
190 mFixedHeightPixels( fixedHeight ),
192 mIsFixedSizeBitmap( true )
196 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
197 unsigned int verticalDpi )
198 : mFreeTypeLibrary( NULL ),
199 mDpiHorizontal( horizontalDpi ),
200 mDpiVertical( verticalDpi ),
201 mDefaultFontDescription(),
205 mValidatedFontCache(),
206 mFontDescriptionCache( 1u ),
208 mVectorFontCache( NULL ),
210 mDefaultFontDescriptionCached( false )
212 int error = FT_Init_FreeType( &mFreeTypeLibrary );
213 if( FT_Err_Ok != error )
215 DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
218 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
219 mVectorFontCache = new VectorFontCache( mFreeTypeLibrary );
223 FontClient::Plugin::~Plugin()
225 for( std::vector<FallbackCacheItem>::iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end();
229 FallbackCacheItem& item = *it;
231 if( item.fallbackFonts )
233 delete item.fallbackFonts;
234 item.fallbackFonts = NULL;
238 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
239 delete mVectorFontCache;
242 FT_Done_FreeType( mFreeTypeLibrary );
245 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
246 unsigned int verticalDpi )
248 mDpiHorizontal = horizontalDpi;
249 mDpiVertical = verticalDpi;
252 void FontClient::Plugin::ResetSystemDefaults()
254 mDefaultFontDescriptionCached = false;
257 void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList )
259 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::SetFontList family(%s)\n", fontDescription.family.c_str() );
263 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
265 FcResult result = FcResultMatch;
267 // Match the pattern.
268 FcFontSet* fontSet = FcFontSort( NULL /* use default configure */,
270 false /* don't trim */,
274 if( NULL != fontSet )
276 // Reserve some space to avoid reallocations.
277 fontList.reserve( fontSet->nfont );
279 for( int i = 0u; i < fontSet->nfont; ++i )
281 FcPattern* fontPattern = fontSet->fonts[i];
285 // Skip fonts with no path
286 if( GetFcString( fontPattern, FC_FILE, path ) )
288 fontList.push_back( FontDescription() );
289 FontDescription& newFontDescription = fontList.back();
291 newFontDescription.path = path;
296 GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
297 GetFcInt( fontPattern, FC_WIDTH, width );
298 GetFcInt( fontPattern, FC_WEIGHT, weight );
299 GetFcInt( fontPattern, FC_SLANT, slant );
300 newFontDescription.width = IntToWidthType( width );
301 newFontDescription.weight = IntToWeightType( weight );
302 newFontDescription.slant = IntToSlantType( slant );
306 FcFontSetDestroy( fontSet );
309 FcPatternDestroy( fontFamilyPattern );
312 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
314 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultFonts mDefaultFonts(%s)\n", ( mDefaultFonts.empty()?"empty":"valid" ) );
316 if( mDefaultFonts.empty() )
318 FontDescription fontDescription;
319 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
320 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
321 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
322 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
323 SetFontList( fontDescription, mDefaultFonts );
326 defaultFonts = mDefaultFonts;
329 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
331 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultPlatformFontDescription\n");
333 if( !mDefaultFontDescriptionCached )
335 FcInitReinitialize(); // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
337 FcPattern* matchPattern = FcPatternCreate();
338 FcConfigSubstitute(NULL, matchPattern, FcMatchPattern);
339 FcDefaultSubstitute( matchPattern );
341 MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription );
342 FcPatternDestroy( matchPattern );
344 mDefaultFontDescriptionCached = true;
347 fontDescription.path = mDefaultFontDescription.path;
348 fontDescription.family = mDefaultFontDescription.family;
349 fontDescription.width = mDefaultFontDescription.width;
350 fontDescription.weight = mDefaultFontDescription.weight;
351 fontDescription.slant = mDefaultFontDescription.slant;
354 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
356 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetSystemFonts\n");
358 if( mSystemFonts.empty() )
363 systemFonts = mSystemFonts;
366 void FontClient::Plugin::GetDescription( FontId id,
367 FontDescription& fontDescription ) const
369 for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
370 endIt = mFontIdCache.end();
374 const FontIdCacheItem& item = *it;
376 if( item.fontId == id )
378 fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
383 DALI_LOG_ERROR( "FontClient::Plugin::GetDescription. No description found for the font ID %d\n", id );
386 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
388 const FontId index = id - 1u;
391 index < mFontCache.size() )
393 return ( *( mFontCache.begin() + index ) ).mPointSize;
397 DALI_LOG_ERROR( "FontClient::Plugin::GetPointSize. Invalid font ID %d\n", id );
400 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
403 FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
405 PointSize26Dot6 requestedSize,
408 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFontForCharacter\n");
411 bool foundColor(false);
413 // Traverse the list of fonts.
414 // Check for each default font if supports the character.
416 for( FontList::const_iterator it = fontList.begin(), endIt = fontList.end();
420 const FontDescription& description = *it;
422 FcPattern* pattern = CreateFontFamilyPattern( description );
424 FcResult result = FcResultMatch;
425 FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result );
427 FcCharSet* charSet = NULL;
428 FcPatternGetCharSet( match, FC_CHARSET, 0u, &charSet );
430 if( FcCharSetHasChar( charSet, charcode ) )
432 Vector< PointSize26Dot6 > fixedSizes;
433 GetFixedSizes( description,
436 const Vector< PointSize26Dot6 >::SizeType count = fixedSizes.Count();
439 // If the font is not scalable, pick the largest size <= requestedSize
440 PointSize26Dot6 size = fixedSizes[0];
441 for( unsigned int i=1; i<count; ++i )
443 if( fixedSizes[i] <= requestedSize &&
444 fixedSizes[i] > size )
446 size = fixedSizes[i];
449 requestedSize = size;
452 fontId = GetFontId( description,
458 BufferImage bitmap = CreateBitmap( fontId, GetGlyphIndex(fontId,charcode) );
460 Pixel::BGRA8888 == bitmap.GetPixelFormat() )
466 // Keep going unless we prefer a different (color) font
467 if( !preferColor || foundColor )
469 FcPatternDestroy( match );
470 FcPatternDestroy( pattern );
475 FcPatternDestroy( match );
476 FcPatternDestroy( pattern );
482 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
483 PointSize26Dot6 requestedSize,
486 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindDefaultFont DefaultFontsList(%s)\n", (mDefaultFonts.empty()?"empty":"created") );
490 // Create the list of default fonts if it has not been created.
491 if( mDefaultFonts.empty() )
493 FontDescription fontDescription;
494 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
495 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
496 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
497 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
498 SetFontList( fontDescription, mDefaultFonts );
501 // Traverse the list of default fonts.
502 // Check for each default font if supports the character.
503 fontId = FindFontForCharacter( mDefaultFonts, charcode, requestedSize, preferColor );
508 FontId FontClient::Plugin::FindFallbackFont( FontId preferredFont,
510 PointSize26Dot6 requestedSize,
513 // The font id to be returned.
516 FontDescription fontDescription;
517 GetDescription( preferredFont, fontDescription );
519 // Check first if the font's description has been queried before.
520 FontList* fontList( NULL );
522 if( !FindFallbackFontList( fontDescription, fontList ) )
524 fontList = new FontList;
525 SetFontList( fontDescription, *fontList );
527 // Add the font-list to the cache.
528 mFallbackCache.push_back( FallbackCacheItem(fontDescription, fontList) );
533 fontId = FindFontForCharacter( *fontList, charcode, requestedSize, preferColor );
539 FontId FontClient::Plugin::GetFontId( const FontPath& path,
540 PointSize26Dot6 pointSize,
542 bool cacheDescription )
544 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId fontPatch:%s\n", path.c_str() );
548 if( NULL != mFreeTypeLibrary )
551 if( FindFont( path, pointSize, faceIndex, foundId ) )
557 id = CreateFont( path, pointSize, faceIndex, cacheDescription );
564 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
565 PointSize26Dot6 pointSize,
566 FaceIndex faceIndex )
568 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId font family(%s)\n", fontDescription.family.c_str() );
570 // This method uses three vectors which caches:
571 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
572 // * The path to font file names.
573 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
575 // 1) Checks in the cache if the font's description has been validated before.
576 // If it was it gets an index to the vector with paths to font file names. Otherwise,
577 // retrieves using font config a path to a font file name which matches with the
578 // font's description. The path is stored in the cache.
580 // 2) Checks in the cache if the pair 'font point size, index to the vector with paths to
581 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
582 // the GetFontId() method with the path to the font file name and the point size to
585 // The font id to be returned.
588 // Check first if the font's description have been validated before.
589 FontDescriptionId validatedFontId = 0u;
591 if( !FindValidatedFont( fontDescription,
594 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId Validating Font\n");
596 // Use font config to validate the font's description.
597 ValidateFont( fontDescription,
601 // Check if exists a pair 'validatedFontId, pointSize' in the cache.
602 if( !FindFont( validatedFontId, pointSize, fontId ) )
604 // Retrieve the font file name path.
605 const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
607 // Retrieve the font id. Do not cache the description as it has been already cached.
608 fontId = GetFontId( description.path,
613 // Cache the pair 'validatedFontId, pointSize' to improve the following queries.
614 mFontIdCache.push_back( FontIdCacheItem( validatedFontId,
622 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
623 FontDescriptionId& validatedFontId )
625 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont Validating Font family(%s) \n", fontDescription.family.c_str() );
627 // Create a font pattern.
628 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
630 FontDescription description;
632 bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description );
633 FcPatternDestroy( fontFamilyPattern );
637 // Set the index to the vector of paths to font file names.
638 validatedFontId = mFontDescriptionCache.size();
640 // Add the path to the cache.
641 mFontDescriptionCache.push_back( description );
643 // Cache the index and the matched font's description.
644 FontDescriptionCacheItem item( description,
647 mValidatedFontCache.push_back( item );
649 if( ( fontDescription.family != description.family ) ||
650 ( fontDescription.width != description.width ) ||
651 ( fontDescription.weight != description.weight ) ||
652 ( fontDescription.slant != description.slant ) )
654 // Cache the given font's description if it's different than the matched.
655 FontDescriptionCacheItem item( fontDescription,
658 mValidatedFontCache.push_back( item );
663 DALI_LOG_ERROR( "FontClient::Plugin::ValidateFont failed for font %s %d %d %d\n",
664 fontDescription.family.c_str(),
665 fontDescription.width,
666 fontDescription.weight,
667 fontDescription.slant );
670 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont validatedFontId(%u) font family(%s)\n", validatedFontId, fontDescription.family.c_str() );
673 void FontClient::Plugin::GetFontMetrics( FontId fontId,
674 FontMetrics& metrics,
675 int desiredFixedSize )
678 fontId-1 < mFontCache.size() )
680 const CacheItem& font = mFontCache[fontId-1];
682 metrics = font.mMetrics;
684 // Adjust the metrics if the fixed-size font should be down-scaled
685 if( font.mIsFixedSizeBitmap &&
686 ( desiredFixedSize > 0 ) )
688 float scaleFactor = static_cast<float>(desiredFixedSize) / static_cast<float>(font.mFixedHeightPixels);
690 metrics.ascender *= scaleFactor;
691 metrics.descender *= scaleFactor;
692 metrics.height *= scaleFactor;
693 metrics.underlinePosition *= scaleFactor;
694 metrics.underlineThickness *= scaleFactor;
699 DALI_LOG_ERROR( "Invalid font ID %d\n", fontId );
703 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
706 GlyphIndex index( 0 );
709 fontId-1 < mFontCache.size() )
711 FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
713 index = FT_Get_Char_Index( ftFace, charcode );
719 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
723 int desiredFixedSize )
725 if( VECTOR_GLYPH == type )
727 return GetVectorMetrics( array, size, horizontal, desiredFixedSize );
730 return GetBitmapMetrics( array, size, horizontal, desiredFixedSize );
733 bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
736 int desiredFixedSize )
738 bool success( true );
740 for( unsigned int i=0; i<size; ++i )
742 FontId fontId = array[i].fontId;
745 fontId-1 < mFontCache.size() )
747 const CacheItem& font = mFontCache[fontId-1];
749 FT_Face ftFace = font.mFreeTypeFace;
751 #ifdef FREETYPE_BITMAP_SUPPORT
752 // Check to see if we should be loading a Fixed Size bitmap?
753 if ( font.mIsFixedSizeBitmap )
755 int error = FT_Load_Glyph( ftFace, array[i].index, FT_LOAD_COLOR );
756 if ( FT_Err_Ok == error )
758 array[i].width = font.mFixedWidthPixels;
759 array[i].height = font.mFixedHeightPixels;
760 array[i].advance = font.mFixedWidthPixels;
761 array[i].xBearing = 0.0f;
762 array[i].yBearing = font.mFixedHeightPixels;
764 // Adjust the metrics if the fixed-size font should be down-scaled
765 if( desiredFixedSize > 0 )
767 float scaleFactor = static_cast<float>(desiredFixedSize) / static_cast<float>(font.mFixedHeightPixels);
769 array[i].width *= scaleFactor;
770 array[i].height *= scaleFactor;
771 array[i].advance *= scaleFactor;
772 array[i].xBearing *= scaleFactor;
773 array[i].yBearing *= scaleFactor;
775 array[i].scaleFactor = scaleFactor;
780 DALI_LOG_ERROR( "FreeType Bitmap Load_Glyph error %d\n", error );
787 int error = FT_Load_Glyph( ftFace, array[i].index, FT_LOAD_DEFAULT );
789 if( FT_Err_Ok == error )
791 array[i].width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
792 array[i].height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
795 array[i].xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
796 array[i].yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
800 array[i].xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
801 array[i].yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
819 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
822 int desiredFixedSize )
824 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
825 bool success( true );
827 for( unsigned int i=0; i<size; ++i )
829 FontId fontId = array[i].fontId;
832 fontId-1 < mFontCache.size() )
834 CacheItem& font = mFontCache[fontId-1];
836 if( ! font.mVectorFontId )
838 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
841 mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
843 // Vector metrics are in EMs, convert to pixels
844 float scale = (static_cast<float>(font.mPointSize)/64.0f) * mDpiVertical/72.0f;
845 array[i].width *= scale;
846 array[i].height *= scale;
847 array[i].xBearing *= scale;
848 array[i].yBearing *= scale;
849 array[i].advance *= scale;
863 BufferImage FontClient::Plugin::CreateBitmap( FontId fontId,
864 GlyphIndex glyphIndex )
869 fontId-1 < mFontCache.size() )
871 FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
875 #ifdef FREETYPE_BITMAP_SUPPORT
876 // Check to see if this is fixed size bitmap
877 if ( mFontCache[fontId-1].mIsFixedSizeBitmap )
879 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
884 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_DEFAULT );
886 if( FT_Err_Ok == error )
889 error = FT_Get_Glyph( ftFace->glyph, &glyph );
891 // Convert to bitmap if necessary
892 if ( FT_Err_Ok == error )
894 if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
896 error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
897 if ( FT_Err_Ok == error )
899 FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph;
900 ConvertBitmap( bitmap, bitmapGlyph->bitmap );
904 DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error );
909 ConvertBitmap( bitmap, ftFace->glyph->bitmap );
912 // Created FT_Glyph object must be released with FT_Done_Glyph
913 FT_Done_Glyph( glyph );
918 DALI_LOG_ERROR( "FT_Load_Glyph Failed with error: %d\n", error );
925 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
930 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
932 fontId-1 < mFontCache.size() )
934 CacheItem& font = mFontCache[fontId-1];
936 if( ! font.mVectorFontId )
938 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
941 mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
946 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 pointSize )
948 // First look into the cache if there is an ellipsis glyph for the requested point size.
949 for( Vector<EllipsisItem>::ConstIterator it = mEllipsisCache.Begin(),
950 endIt = mEllipsisCache.End();
954 const EllipsisItem& item = *it;
956 if( fabsf( item.size - pointSize ) < Math::MACHINE_EPSILON_1000 )
958 // Use the glyph in the cache.
963 // No glyph has been found. Create one.
964 mEllipsisCache.PushBack( EllipsisItem() );
965 EllipsisItem& item = *( mEllipsisCache.End() - 1u );
967 item.size = pointSize;
969 // Find a font for the ellipsis glyph.
970 item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
974 // Set the character index to access the glyph inside the font.
975 item.glyph.index = FT_Get_Char_Index( mFontCache[item.glyph.fontId-1].mFreeTypeFace,
976 ELLIPSIS_CHARACTER );
978 GetBitmapMetrics( &item.glyph, 1u, true, 0 );
983 void FontClient::Plugin::InitSystemFonts()
985 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::InitSystemFonts \n");
987 FcFontSet* fontSet = GetFcFontSet();
991 // Reserve some space to avoid reallocations.
992 mSystemFonts.reserve( fontSet->nfont );
994 for( int i = 0u; i < fontSet->nfont; ++i )
996 FcPattern* fontPattern = fontSet->fonts[i];
1000 // Skip fonts with no path
1001 if( GetFcString( fontPattern, FC_FILE, path ) )
1003 mSystemFonts.push_back( FontDescription() );
1004 FontDescription& fontDescription = mSystemFonts.back();
1006 fontDescription.path = path;
1011 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1012 GetFcInt( fontPattern, FC_WIDTH, width );
1013 GetFcInt( fontPattern, FC_WEIGHT, weight );
1014 GetFcInt( fontPattern, FC_SLANT, slant );
1015 fontDescription.width = IntToWidthType( width );
1016 fontDescription.weight = IntToWeightType( weight );
1017 fontDescription.slant = IntToSlantType( slant );
1018 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::InitSystemFonts font family(%s)\n", fontDescription.family.c_str() );
1023 FcFontSetDestroy( fontSet );
1027 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription )
1029 FcResult result = FcResultMatch;
1030 FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result );
1039 GetFcString( match, FC_FILE, fontDescription.path );
1040 GetFcString( match, FC_FAMILY, fontDescription.family );
1041 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::MatchFontDescriptionToPattern matched:%s \n", fontDescription.family.c_str());
1042 GetFcInt( match, FC_WIDTH, width );
1043 GetFcInt( match, FC_WEIGHT, weight );
1044 GetFcInt( match, FC_SLANT, slant );
1045 fontDescription.width = IntToWidthType( width );
1046 fontDescription.weight = IntToWeightType( weight );
1047 fontDescription.slant = IntToSlantType( slant );
1048 // destroyed the matched pattern
1049 FcPatternDestroy( match );
1056 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription )
1058 // create the cached font family lookup pattern
1059 // a pattern holds a set of names, each name refers to a property of the font
1060 FcPattern* fontFamilyPattern = FcPatternCreate();
1062 // add a property to the pattern for the font family
1063 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1065 FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, FONT_WIDTH_TYPE_TO_INT[fontDescription.width] );
1066 FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight] );
1067 FcPatternAddInteger( fontFamilyPattern, FC_SLANT, FONT_SLANT_TYPE_TO_INT[fontDescription.slant] );
1069 // Add a property of the pattern, to say we want to match TrueType fonts
1070 FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
1072 // modify the config, with the mFontFamilyPatterm
1073 FcConfigSubstitute( NULL /* use default configure */, fontFamilyPattern, FcMatchPattern );
1075 // provide default values for unspecified properties in the font pattern
1076 // e.g. patterns without a specified style or weight are set to Medium
1077 FcDefaultSubstitute( fontFamilyPattern );
1079 return fontFamilyPattern;
1082 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1084 // create a new pattern.
1085 // a pattern holds a set of names, each name refers to a property of the font
1086 FcPattern* pattern = FcPatternCreate();
1088 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1089 FcObjectSet* objectSet = FcObjectSetCreate();
1091 // build an object set from a list of property names
1092 FcObjectSetAdd( objectSet, FC_FILE );
1093 FcObjectSetAdd( objectSet, FC_FAMILY );
1094 FcObjectSetAdd( objectSet, FC_WIDTH );
1095 FcObjectSetAdd( objectSet, FC_WEIGHT );
1096 FcObjectSetAdd( objectSet, FC_SLANT );
1098 // get a list of fonts
1099 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1100 FcFontSet* fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
1102 // clear up the object set
1105 FcObjectSetDestroy( objectSet );
1107 // clear up the pattern
1110 FcPatternDestroy( pattern );
1116 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
1117 const char* const n,
1118 std::string& string )
1120 FcChar8* file = NULL;
1121 const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
1123 if( FcResultMatch == retVal )
1125 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1126 string.assign( reinterpret_cast<const char*>( file ) );
1134 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
1136 const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
1138 if( FcResultMatch == retVal )
1146 FontId FontClient::Plugin::CreateFont( const FontPath& path,
1147 PointSize26Dot6 pointSize,
1148 FaceIndex faceIndex,
1149 bool cacheDescription )
1153 // Create & cache new font face
1155 int error = FT_New_Face( mFreeTypeLibrary,
1160 if( FT_Err_Ok == error )
1162 // Check to see if the font contains fixed sizes?
1163 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1165 // Ensure this size is available
1166 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1168 if ( static_cast<FT_Pos>(pointSize) == ftFace->available_sizes[ i ].size )
1170 // Tell Freetype to use this size
1171 error = FT_Select_Size( ftFace, i );
1172 if ( FT_Err_Ok != error )
1174 DALI_LOG_ERROR( "FreeType Select_Size error: %d\n", error );
1178 float fixedWidth = static_cast< float >( ftFace->available_sizes[ i ].width );
1179 float fixedHeight = static_cast< float >( ftFace->available_sizes[ i ].height );
1181 // Indicate that the font is a fixed sized bitmap
1182 FontMetrics metrics( fixedHeight, // The ascender in pixels.
1184 fixedHeight, // The height in pixels.
1188 mFontCache.push_back( CacheItem( ftFace, path, pointSize, faceIndex, metrics, fixedWidth, fixedHeight ) );
1189 id = mFontCache.size();
1191 if( cacheDescription )
1193 CacheFontPath( ftFace, id, pointSize, path );
1200 // Can't find this size
1201 std::stringstream sizes;
1202 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1208 sizes << ftFace->available_sizes[ i ].size;
1210 DALI_LOG_ERROR( "FreeType Font: %s, does not contain Bitmaps of size: %d. Available sizes are: %s\n",
1211 path.c_str(), pointSize, sizes.str().c_str() );
1215 error = FT_Set_Char_Size( ftFace,
1221 if( FT_Err_Ok == error )
1224 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1226 FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266,
1227 static_cast< float >( ftMetrics.descender ) * FROM_266,
1228 static_cast< float >( ftMetrics.height ) * FROM_266,
1229 static_cast< float >( ftFace->underline_position ) * FROM_266,
1230 static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
1232 mFontCache.push_back( CacheItem( ftFace, path, pointSize, faceIndex, metrics ) );
1233 id = mFontCache.size();
1235 if( cacheDescription )
1237 CacheFontPath( ftFace, id, pointSize, path );
1242 DALI_LOG_ERROR( "FreeType Set_Char_Size error: %d for pointSize %d\n", error, pointSize );
1248 DALI_LOG_ERROR( "FreeType New_Face error: %d for %s\n", error, path.c_str() );
1254 void FontClient::Plugin::ConvertBitmap( BufferImage& destBitmap,
1255 FT_Bitmap srcBitmap )
1257 if( srcBitmap.width*srcBitmap.rows > 0 )
1259 switch( srcBitmap.pixel_mode )
1261 case FT_PIXEL_MODE_GRAY:
1263 if( srcBitmap.pitch == static_cast< int >( srcBitmap.width ) )
1265 destBitmap = BufferImage::New( srcBitmap.width, srcBitmap.rows, Pixel::L8 );
1267 PixelBuffer* destBuffer = destBitmap.GetBuffer();
1270 memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows );
1274 DALI_LOG_ERROR( "GetBuffer returns null\n" );
1280 #ifdef FREETYPE_BITMAP_SUPPORT
1281 case FT_PIXEL_MODE_BGRA:
1283 if ( srcBitmap.pitch == static_cast< int >( srcBitmap.width << 2 ) )
1285 destBitmap = BufferImage::New( srcBitmap.width, srcBitmap.rows, Pixel::BGRA8888 );
1287 PixelBuffer* destBuffer = destBitmap.GetBuffer();
1290 memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows*4 );
1294 DALI_LOG_ERROR( "GetBuffer returns null\n" );
1302 DALI_LOG_ERROR( "FontClient Unable to create Bitmap of this PixelType\n" );
1309 bool FontClient::Plugin::FindFont( const FontPath& path,
1310 PointSize26Dot6 pointSize,
1311 FaceIndex faceIndex,
1312 FontId& fontId ) const
1315 for( std::vector<CacheItem>::const_iterator it = mFontCache.begin(),
1316 endIt = mFontCache.end();
1320 const CacheItem& cacheItem = *it;
1322 if( cacheItem.mPointSize == pointSize &&
1323 cacheItem.mFaceIndex == faceIndex &&
1324 cacheItem.mPath == path )
1334 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
1335 FontDescriptionId& validatedFontId )
1337 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont fontDescription family(%s)\n", fontDescription.family.c_str() );
1339 validatedFontId = 0u;
1341 for( std::vector<FontDescriptionCacheItem>::const_iterator it = mValidatedFontCache.begin(),
1342 endIt = mValidatedFontCache.end();
1346 const FontDescriptionCacheItem& item = *it;
1348 if( !fontDescription.family.empty() &&
1349 ( fontDescription.family == item.fontDescription.family ) &&
1350 ( fontDescription.width == item.fontDescription.width ) &&
1351 ( fontDescription.weight == item.fontDescription.weight ) &&
1352 ( fontDescription.slant == item.fontDescription.slant ) )
1354 validatedFontId = item.index;
1356 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont validated font family(%s) font id (%u) \n", fontDescription.family.c_str(), validatedFontId );
1362 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont NOT VALIDATED return false\n" );
1367 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
1368 FontList*& fontList )
1370 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList fontDescription family(%s)\n", fontDescription.family.c_str() );
1374 for( std::vector<FallbackCacheItem>::const_iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end();
1378 const FallbackCacheItem& item = *it;
1380 if( !fontDescription.family.empty() &&
1381 ( fontDescription.family == item.fontDescription.family ) &&
1382 ( fontDescription.width == item.fontDescription.width ) &&
1383 ( fontDescription.weight == item.fontDescription.weight ) &&
1384 ( fontDescription.slant == item.fontDescription.slant ) )
1386 fontList = item.fallbackFonts;
1388 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList font family(%s) font-list (%p) \n", fontDescription.family.c_str(), fontList );
1394 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList NOT FOUND return false\n" );
1399 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
1400 PointSize26Dot6 pointSize,
1405 for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
1406 endIt = mFontIdCache.end();
1410 const FontIdCacheItem& item = *it;
1412 if( ( validatedFontId == item.validatedFontId ) &&
1413 ( pointSize == item.pointSize ) )
1415 fontId = item.fontId;
1423 bool FontClient::Plugin::IsScalable( const FontPath& path )
1426 int error = FT_New_Face( mFreeTypeLibrary,
1430 if( FT_Err_Ok != error )
1432 DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() );
1434 return ( ftFace->num_fixed_sizes == 0 );
1437 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
1439 // Create a font pattern.
1440 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1442 FcResult result = FcResultMatch;
1444 // match the pattern
1445 FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
1446 bool isScalable = true;
1450 // Get the path to the font file name.
1452 GetFcString( match, FC_FILE, path );
1453 isScalable = IsScalable( path );
1457 DALI_LOG_ERROR( "FreeType Cannot check font: %s %d %d %d\n",
1458 fontDescription.family.c_str(),
1459 fontDescription.width,
1460 fontDescription.weight,
1461 fontDescription.slant );
1463 FcPatternDestroy( fontFamilyPattern );
1464 FcPatternDestroy( match );
1468 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
1470 // Empty the caller container
1474 int error = FT_New_Face( mFreeTypeLibrary,
1478 if( FT_Err_Ok != error )
1480 DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() );
1483 // Fetch the number of fixed sizes available
1484 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1486 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1488 sizes.PushBack( ftFace->available_sizes[ i ].size );
1493 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
1494 Vector< PointSize26Dot6 >& sizes )
1496 // Create a font pattern.
1497 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1499 FcResult result = FcResultMatch;
1501 // match the pattern
1502 FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
1506 // Get the path to the font file name.
1508 GetFcString( match, FC_FILE, path );
1509 GetFixedSizes( path, sizes );
1513 DALI_LOG_ERROR( "FreeType Cannot check font: %s %d %d %d\n",
1514 fontDescription.family.c_str(),
1515 fontDescription.width,
1516 fontDescription.weight,
1517 fontDescription.slant );
1519 FcPatternDestroy( match );
1520 FcPatternDestroy( fontFamilyPattern );
1523 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 pointSize, const FontPath& path )
1525 FontDescription description;
1526 description.path = path;
1527 description.family = FontFamily( ftFace->family_name );
1528 description.weight = FontWeight::NORMAL;
1529 description.width = FontWidth::NORMAL;
1530 description.slant = FontSlant::NORMAL;
1532 // Note FreeType doesn't give too much info to build a proper font style.
1533 if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
1535 description.slant = FontSlant::ITALIC;
1537 if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
1539 description.weight = FontWeight::BOLD;
1542 FontDescriptionId validatedFontId = 0u;
1543 if( !FindValidatedFont( description,
1546 // Set the index to the vector of paths to font file names.
1547 validatedFontId = mFontDescriptionCache.size();
1549 // Add the path to the cache.
1550 mFontDescriptionCache.push_back( description );
1552 // Cache the index and the font's description.
1553 FontDescriptionCacheItem item( description,
1556 mValidatedFontCache.push_back( item );
1558 // Cache the pair 'validatedFontId, pointSize' to improve the following queries.
1559 mFontIdCache.push_back( FontIdCacheItem( validatedFontId,
1565 } // namespace Internal
1567 } // namespace TextAbstraction