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=f37bdc271f176909f899cf45d1ad70307776f262;hp=6553c51ef702bd8685304173da20ce07d0af84a9;hb=4d763eb68b5aa2448dfc81d90fc5ce598c68c99f;hpb=cc82bd9b187cda8fe2c8336b73fd1fa9376cfebd diff --git a/dali-toolkit/internal/text/multi-language-support-impl.cpp b/dali-toolkit/internal/text/multi-language-support-impl.cpp index 6553c51..f37bdc2 100644 --- a/dali-toolkit/internal/text/multi-language-support-impl.cpp +++ b/dali-toolkit/internal/text/multi-language-support-impl.cpp @@ -19,17 +19,9 @@ #include // EXTERNAL INCLUDES -#include #include -#include -#include -#include - -// INTERNAL INCLUDES -#include -#include -#include -#include +#include +#include namespace Dali { @@ -40,7 +32,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; @@ -53,40 +45,78 @@ 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. + * @brief Merges the font descriptions to retrieve the font Id for each character. * - * @return The font id. + * @param[in] fontDescriptions The font descriptions. + * @param[out] fontIds The font id for each character. + * @param[in] defaultFontDescription The default font description. + * @param[in] defaultPointSize The default font size. */ -FontId GetFontId( Length index, - Vector::ConstIterator& fontRunIt, - const Vector::ConstIterator& fontRunEndIt ) +void MergeFontDescriptions( const Vector& fontDescriptions, + Vector& fontIds, + const TextAbstraction::FontDescription& defaultFontDescription, + TextAbstraction::PointSize26Dot6 defaultPointSize ) { - FontId fontId = 0u; + // Get the handle to the font client. + TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); - if( fontRunIt != fontRunEndIt ) - { - const FontRun& fontRun = *fontRunIt; + // Pointer to the font id buffer. + FontId* fontIdsBuffer = fontIds.Begin(); - if( ( index >= fontRun.characterRun.characterIndex ) && - ( index < fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) ) + // Traverse all the characters. + const Length numberOfCharacters = fontIds.Count(); + for( CharacterIndex index = 0u; index < numberOfCharacters; ++index ) + { + // The default font description and font point size. + TextAbstraction::FontDescription fontDescription = defaultFontDescription; + TextAbstraction::PointSize26Dot6 fontSize = defaultPointSize; + bool defaultFont = true; + + // Traverse all the font descriptions. + for( Vector::ConstIterator it = fontDescriptions.Begin(), + endIt = fontDescriptions.End(); + it != endIt; + ++it ) { - fontId = fontRun.fontId; + // Check whether the character's font is modified by the current font description. + const FontDescriptionRun& fontRun = *it; + if( ( index >= fontRun.characterRun.characterIndex ) && + ( index < fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) ) + { + if( fontRun.familyDefined ) + { + fontDescription.family = std::string( fontRun.familyName, fontRun.familyLength ); + defaultFont = false; + } + if( fontRun.weightDefined ) + { + fontDescription.weight = fontRun.weight; + defaultFont = false; + } + if( fontRun.widthDefined ) + { + fontDescription.width = fontRun.width; + defaultFont = false; + } + if( fontRun.slantDefined ) + { + fontDescription.slant = fontRun.slant; + defaultFont = false; + } + if( fontRun.sizeDefined ) + { + fontSize = fontRun.size; + defaultFont = false; + } + } } - if( index + 1u == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) + // Get the font id if is not the default font. + if( !defaultFont ) { - // All the characters of the current run have been traversed. Get the next one for the next iteration. - ++fontRunIt; + *( fontIdsBuffer + index ) = fontClient.GetFontId( fontDescription, fontSize ); } } - - return fontId; } /** @@ -126,24 +156,6 @@ 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(), @@ -212,7 +224,6 @@ Text::MultilanguageSupport MultilanguageSupport::Get() } void MultilanguageSupport::SetScripts( const Vector& text, - const Vector& lineBreakInfo, Vector& scripts ) { const Length numberOfCharacters = text.Count(); @@ -232,8 +243,8 @@ void MultilanguageSupport::SetScripts( const Vector& text, // Reserve some space to reduce the number of reallocations. scripts.Reserve( numberOfCharacters << 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; @@ -242,14 +253,15 @@ void MultilanguageSupport::SetScripts( const Vector& text, Length numberOfAllScriptCharacters = 0u; // Pointers to the text and break info buffers. - const Character* textBuffer = text.Begin(); - const LineBreakInfo* breakInfoBuffer = lineBreakInfo.Begin(); + const Character* const textBuffer = text.Begin(); // Traverse all characters and set the scripts. for( Length index = 0u; index < numberOfCharacters; ++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: @@ -262,19 +274,25 @@ void MultilanguageSupport::SetScripts( const Vector& text, // Skip those characters valid for many scripts like white spaces or '\n'. bool endOfText = index == numberOfCharacters; while( !endOfText && - IsValidForAllScripts( character ) ) + ( TextAbstraction::COMMON == script ) ) { // 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 (if the last one is not unknown). + if( TextAbstraction::UNKNOWN != currentScriptRun.script ) + { + currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; + numberOfAllScriptCharacters = 0u; + } } // Get the next character. @@ -283,7 +301,7 @@ void MultilanguageSupport::SetScripts( const Vector& text, if( !endOfText ) { character = *( textBuffer + index ); - breakInfo = *( breakInfoBuffer + index ); + script = TextAbstraction::GetCharacterScript( character ); } } @@ -294,25 +312,33 @@ 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 ) ) { // 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; @@ -326,67 +352,58 @@ void MultilanguageSupport::SetScripts( const Vector& text, // 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 ) - { - // There are only white spaces in the last script. Set the latin script. - currentScriptRun.script = TextAbstraction::LATIN; - } - // Store the last run. - scripts.PushBack( currentScriptRun ); + DALI_ASSERT_DEBUG( ( 0u != currentScriptRun.characterRun.numberOfCharacters ) && "MultilanguageSupport::SetScripts() Trying to insert a script run with zero characters." ); + + if( TextAbstraction::UNKNOWN == currentScriptRun.script ) + { + // There are only white spaces in the last script. Set the latin script. + currentScriptRun.script = TextAbstraction::LATIN; } -} -void MultilanguageSupport::ReplaceScripts( LogicalModel& model, - CharacterIndex characterIndex, - Length numberOfCharactersToRemove, - Length numberOfCharactersToInsert ) -{ + // Store the last run. + scripts.PushBack( currentScriptRun ); } void MultilanguageSupport::ValidateFonts( const Vector& text, const Vector& scripts, + const Vector& fontDescriptions, + FontId defaultFontId, Vector& fonts ) { + // Clear any previously validated font. + fonts.Clear(); + + DALI_LOG_INFO( gLogFilter, Debug::General, "-->MultilanguageSupport::ValidateFonts\n" ); const Length numberOfCharacters = text.Count(); 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(); - // Traverse the characters and validate/set the fonts. // Get the caches. @@ -394,39 +411,66 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, 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.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 ); + MergeFontDescriptions( fontDescriptions, + fontIds, + defaultFontDescription, + defaultPointSize ); + + const Character* const textBuffer = text.Begin(); + const FontId* const fontIdsBuffer = fontIds.Begin(); Vector::ConstIterator scriptRunIt = scripts.Begin(); Vector::ConstIterator scriptRunEndIt = scripts.End(); for( Length index = 0u; index < numberOfCharacters; ++index ) { // Get the character. - const Character character = *( text.Begin() + index ); + const Character character = *( textBuffer + index ); // Get the font for the character. - FontId fontId = GetFontId( index, - fontRunIt, - fontRunEndIt ); + FontId fontId = *( fontIdsBuffer + index ); // Get the script for the character. Script script = GetScript( index, scriptRunIt, scriptRunEndIt ); +#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 + if( TextAbstraction::UNKNOWN == script ) { DALI_LOG_WARNING( "MultilanguageSupport::ValidateFonts. Unknown script!" ); @@ -434,64 +478,77 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, } // Whether the font being validated is a default one not set by the user. - const bool isDefault = ( 0u == fontId ); + FontId preferredFont = fontId; - // The default font point size. - PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE; + // Validate if the font set by the user supports the character. - if( !isDefault ) - { - // Validate if the font set by the user supports the character. + // Check first in the caches. - // Check first in the caches. + // The user may have set the default font. Check it. Otherwise check in the valid fonts cache. + if( fontId != *( defaultFontPerScriptCacheBuffer + script ) ) + { + // Check in the valid fonts cache. + ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script ); - // The user may have set the default font. Check it. Otherwise check in the valid fonts cache. - if( fontId != *( defaultFontPerScriptCacheBuffer + script ) ) + if( NULL == validateFontsPerScript ) { - // Check in the valid fonts cache. - ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script ); + validateFontsPerScript = new ValidateFontsPerScript(); - if( NULL == validateFontsPerScript ) - { - validateFontsPerScript = new ValidateFontsPerScript(); - } + *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript; + } - if( NULL != validateFontsPerScript ) + if( NULL != validateFontsPerScript ) + { + if( !validateFontsPerScript->FindValidFont( fontId ) ) { - if( !validateFontsPerScript->FindValidFont( fontId ) ) - { - // Use the font client to validate the font. - GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character ); + // Use the font client to validate the font. + GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character ); - // Emojis are present in many monochrome fonts; prefer color by default. - if( TextAbstraction::EMOJI == script && - 0u != glyphIndex ) + // 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() ) ) { - BufferImage bitmap = fontClient.CreateBitmap( fontId, glyphIndex ); - if( bitmap && - Pixel::BGRA8888 != bitmap.GetPixelFormat() ) - { - glyphIndex = 0; - } + glyphIndex = 0u; } + } - if( 0u == glyphIndex ) + if( 0u == glyphIndex ) + { + // 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. + + // At this point the validated font supports the given character. However, characters + // common for all scripts, like white spaces or new paragraph characters, need to be + // processed differently. + // + // i.e. A white space can have assigned a DEVANAGARI script but the font assigned may not + // support none of the DEVANAGARI glyphs. This font can't be added to the cache as a valid + // font for the DEVANAGARI script but the COMMON one. + if( TextAbstraction::IsCommonScript( character ) ) { - // Get the point size of the current font. It will be used to get a default font id. - pointSize = fontClient.GetPointSize( fontId ); + validateFontsPerScript = *( validFontsPerScriptCacheBuffer + TextAbstraction::COMMON ); - // 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->mValidFonts.PushBack( fontId ); + if( NULL == validateFontsPerScript ) + { + validateFontsPerScript = new ValidateFontsPerScript(); + + *( validFontsPerScriptCacheBuffer + TextAbstraction::COMMON ) = validateFontsPerScript; + } } + + validateFontsPerScript->mValidFonts.PushBack( fontId ); } } } - } // !isDefault + } // The font has not been validated. Find a default one. if( 0u == fontId ) @@ -505,8 +562,8 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, // 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, preferColor ); + // Find a fallback-font. + fontId = fontClient.FindFallbackFont( preferredFont, character, defaultPointSize, preferColor ); // If the system does not support a suitable font, fallback to Latin if( 0u == fontId ) @@ -515,23 +572,30 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, } if( 0u == fontId ) { - fontId = fontClient.FindDefaultFont( UTF32_A, pointSize ); + fontId = fontClient.FindDefaultFont( UTF32_A, defaultPointSize ); } -#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; } } +#ifdef DEBUG_ENABLED + { + 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 + // The font is now validated. - if( ( fontId != currentFontRun.fontId ) || - ( isDefault != currentFontRun.isDefault ) ) + if( fontId != currentFontRun.fontId ) { // Current run needs to be stored and a new one initialized. @@ -545,7 +609,6 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; currentFontRun.characterRun.numberOfCharacters = 0u; currentFontRun.fontId = fontId; - currentFontRun.isDefault = isDefault; } // Add one more character to the run. @@ -557,13 +620,7 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, // Store the last run. fonts.PushBack( currentFontRun ); } -} - -void MultilanguageSupport::ValidateFonts( LogicalModel& model, - CharacterIndex characterIndex, - Length numberOfCharactersToRemove, - Length numberOfCharactersToInsert ) -{ + DALI_LOG_INFO( gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n" ); } } // namespace Internal