2 * Copyright (c) 2014 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 "font-controller-impl.h"
22 #include <dali/public-api/common/dali-common.h>
23 #include <dali/integration-api/debug.h>
26 #include <fontconfig/fontconfig.h>
35 #if defined(DEBUG_ENABLED)
38 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::Concise, false, "LOG_FONT_CONTROLLER");
39 } // unnamed namespace
44 const std::string SETTING_FONT_PRELOAD_FONT_PATH( FONT_PRELOADED_PATH );
45 const std::string SETTING_FONT_DOWNLOADED_FONT_PATH( FONT_DOWNLOADED_PATH );
46 const std::string SETTING_FONT_APP_FONT_PATH( FONT_APPLICATION_PATH );
48 const uint32_t UNICODE_CR_LF = 0x85;
49 const uint32_t UNICODE_CHAR_START = 0x20; // start range of unicode characters (below are control chars)
50 const std::string FONT_FORMAT( "TrueType" );
51 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
52 const std::string DEFAULT_FONT_STYLE( "Regular" );
54 const std::string NULL_FONT_FAMILY_NAME( "" );
55 const FontController::StyledFontFamily NULL_STYLED_FONT_FAMILY( std::make_pair( NULL_FONT_FAMILY_NAME, std::string( "" ) ) );
58 * @param[in] pattern pointer to a font config pattern
59 * @param[out] familyName font family name or an empty string if the font is not found.
60 * @return Whether a font is found.
62 bool GetFontFamily( const FcPattern* pattern, std::string& familyName )
64 FcChar8* family = NULL;
65 const FcResult retVal = FcPatternGetString( pattern, FC_FAMILY, 0u, &family );
67 if( FcResultMatch != retVal )
73 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
74 familyName.assign( reinterpret_cast<const char*>( family ) );
80 * @param[in] pattern pointer to a font config pattern
81 * @param[out] fontStyle font style name or an empty string if the font has no style.
82 * @return Whether a font style is found.
84 bool GetFontStyle( const FcPattern* pattern, std::string& fontStyle )
86 FcChar8* style = NULL;
87 const FcResult retVal = FcPatternGetString( pattern, FC_STYLE, 0u, &style );
89 if( FcResultMatch != retVal)
96 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
97 fontStyle.assign( reinterpret_cast<const char*>( style ) );
103 * @param[in] pattern pointer to a font config pattern
104 * @param[out] fileName font file name or an empty string if the font is not found.
105 * @return Whether a font is found.
107 bool GetFileName( const FcPattern* pattern, std::string& fileName )
109 FcChar8* file = NULL;
110 const FcResult retVal = FcPatternGetString( pattern, FC_FILE, 0u, &file );
112 if( FcResultMatch != retVal )
119 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
120 fileName.assign( reinterpret_cast<const char*>( file ) );
125 bool CheckFontInstallPath( FontController::FontListMode listMode, const std::string& fileName )
129 case FontController::LIST_SYSTEM_FONTS:
131 const std::string& preloadPath( SETTING_FONT_PRELOAD_FONT_PATH );
132 const std::string& downloadPath( SETTING_FONT_DOWNLOADED_FONT_PATH );
133 const std::size_t preloadLength = preloadPath.length();
134 const std::size_t downloadLength = downloadPath.length();
136 if( ( 0u == preloadPath.compare( 0u, preloadLength, fileName, 0u, preloadLength ) ) ||
137 ( 0u == downloadPath.compare( 0u, downloadLength, fileName, 0u, downloadLength ) ) )
143 case FontController::LIST_APPLICATION_FONTS:
145 const std::string& appPath( SETTING_FONT_APP_FONT_PATH );
146 const std::size_t appLength = appPath.length();
148 if( 0u == appPath.compare( 0u, appLength, fileName, 0u, appLength ) )
156 DALI_ASSERT_DEBUG( false && "unhandled FontListMode" );
162 } // unnamed namespace
164 FontController::FontController()
167 FcConfigEnableHome(true);
170 FontController::~FontController()
172 // clear the font family cache
173 ClearFontFamilyCache();
175 // Clear the preferred font list.
176 ClearPreferredFontList();
179 const std::string& FontController::GetFontPath( const StyledFontFamily& styledFontFamily )
181 DALI_ASSERT_DEBUG( !styledFontFamily.first.empty() && !styledFontFamily.second.empty() && "FontController::GetFontPath(): The font name or the font style is empty. Probably they have not been validated." );
183 // lock the mFontFamilyCacheMutex and don't release it until the function finishes.
184 // If we release it then another thread may try to create the same duplicate data.
185 boost::mutex::scoped_lock lock( mFontFamilyCacheMutex );
187 StyledFontFamily closestStyledFontFamilyMatch;
189 // first check to see if the font has been matched before.
190 closestStyledFontFamilyMatch = GetMatchedFont( styledFontFamily );
192 if( closestStyledFontFamilyMatch.first.empty() )
194 // The font is not in the matches font cache. Use the given one.
195 closestStyledFontFamilyMatch = styledFontFamily;
198 return GetCachedFontPath( closestStyledFontFamilyMatch );
201 void FontController::GetFontList( FontListMode fontListMode, FontList& fontList )
203 // protect the mFontList from access by multiple threads
204 // this is locked for the entire function, because we don't want two functions
205 // trying to fill the cache with duplicate data.
206 boost::mutex::scoped_lock sharedDatalock( mFontListMutex );
208 // if we have already scanned for fonts, return the cached values
209 if ( !mFontSystemList.empty() )
211 GetCachedFontList( fontListMode, fontList );
216 // font list needs to be cached
218 // font config isn't thread safe
219 boost::mutex::scoped_lock lock( mFontConfigMutex );
221 // use font config to get the font set which contains a list of fonts
222 FcFontSet* fontSet = GetFontSet();
224 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "number of fonts found: %d\n", fontSet->nfont );
228 std::string preload_path(SETTING_FONT_PRELOAD_FONT_PATH);
229 std::string download_path(SETTING_FONT_DOWNLOADED_FONT_PATH);
230 std::string application_path(SETTING_FONT_APP_FONT_PATH);
232 for( int i = 0u; i < fontSet->nfont; ++i )
234 FcPattern* fontPattern = fontSet->fonts[i];
235 std::string fileName;
237 if( !GetFileName( fontPattern, fileName ) )
239 continue; // Has no file name. Jump to the next iteration.
242 // this is checking to make sure the font is in either the normal font path, or download path
243 if( 0u == preload_path.compare( 0u, preload_path.length(), fileName, 0u, preload_path.length() ) ||
244 0u == download_path.compare( 0u, download_path.length(), fileName, 0u, download_path.length() ) ||
245 0u == application_path.compare( 0u, application_path.length(), fileName, 0u, application_path.length() ) )
247 StyledFontFamily styledFontFamily;
249 if( !GetFontFamily( fontPattern, styledFontFamily.first ) )
251 continue; // Has no font name. Jump to the next iteration.
254 GetFontStyle( fontPattern, styledFontFamily.second );
256 // Add the font to the either the system or application font list
257 AddToFontList( fileName, styledFontFamily );
260 // delete the font set
261 FcFontSetDestroy( fontSet );
265 DALI_ASSERT_ALWAYS( false && "No valid fonts found on system." );
268 // return the font list for the specified mode
269 GetCachedFontList( fontListMode, fontList );
272 bool FontController::ValidateFontFamilyName( const StyledFontFamily& styledFontFamily,
273 bool& isDefaultSystemFontFamily,
274 bool& isDefaultSystemFontStyle,
275 StyledFontFamily& closestStyledFontFamilyMatch )
277 // Initialize the defaults to false as the validation process supposes the given font is correct.
278 isDefaultSystemFontFamily = false;
279 isDefaultSystemFontStyle = false;
281 // default the closest Match to empty
282 closestStyledFontFamilyMatch.first.clear();
283 closestStyledFontFamilyMatch.second.clear();
285 // lock the mFontFamilyCacheMutex and don't release it until the function finishes.
286 // If we release it then another thread may try to create the same duplicate data.
287 boost::mutex::scoped_lock lock( mFontFamilyCacheMutex );
289 StyledFontFamily styledFontFamilyToCheck = styledFontFamily;
291 // if the font is blank, then use the default font if it has been cached
292 if( styledFontFamilyToCheck.first.empty() && ( !mDefaultStyledFont.first.empty() ) )
294 styledFontFamilyToCheck.first = mDefaultStyledFont.first;
296 // No font family is given, default system font is used.
297 isDefaultSystemFontFamily = true;
300 if( styledFontFamilyToCheck.second.empty() && ( !mDefaultStyledFont.second.empty() ) )
302 styledFontFamilyToCheck.second = mDefaultStyledFont.second;
304 // No font style is given, default system font is used.
305 isDefaultSystemFontStyle = true;
308 // first check to see if the font has been matched before.
309 closestStyledFontFamilyMatch = GetMatchedFont( styledFontFamilyToCheck );
311 if( !closestStyledFontFamilyMatch.first.empty() )
313 // The font has been cached before.
318 const std::string& fontFileName = GetCachedFontPath( styledFontFamilyToCheck );
320 if( !fontFileName.empty() )
322 // The font has been cached before.
324 closestStyledFontFamilyMatch = styledFontFamilyToCheck;
329 DALI_LOG_INFO( gLogFilter, Debug::Verbose,"Failed to find %s %s in cache, querying FontConfig for a match\n", styledFontFamily.first.c_str(), styledFontFamily.second.c_str() );
331 // it's not in the cache, find a match using font config and add it to the cache
332 boost::mutex::scoped_lock fcLock( mFontConfigMutex );
334 // create the pattern
335 FcPattern* fontFamilyPattern = CreateFontFamilyPattern( styledFontFamilyToCheck );
337 FcResult result(FcResultMatch);
340 FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
342 bool validFont = false;
348 CacheFontInfo( match, styledFontFamilyToCheck, closestStyledFontFamilyMatch );
350 // destroyed the matched pattern
351 FcPatternDestroy( match );
355 DALI_LOG_ERROR( "FcFontMatch failed for font %s %s\n", styledFontFamilyToCheck.first.c_str(), styledFontFamilyToCheck.second.c_str() );
358 // destroy the pattern
359 FcPatternDestroy( fontFamilyPattern );
364 const FontController::StyledFontFamily& FontController::GetFontFamilyForChars( const Integration::TextArray& charsRequested )
366 if( 0u == mPreferredFonts.Count() )
368 CreatePreferedFontList();
371 // Cycle through the preferred list of fonts on the system for 'Tizen'.
372 for( std::size_t n = 0u; n < mPreferredFonts.Count(); ++n )
374 const StyledFontFamily& font = *mPreferredFonts[n];
376 if( !mPreferredFontsValidated[n] )
378 // First make sure it is validated and cached so we can access it's character set object
379 bool isDefaultSystemFontFamily = false;
380 bool isDefaultSystemFontStyle = false;
381 StyledFontFamily closestStyledFontFamilyMatch;
382 ValidateFontFamilyName( font,
383 isDefaultSystemFontFamily,
384 isDefaultSystemFontStyle,
385 closestStyledFontFamilyMatch );
387 mPreferredFontsValidated[n] = true;
390 const std::string& filePath = GetFontPath( font );
392 if( filePath.empty() )
397 const bool matched = FontFamilySupportsText( font, charsRequested );
404 // return empty string
405 return NULL_STYLED_FONT_FAMILY;
408 void FontController::CacheFontInfo( FcPattern* pattern, const StyledFontFamily& inputStyledFontFamily, StyledFontFamily& closestStyledFontFamilyMatch )
410 // Check we can get the following data from the pattern
412 if( !GetFontFamily( pattern, closestStyledFontFamilyMatch.first ) )
414 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "CacheFontInfo failed to get family information from pattern %s %s\n", inputStyledFontFamily.first.c_str(), inputStyledFontFamily.second.c_str() );
418 std::string fileName;
419 if( !GetFileName( pattern, fileName ) )
421 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "CacheFontInfo failed to get file information from pattern %s %s\n", inputStyledFontFamily.first.c_str(), inputStyledFontFamily.second.c_str() );
425 FcCharSet* matchedCharSet = NULL;
426 const FcResult retVal = FcPatternGetCharSet( pattern, FC_CHARSET, 0u, &matchedCharSet );
427 if( retVal != FcResultMatch )
429 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "CacheFontInfo failed to get character set from pattern %s %s\n", inputStyledFontFamily.first.c_str(), inputStyledFontFamily.second.c_str() );
433 GetFontStyle( pattern, closestStyledFontFamilyMatch.second );
435 // Add the match to the font cache
436 AddCachedFont( closestStyledFontFamilyMatch, fileName, matchedCharSet );
438 if( ( !inputStyledFontFamily.first.empty() &&
439 ( inputStyledFontFamily.first != closestStyledFontFamilyMatch.first ) ) &&
440 ( !inputStyledFontFamily.second.empty() &&
441 ( inputStyledFontFamily.second != closestStyledFontFamilyMatch.second ) ) )
443 // if the font family used to create the pattern was not found in the match then
444 // store it in the MissingFont container
445 AddMatchedFont( inputStyledFontFamily, closestStyledFontFamilyMatch );
449 bool FontController::AllGlyphsSupported( const StyledFontFamily& styledFontFamily, const Integration::TextArray& text )
451 // The font has already been validated by the font implementation.
453 return FontFamilySupportsText( styledFontFamily, text );
456 void FontController::SetDefaultFontFamily( const StyledFontFamily& styledFontFamily )
458 // reload font configuration files
459 const bool ok = FcInitReinitialize();
460 DALI_ASSERT_ALWAYS( ok && "FcInitReinitialize failed");
462 CreatePreferedFontList();
465 void FontController::AddCachedFont( const StyledFontFamily& styledFontFamily, const std::string& fontPath, _FcCharSet *characterSet )
467 FontFamilyLookup::const_iterator iter = mFontFamilyCache.find( styledFontFamily );
468 if( iter == mFontFamilyCache.end() )
470 // store the path and chacter set
472 item.FontFileName = fontPath;
473 item.FcCharSet = FcCharSetCopy( characterSet ); // increase the ref count on the char set
474 mFontFamilyCache[ styledFontFamily ] = item;
476 DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Caching font %s %s\n", styledFontFamily.first.c_str(), styledFontFamily.second.c_str() );
480 void FontController::GetCachedFontList( FontListMode fontListMode, FontList& fontList ) const
482 // return a list of fonts, for the FontListMode
483 switch( fontListMode )
485 case LIST_SYSTEM_FONTS:
487 fontList.insert( fontList.end(), mFontSystemList.begin(), mFontSystemList.end() );
490 case LIST_APPLICATION_FONTS:
492 fontList.insert( fontList.end(), mFontApplicationList.begin(), mFontApplicationList.end() );
497 // add both system and application fonts together
498 fontList.insert( fontList.end(), mFontSystemList.begin(), mFontSystemList.end() );
499 fontList.insert( fontList.end(), mFontApplicationList.begin(), mFontApplicationList.end() );
503 DALI_ASSERT_ALWAYS( false && "GetCachedFontList called with invalid value." );
506 const std::string& FontController::GetCachedFontPath( const StyledFontFamily& styledFontFamily ) const
508 if( !mFontFamilyCache.empty() )
510 FontFamilyLookup::const_iterator iter = mFontFamilyCache.find( styledFontFamily );
511 if( iter != mFontFamilyCache.end() )
513 return (*iter).second.FontFileName;
517 return NULL_FONT_FAMILY_NAME;
520 FcCharSet* FontController::GetCachedFontCharacterSet( const StyledFontFamily& styledFontFamily ) const
522 if( !mFontFamilyCache.empty() )
524 FontFamilyLookup::const_iterator iter = mFontFamilyCache.find( styledFontFamily );
525 if( iter != mFontFamilyCache.end() )
527 return (*iter).second.FcCharSet;
533 _FcPattern* FontController::CreateFontFamilyPattern( const StyledFontFamily& styledFontFamily )
535 // create the cached font family lookup pattern
536 // a pattern holds a set of names, each name refers to a property of the font
537 FcPattern* fontFamilyPattern = FcPatternCreate();
539 // add a property to the pattern for the font family
540 FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( styledFontFamily.first.c_str() ) );
542 // add a property to the pattern for the font family
543 FcPatternAddString( fontFamilyPattern, FC_STYLE, reinterpret_cast<const FcChar8*>( styledFontFamily.second.c_str() ) );
545 // Add a property of the pattern, to say we want to match TrueType fonts
546 FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
548 // modify the config, with the mFontFamilyPatterm
549 FcConfigSubstitute( NULL /* use default configure */, fontFamilyPattern, FcMatchPattern );
551 // provide default values for unspecified properties in the font pattern
552 // e.g. patterns without a specified style or weight are set to Medium
553 FcDefaultSubstitute( fontFamilyPattern );
555 return fontFamilyPattern;
558 bool FontController::IsAControlCharacter( uint32_t character ) const
560 // UNICODE_CHAR_START is the space character
561 // below it are the control characters that we want to ignore.
563 return( ( character < UNICODE_CHAR_START ) ||
564 ( character == UNICODE_CR_LF ) );
567 bool FontController::FontFamilySupportsText( const StyledFontFamily& styledFontFamily, const Integration::TextArray& text )
569 FcCharSet* charSet = GetCachedFontCharacterSet( styledFontFamily );
571 DALI_ASSERT_ALWAYS( charSet && "No cached character set for font family" );
573 const size_t textLength = text.Count();
575 // quick early exit before accessing font config for text arrays which are just a single control character
576 if( textLength == 1u )
578 if( IsAControlCharacter( *text.Begin() ) )
584 // protect font config
585 boost::mutex::scoped_lock fcLock( mFontConfigMutex );
587 for( Integration::TextArray::ConstIterator iter = text.Begin(), endIter = text.End(); iter != endIter; ++iter )
589 const uint32_t character = (*iter);
591 // if it's a control character then don't test it
592 if( IsAControlCharacter( character ) )
597 // test to see if the character set supports the character
598 if( !FcCharSetHasChar( charSet, character ) )
606 void FontController::ClearFontFamilyCache()
608 // should be called by the destructor only
610 for( FontFamilyLookup::iterator iter = mFontFamilyCache.begin(), enditer = mFontFamilyCache.end(); iter != enditer; ++iter )
612 FontCacheItem& cacheItem = (*iter).second;
614 // this reduces the character sets ref count by 1.
615 FcCharSetDestroy( cacheItem.FcCharSet );
618 mFontFamilyCache.clear();
621 void FontController::AddToFontList( const std::string& fileName, const StyledFontFamily& styledFontFamily )
623 const bool systemFont = CheckFontInstallPath( LIST_SYSTEM_FONTS, fileName );
625 FontList* fontList(NULL);
629 fontList = &mFontSystemList;
633 fontList = &mFontApplicationList;
636 // check the font family doesn't already exist in the vector, then add it
637 if( fontList->end() == std::find( fontList->begin(), fontList->end(), styledFontFamily ) )
639 fontList->push_back( styledFontFamily );
643 _FcFontSet* FontController::GetFontSet() const
645 // create a new pattern.
646 // a pattern holds a set of names, each name refers to a property of the font
647 FcPattern* pattern = FcPatternCreate();
649 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
650 FcObjectSet* objectSet = FcObjectSetCreate();
652 // build an object set from a list of property names
653 FcObjectSetAdd( objectSet, FC_FAMILY );
654 FcObjectSetAdd( objectSet, FC_STYLE );
655 FcObjectSetAdd( objectSet, FC_FILE );
657 // get a list of fonts
658 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
659 FcFontSet* fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
661 // clear up the object set
664 FcObjectSetDestroy( objectSet );
666 // clear up the pattern
669 FcPatternDestroy( pattern );
675 _FcCharSet* FontController::CreateCharacterSet( const Integration::TextArray& charsRequested )
677 // create the character set object
678 FcCharSet* charSet = FcCharSetCreate();
680 bool validCharAdded(false);
682 // add valid characters to the character set.
683 for( Integration::TextArray::ConstIterator iter = charsRequested.Begin(), endIter = charsRequested.End(); iter != endIter; ++iter )
685 const uint32_t character = (*iter);
687 // if it's not a control character then add it
688 if( !IsAControlCharacter( character ) )
690 FcBool ok = FcCharSetAddChar( charSet, character );
693 validCharAdded = true;
697 // if no characters have been added to the character set, then return null
698 if( !validCharAdded )
700 FcCharSetDestroy(charSet);
706 void FontController::AddMatchedFont( const StyledFontFamily& missingStyledFontFamily, StyledFontFamily& closestStyledFontFamilyMatch )
708 // we store the missing font, and the name of the font that font config found as the closet match
709 mMatchedFontsFound[ missingStyledFontFamily ] = closestStyledFontFamilyMatch;
712 const FontController::StyledFontFamily& FontController::GetMatchedFont( const StyledFontFamily& styledFontFamily ) const
714 if( mMatchedFontsFound.empty() )
716 return NULL_STYLED_FONT_FAMILY;
719 MatchedFontLookup::const_iterator iter = mMatchedFontsFound.find( styledFontFamily );
720 if( iter != mMatchedFontsFound.end() )
725 return NULL_STYLED_FONT_FAMILY;
728 void FontController::CreatePreferedFontList( )
730 StyledFontFamily tizenFont;
731 tizenFont.first = DEFAULT_FONT_FAMILY_NAME;
732 tizenFont.second = DEFAULT_FONT_STYLE;
734 // clear the current list
735 ClearPreferredFontList();
737 FcPattern* searchPattern = CreateFontFamilyPattern( tizenFont );
739 FcResult result(FcResultMatch);
741 // Match the pattern.
742 StyledFontFamily previousFont;
744 FcFontSet* fontSet = FcFontSort( NULL /* use default configure */, searchPattern, false /* don't trim */, NULL, &result );
746 for( int i = 0u; i < fontSet->nfont; ++i )
748 // we have already filled in the first entry with the default font
749 FcPattern* pattern = fontSet->fonts[i];
751 StyledFontFamily* styledFont = new StyledFontFamily();
753 GetFontFamily( pattern, styledFont->first );
754 GetFontStyle( pattern, styledFont->second );
756 if( *styledFont != previousFont )
758 mPreferredFonts.PushBack( styledFont );
762 mDefaultStyledFont = *styledFont;
764 previousFont = *styledFont;
767 // Set all fonts to non validated.
768 mPreferredFontsValidated.Resize( fontSet->nfont, false );
770 FcPatternDestroy( searchPattern );
771 FcFontSetDestroy( fontSet );
774 void FontController::ClearPreferredFontList()
776 for( Vector<StyledFontFamily*>::Iterator it = mPreferredFonts.Begin(), endIt = mPreferredFonts.End(); it != endIt; ++it )
780 mPreferredFonts.Clear();
783 } // namespace SlpPlatform
787 // Implementation of Dali::Platform::FontController::New()
788 Dali::Platform::FontController* Dali::Platform::FontController::New()
790 return new Dali::SlpPlatform::FontController();