X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Fmulti-language-support-impl.cpp;h=8b491b064942a653eff2a008a625078a05aff78e;hp=47db1e2bbe641d9dcd88aaa0b2557dd50a1a519e;hb=6f990775daf7adf6170db59f0b99e00ae25fceed;hpb=c3f7ea6cb0c0b75c2276193aff88b5c7a679a2d5 diff --git a/dali-toolkit/internal/text/multi-language-support-impl.cpp b/dali-toolkit/internal/text/multi-language-support-impl.cpp index 47db1e2..8b491b0 100644 --- a/dali-toolkit/internal/text/multi-language-support-impl.cpp +++ b/dali-toolkit/internal/text/multi-language-support-impl.cpp @@ -19,17 +19,12 @@ #include // EXTERNAL INCLUDES -#include #include #include #include -#include // INTERNAL INCLUDES -#include -#include -#include -#include +#include namespace Dali { @@ -40,7 +35,7 @@ namespace Toolkit namespace { #if defined(DEBUG_ENABLED) -Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_MULTI_LANGUAGE_SUPPORT"); +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_MULTI_LANGUAGE_SUPPORT"); #endif const Dali::Toolkit::Text::Character UTF32_A = 0x0041; @@ -52,112 +47,37 @@ namespace Text namespace Internal { -/** - * @brief Retrieves the font Id from the font run for a given character's @p index. - * - * If the character's index exceeds the current font run it increases the iterator to get the next one. - * - * @param[in] index The character's index. - * @param[in,out] fontRunIt Iterator to the current font run. - * @param[in] fontRunEndIt Iterator to one after the last font run. - * - * @return The font id. - */ -FontId GetFontId( Length index, - Vector::ConstIterator& fontRunIt, - const Vector::ConstIterator& fontRunEndIt ) +bool ValidateFontsPerScript::IsValidFont( FontId fontId ) const { - FontId fontId = 0u; - - if( fontRunIt != fontRunEndIt ) - { - const FontRun& fontRun = *fontRunIt; - - if( ( index >= fontRun.characterRun.characterIndex ) && - ( index < fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) ) - { - fontId = fontRun.fontId; - } - - if( index + 1u == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) - { - // All the characters of the current run have been traversed. Get the next one for the next iteration. - ++fontRunIt; - } - } - - return fontId; -} - -/** - * @brief Retrieves the script Id from the script run for a given character's @p index. - * - * If the character's index exceeds the current script run it increases the iterator to get the next one. - * - * @param[in] index The character's index. - * @param[in,out] scriptRunIt Iterator to the current font run. - * @param[in] scriptRunEndIt Iterator to one after the last script run. - * - * @return The script. - */ -Script GetScript( Length index, - Vector::ConstIterator& scriptRunIt, - const Vector::ConstIterator& scriptRunEndIt ) -{ - Script script = TextAbstraction::UNKNOWN; - - if( scriptRunIt != scriptRunEndIt ) + for( Vector::ConstIterator it = mValidFonts.Begin(), + endIt = mValidFonts.End(); + it != endIt; + ++it ) { - const ScriptRun& scriptRun = *scriptRunIt; - - if( ( index >= scriptRun.characterRun.characterIndex ) && - ( index < scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters ) ) - { - script = scriptRun.script; - } - - if( index + 1u == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters ) + if( fontId == *it ) { - // All the characters of the current run have been traversed. Get the next one for the next iteration. - ++scriptRunIt; + return true; } } - return script; -} - -/** - * @brief Whether the character is valid for all scripts. i.e. the white space. - * - * @param[in] character The character. - * - * @return @e true if the character is valid for all scripts. - */ -bool IsValidForAllScripts( Character character ) -{ - return ( TextAbstraction::IsWhiteSpace( character ) || - TextAbstraction::IsZeroWidthNonJoiner( character ) || - TextAbstraction::IsZeroWidthJoiner( character ) || - TextAbstraction::IsZeroWidthSpace( character ) || - TextAbstraction::IsLeftToRightMark( character ) || - TextAbstraction::IsRightToLeftMark( character ) || - TextAbstraction::IsThinSpace( character ) ); + return false; } -bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const +FontId DefaultFonts::FindFont( TextAbstraction::FontClient& fontClient, PointSize26Dot6 size ) const { - for( Vector::ConstIterator it = mValidFonts.Begin(), - endIt = mValidFonts.End(); + for( Vector::ConstIterator it = mFonts.Begin(), + endIt = mFonts.End(); it != endIt; ++it ) { - if( fontId == *it ) + const FontId fontId = *it; + if( size == fontClient.GetPointSize( fontId ) ) { - return true; + return fontId; } } - return false; + return 0u; } MultilanguageSupport::MultilanguageSupport() @@ -166,7 +86,7 @@ MultilanguageSupport::MultilanguageSupport() { // Initializes the default font cache to zero (invalid font). // Reserves space to cache the default fonts and access them with the script as an index. - mDefaultFontPerScriptCache.Resize( TextAbstraction::UNKNOWN, 0u ); + mDefaultFontPerScriptCache.Resize( TextAbstraction::UNKNOWN, NULL ); // Initializes the valid fonts cache to NULL (no valid fonts). // Reserves space to cache the valid fonts and access them with the script as an index. @@ -175,8 +95,16 @@ MultilanguageSupport::MultilanguageSupport() MultilanguageSupport::~MultilanguageSupport() { - // Destroy the valid fonts per script cache. + // Destroy the default font per script cache. + for( Vector::Iterator it = mDefaultFontPerScriptCache.Begin(), + endIt = mDefaultFontPerScriptCache.End(); + it != endIt; + ++it ) + { + delete *it; + } + // Destroy the valid fonts per script cache. for( Vector::Iterator it = mValidFontsPerScriptCache.Begin(), endIt = mValidFontsPerScriptCache.End(); it != endIt; @@ -212,28 +140,45 @@ Text::MultilanguageSupport MultilanguageSupport::Get() } void MultilanguageSupport::SetScripts( const Vector& text, - const Vector& lineBreakInfo, + CharacterIndex startIndex, + Length numberOfCharacters, Vector& scripts ) { - const Length numberOfCharacters = text.Count(); - if( 0u == numberOfCharacters ) { // Nothing to do if there are no characters. return; } + // Find the first index where to insert the script. + ScriptRunIndex scriptIndex = 0u; + if( 0u != startIndex ) + { + for( Vector::ConstIterator it = scripts.Begin(), + endIt = scripts.End(); + it != endIt; + ++it, ++scriptIndex ) + { + const ScriptRun& run = *it; + if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters ) + { + // Run found. + break; + } + } + } + // Stores the current script run. ScriptRun currentScriptRun; - currentScriptRun.characterRun.characterIndex = 0u; + currentScriptRun.characterRun.characterIndex = startIndex; currentScriptRun.characterRun.numberOfCharacters = 0u; currentScriptRun.script = TextAbstraction::UNKNOWN; // Reserve some space to reduce the number of reallocations. - scripts.Reserve( numberOfCharacters << 2u ); + scripts.Reserve( text.Count() << 2u ); - // Whether the first valid script need to be set. - bool firstValidScript = true; + // Whether the first valid script needs to be set. + bool isFirstScriptToBeSet = true; // Whether the first valid script is a right to left script. bool isParagraphRTL = false; @@ -241,15 +186,17 @@ void MultilanguageSupport::SetScripts( const Vector& text, // Count the number of characters which are valid for all scripts. i.e. white spaces or '\n'. Length numberOfAllScriptCharacters = 0u; - // Pointers to the text and break info buffers. - const Character* textBuffer = text.Begin(); - const LineBreakInfo* breakInfoBuffer = lineBreakInfo.Begin(); + // Pointers to the text buffer. + const Character* const textBuffer = text.Begin(); // Traverse all characters and set the scripts. - for( Length index = 0u; index < numberOfCharacters; ++index ) + const Length lastCharacter = startIndex + numberOfCharacters; + for( Length index = startIndex; index < lastCharacter; ++index ) { Character character = *( textBuffer + index ); - LineBreakInfo breakInfo = *( breakInfoBuffer + index ); + + // Get the script of the character. + Script script = TextAbstraction::GetCharacterScript( character ); // Some characters (like white spaces) are valid for many scripts. The rules to set a script // for them are: @@ -260,32 +207,61 @@ void MultilanguageSupport::SetScripts( const Vector& text, // script of the first character of the paragraph with a defined script. // Skip those characters valid for many scripts like white spaces or '\n'. - bool endOfText = index == numberOfCharacters; + bool endOfText = index == lastCharacter; while( !endOfText && - IsValidForAllScripts( character ) ) + ( TextAbstraction::COMMON == script ) ) { + if( TextAbstraction::EMOJI == currentScriptRun.script ) + { + // Emojis doesn't mix well with characters common to all scripts. Insert the emoji run. + scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun ); + ++scriptIndex; + + // Initialize the new one. + currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; + currentScriptRun.characterRun.numberOfCharacters = 0u; + currentScriptRun.script = TextAbstraction::UNKNOWN; + numberOfAllScriptCharacters = 0u; + } + // Count all these characters to be added into a script. ++numberOfAllScriptCharacters; - if( TextAbstraction::LINE_MUST_BREAK == breakInfo ) + if( TextAbstraction::IsNewParagraph( character ) ) { - // The next character is a new paragraph. - // Know when there is a new paragraph is needed because if there is a white space + // The character is a new paragraph. + // To know when there is a new paragraph is needed because if there is a white space // between two scripts with different directions, it is added to the script with // the same direction than the first script of the paragraph. - firstValidScript = true; - isParagraphRTL = false; + isFirstScriptToBeSet = true; + + // Characters common to all scripts at the end of the paragraph are added to the last script. + currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; + + // Store the script run. + if( TextAbstraction::UNKNOWN == currentScriptRun.script ) + { + currentScriptRun.script = TextAbstraction::LATIN; + } + scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun ); + ++scriptIndex; + + // Initialize the new one. + currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; + currentScriptRun.characterRun.numberOfCharacters = 0u; + currentScriptRun.script = TextAbstraction::UNKNOWN; + numberOfAllScriptCharacters = 0u; } // Get the next character. ++index; - endOfText = index == numberOfCharacters; + endOfText = index == lastCharacter; if( !endOfText ) { character = *( textBuffer + index ); - breakInfo = *( breakInfoBuffer + index ); + script = TextAbstraction::GetCharacterScript( character ); } - } + } // end while( !endOfText && ( TextAbstraction::COMMON == script ) ) if( endOfText ) { @@ -294,62 +270,76 @@ void MultilanguageSupport::SetScripts( const Vector& text, break; } - // Get the script of the character. - Script script = TextAbstraction::GetCharacterScript( character ); - // Check if it is the first character of a paragraph. - if( firstValidScript && - ( TextAbstraction::UNKNOWN != script ) ) + if( isFirstScriptToBeSet && + ( TextAbstraction::UNKNOWN != script ) && + ( TextAbstraction::COMMON != script ) && + ( TextAbstraction::EMOJI != script ) ) { // Sets the direction of the first valid script. isParagraphRTL = TextAbstraction::IsRightToLeftScript( script ); - firstValidScript = false; + isFirstScriptToBeSet = false; } - if( script != currentScriptRun.script ) + if( ( script != currentScriptRun.script ) && + ( TextAbstraction::COMMON != script ) ) { // Current run needs to be stored and a new one initialized. - if( isParagraphRTL != TextAbstraction::IsRightToLeftScript( script ) ) + if( ( isParagraphRTL == TextAbstraction::IsRightToLeftScript( currentScriptRun.script ) ) && + ( TextAbstraction::UNKNOWN != currentScriptRun.script ) ) + { + // Previous script has the same direction than the first script of the paragraph. + // All the previously skipped characters need to be added to the previous script before it's stored. + currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; + numberOfAllScriptCharacters = 0u; + } + else if( ( TextAbstraction::IsRightToLeftScript( currentScriptRun.script ) == TextAbstraction::IsRightToLeftScript( script ) ) && + ( TextAbstraction::UNKNOWN != currentScriptRun.script ) ) { - // Current script has different direction than the first script of the paragraph. + // Current script and previous one have the same direction. // All the previously skipped characters need to be added to the previous script before it's stored. currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; numberOfAllScriptCharacters = 0u; } + else if( ( TextAbstraction::UNKNOWN == currentScriptRun.script ) && + ( TextAbstraction::EMOJI == script ) ) + { + currentScriptRun.script = TextAbstraction::LATIN; + currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; + numberOfAllScriptCharacters = 0u; + } if( 0u != currentScriptRun.characterRun.numberOfCharacters ) { // Store the script run. - scripts.PushBack( currentScriptRun ); + scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun ); + ++scriptIndex; } // Initialize the new one. currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; - currentScriptRun.characterRun.numberOfCharacters = numberOfAllScriptCharacters; // Adds the white spaces which are at the begining of the script. + currentScriptRun.characterRun.numberOfCharacters = numberOfAllScriptCharacters + 1u; // Adds the white spaces which are at the begining of the script. currentScriptRun.script = script; numberOfAllScriptCharacters = 0u; } else { - // Adds white spaces between characters. - currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; - numberOfAllScriptCharacters = 0u; - } + if( TextAbstraction::UNKNOWN != currentScriptRun.script ) + { + // Adds white spaces between characters. + currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; + numberOfAllScriptCharacters = 0u; + } - if( TextAbstraction::LINE_MUST_BREAK == breakInfo ) - { - // The next character is a new paragraph. - firstValidScript = true; - isParagraphRTL = false; + // Add one more character to the run. + ++currentScriptRun.characterRun.numberOfCharacters; } - - // Add one more character to the run. - ++currentScriptRun.characterRun.numberOfCharacters; } // Add remaining characters into the last script. currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; + if( 0u != currentScriptRun.characterRun.numberOfCharacters ) { if( TextAbstraction::UNKNOWN == currentScriptRun.script ) @@ -359,213 +349,401 @@ void MultilanguageSupport::SetScripts( const Vector& text, } // Store the last run. - scripts.PushBack( currentScriptRun ); + scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun ); + ++scriptIndex; } -} -void MultilanguageSupport::ReplaceScripts( LogicalModel& model, - CharacterIndex characterIndex, - Length numberOfCharactersToRemove, - Length numberOfCharactersToInsert ) -{ + if( scriptIndex < scripts.Count() ) + { + // Update the indices of the next script runs. + const ScriptRun& run = *( scripts.Begin() + scriptIndex - 1u ); + CharacterIndex nextCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters; + + for( Vector::Iterator it = scripts.Begin() + scriptIndex, + endIt = scripts.End(); + it != endIt; + ++it ) + { + ScriptRun& run = *it; + run.characterRun.characterIndex = nextCharacterIndex; + nextCharacterIndex += run.characterRun.numberOfCharacters; + } + } } void MultilanguageSupport::ValidateFonts( const Vector& text, const Vector& scripts, + const Vector& fontDescriptions, + FontId defaultFontId, + CharacterIndex startIndex, + Length numberOfCharacters, Vector& fonts ) { - const Length numberOfCharacters = text.Count(); + DALI_LOG_INFO( gLogFilter, Debug::General, "-->MultilanguageSupport::ValidateFonts\n" ); if( 0u == numberOfCharacters ) { + DALI_LOG_INFO( gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n" ); // Nothing to do if there are no characters. return; } - // Copy the fonts set by application developers. - const Length numberOfFontRuns = fonts.Count(); - const Vector definedFonts = fonts; - fonts.Clear(); + // Find the first index where to insert the font run. + FontRunIndex fontIndex = 0u; + if( 0u != startIndex ) + { + for( Vector::ConstIterator it = fonts.Begin(), + endIt = fonts.End(); + it != endIt; + ++it, ++fontIndex ) + { + const FontRun& run = *it; + if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters ) + { + // Run found. + break; + } + } + } // Traverse the characters and validate/set the fonts. // Get the caches. - FontId* defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin(); + DefaultFonts** defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin(); ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin(); // Stores the validated font runs. - fonts.Reserve( numberOfFontRuns ); + fonts.Reserve( fontDescriptions.Count() ); // Initializes a validated font run. FontRun currentFontRun; - currentFontRun.characterRun.characterIndex = 0u; + currentFontRun.characterRun.characterIndex = startIndex; currentFontRun.characterRun.numberOfCharacters = 0u; currentFontRun.fontId = 0u; - currentFontRun.isDefault = false; // Get the font client. TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); - // Iterators of the font and script runs. - Vector::ConstIterator fontRunIt = definedFonts.Begin(); - Vector::ConstIterator fontRunEndIt = definedFonts.End(); + // Get the default font description and default size. + TextAbstraction::FontDescription defaultFontDescription; + TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE; + if( defaultFontId > 0u ) + { + fontClient.GetDescription( defaultFontId, defaultFontDescription ); + defaultPointSize = fontClient.GetPointSize( defaultFontId ); + } + + // Merge font descriptions + Vector fontIds; + fontIds.Resize( numberOfCharacters, defaultFontId ); + Vector isDefaultFont; + isDefaultFont.Resize( numberOfCharacters, true ); + MergeFontDescriptions( fontDescriptions, + fontIds, + isDefaultFont, + defaultFontDescription, + defaultPointSize, + startIndex, + numberOfCharacters ); + + const Character* const textBuffer = text.Begin(); + const FontId* const fontIdsBuffer = fontIds.Begin(); + const bool* const isDefaultFontBuffer = isDefaultFont.Begin(); Vector::ConstIterator scriptRunIt = scripts.Begin(); Vector::ConstIterator scriptRunEndIt = scripts.End(); + bool isNewParagraphCharacter = false; - for( Length index = 0u; index < numberOfCharacters; ++index ) + PointSize26Dot6 currentPointSize = defaultPointSize; + FontId currentFontId = 0u; + FontId previousFontId = 0u; + bool isPreviousEmojiScript = false; + + // Whether it's the first set of characters to be validated. + // Used in case the paragraph starts with characters common to all scripts. + bool isFirstSetToBeValidated = true; + + CharacterIndex lastCharacter = startIndex + numberOfCharacters; + for( Length index = startIndex; index < lastCharacter; ++index ) { - // Get the character. - const Character character = *( text.Begin() + index ); + // Get the current character. + const Character character = *( textBuffer + index ); + + // Get the font for the current character. + FontId fontId = *( fontIdsBuffer + index - startIndex ); + + // Whether the font being validated for the current character is a default one not set by the user. + const bool isDefault = *( isDefaultFontBuffer + index - startIndex ); + + // Get the script for the current character. + const Script script = GetScript( index, + scriptRunIt, + scriptRunEndIt ); + + // Get the current point size. + if( currentFontId != fontId ) + { + currentPointSize = fontClient.GetPointSize( fontId ); + currentFontId = fontId; + } + +#ifdef DEBUG_ENABLED + { + Dali::TextAbstraction::FontDescription description; + fontClient.GetDescription( fontId, description ); + + DALI_LOG_INFO( gLogFilter, + Debug::Verbose, + " Initial font set\n Character : %x, Script : %s, Font : %s \n", + character, + Dali::TextAbstraction::ScriptName[script], + description.path.c_str() ); + } +#endif - // Get the font for the character. - FontId fontId = GetFontId( index, - fontRunIt, - fontRunEndIt ); + // Validate whether the current character is supported by the given font. + bool isValidFont = false; - // Get the script for the character. - Script script = GetScript( index, - scriptRunIt, - scriptRunEndIt ); + // Check first in the cache of default fonts per script and size. - if( TextAbstraction::UNKNOWN == script ) + DefaultFonts* defaultFonts = *( defaultFontPerScriptCacheBuffer + script ); + FontId cachedDefaultFontId = 0u; + if( NULL != defaultFonts ) { - DALI_LOG_WARNING( "MultilanguageSupport::ValidateFonts. Unknown script!" ); - script = TextAbstraction::LATIN; + cachedDefaultFontId = defaultFonts->FindFont( fontClient, currentPointSize ); } - // Whether the font being validated is a default one not set by the user. - const bool isDefault = ( 0u == fontId ); + // Whether the cached default font is valid. + const bool isValidCachedDefaultFont = 0u != cachedDefaultFontId; + + // The font is valid if it matches with the default one for the current script and size and it's different than zero. + isValidFont = isValidCachedDefaultFont && ( fontId == cachedDefaultFontId ); - // The default font point size. - PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE; + bool isCommonScript = false; + bool isEmojiScript = TextAbstraction::EMOJI == script; - if( !isDefault ) + if( isEmojiScript && !isPreviousEmojiScript ) { - // Validate if the font set by the user supports the character. + if( 0u != currentFontRun.characterRun.numberOfCharacters ) + { + // Store the font run. + fonts.Insert( fonts.Begin() + fontIndex, currentFontRun ); + ++fontIndex; + } + + // Initialize the new one. + currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; + currentFontRun.characterRun.numberOfCharacters = 0u; + currentFontRun.fontId = fontId; + } + - // Check first in the caches. + // If the given font is not valid, it means either: + // - there is no cached font for the current script yet or, + // - the user has set a different font than the default one for the current script or, + // - the platform default font is different than the default font for the current script. - // The user may have set the default font. Check it. Otherwise check in the valid fonts cache. - if( fontId != *( defaultFontPerScriptCacheBuffer + script ) ) + // Need to check if the given font supports the current character. + if( !isValidFont ) // (1) + { + // Whether the current character is common for all scripts (i.e. white spaces, ...) + + // Is not desirable to cache fonts for the common script. + // + // i.e. Consider the text " हिंदी", the 'white space' has assigned the DEVANAGARI script. + // The user may have set a font or the platform's default is used. + // + // As the 'white space' is the first character, no font is cached so the font validation + // retrieves a glyph from the given font. + // + // Many fonts support 'white spaces' so probably the font set by the user or the platform's default + // supports the 'white space'. However, that font may not support the DEVANAGARI script. + isCommonScript = TextAbstraction::IsCommonScript( character ); + + if( isCommonScript ) + { + if( isValidCachedDefaultFont && + ( isDefault || ( currentFontId == previousFontId ) ) && + !isEmojiScript ) + { + // At this point the character common for all scripts has no font assigned. + // If there is a valid previously cached default font for it, use that one. + fontId = cachedDefaultFontId; + isValidFont = true; + } + } + else { // Check in the valid fonts cache. ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script ); - if( NULL == validateFontsPerScript ) + if( NULL != validateFontsPerScript ) { - validateFontsPerScript = new ValidateFontsPerScript(); - - *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript; + isValidFont = validateFontsPerScript->IsValidFont( fontId ); } - if( NULL != validateFontsPerScript ) + if( !isValidFont ) // (2) { - if( !validateFontsPerScript->FindValidFont( fontId ) ) + // Use the font client to validate the font. + const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character ); + + // The font is valid if there is a glyph for that character. + isValidFont = 0u != glyphIndex; + + // Emojis are present in many monochrome fonts; prefer color by default. + if( isValidFont && + isEmojiScript ) { - // Use the font client to validate the font. - GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character ); + const PixelData bitmap = fontClient.CreateBitmap( fontId, glyphIndex ); + + // For color emojis, the font is valid if the bitmap is RGBA. + isValidFont = bitmap && ( Pixel::BGRA8888 == bitmap.GetPixelFormat() ); + } - // Emojis are present in many monochrome fonts; prefer color by default. - if( TextAbstraction::EMOJI == script && - 0u != glyphIndex ) + // If there is a valid font, cache it. + if( isValidFont ) + { + if( NULL == validateFontsPerScript ) { - BufferImage bitmap = fontClient.CreateBitmap( fontId, glyphIndex ); - if( bitmap && - Pixel::BGRA8888 != bitmap.GetPixelFormat() ) - { - glyphIndex = 0; - } + validateFontsPerScript = new ValidateFontsPerScript(); + + *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript; } - if( 0u == glyphIndex ) - { - // Get the point size of the current font. It will be used to get a default font id. - pointSize = fontClient.GetPointSize( fontId ); + validateFontsPerScript->mValidFonts.PushBack( fontId ); + } - // The font is not valid. Set to zero and a default one will be set. - fontId = 0u; + if( !isValidFont ) // (3) + { + // The given font has not been validated. + + if( isValidCachedDefaultFont ) + { + // Use the cached default font for the script if there is one. + fontId = cachedDefaultFontId; } else { - // Add the font to the valid font cache. - validateFontsPerScript->mValidFonts.PushBack( fontId ); - } - } - } - } - } // !isDefault + // There is no valid cached default font for the script. - // The font has not been validated. Find a default one. - if( 0u == fontId ) - { - // The character has no font assigned. Get a default one from the cache - fontId = *( defaultFontPerScriptCacheBuffer + script ); + DefaultFonts* defaultFontsPerScript = NULL; - // If the cache has not a default font, get one from the font client. - if( 0u == fontId ) - { - // Emojis are present in many monochrome fonts; prefer color by default. - bool preferColor = ( TextAbstraction::EMOJI == script ); + // Emojis are present in many monochrome fonts; prefer color by default. + const bool preferColor = ( TextAbstraction::EMOJI == script ); - // Find a default font. - fontId = fontClient.FindDefaultFont( character, pointSize, preferColor ); + // Find a fallback-font. + fontId = fontClient.FindFallbackFont( currentFontId, character, currentPointSize, preferColor ); - // If the system does not support a suitable font, fallback to Latin - if( 0u == fontId ) - { - fontId = *( defaultFontPerScriptCacheBuffer + TextAbstraction::LATIN ); - } - if( 0u == fontId ) - { - fontId = fontClient.FindDefaultFont( UTF32_A, pointSize ); - } + if( 0u == fontId ) + { + // If the system does not support a suitable font, fallback to Latin + defaultFontsPerScript = *( defaultFontPerScriptCacheBuffer + TextAbstraction::LATIN ); + if( NULL != defaultFontsPerScript ) + { + fontId = defaultFontsPerScript->FindFont( fontClient, currentPointSize ); + } + } + + if( 0u == fontId ) + { + fontId = fontClient.FindDefaultFont( UTF32_A, currentPointSize ); + } + + // Cache the font. + if( NULL == defaultFontsPerScript ) + { + defaultFontsPerScript = *( defaultFontPerScriptCacheBuffer + script ); + + if( NULL == defaultFontsPerScript ) + { + defaultFontsPerScript = new DefaultFonts(); + *( defaultFontPerScriptCacheBuffer + script ) = defaultFontsPerScript; + } + } + defaultFontsPerScript->mFonts.PushBack( fontId ); + } + } // !isValidFont (3) + } // !isValidFont (2) + } // !isCommonScript + } // !isValidFont (1) #ifdef DEBUG_ENABLED - Dali::TextAbstraction::FontDescription description; - fontClient.GetDescription( fontId, description ); - DALI_LOG_INFO( gLogFilter, Debug::Concise, "Script: %s; Selected font: %s\n", Dali::TextAbstraction::ScriptName[script], description.path.c_str() ); + { + Dali::TextAbstraction::FontDescription description; + fontClient.GetDescription( fontId, description ); + DALI_LOG_INFO( gLogFilter, + Debug::Verbose, + " Validated font set\n Character : %x, Script : %s, Font : %s \n", + character, + Dali::TextAbstraction::ScriptName[script], + description.path.c_str() ); + } #endif - // Cache the font. - *( defaultFontPerScriptCacheBuffer + script ) = fontId; - } + + if( isFirstSetToBeValidated && !isCommonScript ) + { + currentFontRun.fontId = fontId; + isFirstSetToBeValidated = false; } // The font is now validated. - if( ( fontId != currentFontRun.fontId ) || - ( isDefault != currentFontRun.isDefault ) ) + isNewParagraphCharacter ) { // Current run needs to be stored and a new one initialized. if( 0u != currentFontRun.characterRun.numberOfCharacters ) { // Store the font run. - fonts.PushBack( currentFontRun ); + fonts.Insert( fonts.Begin() + fontIndex, currentFontRun ); + ++fontIndex; } // Initialize the new one. currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; currentFontRun.characterRun.numberOfCharacters = 0u; currentFontRun.fontId = fontId; - currentFontRun.isDefault = isDefault; + + if( isNewParagraphCharacter ) + { + isFirstSetToBeValidated = true; + } } // Add one more character to the run. ++currentFontRun.characterRun.numberOfCharacters; - } + + // Whether the current character is a new paragraph character. + isNewParagraphCharacter = TextAbstraction::IsNewParagraph( character ); + previousFontId = currentFontId; + isPreviousEmojiScript = isEmojiScript; + } // end traverse characters. if( 0u != currentFontRun.characterRun.numberOfCharacters ) { // Store the last run. - fonts.PushBack( currentFontRun ); + fonts.Insert( fonts.Begin() + fontIndex, currentFontRun ); + ++fontIndex; } -} -void MultilanguageSupport::ValidateFonts( LogicalModel& model, - CharacterIndex characterIndex, - Length numberOfCharactersToRemove, - Length numberOfCharactersToInsert ) -{ + if( fontIndex < fonts.Count() ) + { + // Update the indices of the next font runs. + const FontRun& run = *( fonts.Begin() + fontIndex - 1u ); + CharacterIndex nextCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters; + + for( Vector::Iterator it = fonts.Begin() + fontIndex, + endIt = fonts.End(); + it != endIt; + ++it ) + { + FontRun& run = *it; + + run.characterRun.characterIndex = nextCharacterIndex; + nextCharacterIndex += run.characterRun.numberOfCharacters; + } + } + + DALI_LOG_INFO( gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n" ); } } // namespace Internal