2 * Copyright (c) 2018 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/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/text-abstraction/font-client-helper.h>
28 #include <dali/internal/imaging/common/image-operations.h>
29 #include <dali/internal/adaptor/common/adaptor-impl.h>
30 #include <dali/devel-api/adaptor-framework/image-loading.h>
33 #include <fontconfig/fontconfig.h>
38 #if defined(DEBUG_ENABLED)
39 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
43 * Conversion from Fractional26.6 to float
45 const float FROM_266 = 1.0f / 64.0f;
46 const float POINTS_PER_INCH = 72.f;
48 const std::string FONT_FORMAT( "TrueType" );
49 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
50 const int DEFAULT_FONT_WIDTH = 100; // normal
51 const int DEFAULT_FONT_WEIGHT = 80; // normal
52 const int DEFAULT_FONT_SLANT = 0; // normal
54 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
56 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
58 // NONE -1 --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
68 const int FONT_WIDTH_TYPE_TO_INT[] = { -1, 50, 63, 75, 87, 100, 113, 125, 150, 200 };
69 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / sizeof( int );
71 // NONE -1 --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
73 // ULTRA_LIGHT, EXTRA_LIGHT 40
75 // DEMI_LIGHT, SEMI_LIGHT 55
79 // DEMI_BOLD, SEMI_BOLD 180
81 // ULTRA_BOLD, EXTRA_BOLD 205
82 // BLACK, HEAVY, EXTRA_BLACK 210
83 const int FONT_WEIGHT_TYPE_TO_INT[] = { -1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210 };
84 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof( FONT_WEIGHT_TYPE_TO_INT ) / sizeof( int );
86 // NONE -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
90 const int FONT_SLANT_TYPE_TO_INT[] = { -1, 0, 100, 110 };
91 const unsigned int NUM_FONT_SLANT_TYPE = sizeof( FONT_SLANT_TYPE_TO_INT ) / sizeof( int );
100 namespace TextAbstraction
107 * @brief Returns the FontWidth's enum index for the given width value.
109 * @param[in] width The width value.
111 * @return The FontWidth's enum index.
113 FontWidth::Type IntToWidthType( int width )
115 return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
119 * @brief Returns the FontWeight's enum index for the given weight value.
121 * @param[in] weight The weight value.
123 * @return The FontWeight's enum index.
125 FontWeight::Type IntToWeightType( int weight )
127 return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
131 * @brief Returns the FontSlant's enum index for the given slant value.
133 * @param[in] slant The slant value.
135 * @return The FontSlant's enum index.
137 FontSlant::Type IntToSlantType( int slant )
139 return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
142 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets )
143 : fontDescription{ std::move( font ) },
144 fallbackFonts{ fallbackFonts },
145 characterSets{ characterSets }
149 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
150 FontDescriptionId index )
151 : fontDescription{ fontDescription },
156 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( FontDescription&& fontDescription,
157 FontDescriptionId index )
158 : fontDescription{ std::move( fontDescription ) },
163 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem( FontDescriptionId validatedFontId,
164 PointSize26Dot6 requestedPointSize,
166 : validatedFontId( validatedFontId ),
167 requestedPointSize( requestedPointSize ),
172 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
173 const FontPath& path,
174 PointSize26Dot6 requestedPointSize,
176 const FontMetrics& metrics )
177 : mFreeTypeFace( ftFace ),
179 mRequestedPointSize( requestedPointSize ),
182 mCharacterSet( nullptr ),
183 mFixedSizeIndex( 0 ),
184 mFixedWidthPixels( 0.f ),
185 mFixedHeightPixels( 0.f ),
188 mIsFixedSizeBitmap( false ),
189 mHasColorTables( false )
193 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
194 const FontPath& path,
195 PointSize26Dot6 requestedPointSize,
197 const FontMetrics& metrics,
201 bool hasColorTables )
202 : mFreeTypeFace( ftFace ),
204 mRequestedPointSize( requestedPointSize ),
207 mCharacterSet( nullptr ),
208 mFixedSizeIndex( fixedSizeIndex ),
209 mFixedWidthPixels( fixedWidth ),
210 mFixedHeightPixels( fixedHeight ),
213 mIsFixedSizeBitmap( true ),
214 mHasColorTables( hasColorTables )
218 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
219 unsigned int verticalDpi )
220 : mFreeTypeLibrary( nullptr ),
221 mDpiHorizontal( horizontalDpi ),
222 mDpiVertical( verticalDpi ),
223 mDefaultFontDescription(),
228 mValidatedFontCache(),
229 mFontDescriptionCache( 1u ),
230 mCharacterSetCache(),
231 mFontDescriptionSizeCache(),
232 mVectorFontCache( nullptr ),
234 mEmbeddedItemCache(),
235 mDefaultFontDescriptionCached( false )
237 mCharacterSetCache.Resize( 1u );
239 int error = FT_Init_FreeType( &mFreeTypeLibrary );
240 if( FT_Err_Ok != error )
242 DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Init error: %d\n", error );
245 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
246 mVectorFontCache = new VectorFontCache( mFreeTypeLibrary );
250 FontClient::Plugin::~Plugin()
252 for( auto& item : mFallbackCache )
254 if( item.fallbackFonts )
256 delete item.fallbackFonts;
257 delete item.characterSets;
258 item.fallbackFonts = nullptr;
259 item.characterSets = nullptr;
263 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
264 delete mVectorFontCache;
266 DestroyMatchedPatterns();
267 FT_Done_FreeType( mFreeTypeLibrary );
270 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
271 unsigned int verticalDpi )
273 mDpiHorizontal = horizontalDpi;
274 mDpiVertical = verticalDpi;
277 void FontClient::Plugin::ResetSystemDefaults()
279 mDefaultFontDescriptionCached = false;
282 void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList )
284 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n" );
285 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
286 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
287 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
288 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
292 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
294 FcResult result = FcResultMatch;
296 // Match the pattern.
297 FcFontSet* fontSet = FcFontSort( nullptr /* use default configure */,
299 false /* don't trim */,
303 if( nullptr != fontSet )
305 DALI_LOG_INFO( gLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont );
306 // Reserve some space to avoid reallocations.
307 fontList.reserve( fontSet->nfont );
309 for( int i = 0u; i < fontSet->nfont; ++i )
311 FcPattern* fontPattern = fontSet->fonts[i];
315 // Skip fonts with no path
316 if( GetFcString( fontPattern, FC_FILE, path ) )
318 FcCharSet* characterSet = nullptr;
319 FcPatternGetCharSet( fontPattern, FC_CHARSET, 0u, &characterSet );
321 characterSetList.PushBack( characterSet );
322 fontList.push_back( FontDescription() );
323 FontDescription& newFontDescription = fontList.back();
325 newFontDescription.path = std::move( path );
330 GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
331 GetFcInt( fontPattern, FC_WIDTH, width );
332 GetFcInt( fontPattern, FC_WEIGHT, weight );
333 GetFcInt( fontPattern, FC_SLANT, slant );
334 newFontDescription.width = IntToWidthType( width );
335 newFontDescription.weight = IntToWeightType( weight );
336 newFontDescription.slant = IntToSlantType( slant );
338 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", newFontDescription.family.c_str() );
339 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", newFontDescription.path.c_str() );
340 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[newFontDescription.width] );
341 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[newFontDescription.weight] );
342 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant] );
346 FcFontSetDestroy( fontSet );
350 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " No fonts found.\n" );
353 FcPatternDestroy( fontFamilyPattern );
354 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n" );
357 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
359 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n" );
361 if( mDefaultFonts.empty() )
363 FontDescription fontDescription;
364 fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
365 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
366 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
367 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
368 SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
371 defaultFonts = mDefaultFonts;
373 DALI_LOG_INFO( gLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size() );
374 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n" );
377 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
379 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
381 if( !mDefaultFontDescriptionCached )
383 // Clear any font config stored info in the caches.
384 mDefaultFontCharacterSets.Clear();
385 mCharacterSetCache.Clear();
387 for( auto& item : mFallbackCache )
389 item.characterSets->Clear();
392 for( auto& item : mFontFaceCache )
394 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
395 item.mCharacterSet = nullptr;
398 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
399 FcInitReinitialize();
401 FcPattern* matchPattern = FcPatternCreate();
405 FcConfigSubstitute( nullptr, matchPattern, FcMatchPattern );
406 FcDefaultSubstitute( matchPattern );
408 FcCharSet* characterSet = nullptr;
409 MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription, &characterSet );
410 FcPatternDestroy( matchPattern );
413 // Create again the character sets as they are not valid after FcInitReinitialize()
415 for( const auto& description : mDefaultFonts )
417 mDefaultFontCharacterSets.PushBack( CreateCharacterSetFromDescription( description ) );
420 for( const auto& description : mFontDescriptionCache )
422 mCharacterSetCache.PushBack( CreateCharacterSetFromDescription( description ) );
425 for( auto& item : mFallbackCache )
427 if( nullptr != item.fallbackFonts )
429 if( nullptr == item.characterSets )
431 item.characterSets = new CharacterSetList;
434 for( const auto& description : *( item.fallbackFonts ) )
436 item.characterSets->PushBack( CreateCharacterSetFromDescription( description ) );
441 mDefaultFontDescriptionCached = true;
444 fontDescription.path = mDefaultFontDescription.path;
445 fontDescription.family = mDefaultFontDescription.family;
446 fontDescription.width = mDefaultFontDescription.width;
447 fontDescription.weight = mDefaultFontDescription.weight;
448 fontDescription.slant = mDefaultFontDescription.slant;
450 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
451 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
452 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
453 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
454 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
455 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
458 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
460 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
462 if( mSystemFonts.empty() )
467 systemFonts = mSystemFonts;
468 DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size() );
469 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
472 void FontClient::Plugin::GetDescription( FontId id,
473 FontDescription& fontDescription ) const
475 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
476 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
477 const FontId index = id - 1u;
479 if( ( id > 0u ) && ( index < mFontIdCache.Count() ) )
481 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
482 switch( fontIdCacheItem.type )
484 case FontDescription::FACE_FONT:
486 for( const auto& item : mFontDescriptionSizeCache )
488 if( item.fontId == fontIdCacheItem.id )
490 fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
492 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
493 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
494 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
495 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
496 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
497 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
503 case FontDescription::BITMAP_FONT:
505 fontDescription.type = FontDescription::BITMAP_FONT;
506 fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name;
511 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
512 fontDescription.type = FontDescription::INVALID;
513 fontDescription.family.clear();
518 DALI_LOG_INFO( gLogFilter, Debug::General, " No description found for the font ID %d\n", id );
519 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
522 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
524 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
525 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
526 const FontId index = id - 1u;
529 ( index < mFontIdCache.Count() ) )
531 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
533 switch( fontIdCacheItem.type )
535 case FontDescription::FACE_FONT:
537 DALI_LOG_INFO( gLogFilter, Debug::General, " point size : %d\n", ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize );
538 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
539 return ( *( mFontFaceCache.begin() + fontIdCacheItem.id ) ).mRequestedPointSize;
541 case FontDescription::BITMAP_FONT:
543 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
547 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
553 DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font ID %d\n", id );
556 DALI_LOG_INFO( gLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE );
557 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
558 return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
561 bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character character )
563 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
564 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
565 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character );
567 if( ( fontId < 1u ) || ( fontId > mFontIdCache.Count() ) )
569 DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n",mFontFaceCache.size());
570 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
576 bool isSupported = false;
578 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId];
580 switch( fontIdCacheItem.type )
582 case FontDescription::FACE_FONT:
584 FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id];
586 if( nullptr == cacheItem.mCharacterSet )
588 // Create again the character set.
589 // It can be null if the ResetSystemDefaults() method has been called.
591 FontDescription description;
592 description.path = cacheItem.mPath;
593 description.family = std::move( FontFamily( cacheItem.mFreeTypeFace->family_name ) );
594 description.weight = FontWeight::NONE;
595 description.width = FontWidth::NONE;
596 description.slant = FontSlant::NONE;
598 // Note FreeType doesn't give too much info to build a proper font style.
599 if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC )
601 description.slant = FontSlant::ITALIC;
603 if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD )
605 description.weight = FontWeight::BOLD;
608 cacheItem.mCharacterSet = CreateCharacterSetFromDescription( description );
611 isSupported = FcCharSetHasChar( cacheItem.mCharacterSet, character );
614 case FontDescription::BITMAP_FONT:
616 const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font;
618 for( const auto& glyph : bitmapFont.glyphs )
620 if( glyph.utf32 == character )
630 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
634 DALI_LOG_INFO( gLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false") );
635 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
639 FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
640 const CharacterSetList& characterSetList,
642 PointSize26Dot6 requestedPointSize,
645 DALI_ASSERT_DEBUG( ( fontList.size() == characterSetList.Count() ) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets." );
647 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n" );
648 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character );
649 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
650 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
653 bool foundColor = false;
655 DALI_LOG_INFO( gLogFilter, Debug::General, " number of fonts : %d\n", fontList.size() );
657 // Traverse the list of fonts.
658 // Check for each font if supports the character.
659 for( unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index )
661 const FontDescription& description = fontList[index];
662 const FcCharSet* const characterSet = characterSetList[index];
664 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", description.family.c_str() );
665 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str() );
666 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width] );
667 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight] );
668 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant] );
670 bool foundInRanges = false;
671 if( nullptr != characterSet )
673 foundInRanges = FcCharSetHasChar( characterSet, character );
678 fontId = GetFontId( description,
682 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " font id : %d\n", fontId );
686 if( ( fontId > 0 ) &&
687 ( fontId - 1u < mFontIdCache.Count() ) )
689 const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id];
691 foundColor = item.mHasColorTables;
694 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " foundColor : %s\n", ( foundColor ? "true" : "false" ) );
697 // Keep going unless we prefer a different (color) font.
698 if( !preferColor || foundColor )
705 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
706 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n" );
710 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
711 PointSize26Dot6 requestedPointSize,
714 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n" );
715 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", charcode );
716 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
717 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
721 // Create the list of default fonts if it has not been created.
722 if( mDefaultFonts.empty() )
724 FontDescription fontDescription;
725 fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
726 fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
727 fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
728 fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
730 SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
732 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size() );
735 // Traverse the list of default fonts.
736 // Check for each default font if supports the character.
737 fontId = FindFontForCharacter( mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor );
739 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
740 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n" );
745 FontId FontClient::Plugin::FindFallbackFont( Character charcode,
746 const FontDescription& preferredFontDescription,
747 PointSize26Dot6 requestedPointSize,
750 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n" );
751 DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", charcode );
752 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
753 DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) );
755 // The font id to be returned.
758 FontDescription fontDescription;
760 // Fill the font description with the preferred font description and complete with the defaults.
761 fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
762 fontDescription.weight = ( ( FontWeight::NONE == preferredFontDescription.weight ) ? IntToWeightType( DEFAULT_FONT_WEIGHT ) : preferredFontDescription.weight );
763 fontDescription.width = ( ( FontWidth::NONE == preferredFontDescription.width ) ? IntToWidthType( DEFAULT_FONT_WIDTH ) : preferredFontDescription.width );
764 fontDescription.slant = ( ( FontSlant::NONE == preferredFontDescription.slant ) ? IntToSlantType( DEFAULT_FONT_SLANT ) : preferredFontDescription.slant );
766 DALI_LOG_INFO( gLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n" );
767 DALI_LOG_INFO( gLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str() );
768 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight] );
769 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width] );
770 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant] );
772 // Check first if the font's description has been queried before.
773 FontList* fontList = nullptr;
774 CharacterSetList* characterSetList = nullptr;
776 if( !FindFallbackFontList( fontDescription, fontList, characterSetList ) )
778 fontList = new FontList;
779 characterSetList = new CharacterSetList;
781 SetFontList( fontDescription, *fontList, *characterSetList );
783 // Add the font-list to the cache.
784 mFallbackCache.push_back( std::move( FallbackCacheItem( std::move( fontDescription ), fontList, characterSetList ) ) );
787 if( fontList && characterSetList )
789 fontId = FindFontForCharacter( *fontList, *characterSetList, charcode, requestedPointSize, preferColor );
792 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
793 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
797 FontId FontClient::Plugin::GetFontId( const FontPath& path,
798 PointSize26Dot6 requestedPointSize,
800 bool cacheDescription )
802 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
803 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
804 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
808 if( nullptr != mFreeTypeLibrary )
811 if( FindFont( path, requestedPointSize, faceIndex, foundId ) )
817 id = CreateFont( path, requestedPointSize, faceIndex, cacheDescription );
821 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
822 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
827 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
828 PointSize26Dot6 requestedPointSize,
829 FaceIndex faceIndex )
831 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
832 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
833 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
834 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
835 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
836 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
837 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
839 // This method uses three vectors which caches:
840 // * The bitmap font cache
841 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
842 // * The path to font file names.
843 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
845 // 1) Checks if the font description matches with a previously loaded bitmap font.
846 // Returns if a font is found.
847 // 2) Checks in the cache if the font's description has been validated before.
848 // If it was it gets an index to the vector with paths to font file names. Otherwise,
849 // retrieves using font config a path to a font file name which matches with the
850 // font's description. The path is stored in the cache.
852 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
853 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
854 // the GetFontId() method with the path to the font file name and the point size to
857 // The font id to be returned.
860 // Check first if the font description matches with a previously loaded bitmap font.
861 if( FindBitmapFont( fontDescription.family, fontId ) )
866 // Check if the font's description have been validated before.
867 FontDescriptionId validatedFontId = 0u;
869 if( !FindValidatedFont( fontDescription,
872 // Use font config to validate the font's description.
873 ValidateFont( fontDescription,
877 FontId fontFaceId = 0u;
878 // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
879 if( !FindFont( validatedFontId, requestedPointSize, fontFaceId ) )
881 // Retrieve the font file name path.
882 const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
884 // Retrieve the font id. Do not cache the description as it has been already cached.
885 fontId = GetFontId( description.path,
890 fontFaceId = mFontIdCache[fontId-1u].id;
891 mFontFaceCache[fontFaceId].mCharacterSet = mCharacterSetCache[validatedFontId];
893 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
894 mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
900 fontId = mFontFaceCache[fontFaceId].mFontId + 1u;
903 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId );
904 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
909 FontId FontClient::Plugin::GetFontId( const BitmapFont& bitmapFont )
911 for( const auto& item : mBitmapFontCache )
913 if( bitmapFont.name == item.font.name )
919 BitmapFontCacheItem bitmapFontCacheItem;
920 bitmapFontCacheItem.font = bitmapFont;
921 bitmapFontCacheItem.id = mFontIdCache.Count();
923 // Resize the vector with the pixel buffers.
924 bitmapFontCacheItem.pixelBuffers.resize( bitmapFont.glyphs.size() );
926 // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero.
927 unsigned int index = 0u;
928 for( auto& glyph : bitmapFontCacheItem.font.glyphs )
930 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
932 if( EqualsZero( glyph.ascender ) && EqualsZero( glyph.descender ) )
935 pixelBuffer = LoadImageFromFile( glyph.url );
939 glyph.ascender = static_cast<float>(pixelBuffer.GetHeight());
943 bitmapFontCacheItem.font.ascender = std::max( glyph.ascender, bitmapFontCacheItem.font.ascender );
944 bitmapFontCacheItem.font.descender = std::min( glyph.descender, bitmapFontCacheItem.font.descender );
949 FontIdCacheItem fontIdCacheItem;
950 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
951 fontIdCacheItem.id = mBitmapFontCache.size();
953 mBitmapFontCache.push_back( std::move( bitmapFontCacheItem ) );
954 mFontIdCache.PushBack( fontIdCacheItem );
956 return bitmapFontCacheItem.id + 1u;
959 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
960 FontDescriptionId& validatedFontId )
962 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n" );
963 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
964 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
965 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
966 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
967 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
969 // Create a font pattern.
970 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
972 FontDescription description;
974 FcCharSet* characterSet = nullptr;
975 bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description, &characterSet );
976 FcPatternDestroy( fontFamilyPattern );
978 if( matched && ( nullptr != characterSet ) )
980 // Set the index to the vector of paths to font file names.
981 validatedFontId = mFontDescriptionCache.size();
983 DALI_LOG_INFO( gLogFilter, Debug::General, " matched description; family : [%s]\n", description.family.c_str() );
984 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str() );
985 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width] );
986 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight] );
987 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant] );
988 DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId );
990 // Add the path to the cache.
991 description.type = FontDescription::FACE_FONT;
992 mFontDescriptionCache.push_back( description );
993 mCharacterSetCache.PushBack( characterSet );
995 // Cache the index and the matched font's description.
996 FontDescriptionCacheItem item( description,
999 mValidatedFontCache.push_back( std::move( item ) );
1001 if( ( fontDescription.family != description.family ) ||
1002 ( fontDescription.width != description.width ) ||
1003 ( fontDescription.weight != description.weight ) ||
1004 ( fontDescription.slant != description.slant ) )
1006 // Cache the given font's description if it's different than the matched.
1007 FontDescriptionCacheItem item( fontDescription,
1010 mValidatedFontCache.push_back( std::move( item ) );
1015 DALI_LOG_INFO( gLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str() );
1018 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n" );
1021 void FontClient::Plugin::GetFontMetrics( FontId fontId,
1022 FontMetrics& metrics )
1024 const FontId index = fontId - 1u;
1026 if( ( fontId > 0 ) &&
1027 ( index < mFontIdCache.Count() ) )
1029 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1031 switch( fontIdCacheItem.type )
1033 case FontDescription::FACE_FONT:
1035 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1037 metrics = font.mMetrics;
1039 // Adjust the metrics if the fixed-size font should be down-scaled
1040 if( font.mIsFixedSizeBitmap )
1042 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1044 if( desiredFixedSize > 0.f )
1046 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1048 metrics.ascender = metrics.ascender * scaleFactor;
1049 metrics.descender = metrics.descender * scaleFactor;
1050 metrics.height = metrics.height * scaleFactor;
1051 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
1052 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
1057 case FontDescription::BITMAP_FONT:
1059 const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1061 metrics.ascender = bitmapFontCacheItem.font.ascender;
1062 metrics.descender = bitmapFontCacheItem.font.descender;
1063 metrics.height = metrics.ascender - metrics.descender;
1064 metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition;
1065 metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness;
1070 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1076 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
1080 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
1081 Character charcode )
1083 GlyphIndex glyphIndex = 0u;
1084 const FontId index = fontId - 1u;
1086 if( ( fontId > 0u ) &&
1087 ( index < mFontIdCache.Count() ) )
1089 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1091 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1093 FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1095 glyphIndex = FT_Get_Char_Index( ftFace, charcode );
1102 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
1107 if( VECTOR_GLYPH == type )
1109 return GetVectorMetrics( array, size, horizontal );
1112 return GetBitmapMetrics( array, size, horizontal );
1115 bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
1119 bool success( true );
1121 for( unsigned int i=0; i<size; ++i )
1123 GlyphInfo& glyph = array[i];
1125 FontId index = glyph.fontId - 1u;
1127 if( ( glyph.fontId > 0u ) &&
1128 ( index < mFontIdCache.Count() ) )
1130 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1132 switch( fontIdCacheItem.type )
1134 case FontDescription::FACE_FONT:
1136 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
1138 FT_Face ftFace = font.mFreeTypeFace;
1140 #ifdef FREETYPE_BITMAP_SUPPORT
1141 // Check to see if we should be loading a Fixed Size bitmap?
1142 if( font.mIsFixedSizeBitmap )
1144 FT_Select_Size( ftFace, font.mFixedSizeIndex ); ///< @todo: needs to be investigated why it's needed to select the size again.
1145 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR );
1146 if ( FT_Err_Ok == error )
1148 glyph.width = font.mFixedWidthPixels;
1149 glyph.height = font.mFixedHeightPixels;
1150 glyph.advance = font.mFixedWidthPixels;
1151 glyph.xBearing = 0.0f;
1152 glyph.yBearing = font.mFixedHeightPixels;
1154 // Adjust the metrics if the fixed-size font should be down-scaled
1155 const float desiredFixedSize = static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
1157 if( desiredFixedSize > 0.f )
1159 const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels;
1161 glyph.width = glyph.width * scaleFactor ;
1162 glyph.height = glyph.height * scaleFactor;
1163 glyph.advance = glyph.advance * scaleFactor;
1164 glyph.xBearing = glyph.xBearing * scaleFactor;
1165 glyph.yBearing = glyph.yBearing * scaleFactor;
1167 glyph.scaleFactor = scaleFactor;
1172 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error );
1179 int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT );
1181 if( FT_Err_Ok == error )
1183 glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1184 glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
1187 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
1188 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
1192 glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
1193 glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
1203 case FontDescription::BITMAP_FONT:
1205 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1207 unsigned int index = 0u;
1208 for( auto& item : bitmapFontCacheItem.font.glyphs )
1210 if( item.utf32 == glyph.index )
1212 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1215 pixelBuffer = LoadImageFromFile( item.url );
1218 glyph.width = static_cast< float >( pixelBuffer.GetWidth() );
1219 glyph.height = static_cast< float >( pixelBuffer.GetHeight() );
1220 glyph.xBearing = 0.f;
1221 glyph.yBearing = glyph.height + item.descender;
1222 glyph.advance = glyph.width;
1223 glyph.scaleFactor = 1.f;
1234 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1240 // Check if it's an embedded image.
1241 if( ( 0u == glyph.fontId ) && ( 0u != glyph.index ) && ( glyph.index <= mEmbeddedItemCache.Count() ) )
1243 const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u];
1245 glyph.width = static_cast<float>( item.width );
1246 glyph.height = static_cast<float>( item.height );
1247 glyph.xBearing = 0.f;
1248 glyph.yBearing = glyph.height;
1249 glyph.advance = glyph.width;
1250 glyph.scaleFactor = 1.f;
1262 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
1266 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1267 bool success( true );
1269 for( unsigned int i = 0u; i < size; ++i )
1271 FontId fontId = array[i].fontId;
1273 if( ( fontId > 0u ) &&
1274 ( fontId - 1u ) < mFontIdCache.Count() )
1276 FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id];
1278 if( ! font.mVectorFontId )
1280 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1283 mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
1285 // Vector metrics are in EMs, convert to pixels
1286 const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
1287 array[i].width *= scale;
1288 array[i].height *= scale;
1289 array[i].xBearing *= scale;
1290 array[i].yBearing *= scale;
1291 array[i].advance *= scale;
1305 void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth )
1307 const FontId index = fontId - 1u;
1309 if( ( fontId > 0u ) &&
1310 ( index < mFontIdCache.Count() ) )
1312 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1314 switch( fontIdCacheItem.type )
1316 case FontDescription::FACE_FONT:
1318 // For the software italics.
1319 bool isShearRequired = false;
1321 const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id];
1322 FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace;
1326 #ifdef FREETYPE_BITMAP_SUPPORT
1327 // Check to see if this is fixed size bitmap
1328 if( fontFaceCacheItem.mIsFixedSizeBitmap )
1330 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1335 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT );
1337 if( FT_Err_Ok == error )
1339 if( isBoldRequired && !( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) )
1341 // Does the software bold.
1342 FT_GlyphSlot_Embolden( ftFace->glyph );
1345 if( isItalicRequired && !( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) )
1347 // Will do the software italic.
1348 isShearRequired = true;
1352 error = FT_Get_Glyph( ftFace->glyph, &glyph );
1354 // Convert to bitmap if necessary
1355 if ( FT_Err_Ok == error )
1357 if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
1359 // Check whether we should create a bitmap for the outline
1360 if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 )
1364 error = FT_Stroker_New( mFreeTypeLibrary, &stroker );
1366 if( FT_Err_Ok == error )
1368 FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 );
1369 error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 );
1371 if( FT_Err_Ok == error )
1373 FT_Stroker_Done( stroker );
1377 DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error );
1382 DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error );
1386 error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
1387 if( FT_Err_Ok == error )
1389 FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph );
1390 ConvertBitmap( data, bitmapGlyph->bitmap, isShearRequired );
1394 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error );
1399 ConvertBitmap( data, ftFace->glyph->bitmap, isShearRequired );
1402 // Created FT_Glyph object must be released with FT_Done_Glyph
1403 FT_Done_Glyph( glyph );
1408 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error );
1412 case FontDescription::BITMAP_FONT:
1414 BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id];
1416 unsigned int index = 0u;
1417 for( auto& item : bitmapFontCacheItem.font.glyphs )
1419 if( item.utf32 == glyphIndex )
1421 Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index];
1424 pixelBuffer = LoadImageFromFile( item.url );
1427 data.width = pixelBuffer.GetWidth();
1428 data.height = pixelBuffer.GetHeight();
1430 ConvertBitmap( data, data.width, data.height, pixelBuffer.GetBuffer() );
1432 // Sets the pixel format.
1433 data.format = pixelBuffer.GetPixelFormat();
1442 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1448 if( ( 0u != glyphIndex ) && ( glyphIndex <= mEmbeddedItemCache.Count() ) )
1450 // It's an embedded item.
1451 const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u];
1453 data.width = item.width;
1454 data.height = item.height;
1455 if( 0u != item.pixelBufferId )
1457 Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId-1u].pixelBuffer;
1460 ConvertBitmap( data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer() );
1462 // Sets the pixel format.
1463 data.format = pixelBuffer.GetPixelFormat();
1468 // Creates the output buffer
1469 const unsigned int bufferSize = data.width * data.height * 4u;
1470 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1472 memset( data.buffer, 0u, bufferSize );
1474 // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it.
1480 PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
1482 TextAbstraction::FontClient::GlyphBufferData data;
1484 CreateBitmap( fontId, glyphIndex, false, false, data, outlineWidth );
1486 return PixelData::New( data.buffer,
1487 data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
1491 PixelData::DELETE_ARRAY );
1494 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
1499 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1500 if( ( fontId > 0u ) &&
1501 ( fontId - 1u < mFontIdCache.Count() ) )
1503 const FontId fontFaceId = mFontIdCache[fontId - 1u].id;
1504 FontFaceCacheItem& font = mFontFaceCache[fontFaceId];
1506 if( ! font.mVectorFontId )
1508 font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1511 mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
1516 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
1518 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" );
1519 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize );
1521 // First look into the cache if there is an ellipsis glyph for the requested point size.
1522 for( const auto& item : mEllipsisCache )
1524 if( item.requestedPointSize != requestedPointSize )
1526 // Use the glyph in the cache.
1528 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1529 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1530 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1536 // No glyph has been found. Create one.
1537 mEllipsisCache.PushBack( EllipsisItem() );
1538 EllipsisItem& item = *( mEllipsisCache.End() - 1u );
1540 item.requestedPointSize = requestedPointSize;
1542 // Find a font for the ellipsis glyph.
1543 item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
1547 // Set the character index to access the glyph inside the font.
1548 item.glyph.index = FT_Get_Char_Index( mFontFaceCache[mFontIdCache[item.glyph.fontId-1u].id].mFreeTypeFace,
1549 ELLIPSIS_CHARACTER );
1551 GetBitmapMetrics( &item.glyph, 1u, true );
1553 DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index );
1554 DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId );
1555 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1560 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1562 FT_Error error = -1;
1564 const FontId index = fontId - 1u;
1566 #ifdef FREETYPE_BITMAP_SUPPORT
1567 if( ( fontId > 0u ) &&
1568 ( index < mFontIdCache.Count() ) )
1570 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1572 switch( fontIdCacheItem.type )
1574 case FontDescription::FACE_FONT:
1576 const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id];
1577 FT_Face ftFace = item.mFreeTypeFace;
1579 // Check to see if this is fixed size bitmap
1580 if( item.mHasColorTables )
1582 error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1586 case FontDescription::BITMAP_FONT:
1588 error = FT_Err_Ok; // Will return true;
1593 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
1599 return FT_Err_Ok == error;
1602 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId )
1604 FT_Face fontFace = nullptr;
1606 const FontId index = fontId - 1u;
1607 if( ( fontId > 0u ) &&
1608 ( index < mFontIdCache.Count() ) )
1610 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
1612 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
1614 fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace;
1620 FontDescription::Type FontClient::Plugin::GetFontType( FontId fontId )
1622 const FontId index = fontId - 1u;
1623 if( ( fontId > 0u ) &&
1624 ( index < mFontIdCache.Count() ) )
1626 return mFontIdCache[index].type;
1628 return FontDescription::INVALID;
1631 bool FontClient::Plugin::AddCustomFontDirectory( const FontPath& path )
1633 // NULL as first parameter means the current configuration is used.
1634 return FcConfigAppFontAddDir( NULL, reinterpret_cast<const FcChar8 *>( path.c_str() ) );
1637 GlyphIndex FontClient::Plugin::CreateEmbeddedItem( const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat )
1639 EmbeddedItem embeddedItem;
1641 embeddedItem.pixelBufferId = 0u;
1642 embeddedItem.width = description.width;
1643 embeddedItem.height = description.height;
1645 pixelFormat = Pixel::A8;
1647 if( !description.url.empty() )
1649 // Check if the url is in the cache.
1650 PixelBufferId index = 0u;
1652 for( const auto& cacheItem : mPixelBufferCache )
1655 if( cacheItem.url == description.url )
1657 // The url is in the pixel buffer cache.
1658 // Set the index +1 to the vector.
1659 embeddedItem.pixelBufferId = index;
1664 Devel::PixelBuffer pixelBuffer;
1665 if( 0u == embeddedItem.pixelBufferId )
1667 // The pixel buffer is not in the cache. Create one and cache it.
1669 // Load the image from the url.
1670 pixelBuffer = LoadImageFromFile( description.url );
1672 // Create the cache item.
1673 PixelBufferCacheItem pixelBufferCacheItem;
1674 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1675 pixelBufferCacheItem.url = description.url;
1677 // Store the cache item in the cache.
1678 mPixelBufferCache.push_back( std::move( pixelBufferCacheItem ) );
1680 // Set the pixel buffer id to the embedded item.
1681 embeddedItem.pixelBufferId = mPixelBufferCache.size();
1685 // Retrieve the pixel buffer from the cache to set the pixel format.
1686 pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId-1u].pixelBuffer;
1691 // Set the size of the embedded item if it has not been set.
1692 if( 0u == embeddedItem.width )
1694 embeddedItem.width = static_cast<unsigned int>( pixelBuffer.GetWidth() );
1697 if( 0u == embeddedItem.height )
1699 embeddedItem.height = static_cast<unsigned int>( pixelBuffer.GetHeight() );
1702 // Set the pixel format.
1703 pixelFormat = pixelBuffer.GetPixelFormat();
1707 // Find if the same embeddedItem has already been created.
1708 unsigned int index = 0u;
1709 for( const auto& item : mEmbeddedItemCache )
1712 if( ( item.pixelBufferId == embeddedItem.pixelBufferId ) &&
1713 ( item.width == embeddedItem.width ) &&
1714 ( item.height == embeddedItem.height ) )
1720 // Cache the embedded item.
1721 mEmbeddedItemCache.PushBack( embeddedItem );
1723 return mEmbeddedItemCache.Count();
1726 void FontClient::Plugin::InitSystemFonts()
1728 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1730 FcFontSet* fontSet = GetFcFontSet();
1734 DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont );
1736 // Reserve some space to avoid reallocations.
1737 mSystemFonts.reserve( fontSet->nfont );
1739 for( int i = 0u; i < fontSet->nfont; ++i )
1741 FcPattern* fontPattern = fontSet->fonts[i];
1745 // Skip fonts with no path
1746 if( GetFcString( fontPattern, FC_FILE, path ) )
1748 mSystemFonts.push_back( FontDescription() );
1749 FontDescription& fontDescription = mSystemFonts.back();
1751 fontDescription.path = std::move( path );
1756 GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1757 GetFcInt( fontPattern, FC_WIDTH, width );
1758 GetFcInt( fontPattern, FC_WEIGHT, weight );
1759 GetFcInt( fontPattern, FC_SLANT, slant );
1760 fontDescription.width = IntToWidthType( width );
1761 fontDescription.weight = IntToWeightType( weight );
1762 fontDescription.slant = IntToSlantType( slant );
1764 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str() );
1765 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1766 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1767 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1768 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1772 FcFontSetDestroy( fontSet );
1774 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1777 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1779 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1781 FcResult result = FcResultMatch;
1782 FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result );
1784 const bool matched = nullptr != match;
1785 DALI_LOG_INFO( gLogFilter, Debug::General, " pattern matched : %s\n", ( matched ? "true" : "false" ) );
1792 GetFcString( match, FC_FILE, fontDescription.path );
1793 GetFcString( match, FC_FAMILY, fontDescription.family );
1794 GetFcInt( match, FC_WIDTH, width );
1795 GetFcInt( match, FC_WEIGHT, weight );
1796 GetFcInt( match, FC_SLANT, slant );
1797 fontDescription.width = IntToWidthType( width );
1798 fontDescription.weight = IntToWeightType( weight );
1799 fontDescription.slant = IntToSlantType( slant );
1801 // Cache the character ranges.
1802 FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1804 // destroyed the matched pattern
1805 FcPatternDestroy( match );
1807 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
1808 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
1809 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
1810 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1811 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1814 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1818 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1820 // create the cached font family lookup pattern
1821 // a pattern holds a set of names, each name refers to a property of the font
1822 FcPattern* fontFamilyPattern = FcPatternCreate();
1824 if( !fontFamilyPattern )
1829 // add a property to the pattern for the font family
1830 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1832 // add a property to the pattern for local setting.
1833 const char* locale = setlocale( LC_MESSAGES, NULL );
1836 FcPatternAddString( fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>( locale ) );
1839 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1843 width = DEFAULT_FONT_WIDTH;
1846 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1850 weight = DEFAULT_FONT_WEIGHT;
1853 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1857 slant = DEFAULT_FONT_SLANT;
1860 FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1861 FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1862 FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1864 // Add a property of the pattern, to say we want to match TrueType fonts
1865 FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
1867 // modify the config, with the mFontFamilyPatterm
1868 FcConfigSubstitute( nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern );
1870 // provide default values for unspecified properties in the font pattern
1871 // e.g. patterns without a specified style or weight are set to Medium
1872 FcDefaultSubstitute( fontFamilyPattern );
1874 return fontFamilyPattern;
1877 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1879 // create a new pattern.
1880 // a pattern holds a set of names, each name refers to a property of the font
1881 FcPattern* pattern = FcPatternCreate();
1883 FcFontSet* fontset = NULL;
1885 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1886 FcObjectSet* objectSet = FcObjectSetCreate();
1890 // build an object set from a list of property names
1891 FcObjectSetAdd( objectSet, FC_FILE );
1892 FcObjectSetAdd( objectSet, FC_FAMILY );
1893 FcObjectSetAdd( objectSet, FC_WIDTH );
1894 FcObjectSetAdd( objectSet, FC_WEIGHT );
1895 FcObjectSetAdd( objectSet, FC_SLANT );
1897 // get a list of fonts
1898 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1899 fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
1901 // clear up the object set
1902 FcObjectSetDestroy( objectSet );
1904 // clear up the pattern
1907 FcPatternDestroy( pattern );
1913 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
1914 const char* const n,
1915 std::string& string )
1917 FcChar8* file = nullptr;
1918 const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
1920 if( FcResultMatch == retVal )
1922 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1923 string.assign( reinterpret_cast<const char*>( file ) );
1931 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
1933 const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
1935 if( FcResultMatch == retVal )
1943 FontId FontClient::Plugin::CreateFont( const FontPath& path,
1944 PointSize26Dot6 requestedPointSize,
1945 FaceIndex faceIndex,
1946 bool cacheDescription )
1948 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" );
1949 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
1950 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
1954 // Create & cache new font face
1956 int error = FT_New_Face( mFreeTypeLibrary,
1961 if( FT_Err_Ok == error )
1963 // Check if a font is scalable.
1964 const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) );
1965 const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes );
1966 const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) );
1967 FontId fontFaceId = 0u;
1969 DALI_LOG_INFO( gLogFilter, Debug::General, " isScalable : [%s]\n", ( isScalable ? "true" : "false" ) );
1970 DALI_LOG_INFO( gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) );
1971 DALI_LOG_INFO( gLogFilter, Debug::General, " hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) );
1973 // Check to see if the font contains fixed sizes?
1974 if( !isScalable && hasFixedSizedBitmaps )
1976 PointSize26Dot6 actualPointSize = 0u;
1977 int fixedSizeIndex = 0;
1978 for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex )
1980 const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
1981 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize );
1983 if( fixedSize >= requestedPointSize )
1985 actualPointSize = fixedSize;
1990 if( 0u == actualPointSize )
1992 // The requested point size is bigger than the bigest fixed size.
1993 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
1994 actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
1997 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize );
1999 // Tell Freetype to use this size
2000 error = FT_Select_Size( ftFace, fixedSizeIndex );
2001 if ( FT_Err_Ok != error )
2003 DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error );
2007 const float fixedWidth = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].width );
2008 const float fixedHeight = static_cast<float>( ftFace->available_sizes[ fixedSizeIndex ].height );
2010 // Indicate that the font is a fixed sized bitmap
2011 FontMetrics metrics( fixedHeight, // The ascender in pixels.
2013 fixedHeight, // The height in pixels.
2017 // Create the FreeType font face item to cache.
2018 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables );
2020 // Set the index to the font's id cache.
2021 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2023 // Create the font id item to cache.
2024 FontIdCacheItem fontIdCacheItem;
2025 fontIdCacheItem.type = FontDescription::FACE_FONT;
2027 // Set the index to the FreeType font face cache.
2028 fontIdCacheItem.id = mFontFaceCache.size();
2029 fontFaceId = fontIdCacheItem.id + 1u;
2032 mFontFaceCache.push_back( fontFaceCacheItem );
2033 mFontIdCache.PushBack( fontIdCacheItem );
2035 // Set the font id to be returned.
2036 id = mFontIdCache.Count();
2041 error = FT_Set_Char_Size( ftFace,
2047 if( FT_Err_Ok == error )
2050 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
2052 FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266,
2053 static_cast< float >( ftMetrics.descender ) * FROM_266,
2054 static_cast< float >( ftMetrics.height ) * FROM_266,
2055 static_cast< float >( ftFace->underline_position ) * FROM_266,
2056 static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
2058 // Create the FreeType font face item to cache.
2059 FontFaceCacheItem fontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics );
2061 // Set the index to the font's id cache.
2062 fontFaceCacheItem.mFontId = mFontIdCache.Count();
2064 // Create the font id item to cache.
2065 FontIdCacheItem fontIdCacheItem;
2066 fontIdCacheItem.type = FontDescription::FACE_FONT;
2068 // Set the index to the FreeType font face cache.
2069 fontIdCacheItem.id = mFontFaceCache.size();
2070 fontFaceId = fontIdCacheItem.id + 1u;
2073 mFontFaceCache.push_back( fontFaceCacheItem );
2074 mFontIdCache.PushBack( fontIdCacheItem );
2076 // Set the font id to be returned.
2077 id = mFontIdCache.Count();
2081 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize );
2085 if( 0u != fontFaceId )
2087 if( cacheDescription )
2089 CacheFontPath( ftFace, fontFaceId, requestedPointSize, path );
2095 DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str() );
2098 DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id );
2099 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" );
2104 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer )
2106 // Set the input dimensions.
2107 const ImageDimensions inputDimensions( srcWidth, srcHeight );
2109 // Set the output dimensions.
2110 // If the output dimension is not given, the input dimension is set
2111 // and won't be downscaling.
2112 data.width = ( data.width == 0 ) ? srcWidth : data.width;
2113 data.height = ( data.height == 0 ) ? srcHeight : data.height;
2114 const ImageDimensions desiredDimensions( data.width, data.height );
2116 // Creates the output buffer
2117 const unsigned int bufferSize = data.width * data.height * 4u;
2118 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2120 if( inputDimensions == desiredDimensions )
2122 // There isn't downscaling.
2123 memcpy( data.buffer, srcBuffer, bufferSize );
2127 Dali::Internal::Platform::LanczosSample4BPP( srcBuffer,
2130 desiredDimensions );
2134 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired )
2136 if( srcBitmap.width*srcBitmap.rows > 0 )
2138 switch( srcBitmap.pixel_mode )
2140 case FT_PIXEL_MODE_GRAY:
2142 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
2144 uint8_t* pixelsIn = srcBitmap.buffer;
2145 unsigned int width = srcBitmap.width;
2146 unsigned height = srcBitmap.rows;
2148 std::unique_ptr<uint8_t, void(*)(void*)> pixelsOutPtr( nullptr, free );
2150 if( isShearRequired )
2153 * Glyphs' bitmaps with no slant retrieved from FreeType:
2163 * Expected glyphs' bitmaps with italic slant:
2164 * ____________ ______
2171 * ------------ ------
2173 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
2183 * This difference in some bitmaps' width causes an overlap of some glyphs. This is the reason why a shear operation is done here instead of relying on the experimental FT_GlyphSlot_Oblique() implementation.
2185 unsigned int widthOut = 0u;
2186 unsigned int heightOut = 0u;
2187 uint8_t* pixelsOut = nullptr;
2189 Dali::Internal::Platform::HorizontalShear( pixelsIn,
2193 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
2200 pixelsIn = pixelsOut;
2201 pixelsOutPtr.reset( pixelsOut );
2204 const unsigned int bufferSize = width * height;
2205 data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
2207 data.height = height;
2208 data.format = Pixel::L8; // Sets the pixel format.
2209 memcpy( data.buffer, pixelsIn, bufferSize );
2214 #ifdef FREETYPE_BITMAP_SUPPORT
2215 case FT_PIXEL_MODE_BGRA:
2217 if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
2219 ConvertBitmap( data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer );
2221 // Sets the pixel format.
2222 data.format = Pixel::BGRA8888;
2229 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" );
2236 bool FontClient::Plugin::FindFont( const FontPath& path,
2237 PointSize26Dot6 requestedPointSize,
2238 FaceIndex faceIndex,
2239 FontId& fontId ) const
2241 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2242 DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() );
2243 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2244 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size() );
2247 for( const auto& cacheItem : mFontFaceCache )
2249 if( cacheItem.mRequestedPointSize == requestedPointSize &&
2250 cacheItem.mFaceIndex == faceIndex &&
2251 cacheItem.mPath == path )
2253 fontId = cacheItem.mFontId + 1u;
2255 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2256 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2262 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found\n" );
2263 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2268 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
2269 FontDescriptionId& validatedFontId )
2271 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" );
2272 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2273 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2274 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2275 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2276 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2277 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size() );
2279 validatedFontId = 0u;
2281 for( const auto& item : mValidatedFontCache )
2283 if( !fontDescription.family.empty() &&
2284 ( fontDescription.family == item.fontDescription.family ) &&
2285 ( fontDescription.width == item.fontDescription.width ) &&
2286 ( fontDescription.weight == item.fontDescription.weight ) &&
2287 ( fontDescription.slant == item.fontDescription.slant ) )
2289 validatedFontId = item.index;
2291 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId );
2292 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2297 DALI_LOG_INFO( gLogFilter, Debug::General, " validated font not found\n" );
2298 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
2302 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
2303 FontList*& fontList,
2304 CharacterSetList*& characterSetList )
2306 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" );
2307 DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() );
2308 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() );
2309 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] );
2310 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
2311 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
2312 DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size() );
2316 for( const auto& item : mFallbackCache )
2318 if( !fontDescription.family.empty() &&
2319 ( fontDescription.family == item.fontDescription.family ) &&
2320 ( fontDescription.width == item.fontDescription.width ) &&
2321 ( fontDescription.weight == item.fontDescription.weight ) &&
2322 ( fontDescription.slant == item.fontDescription.slant ) )
2324 fontList = item.fallbackFonts;
2325 characterSetList = item.characterSets;
2327 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list found.\n" );
2328 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2333 DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list not found.\n" );
2334 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
2338 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
2339 PointSize26Dot6 requestedPointSize,
2342 DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
2343 DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId );
2344 DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize );
2348 for( const auto& item : mFontDescriptionSizeCache )
2350 if( ( validatedFontId == item.validatedFontId ) &&
2351 ( requestedPointSize == item.requestedPointSize ) )
2353 fontId = item.fontId;
2355 DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId );
2356 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2361 DALI_LOG_INFO( gLogFilter, Debug::General, " font not found.\n" );
2362 DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
2366 bool FontClient::Plugin::FindBitmapFont( const FontFamily& bitmapFont, FontId& fontId ) const
2370 for( const auto& item : mBitmapFontCache )
2372 if( bitmapFont == item.font.name )
2374 fontId = item.id + 1u;
2382 bool FontClient::Plugin::IsScalable( const FontPath& path )
2384 bool isScalable = false;
2387 int error = FT_New_Face( mFreeTypeLibrary,
2391 if( FT_Err_Ok != error )
2393 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() );
2397 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
2403 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
2405 // Create a font pattern.
2406 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
2408 FcResult result = FcResultMatch;
2410 // match the pattern
2411 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result );
2412 bool isScalable = false;
2416 // Get the path to the font file name.
2418 GetFcString( match, FC_FILE, path );
2419 isScalable = IsScalable( path );
2423 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2425 FcPatternDestroy( fontFamilyPattern );
2426 FcPatternDestroy( match );
2430 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
2432 // Empty the caller container
2436 int error = FT_New_Face( mFreeTypeLibrary,
2440 if( FT_Err_Ok != error )
2442 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() );
2445 // Fetch the number of fixed sizes available
2446 if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
2448 for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
2450 sizes.PushBack( ftFace->available_sizes[ i ].size );
2455 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
2456 Vector< PointSize26Dot6 >& sizes )
2458 // Create a font pattern.
2459 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
2461 FcResult result = FcResultMatch;
2463 // match the pattern
2464 FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result );
2468 // Get the path to the font file name.
2470 GetFcString( match, FC_FILE, path );
2471 GetFixedSizes( path, sizes );
2475 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
2477 FcPatternDestroy( match );
2478 FcPatternDestroy( fontFamilyPattern );
2481 bool FontClient::Plugin::HasItalicStyle( FontId fontId ) const
2483 bool hasItalicStyle = false;
2485 const FontId index = fontId - 1u;
2487 if( ( fontId > 0 ) &&
2488 ( index < mFontIdCache.Count() ) )
2490 const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
2492 if( FontDescription::FACE_FONT == fontIdCacheItem.type )
2494 const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id];
2496 hasItalicStyle = 0u != ( font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC );
2501 DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
2504 return hasItalicStyle;
2507 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path )
2509 FontDescription description;
2510 description.path = path;
2511 description.family = std::move( FontFamily( ftFace->family_name ) );
2512 description.weight = FontWeight::NONE;
2513 description.width = FontWidth::NONE;
2514 description.slant = FontSlant::NONE;
2516 // Note FreeType doesn't give too much info to build a proper font style.
2517 if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
2519 description.slant = FontSlant::ITALIC;
2521 if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
2523 description.weight = FontWeight::BOLD;
2526 FontDescriptionId validatedFontId = 0u;
2527 if( !FindValidatedFont( description,
2530 // Set the index to the vector of paths to font file names.
2531 validatedFontId = mFontDescriptionCache.size();
2533 FcPattern* pattern = CreateFontFamilyPattern( description );
2535 FcResult result = FcResultMatch;
2536 FcPattern* match = FcFontMatch( nullptr, pattern, &result );
2538 FcCharSet* characterSet = nullptr;
2539 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2541 FcPatternDestroy( pattern );
2543 mMatchedFcPatternCache.PushBack( match );
2545 const FontId fontFaceId = id - 1u;
2546 mFontFaceCache[fontFaceId].mCharacterSet = characterSet;
2548 // Add the path to the cache.
2549 description.type = FontDescription::FACE_FONT;
2550 mFontDescriptionCache.push_back( description );
2551 mCharacterSetCache.PushBack( characterSet );
2553 // Cache the index and the font's description.
2554 mValidatedFontCache.push_back( std::move( FontDescriptionCacheItem( std::move( description ),
2555 validatedFontId) ) );
2557 // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
2558 mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
2564 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description )
2566 FcCharSet* characterSet = nullptr;
2568 FcPattern* pattern = CreateFontFamilyPattern( description );
2570 if( nullptr != pattern )
2572 FcResult result = FcResultMatch;
2573 FcPattern* match = FcFontMatch( nullptr, pattern, &result );
2575 FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
2576 mMatchedFcPatternCache.PushBack( match );
2578 FcPatternDestroy( pattern );
2581 return characterSet;
2584 void FontClient::Plugin::DestroyMatchedPatterns()
2586 for (auto & object : mMatchedFcPatternCache)
2588 FcPatternDestroy(reinterpret_cast<FcPattern*>(object));
2590 mMatchedFcPatternCache.Clear();
2593 } // namespace Internal
2595 } // namespace TextAbstraction