+ systemFonts = mSystemFonts;
+}
+
+void FontClient::Plugin::GetDescription( FontId id,
+ FontDescription& fontDescription ) const
+{
+ for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
+ endIt = mFontIdCache.end();
+ it != endIt;
+ ++it )
+ {
+ const FontIdCacheItem& item = *it;
+
+ if( item.fontId == id )
+ {
+ fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
+ return;
+ }
+ }
+
+ DALI_LOG_ERROR( "FontClient::Plugin::GetDescription. No description found for the font ID %d\n", id );
+}
+
+PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
+{
+ const FontId index = id - 1u;
+
+ if( id > 0u &&
+ index < mFontCache.size() )
+ {
+ return ( *( mFontCache.begin() + index ) ).mRequestedPointSize;
+ }
+ else
+ {
+ DALI_LOG_ERROR( "FontClient::Plugin::GetPointSize. Invalid font ID %d\n", id );
+ }
+
+ return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
+}
+
+FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
+ Character charcode,
+ PointSize26Dot6 requestedPointSize,
+ bool preferColor )
+{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFontForCharacter\n");
+
+ FontId fontId = 0u;
+ bool foundColor = false;
+
+ // Traverse the list of fonts.
+ // Check for each font if supports the character.
+ for( FontList::const_iterator it = fontList.begin(), endIt = fontList.end();
+ it != endIt;
+ ++it )
+ {
+ const FontDescription& description = *it;
+
+ FcPattern* pattern = CreateFontFamilyPattern( description );
+
+ FcResult result = FcResultMatch;
+ FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result );
+
+ FcCharSet* charSet = NULL;
+ FcPatternGetCharSet( match, FC_CHARSET, 0u, &charSet );
+
+ if( FcCharSetHasChar( charSet, charcode ) )
+ {
+ Vector< PointSize26Dot6 > fixedSizes;
+ GetFixedSizes( description,
+ fixedSizes );
+
+ PointSize26Dot6 actualPointSize = requestedPointSize;
+
+ const Vector< PointSize26Dot6 >::SizeType count = fixedSizes.Count();
+
+ if( 0 != count )
+ {
+ // If the font is not scalable, pick the largest size <= requestedPointSize
+ actualPointSize = fixedSizes[0];
+ for( unsigned int i=1; i<count; ++i )
+ {
+ if( fixedSizes[i] <= requestedPointSize &&
+ fixedSizes[i] > actualPointSize )
+ {
+ actualPointSize = fixedSizes[i];
+ }
+ }
+ }
+
+ fontId = GetFontId( description,
+ requestedPointSize,
+ actualPointSize,
+ 0u );
+
+ if( preferColor )
+ {
+ foundColor = IsColorGlyph( fontId, GetGlyphIndex( fontId, charcode ) );
+ }
+
+ // Keep going unless we prefer a different (color) font.
+ if( !preferColor || foundColor )
+ {
+ FcPatternDestroy( match );
+ FcPatternDestroy( pattern );
+ break;
+ }
+ }
+
+ FcPatternDestroy( match );
+ FcPatternDestroy( pattern );
+ }
+
+ return fontId;
+}
+
+FontId FontClient::Plugin::FindDefaultFont( Character charcode,
+ PointSize26Dot6 requestedPointSize,
+ bool preferColor )
+{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindDefaultFont DefaultFontsList(%s)\n", (mDefaultFonts.empty()?"empty":"created") );
+
+ FontId fontId(0);
+
+ // Create the list of default fonts if it has not been created.
+ if( mDefaultFonts.empty() )
+ {
+ FontDescription fontDescription;
+ fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
+ fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
+ fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
+ fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
+
+ SetFontList( fontDescription, mDefaultFonts );
+ }
+
+ // Traverse the list of default fonts.
+ // Check for each default font if supports the character.
+ fontId = FindFontForCharacter( mDefaultFonts, charcode, requestedPointSize, preferColor );
+
+ return fontId;
+}
+
+FontId FontClient::Plugin::FindFallbackFont( Character charcode,
+ const FontDescription& preferredFontDescription,
+ PointSize26Dot6 requestedPointSize,
+ bool preferColor )
+{
+ // The font id to be returned.
+ FontId fontId = 0u;
+
+ FontDescription fontDescription;
+
+ // Fill the font description with the preferred font description and complete with the defaults.
+ fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
+ fontDescription.weight = ( ( FontWeight::NONE == preferredFontDescription.weight ) ? IntToWeightType( DEFAULT_FONT_WEIGHT ) : preferredFontDescription.weight );
+ fontDescription.width = ( ( FontWidth::NONE == preferredFontDescription.width ) ? IntToWidthType( DEFAULT_FONT_WIDTH ) : preferredFontDescription.width );
+ fontDescription.slant = ( ( FontSlant::NONE == preferredFontDescription.slant ) ? IntToSlantType( DEFAULT_FONT_SLANT ) : preferredFontDescription.slant );
+
+ // Check first if the font's description has been queried before.
+ FontList* fontList( NULL );
+
+ if( !FindFallbackFontList( fontDescription, fontList ) )
+ {
+ fontList = new FontList;
+ SetFontList( fontDescription, *fontList );
+
+ // Add the font-list to the cache.
+ mFallbackCache.push_back( FallbackCacheItem( fontDescription, fontList ) );
+ }
+
+ if( fontList )
+ {
+ fontId = FindFontForCharacter( *fontList, charcode, requestedPointSize, preferColor );
+ }
+
+ return fontId;
+}
+
+FontId FontClient::Plugin::GetFontId( const FontPath& path,
+ PointSize26Dot6 requestedPointSize,
+ PointSize26Dot6 actualPointSize,
+ FaceIndex faceIndex,
+ bool cacheDescription )
+{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId fontPatch:%s\n", path.c_str() );
+
+ FontId id( 0 );
+
+ if( NULL != mFreeTypeLibrary )
+ {
+ FontId foundId(0);
+ if( FindFont( path, requestedPointSize, faceIndex, foundId ) )
+ {
+ id = foundId;
+ }
+ else
+ {
+ id = CreateFont( path, requestedPointSize, actualPointSize, faceIndex, cacheDescription );
+ }
+ }
+
+ return id;
+}
+
+FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
+ PointSize26Dot6 requestedPointSize,
+ PointSize26Dot6 actualPointSize,
+ FaceIndex faceIndex )
+{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId font family(%s)\n", fontDescription.family.c_str() );
+
+ // This method uses three vectors which caches:
+ // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
+ // * The path to font file names.
+ // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
+
+ // 1) Checks in the cache if the font's description has been validated before.
+ // If it was it gets an index to the vector with paths to font file names. Otherwise,
+ // retrieves using font config a path to a font file name which matches with the
+ // font's description. The path is stored in the cache.
+ //
+ // 2) Checks in the cache if the pair 'font point size, index to the vector with paths to
+ // font file names' exists. If exists, it gets the font id. If it doesn't it calls
+ // the GetFontId() method with the path to the font file name and the point size to
+ // get the font id.
+
+ // The font id to be returned.
+ FontId fontId = 0u;
+
+ // Check first if the font's description have been validated before.
+ FontDescriptionId validatedFontId = 0u;
+
+ if( !FindValidatedFont( fontDescription,
+ validatedFontId ) )
+ {
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId Validating Font\n");
+
+ // Use font config to validate the font's description.
+ ValidateFont( fontDescription,
+ validatedFontId );
+ }
+
+ // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
+ if( !FindFont( validatedFontId, requestedPointSize, fontId ) )
+ {
+ // Retrieve the font file name path.
+ const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
+
+ // Retrieve the font id. Do not cache the description as it has been already cached.
+ fontId = GetFontId( description.path,
+ requestedPointSize,
+ actualPointSize,
+ faceIndex,
+ false );
+
+ // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
+ mFontIdCache.push_back( FontIdCacheItem( validatedFontId,
+ requestedPointSize,
+ fontId ) );
+ }
+
+ return fontId;
+}
+
+void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
+ FontDescriptionId& validatedFontId )
+{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont Validating Font family(%s) \n", fontDescription.family.c_str() );
+
+ // Create a font pattern.
+ FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
+
+ FontDescription description;
+
+ bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description );
+ FcPatternDestroy( fontFamilyPattern );
+
+ if( matched )
+ {
+ // Set the index to the vector of paths to font file names.
+ validatedFontId = mFontDescriptionCache.size();
+
+ // Add the path to the cache.
+ mFontDescriptionCache.push_back( description );
+
+ // Cache the index and the matched font's description.
+ FontDescriptionCacheItem item( description,
+ validatedFontId );
+
+ mValidatedFontCache.push_back( item );
+
+ if( ( fontDescription.family != description.family ) ||
+ ( fontDescription.width != description.width ) ||
+ ( fontDescription.weight != description.weight ) ||
+ ( fontDescription.slant != description.slant ) )
+ {
+ // Cache the given font's description if it's different than the matched.
+ FontDescriptionCacheItem item( fontDescription,
+ validatedFontId );
+
+ mValidatedFontCache.push_back( item );
+ }
+ }
+ else
+ {
+ DALI_LOG_ERROR( "FontClient::Plugin::ValidateFont failed for font %s %d %d %d\n",
+ fontDescription.family.c_str(),
+ fontDescription.width,
+ fontDescription.weight,
+ fontDescription.slant );
+ }
+
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont validatedFontId(%u) font family(%s)\n", validatedFontId, fontDescription.family.c_str() );
+}
+
+void FontClient::Plugin::GetFontMetrics( FontId fontId,
+ FontMetrics& metrics )
+{
+ if( ( fontId > 0 ) &&
+ ( fontId - 1u < mFontCache.size() ) )
+ {
+ const FontFaceCacheItem& font = mFontCache[fontId-1];
+
+ metrics = font.mMetrics;
+
+ // Adjust the metrics if the fixed-size font should be down-scaled
+ if( font.mIsFixedSizeBitmap )
+ {
+ const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
+
+ if( desiredFixedSize > 0.f )
+ {
+ const float scaleFactor = desiredFixedSize / static_cast<float>( font.mFixedHeightPixels );
+
+ metrics.ascender = floorf( metrics.ascender * scaleFactor );
+ metrics.descender = floorf( metrics.descender * scaleFactor );
+ metrics.height = floorf( metrics.height * scaleFactor );
+ metrics.underlinePosition = floorf( metrics.underlinePosition * scaleFactor );
+ metrics.underlineThickness = floorf( metrics.underlineThickness * scaleFactor );
+ }
+ }
+ }
+ else
+ {
+ DALI_LOG_ERROR( "Invalid font ID %d\n", fontId );
+ }
+}
+
+GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
+ Character charcode )
+{
+ GlyphIndex index( 0 );
+
+ if( fontId > 0 &&
+ fontId-1 < mFontCache.size() )
+ {
+ FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
+
+ index = FT_Get_Char_Index( ftFace, charcode );
+ }
+
+ return index;
+}
+
+bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
+ uint32_t size,
+ GlyphType type,
+ bool horizontal )
+{
+ if( VECTOR_GLYPH == type )
+ {
+ return GetVectorMetrics( array, size, horizontal );
+ }
+
+ return GetBitmapMetrics( array, size, horizontal );
+}
+
+bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
+ uint32_t size,
+ bool horizontal )
+{
+ bool success( true );
+
+ for( unsigned int i=0; i<size; ++i )
+ {
+ GlyphInfo& glyph = array[i];
+
+ FontId fontId = glyph.fontId;
+
+ if( fontId > 0 &&
+ fontId-1 < mFontCache.size() )
+ {
+ const FontFaceCacheItem& font = mFontCache[fontId-1];
+
+ FT_Face ftFace = font.mFreeTypeFace;
+
+#ifdef FREETYPE_BITMAP_SUPPORT
+ // Check to see if we should be loading a Fixed Size bitmap?
+ if ( font.mIsFixedSizeBitmap )
+ {
+ int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR );
+ if ( FT_Err_Ok == error )
+ {
+ glyph.width = font.mFixedWidthPixels;
+ glyph.height = font.mFixedHeightPixels;
+ glyph.advance = font.mFixedWidthPixels;
+ glyph.xBearing = 0.0f;
+ glyph.yBearing = font.mFixedHeightPixels;
+
+ // Adjust the metrics if the fixed-size font should be down-scaled
+ const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
+
+ if( desiredFixedSize > 0.f )
+ {
+ const float scaleFactor = desiredFixedSize / static_cast<float>( font.mFixedHeightPixels );
+
+ glyph.width = floorf( glyph.width * scaleFactor );
+ glyph.height = floorf( glyph.height * scaleFactor );
+ glyph.advance = floorf( glyph.advance * scaleFactor );
+ glyph.xBearing = floorf( glyph.xBearing * scaleFactor );
+ glyph.yBearing = floorf( glyph.yBearing * scaleFactor );
+
+ glyph.scaleFactor = scaleFactor;
+ }
+ }
+ else
+ {
+ DALI_LOG_ERROR( "FreeType Bitmap Load_Glyph error %d\n", error );
+ success = false;
+ }
+ }
+ else
+#endif
+ {
+ int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_DEFAULT );
+
+ if( FT_Err_Ok == error )
+ {
+ glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
+ glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
+ if( horizontal )
+ {
+ glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
+ glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
+ }
+ else
+ {
+ glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
+ glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
+ }
+ }
+ else
+ {
+ success = false;
+ }
+ }
+ }
+ else
+ {
+ success = false;
+ }
+ }
+
+ return success;
+}
+
+bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
+ uint32_t size,
+ bool horizontal )
+{
+#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
+ bool success( true );
+
+ for( unsigned int i=0; i<size; ++i )
+ {
+ FontId fontId = array[i].fontId;
+
+ if( fontId > 0 &&
+ fontId-1 < mFontCache.size() )
+ {
+ FontFaceCacheItem& font = mFontCache[fontId-1];
+
+ if( ! font.mVectorFontId )
+ {
+ font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
+ }
+
+ mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
+
+ // Vector metrics are in EMs, convert to pixels
+ const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
+ array[i].width *= scale;
+ array[i].height *= scale;
+ array[i].xBearing *= scale;
+ array[i].yBearing *= scale;
+ array[i].advance *= scale;
+ }
+ else
+ {
+ success = false;
+ }
+ }
+
+ return success;
+#else
+ return false;
+#endif
+}
+
+void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data )
+{
+ if( ( fontId > 0 ) &&
+ ( fontId - 1u < mFontCache.size() ) )
+ {
+ FT_Face ftFace = mFontCache[fontId - 1u].mFreeTypeFace;
+
+ FT_Error error;
+
+#ifdef FREETYPE_BITMAP_SUPPORT
+ // Check to see if this is fixed size bitmap
+ if ( mFontCache[fontId - 1u].mIsFixedSizeBitmap )
+ {
+ error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
+ }
+ else
+#endif
+ {
+ error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_DEFAULT );
+ }
+ if( FT_Err_Ok == error )
+ {
+ FT_Glyph glyph;
+ error = FT_Get_Glyph( ftFace->glyph, &glyph );
+
+ // Convert to bitmap if necessary
+ if ( FT_Err_Ok == error )
+ {
+ if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
+ {
+ error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
+ if ( FT_Err_Ok == error )
+ {
+ FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph;
+ ConvertBitmap( data, bitmapGlyph->bitmap );
+ }
+ else
+ {
+ DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error );
+ }
+ }
+ else
+ {
+ ConvertBitmap( data, ftFace->glyph->bitmap );
+ }
+
+ // Created FT_Glyph object must be released with FT_Done_Glyph
+ FT_Done_Glyph( glyph );
+ }
+ }
+ else
+ {
+ DALI_LOG_ERROR( "FT_Load_Glyph Failed with error: %d\n", error );
+ }
+ }
+}
+
+PixelData FontClient::Plugin::CreateBitmap( FontId fontId,
+ GlyphIndex glyphIndex )
+{
+ TextAbstraction::FontClient::GlyphBufferData data;
+
+ CreateBitmap( fontId, glyphIndex, data );
+
+ return PixelData::New( data.buffer,
+ data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
+ data.width,
+ data.height,
+ data.format,
+ PixelData::DELETE_ARRAY );
+}
+
+void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
+{
+ blob = NULL;
+ blobLength = 0;
+
+#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
+ if( fontId > 0 &&
+ fontId-1 < mFontCache.size() )
+ {
+ FontFaceCacheItem& font = mFontCache[fontId-1];
+
+ if( ! font.mVectorFontId )
+ {
+ font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
+ }
+
+ mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
+ }
+#endif
+}
+
+const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
+{
+ // First look into the cache if there is an ellipsis glyph for the requested point size.
+ for( Vector<EllipsisItem>::ConstIterator it = mEllipsisCache.Begin(),
+ endIt = mEllipsisCache.End();
+ it != endIt;
+ ++it )
+ {
+ const EllipsisItem& item = *it;
+
+ if( fabsf( item.requestedPointSize - requestedPointSize ) < Math::MACHINE_EPSILON_1000 )
+ {
+ // Use the glyph in the cache.
+ return item.glyph;
+ }
+ }
+
+ // No glyph has been found. Create one.
+ mEllipsisCache.PushBack( EllipsisItem() );
+ EllipsisItem& item = *( mEllipsisCache.End() - 1u );
+
+ item.requestedPointSize = requestedPointSize;
+
+ // Find a font for the ellipsis glyph.
+ item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
+ requestedPointSize,
+ false );
+
+ // Set the character index to access the glyph inside the font.
+ item.glyph.index = FT_Get_Char_Index( mFontCache[item.glyph.fontId-1].mFreeTypeFace,
+ ELLIPSIS_CHARACTER );
+
+ GetBitmapMetrics( &item.glyph, 1u, true );
+
+ return item.glyph;
+}
+
+bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
+{
+ FT_Error error = -1;
+
+#ifdef FREETYPE_BITMAP_SUPPORT
+ if( ( fontId > 0 ) &&
+ ( fontId - 1u < mFontCache.size() ) )
+ {
+ const FontFaceCacheItem& item = mFontCache[fontId - 1u];
+ FT_Face ftFace = item.mFreeTypeFace;
+
+ // Check to see if this is fixed size bitmap
+ if( item.mIsFixedSizeBitmap )
+ {
+ error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
+ }
+ }
+#endif
+
+ return FT_Err_Ok == error;