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/public-api/common/vector-wrapper.h>
23 #include <dali/public-api/text-abstraction/glyph-info.h>
24 #include <dali/integration-api/debug.h>
27 #include <fontconfig/fontconfig.h>
30 * Conversion from Fractional26.6 to float
34 const float FROM_266 = 1.0f / 64.0f;
36 const std::string FONT_FORMAT( "TrueType" );
37 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
38 const std::string DEFAULT_FONT_STYLE( "Regular" );
44 namespace TextAbstraction
50 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontFamily& fontFamily,
51 const FontStyle& fontStyle,
52 FontDescriptionId index )
53 : fontFamily( fontFamily ),
54 fontStyle( fontStyle ),
58 FontClient::Plugin::FontIdCacheItem::FontIdCacheItem( FontDescriptionId validatedFontId,
59 PointSize26Dot6 pointSize,
61 : validatedFontId( validatedFontId ),
62 pointSize( pointSize ),
66 FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace,
68 PointSize26Dot6 pointSize,
70 const FontMetrics& metrics )
71 : mFreeTypeFace( ftFace ),
73 mPointSize( pointSize ),
78 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
79 unsigned int verticalDpi )
80 : mFreeTypeLibrary( NULL ),
81 mDpiHorizontal( horizontalDpi ),
82 mDpiVertical( verticalDpi ),
86 mValidatedFontCache(),
87 mFontDescriptionCache( 1u ),
90 int error = FT_Init_FreeType( &mFreeTypeLibrary );
91 if( FT_Err_Ok != error )
93 DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
97 FontClient::Plugin::~Plugin()
99 FT_Done_FreeType( mFreeTypeLibrary );
102 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
103 unsigned int verticalDpi )
105 mDpiHorizontal = horizontalDpi;
106 mDpiVertical = verticalDpi;
109 void FontClient::Plugin::SetDefaultFontFamily( const FontFamily& fontFamilyName,
110 const FontStyle& fontStyle )
112 mDefaultFonts.clear();
114 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontFamilyName,
117 FcResult result = FcResultMatch;
119 // Match the pattern.
120 FcFontSet* fontSet = FcFontSort( NULL /* use default configure */,
122 false /* don't trim */,
126 if( NULL != fontSet )
128 // Reserve some space to avoid reallocations.
129 mDefaultFonts.reserve( fontSet->nfont );
131 for( int i = 0u; i < fontSet->nfont; ++i )
133 FcPattern* fontPattern = fontSet->fonts[i];
137 // Skip fonts with no path
138 if( GetFcString( fontPattern, FC_FILE, path ) )
140 mDefaultFonts.push_back( FontDescription() );
141 FontDescription& fontDescription = mDefaultFonts.back();
143 fontDescription.path = path;
145 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
146 GetFcString( fontPattern, FC_STYLE, fontDescription.style );
150 FcFontSetDestroy( fontSet );
153 FcPatternDestroy( fontFamilyPattern );
156 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
158 if( mDefaultFonts.empty() )
160 SetDefaultFontFamily( DEFAULT_FONT_FAMILY_NAME,
161 DEFAULT_FONT_STYLE );
164 defaultFonts = mDefaultFonts;
167 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
169 if( mSystemFonts.empty() )
174 systemFonts = mSystemFonts;
177 void FontClient::Plugin::GetDescription( FontId id,
178 FontDescription& fontDescription ) const
180 for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
181 endIt = mFontIdCache.end();
185 const FontIdCacheItem& item = *it;
187 if( item.fontId == id )
189 fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
193 DALI_LOG_ERROR( "FontClient::Plugin::GetDescription. No description found for the font ID %d\n", id );
196 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
198 const FontId index = id - 1u;
201 index < mFontCache.size() )
203 return ( *( mFontCache.begin() + index ) ).mPointSize;
207 DALI_LOG_ERROR( "FontClient::Plugin::GetPointSize. Invalid font ID %d\n", id );
210 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
213 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
214 PointSize26Dot6 pointSize )
216 // Create the list of default fonts if it has not been created.
217 if( mDefaultFonts.empty() )
219 SetDefaultFontFamily( DEFAULT_FONT_FAMILY_NAME,
220 DEFAULT_FONT_STYLE );
223 // Traverse the list of default fonts.
224 // Check for each default font if supports the character.
226 for( FontList::const_iterator it = mDefaultFonts.begin(),
227 endIt = mDefaultFonts.end();
231 const FontDescription& description = *it;
233 FcPattern* pattern = CreateFontFamilyPattern( description.family,
236 FcResult result = FcResultMatch;
237 FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result );
239 FcCharSet* charSet = NULL;
240 FcPatternGetCharSet( match, FC_CHARSET, 0u, &charSet );
242 if( FcCharSetHasChar( charSet, charcode ) )
244 return GetFontId( description.family,
254 FontId FontClient::Plugin::GetFontId( const FontPath& path,
255 PointSize26Dot6 pointSize,
257 bool cacheDescription )
261 if( NULL != mFreeTypeLibrary )
264 if( FindFont( path, pointSize, faceIndex, foundId ) )
270 id = CreateFont( path, pointSize, faceIndex, cacheDescription );
277 FontId FontClient::Plugin::GetFontId( const FontFamily& fontFamily,
278 const FontStyle& fontStyle,
279 PointSize26Dot6 pointSize,
280 FaceIndex faceIndex )
282 // This method uses three vectors which caches:
283 // * Pairs of non validated 'fontFamily, fontStyle' and an index to a vector with paths to font file names.
284 // * The path to font file names.
285 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
287 // 1) Checks in the cache if the pair 'fontFamily, fontStyle' has been validated before.
288 // If it was it gets an index to the vector with paths to font file names. Otherwise,
289 // retrieves using font config a path to a font file name which matches with the pair
290 // 'fontFamily, fontStyle'. The path is stored in the chache.
292 // 2) Checks in the cache if the pair 'font point size, index to the vector with paths to
293 // fon file names' exists. If exists, it gets the font id. If it doesn't it calls
294 // the GetFontId() method with the path to the font file name and the point size to
297 // The font id to be returned.
300 // Check first if the pair font family and style have been validated before.
301 FontDescriptionId validatedFontId = 0u;
303 if( !FindValidatedFont( fontFamily,
307 // Use font config to validate the font family name and font style.
309 // Create a font pattern.
310 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontFamily,
313 FcResult result = FcResultMatch;
316 FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
320 // Get the path to the font file name.
321 FontDescription description;
322 GetFcString( match, FC_FILE, description.path );
323 GetFcString( match, FC_FAMILY, description.family );
324 GetFcString( match, FC_STYLE, description.style );
326 // Set the index to the vector of paths to font file names.
327 validatedFontId = mFontDescriptionCache.size();
329 // Add the path to the cache.
330 mFontDescriptionCache.push_back( description );
332 // Cache the index and the pair font family name, font style.
333 FontDescriptionCacheItem item( fontFamily, fontStyle, validatedFontId );
334 mValidatedFontCache.push_back( item );
336 // destroyed the matched pattern
337 FcPatternDestroy( match );
341 DALI_LOG_ERROR( "FontClient::Plugin::GetFontId failed for font %s %s\n", fontFamily.c_str(), fontStyle.c_str() );
344 // destroy the pattern
345 FcPatternDestroy( fontFamilyPattern );
348 // Check if exists a pair 'validatedFontId, pointSize' in the cache.
349 if( !FindFont( validatedFontId, pointSize, fontId ) )
351 // Retrieve the font file name path.
352 const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
354 // Retrieve the font id. Do not cache the description as it has been already cached.
355 fontId = GetFontId( description.path,
360 // Cache the pair 'validatedFontId, pointSize' to improve the following queries.
361 mFontIdCache.push_back( FontIdCacheItem( validatedFontId,
369 void FontClient::Plugin::GetFontMetrics( FontId fontId,
370 FontMetrics& metrics )
373 fontId-1 < mFontCache.size() )
375 metrics = mFontCache[fontId-1].mMetrics;
379 DALI_LOG_ERROR( "Invalid font ID %d\n", fontId );
383 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
386 GlyphIndex index( 0 );
389 fontId-1 < mFontCache.size() )
391 FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
393 index = FT_Get_Char_Index( ftFace, charcode );
399 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
403 bool success( true );
405 for( unsigned int i=0; i<size; ++i )
407 FontId fontId = array[i].fontId;
410 fontId-1 < mFontCache.size() )
412 FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
414 int error = FT_Load_Glyph( ftFace, array[i].index, FT_LOAD_DEFAULT );
416 if( FT_Err_Ok == error )
418 array[i].width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
419 array[i].height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
422 array[i].xBearing = static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
423 array[i].yBearing = static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
424 array[i].advance = static_cast< float >( ftFace->glyph->metrics.horiAdvance ) * FROM_266;
428 array[i].xBearing = static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
429 array[i].yBearing = static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
430 array[i].advance = static_cast< float >( ftFace->glyph->metrics.vertAdvance ) * FROM_266;
447 BitmapImage FontClient::Plugin::CreateBitmap( FontId fontId,
448 GlyphIndex glyphIndex )
453 fontId-1 < mFontCache.size() )
455 FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
457 FT_Error error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_DEFAULT );
458 if( FT_Err_Ok == error )
461 error = FT_Get_Glyph( ftFace->glyph, &glyph );
463 // Convert to bitmap if necessary
464 if ( FT_Err_Ok == error )
466 if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
468 error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
472 DALI_LOG_ERROR( "FT_Glyph_To_Bitmap Failed with error: %d\n", error );
477 DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error );
480 if( FT_Err_Ok == error )
482 // Access the underlying bitmap data
483 FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph;
484 ConvertBitmap( bitmap, bitmapGlyph->bitmap );
487 // Created FT_Glyph object must be released with FT_Done_Glyph
488 FT_Done_Glyph( glyph );
492 DALI_LOG_ERROR( "FT_Load_Glyph Failed with error: %d\n", error );
499 void FontClient::Plugin::InitSystemFonts()
501 FcFontSet* fontSet = GetFcFontSet();
505 // Reserve some space to avoid reallocations.
506 mSystemFonts.reserve( fontSet->nfont );
508 for( int i = 0u; i < fontSet->nfont; ++i )
510 FcPattern* fontPattern = fontSet->fonts[i];
514 // Skip fonts with no path
515 if( GetFcString( fontPattern, FC_FILE, path ) )
517 mSystemFonts.push_back( FontDescription() );
518 FontDescription& fontDescription = mSystemFonts.back();
520 fontDescription.path = path;
522 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
523 GetFcString( fontPattern, FC_STYLE, fontDescription.style );
527 FcFontSetDestroy( fontSet );
531 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontFamily& fontFamily,
532 const FontStyle& fontStyle )
534 // create the cached font family lookup pattern
535 // a pattern holds a set of names, each name refers to a property of the font
536 FcPattern* fontFamilyPattern = FcPatternCreate();
538 // add a property to the pattern for the font family
539 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontFamily.c_str() ) );
541 // add a property to the pattern for the font family
542 FcPatternAddString( fontFamilyPattern, FC_STYLE, reinterpret_cast<const FcChar8*>( fontStyle.c_str() ) );
544 // Add a property of the pattern, to say we want to match TrueType fonts
545 FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
547 // modify the config, with the mFontFamilyPatterm
548 FcConfigSubstitute( NULL /* use default configure */, fontFamilyPattern, FcMatchPattern );
550 // provide default values for unspecified properties in the font pattern
551 // e.g. patterns without a specified style or weight are set to Medium
552 FcDefaultSubstitute( fontFamilyPattern );
554 return fontFamilyPattern;
557 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
559 // create a new pattern.
560 // a pattern holds a set of names, each name refers to a property of the font
561 FcPattern* pattern = FcPatternCreate();
563 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
564 FcObjectSet* objectSet = FcObjectSetCreate();
566 // build an object set from a list of property names
567 FcObjectSetAdd( objectSet, FC_FILE );
568 FcObjectSetAdd( objectSet, FC_FAMILY );
569 FcObjectSetAdd( objectSet, FC_STYLE );
571 // get a list of fonts
572 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
573 FcFontSet* fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
575 // clear up the object set
578 FcObjectSetDestroy( objectSet );
580 // clear up the pattern
583 FcPatternDestroy( pattern );
589 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
591 std::string& string )
593 FcChar8* file = NULL;
594 const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
596 if( FcResultMatch == retVal )
598 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
599 string.assign( reinterpret_cast<const char*>( file ) );
607 FontId FontClient::Plugin::CreateFont( const FontPath& path,
608 PointSize26Dot6 pointSize,
610 bool cacheDescription )
614 // Create & cache new font face
616 int error = FT_New_Face( mFreeTypeLibrary,
621 if( FT_Err_Ok == error )
623 error = FT_Set_Char_Size( ftFace,
629 if( FT_Err_Ok == error )
631 id = mFontCache.size() + 1;
633 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
635 FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266,
636 static_cast< float >( ftMetrics.descender ) * FROM_266,
637 static_cast< float >( ftMetrics.height ) * FROM_266 );
639 mFontCache.push_back( CacheItem( ftFace, path, pointSize, faceIndex, metrics ) );
641 if( cacheDescription )
643 FontDescription description;
644 description.path = path;
645 description.family = FontFamily( ftFace->family_name );
646 description.style = FontStyle( ftFace->style_name );
648 mFontDescriptionCache.push_back( description );
653 DALI_LOG_ERROR( "FreeType Set_Char_Size error: %d for pointSize %d\n", pointSize );
658 DALI_LOG_ERROR( "FreeType New_Face error: %d for %s\n", error, path.c_str() );
664 void FontClient::Plugin::ConvertBitmap( BitmapImage& destBitmap,
665 FT_Bitmap srcBitmap )
667 if( srcBitmap.width*srcBitmap.rows > 0 )
669 // TODO - Support all pixel modes
670 if( FT_PIXEL_MODE_GRAY == srcBitmap.pixel_mode )
672 if( srcBitmap.pitch == srcBitmap.width )
674 destBitmap = BitmapImage::New( srcBitmap.width, srcBitmap.rows, Pixel::L8 );
676 PixelBuffer* destBuffer = destBitmap.GetBuffer();
677 memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows );
683 bool FontClient::Plugin::FindFont( const FontPath& path,
684 PointSize26Dot6 pointSize,
686 FontId& fontId ) const
689 for( std::vector<CacheItem>::const_iterator it = mFontCache.begin(),
690 endIt = mFontCache.end();
694 const CacheItem& cacheItem = *it;
696 if( cacheItem.mPointSize == pointSize &&
697 cacheItem.mFaceIndex == faceIndex &&
698 cacheItem.mPath == path )
708 bool FontClient::Plugin::FindValidatedFont( const FontFamily& fontFamily,
709 const FontStyle& fontStyle,
710 FontDescriptionId& validatedFontId )
712 validatedFontId = 0u;
714 for( std::vector<FontDescriptionCacheItem>::const_iterator it = mValidatedFontCache.begin(),
715 endIt = mValidatedFontCache.end();
719 const FontDescriptionCacheItem& item = *it;
721 if( ( fontFamily == item.fontFamily ) &&
722 ( fontStyle == item.fontStyle ) )
724 validatedFontId = item.index;
733 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
734 PointSize26Dot6 pointSize,
739 for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
740 endIt = mFontIdCache.end();
744 const FontIdCacheItem& item = *it;
746 if( ( validatedFontId == item.validatedFontId ) &&
747 ( pointSize == item.pointSize ) )
749 fontId = item.fontId;
757 } // namespace Internal
759 } // namespace TextAbstraction