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=8e55537b813ad2fcf0dd2ceb2ad77cacea0ad4f0;hp=e0053c8500701aa7cbe07239ddf8f76bdfe3f148;hb=a97820c064ff542c5c5e5151e976c1495af1235e;hpb=1db0a8becea3dbdebaa942d934d91824a92434e7 diff --git a/dali-toolkit/internal/text/multi-language-support-impl.cpp b/dali-toolkit/internal/text/multi-language-support-impl.cpp index e0053c8..8e55537 100644 --- a/dali-toolkit/internal/text/multi-language-support-impl.cpp +++ b/dali-toolkit/internal/text/multi-language-support-impl.cpp @@ -18,17 +18,10 @@ // CLASS HEADER #include -// INTERNAL INCLUDES -#include -#include -#include -#include -#include -#include -#include - // EXTERNAL INCLUDES -#include +#include +#include +#include namespace Dali { @@ -36,6 +29,15 @@ namespace Dali namespace Toolkit { +namespace +{ +#if defined(DEBUG_ENABLED) +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_MULTI_LANGUAGE_SUPPORT"); +#endif + +const Dali::Toolkit::Text::Character UTF32_A = 0x0041; +} + namespace Text { @@ -194,8 +196,6 @@ void MultilanguageSupport::SetScripts( const Vector& text, return; } - // Traverse all characters and set the scripts. - // Stores the current script run. ScriptRun currentScriptRun; currentScriptRun.characterRun.characterIndex = 0u; @@ -205,35 +205,107 @@ void MultilanguageSupport::SetScripts( const Vector& text, // Reserve some space to reduce the number of reallocations. scripts.Reserve( numberOfCharacters << 2u ); - for( Length index = 0u; index < numberOfCharacters; ++index ) - { - const Character character = *( text.Begin() + index ); + // Whether the first valid script needs to be set. + bool isFirstScriptToBeSet = true; - Script script = GetCharacterScript( character ); + // Whether the first valid script is a right to left script. + bool isParagraphRTL = false; - if( TextAbstraction::UNKNOWN == script ) + // 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* const textBuffer = text.Begin(); + + // Traverse all characters and set the scripts. + for( Length index = 0u; index < numberOfCharacters; ++index ) + { + Character character = *( textBuffer + 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: + // - 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 && + ( TextAbstraction::COMMON == script ) ) { - if( IsZeroWidthNonJoiner( character ) || - IsZeroWidthJoiner( character ) || - IsZeroWidthSpace( character ) || - IsLeftToRightMark( character ) || - IsRightToLeftMark( character ) || - IsThinSpace( character ) ) + // Count all these characters to be added into a script. + ++numberOfAllScriptCharacters; + + if( TextAbstraction::IsNewParagraph( character ) ) { - // Keep previous script if the character is a zero width joiner or a zero width non joiner. - script = currentScriptRun.script; + // 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. + 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; + } } - else + + // Get the next character. + ++index; + endOfText = index == numberOfCharacters; + if( !endOfText ) { - script = TextAbstraction::LATIN; - DALI_ASSERT_DEBUG( !"MultilanguageSupport::SetScripts. Unkown script!" ); + character = *( textBuffer + index ); + script = TextAbstraction::GetCharacterScript( character ); } } - if( script != currentScriptRun.script ) + if( endOfText ) + { + // 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; + } + + // Check if it is the first character of a paragraph. + if( isFirstScriptToBeSet && + ( TextAbstraction::UNKNOWN != script ) && + ( TextAbstraction::COMMON != script ) ) + { + // Sets the direction of the first valid script. + isParagraphRTL = TextAbstraction::IsRightToLeftScript( script ); + isFirstScriptToBeSet = false; + } + + if( ( script != currentScriptRun.script ) && + ( TextAbstraction::COMMON != script ) ) { // Current run needs to be stored and a new one initialized. + 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 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; + } + if( 0u != currentScriptRun.characterRun.numberOfCharacters ) { // Store the script run. @@ -242,36 +314,56 @@ void MultilanguageSupport::SetScripts( const Vector& text, // Initialize the new one. currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; - currentScriptRun.characterRun.numberOfCharacters = 0u; + currentScriptRun.characterRun.numberOfCharacters = numberOfAllScriptCharacters + 1u; // Adds the white spaces which are at the begining of the script. currentScriptRun.script = script; + numberOfAllScriptCharacters = 0u; } + else + { + if( TextAbstraction::UNKNOWN != currentScriptRun.script ) + { + // Adds white spaces between characters. + currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; + numberOfAllScriptCharacters = 0u; + } - // Add one more character to the run. - ++currentScriptRun.characterRun.numberOfCharacters; + // Add one more character to the run. + ++currentScriptRun.characterRun.numberOfCharacters; + } } - if( 0u != currentScriptRun.characterRun.numberOfCharacters ) + // Add remaining characters into the last script. + currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; + + DALI_ASSERT_DEBUG( ( 0u != currentScriptRun.characterRun.numberOfCharacters ) && "MultilanguageSupport::SetScripts() Trying to insert a script run with zero characters." ); + + if( TextAbstraction::UNKNOWN == currentScriptRun.script ) { - // Store the last run. - scripts.PushBack( currentScriptRun ); + // There are only white spaces in the last script. Set the latin script. + currentScriptRun.script = TextAbstraction::LATIN; } + + // Store the last run. + scripts.PushBack( currentScriptRun ); } void MultilanguageSupport::ValidateFonts( const Vector& text, const Vector& scripts, Vector& fonts ) { + 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; + const Vector userSetFonts = fonts; fonts.Clear(); // Traverse the characters and validate/set the fonts. @@ -294,8 +386,8 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); // Iterators of the font and script runs. - Vector::ConstIterator fontRunIt = definedFonts.Begin(); - Vector::ConstIterator fontRunEndIt = definedFonts.End(); + Vector::ConstIterator fontRunIt = userSetFonts.Begin(); + Vector::ConstIterator fontRunEndIt = userSetFonts.End(); Vector::ConstIterator scriptRunIt = scripts.Begin(); Vector::ConstIterator scriptRunEndIt = scripts.End(); @@ -314,6 +406,20 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, 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!" ); @@ -323,6 +429,11 @@ 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 ); + DALI_LOG_INFO( gLogFilter, + Debug::Verbose, + " Is a default font : %s\n", + ( isDefault ? "true" : "false" ) ); + // The default font point size. PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE; @@ -337,12 +448,32 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, { // Check in the valid fonts cache. ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script ); + + if( NULL == validateFontsPerScript ) + { + validateFontsPerScript = new ValidateFontsPerScript(); + + *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript; + } + 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 ) { @@ -355,34 +486,32 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, else { // Add the font to the valid font cache. - validateFontsPerScript->mValidFonts.PushBack( fontId ); - } - } - } - 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 ); + // 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 ) ) + { + validateFontsPerScript = *( validFontsPerScriptCacheBuffer + TextAbstraction::COMMON ); + + if( NULL == validateFontsPerScript ) + { + validateFontsPerScript = new ValidateFontsPerScript(); + + *( validFontsPerScriptCacheBuffer + TextAbstraction::COMMON ) = validateFontsPerScript; + } + } - // 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 ); + validateFontsPerScript->mValidFonts.PushBack( fontId ); + } } } } - } + } // !isDefault // The font has not been validated. Find a default one. if( 0u == fontId ) @@ -393,14 +522,40 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, // 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 ); + } // 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 ) || @@ -430,6 +585,7 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, // Store the last run. fonts.PushBack( currentFontRun ); } + DALI_LOG_INFO( gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n" ); } } // namespace Internal