X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Fmulti-language-support-impl.cpp;h=f4fdd18f39c4b899bc7a0f8eda6c3f285b2daeda;hb=ae643988e22f46fd0728e6327c1e537273269eb6;hp=4143b03f692e704e902e679348d089cba52ac754;hpb=306d2f61a1b64179e801fa8a0bb2bd7b4e9dd682;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-toolkit/internal/text/multi-language-support-impl.cpp b/dali-toolkit/internal/text/multi-language-support-impl.cpp index 4143b03..f4fdd18 100644 --- a/dali-toolkit/internal/text/multi-language-support-impl.cpp +++ b/dali-toolkit/internal/text/multi-language-support-impl.cpp @@ -18,13 +18,18 @@ // CLASS HEADER #include -// INTERNAL INCLUDES -#include -#include -#include -#include -#include +// EXTERNAL INCLUDES +#include #include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include namespace Dali { @@ -32,6 +37,15 @@ namespace Dali namespace Toolkit { +namespace +{ +#if defined(DEBUG_ENABLED) +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_MULTI_LANGUAGE_SUPPORT"); +#endif + +const Dali::Toolkit::Text::Character UTF32_A = 0x0041; +} + namespace Text { @@ -65,7 +79,7 @@ FontId GetFontId( Length index, fontId = fontRun.fontId; } - if( index == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) + 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; @@ -102,7 +116,7 @@ Script GetScript( Length index, script = scriptRun.script; } - if( index == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters ) + if( index + 1u == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters ) { // All the characters of the current run have been traversed. Get the next one for the next iteration. ++scriptRunIt; @@ -112,6 +126,24 @@ Script GetScript( Length index, 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 ) ); +} + bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const { for( Vector::ConstIterator it = mValidFonts.Begin(), @@ -179,10 +211,11 @@ Text::MultilanguageSupport MultilanguageSupport::Get() return multilanguageSupportHandle; } -void MultilanguageSupport::SetScripts( LogicalModel& model ) +void MultilanguageSupport::SetScripts( const Vector& text, + const Vector& lineBreakInfo, + Vector& scripts ) { - // 1) Retrieve the text from the model. - const Length numberOfCharacters = model.GetNumberOfCharacters(); + const Length numberOfCharacters = text.Count(); if( 0u == numberOfCharacters ) { @@ -190,76 +223,158 @@ void MultilanguageSupport::SetScripts( LogicalModel& model ) return; } - Vector text; - text.Resize( numberOfCharacters ); - - model.GetText( 0u, - text.Begin(), - numberOfCharacters ); - - // 2) Traverse all characters and set the scripts. - // Stores the current script run. ScriptRun currentScriptRun; currentScriptRun.characterRun.characterIndex = 0u; currentScriptRun.characterRun.numberOfCharacters = 0u; currentScriptRun.script = TextAbstraction::UNKNOWN; - // Temporary stores the script runs. - std::vector scriptRuns; - scriptRuns.reserve( numberOfCharacters << 2u ); // To reduce the number of reallocations. + // Reserve some space to reduce the number of reallocations. + scripts.Reserve( numberOfCharacters << 2u ); - for( Vector::ConstIterator it = text.Begin(), - endIt = text.End(); - it != endIt; - ++it ) + // Whether the first valid script need to be set. + bool firstValidScript = true; + + // Whether the first valid script is a right to left script. + bool isParagraphRTL = false; + + // 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(); + + // Traverse all characters and set the scripts. + for( Length index = 0u; index < numberOfCharacters; ++index ) { - const Character character = *it; + Character character = *( textBuffer + index ); + LineBreakInfo breakInfo = *( breakInfoBuffer + index ); + + // Some characters (like white spaces) are valid for many scripts. The rules to set a script + // for them are: + // - If they are at the begining of a paragraph they get the script of the first character with + // a defined script. If they are at the end, they get the script of the last one. + // - If they are between two scripts with the same direction, they get the script of the previous + // character with a defined script. If the two scripts have different directions, they get the + // 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; + while( !endOfText && + IsValidForAllScripts( character ) ) + { + // Count all these characters to be added into a script. + ++numberOfAllScriptCharacters; - Script script = GetCharacterScript( character ); + if( TextAbstraction::LINE_MUST_BREAK == breakInfo ) + { + // The next character is a new paragraph. + // 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; + } - if( TextAbstraction::UNKNOWN == script ) + // Get the next character. + ++index; + endOfText = index == numberOfCharacters; + if( !endOfText ) + { + character = *( textBuffer + index ); + breakInfo = *( breakInfoBuffer + index ); + } + } + + if( endOfText ) { - script = TextAbstraction::LATIN; - DALI_ASSERT_DEBUG( !"MultilanguageSupport::SetScripts. Unkown script!" ); + // Last characters of the text are 'white spaces'. + // There is nothing else to do. Just add the remaining characters to the last script after this bucle. + 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 ) ) + { + // Sets the direction of the first valid script. + isParagraphRTL = TextAbstraction::IsRightToLeftScript( script ); + firstValidScript = false; } if( script != currentScriptRun.script ) { // Current run needs to be stored and a new one initialized. + if( isParagraphRTL != TextAbstraction::IsRightToLeftScript( script ) ) + { + // Current script has different 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; + } + if( 0u != currentScriptRun.characterRun.numberOfCharacters ) { // Store the script run. - scriptRuns.push_back( currentScriptRun ); + scripts.PushBack( currentScriptRun ); } // Initialize the new one. currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; - currentScriptRun.characterRun.numberOfCharacters = 0u; + currentScriptRun.characterRun.numberOfCharacters = numberOfAllScriptCharacters; // 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::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 remaining characters into the last script. + currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; if( 0u != currentScriptRun.characterRun.numberOfCharacters ) { + if( TextAbstraction::UNKNOWN == currentScriptRun.script ) + { + // There are only white spaces in the last script. Set the latin script. + currentScriptRun.script = TextAbstraction::LATIN; + } + // Store the last run. - scriptRuns.push_back( currentScriptRun ); + scripts.PushBack( currentScriptRun ); } +} - // 3) Set the script runs into the model. - - model.SetScripts( &scriptRuns[0u], - scriptRuns.size() ); +void MultilanguageSupport::ReplaceScripts( LogicalModel& model, + CharacterIndex characterIndex, + Length numberOfCharactersToRemove, + Length numberOfCharactersToInsert ) +{ } -void MultilanguageSupport::ValidateFonts( LogicalModel& model ) +void MultilanguageSupport::ValidateFonts( const Vector& text, + const Vector& scripts, + Vector& fonts ) { - // 1) Retrieve the text from the model. - const Length numberOfCharacters = model.GetNumberOfCharacters(); + const Length numberOfCharacters = text.Count(); if( 0u == numberOfCharacters ) { @@ -267,47 +382,19 @@ void MultilanguageSupport::ValidateFonts( LogicalModel& model ) return; } - Vector text; - text.Resize( numberOfCharacters ); - - Character* textBuffer = text.Begin(); - model.GetText( 0u, - textBuffer, - numberOfCharacters ); - - // 2) Retrieve any font previously set. - - const Length numberOfFontRuns = model.GetNumberOfFontRuns( 0u, numberOfCharacters ); + // Copy the fonts set by application developers. + const Length numberOfFontRuns = fonts.Count(); + const Vector definedFonts = fonts; + fonts.Clear(); - Vector fontRuns; - fontRuns.Reserve( numberOfFontRuns ); - - FontRun* fontRunsBuffer = fontRuns.Begin(); - model.GetFontRuns( fontRunsBuffer, - 0u, - numberOfCharacters ); - - // 3) Retrieve the scripts from the model. - - const Length numberOfScriptRuns = model.GetNumberOfScriptRuns( 0u, numberOfCharacters ); - - Vector scriptRuns; - scriptRuns.Reserve( numberOfScriptRuns ); - - ScriptRun* scriptRunsBuffer = scriptRuns.Begin(); - model.GetScriptRuns( scriptRunsBuffer, - 0u, - numberOfCharacters ); - - // 4) Traverse the characters and validate/set the fonts. + // Traverse the characters and validate/set the fonts. // Get the caches. FontId* defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin(); ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin(); // Stores the validated font runs. - Vector validatedFontRuns; - validatedFontRuns.Reserve( numberOfFontRuns ); + fonts.Reserve( numberOfFontRuns ); // Initializes a validated font run. FontRun currentFontRun; @@ -320,15 +407,15 @@ void MultilanguageSupport::ValidateFonts( LogicalModel& model ) TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); // Iterators of the font and script runs. - Vector::ConstIterator fontRunIt = fontRuns.Begin(); - Vector::ConstIterator fontRunEndIt = fontRuns.End(); - Vector::ConstIterator scriptRunIt = scriptRuns.Begin(); - Vector::ConstIterator scriptRunEndIt = scriptRuns.End(); + Vector::ConstIterator fontRunIt = definedFonts.Begin(); + Vector::ConstIterator fontRunEndIt = definedFonts.End(); + Vector::ConstIterator scriptRunIt = scripts.Begin(); + Vector::ConstIterator scriptRunEndIt = scripts.End(); for( Length index = 0u; index < numberOfCharacters; ++index ) { // Get the character. - const Character character = *( textBuffer + index ); + const Character character = *( text.Begin() + index ); // Get the font for the character. FontId fontId = GetFontId( index, @@ -363,12 +450,33 @@ void MultilanguageSupport::ValidateFonts( LogicalModel& model ) { // Check in the valid fonts cache. ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script ); + + if( NULL == validateFontsPerScript ) + { + validateFontsPerScript = new ValidateFontsPerScript(); + + mValidFontsPerScriptCache.PushBack( validateFontsPerScript ); + validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin(); + } + if( NULL != validateFontsPerScript ) { if( !validateFontsPerScript->FindValidFont( fontId ) ) { // Use the font client to validate the font. - const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character ); + GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character ); + + // Emojis are present in many monochrome fonts; prefer color by default. + if( TextAbstraction::EMOJI == script && + 0u != glyphIndex ) + { + BufferImage bitmap = fontClient.CreateBitmap( fontId, glyphIndex ); + if( bitmap && + Pixel::BGRA8888 != bitmap.GetPixelFormat() ) + { + glyphIndex = 0; + } + } if( 0u == glyphIndex ) { @@ -385,30 +493,8 @@ void MultilanguageSupport::ValidateFonts( LogicalModel& model ) } } } - else - { - // Use the font client to validate the font. - const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character ); - - 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 ); - - // The font is not valid. Set to zero and a default one will be set. - fontId = 0u; - } - else - { - // Add the font to the valid font cache. - validateFontsPerScript = new ValidateFontsPerScript(); - *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript; - - validateFontsPerScript->mValidFonts.PushBack( fontId ); - } - } } - } + } // !isDefault // The font has not been validated. Find a default one. if( 0u == fontId ) @@ -419,9 +505,27 @@ void MultilanguageSupport::ValidateFonts( LogicalModel& model ) // 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 ); + // Find a default font. - fontId = fontClient.FindDefaultFont( character, pointSize ); + fontId = fontClient.FindDefaultFont( character, pointSize, 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 ); + } +#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() ); +#endif // Cache the font. *( defaultFontPerScriptCacheBuffer + script ) = fontId; } @@ -437,7 +541,7 @@ void MultilanguageSupport::ValidateFonts( LogicalModel& model ) if( 0u != currentFontRun.characterRun.numberOfCharacters ) { // Store the font run. - validatedFontRuns.PushBack( currentFontRun ); + fonts.PushBack( currentFontRun ); } // Initialize the new one. @@ -454,12 +558,15 @@ void MultilanguageSupport::ValidateFonts( LogicalModel& model ) if( 0u != currentFontRun.characterRun.numberOfCharacters ) { // Store the last run. - validatedFontRuns.PushBack( currentFontRun ); + fonts.PushBack( currentFontRun ); } +} - // 5) Sets the validated font runs to the model. - model.SetFonts( validatedFontRuns.Begin(), - validatedFontRuns.Count() ); +void MultilanguageSupport::ValidateFonts( LogicalModel& model, + CharacterIndex characterIndex, + Length numberOfCharactersToRemove, + Length numberOfCharactersToInsert ) +{ } } // namespace Internal