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=f24d42716182487d6238657150bb11ddc15ef90b;hp=6ca96ad284b10a8ed37223829b2a95c2312762f4;hb=1621192f7b42f1fa210d6bdf12d853629304bea3;hpb=95faa4c96dcfcc76207eace1d6a00ba0aef714f0 diff --git a/dali-toolkit/internal/text/multi-language-support-impl.cpp b/dali-toolkit/internal/text/multi-language-support-impl.cpp index 6ca96ad..f24d427 100644 --- a/dali-toolkit/internal/text/multi-language-support-impl.cpp +++ b/dali-toolkit/internal/text/multi-language-support-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,19 +19,18 @@ #include // EXTERNAL INCLUDES -#include -#include +#include #include +#include // INTERNAL INCLUDES +#include #include namespace Dali { - namespace Toolkit { - namespace { #if defined(DEBUG_ENABLED) @@ -39,22 +38,20 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_MULT #endif const Dali::Toolkit::Text::Character UTF32_A = 0x0041; -} +} // namespace namespace Text { - namespace Internal { - -bool ValidateFontsPerScript::IsValidFont( FontId fontId ) const +bool ValidateFontsPerScript::IsValidFont(FontId fontId) const { - for( Vector::ConstIterator it = mValidFonts.Begin(), - endIt = mValidFonts.End(); - it != endIt; - ++it ) + for(Vector::ConstIterator it = mValidFonts.Begin(), + endIt = mValidFonts.End(); + it != endIt; + ++it) { - if( fontId == *it ) + if(fontId == *it) { return true; } @@ -63,22 +60,22 @@ bool ValidateFontsPerScript::IsValidFont( FontId fontId ) const return false; } -FontId DefaultFonts::FindFont( TextAbstraction::FontClient& fontClient, - const TextAbstraction::FontDescription& description, - PointSize26Dot6 size ) const +FontId DefaultFonts::FindFont(TextAbstraction::FontClient& fontClient, + const TextAbstraction::FontDescription& description, + PointSize26Dot6 size) const { - for( std::vector::const_iterator it = mFonts.begin(), - endIt = mFonts.end(); - it != endIt; - ++it ) + for(std::vector::const_iterator it = mFonts.begin(), + endIt = mFonts.end(); + it != endIt; + ++it) { const CacheItem& item = *it; - if( ( ( TextAbstraction::FontWeight::NONE == description.weight ) || ( description.weight == item.description.weight ) ) && - ( ( TextAbstraction::FontWidth::NONE == description.width ) || ( description.width == item.description.width ) ) && - ( ( TextAbstraction::FontSlant::NONE == description.slant ) || ( description.slant == item.description.slant ) ) && - ( size == fontClient.GetPointSize( item.fontId ) ) && - ( description.family.empty() || ( description.family == item.description.family ) ) ) + if(((TextAbstraction::FontWeight::NONE == description.weight) || (description.weight == item.description.weight)) && + ((TextAbstraction::FontWidth::NONE == description.width) || (description.width == item.description.width)) && + ((TextAbstraction::FontSlant::NONE == description.slant) || (description.slant == item.description.slant)) && + (size == fontClient.GetPointSize(item.fontId)) && + (description.family.empty() || (description.family == item.description.family))) { return item.fontId; } @@ -87,12 +84,12 @@ FontId DefaultFonts::FindFont( TextAbstraction::FontClient& fontClient, return 0u; } -void DefaultFonts::Cache( const TextAbstraction::FontDescription& description, FontId fontId ) +void DefaultFonts::Cache(const TextAbstraction::FontDescription& description, FontId fontId) { CacheItem item; item.description = description; - item.fontId = fontId; - mFonts.push_back( item ); + item.fontId = fontId; + mFonts.push_back(item); } MultilanguageSupport::MultilanguageSupport() @@ -101,29 +98,29 @@ 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 + 1, NULL ); + mDefaultFontPerScriptCache.Resize(TextAbstraction::GetNumberOfScripts(), 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. - mValidFontsPerScriptCache.Resize( TextAbstraction::UNKNOWN + 1, NULL ); + mValidFontsPerScriptCache.Resize(TextAbstraction::GetNumberOfScripts(), NULL); } MultilanguageSupport::~MultilanguageSupport() { // Destroy the default font per script cache. - for( Vector::Iterator it = mDefaultFontPerScriptCache.Begin(), - endIt = mDefaultFontPerScriptCache.End(); - it != endIt; - ++it ) + 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; - ++it ) + for(Vector::Iterator it = mValidFontsPerScriptCache.Begin(), + endIt = mValidFontsPerScriptCache.End(); + it != endIt; + ++it) { delete *it; } @@ -133,33 +130,33 @@ Text::MultilanguageSupport MultilanguageSupport::Get() { Text::MultilanguageSupport multilanguageSupportHandle; - SingletonService service( SingletonService::Get() ); - if( service ) + SingletonService service(SingletonService::Get()); + if(service) { // Check whether the singleton is already created - Dali::BaseHandle handle = service.GetSingleton( typeid( Text::MultilanguageSupport ) ); - if( handle ) + Dali::BaseHandle handle = service.GetSingleton(typeid(Text::MultilanguageSupport)); + if(handle) { // If so, downcast the handle - MultilanguageSupport* impl = dynamic_cast< Internal::MultilanguageSupport* >( handle.GetObjectPtr() ); - multilanguageSupportHandle = Text::MultilanguageSupport( impl ); + MultilanguageSupport* impl = dynamic_cast(handle.GetObjectPtr()); + multilanguageSupportHandle = Text::MultilanguageSupport(impl); } else // create and register the object { - multilanguageSupportHandle = Text::MultilanguageSupport( new MultilanguageSupport ); - service.Register( typeid( multilanguageSupportHandle ), multilanguageSupportHandle ); + multilanguageSupportHandle = Text::MultilanguageSupport(new MultilanguageSupport); + service.Register(typeid(multilanguageSupportHandle), multilanguageSupportHandle); } } return multilanguageSupportHandle; } -void MultilanguageSupport::SetScripts( const Vector& text, - CharacterIndex startIndex, - Length numberOfCharacters, - Vector& scripts ) +void MultilanguageSupport::SetScripts(const Vector& text, + CharacterIndex startIndex, + Length numberOfCharacters, + Vector& scripts) { - if( 0u == numberOfCharacters ) + if(0u == numberOfCharacters) { // Nothing to do if there are no characters. return; @@ -167,15 +164,15 @@ void MultilanguageSupport::SetScripts( const Vector& text, // Find the first index where to insert the script. ScriptRunIndex scriptIndex = 0u; - if( 0u != startIndex ) + if(0u != startIndex) { - for( Vector::ConstIterator it = scripts.Begin(), - endIt = scripts.End(); - it != endIt; - ++it, ++scriptIndex ) + 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 ) + if(startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters) { // Run found. break; @@ -185,12 +182,12 @@ void MultilanguageSupport::SetScripts( const Vector& text, // Stores the current script run. ScriptRun currentScriptRun; - currentScriptRun.characterRun.characterIndex = startIndex; + currentScriptRun.characterRun.characterIndex = startIndex; currentScriptRun.characterRun.numberOfCharacters = 0u; - currentScriptRun.script = TextAbstraction::UNKNOWN; + currentScriptRun.script = TextAbstraction::UNKNOWN; // Reserve some space to reduce the number of reallocations. - scripts.Reserve( text.Count() << 2u ); + scripts.Reserve(text.Count() << 2u); // Whether the first valid script needs to be set. bool isFirstScriptToBeSet = true; @@ -204,14 +201,18 @@ void MultilanguageSupport::SetScripts( const Vector& text, // Pointers to the text buffer. const Character* const textBuffer = text.Begin(); + // Initialize whether is right to left direction + currentScriptRun.isRightToLeft = false; + // Traverse all characters and set the scripts. - const Length lastCharacter = startIndex + numberOfCharacters; - for( Length index = startIndex; index < lastCharacter; ++index ) + const Length lastCharacter = startIndex + numberOfCharacters - 1u; + + for(Length index = startIndex; index <= lastCharacter; ++index) { - Character character = *( textBuffer + index ); + Character character = *(textBuffer + index); // Get the script of the character. - Script script = TextAbstraction::GetCharacterScript( 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: @@ -222,27 +223,45 @@ 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 == lastCharacter; - while( !endOfText && - ( TextAbstraction::COMMON == script ) ) + bool endOfText = index > lastCharacter; + + //Handle all Emoji Sequence cases + if(IsNewSequence(textBuffer, currentScriptRun.script, index, lastCharacter, 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; - } + AddCurrentScriptAndCreatNewScript(script, + false, + false, + currentScriptRun, + numberOfAllScriptCharacters, + scripts, + scriptIndex); + } + else if(IsScriptChangedToFollowSequence(currentScriptRun.script, character, script)) + { + currentScriptRun.script = script; + } + else if(IsOneOfEmojiScripts(currentScriptRun.script) && (TextAbstraction::COMMON == script)) + { + // Emojis doesn't mix well with characters common to all scripts. Insert the emoji run. + AddCurrentScriptAndCreatNewScript(TextAbstraction::UNKNOWN, + false, + false, + currentScriptRun, + numberOfAllScriptCharacters, + scripts, + scriptIndex); + } + + while(!endOfText && + (TextAbstraction::COMMON == script)) + { + // Check if whether is right to left markup and Keeps true if the previous value was true. + currentScriptRun.isRightToLeft = currentScriptRun.isRightToLeft || TextAbstraction::IsRightToLeftMark(character); // Count all these characters to be added into a script. ++numberOfAllScriptCharacters; - if( TextAbstraction::IsNewParagraph( character ) ) + if(TextAbstraction::IsNewParagraph(character)) { // The character is a new paragraph. // To know when there is a new paragraph is needed because if there is a white space @@ -250,31 +269,42 @@ void MultilanguageSupport::SetScripts( const Vector& text, // 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. - currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; - - // Store the script 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; + AddCurrentScriptAndCreatNewScript(TextAbstraction::UNKNOWN, + false, + false, + currentScriptRun, + numberOfAllScriptCharacters, + scripts, + scriptIndex); } // Get the next character. ++index; - endOfText = index == lastCharacter; - if( !endOfText ) + endOfText = index > lastCharacter; + if(!endOfText) { - character = *( textBuffer + index ); - script = TextAbstraction::GetCharacterScript( character ); + character = *(textBuffer + index); + script = TextAbstraction::GetCharacterScript(character); + + //Handle all Emoji Sequence cases + if(IsNewSequence(textBuffer, currentScriptRun.script, index, lastCharacter, script)) + { + AddCurrentScriptAndCreatNewScript(script, + false, + false, + currentScriptRun, + numberOfAllScriptCharacters, + scripts, + scriptIndex); + } + else if(IsScriptChangedToFollowSequence(currentScriptRun.script, character, script)) + { + currentScriptRun.script = script; + } } } // end while( !endOfText && ( TextAbstraction::COMMON == script ) ) - if( endOfText ) + 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. @@ -282,60 +312,60 @@ void MultilanguageSupport::SetScripts( const Vector& text, } // Check if it is the first character of a paragraph. - if( isFirstScriptToBeSet && - ( TextAbstraction::UNKNOWN != script ) && - ( TextAbstraction::COMMON != script ) && - ( TextAbstraction::EMOJI != script ) ) + if(isFirstScriptToBeSet && + (TextAbstraction::UNKNOWN != script) && + (TextAbstraction::COMMON != script) && + (TextAbstraction::EMOJI != script) && + (TextAbstraction::EMOJI_TEXT != script) && + (TextAbstraction::EMOJI_COLOR != script) && + (!TextAbstraction::IsSymbolScript(script))) { // Sets the direction of the first valid script. - isParagraphRTL = TextAbstraction::IsRightToLeftScript( script ); + isParagraphRTL = currentScriptRun.isRightToLeft || TextAbstraction::IsRightToLeftScript(script); isFirstScriptToBeSet = false; } - if( ( script != currentScriptRun.script ) && - ( TextAbstraction::COMMON != script ) ) + 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 ) ) + 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 ) ) + 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; } - else if( ( TextAbstraction::UNKNOWN == currentScriptRun.script ) && - ( TextAbstraction::EMOJI == script ) ) + else if((TextAbstraction::UNKNOWN == currentScriptRun.script) && + (TextAbstraction::IsSymbolOrEmojiOrTextScript(script))) { currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; numberOfAllScriptCharacters = 0u; } - if( 0u != currentScriptRun.characterRun.numberOfCharacters ) - { - // Store the script run. - scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun ); - ++scriptIndex; - } - - // Initialize the new one. - currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; - currentScriptRun.characterRun.numberOfCharacters = numberOfAllScriptCharacters + 1u; // Adds the white spaces which are at the begining of the script. - currentScriptRun.script = script; - numberOfAllScriptCharacters = 0u; + // Adds the white spaces which are at the begining of the script. + numberOfAllScriptCharacters++; + AddCurrentScriptAndCreatNewScript(script, + TextAbstraction::IsRightToLeftScript(script), + true, + currentScriptRun, + numberOfAllScriptCharacters, + scripts, + scriptIndex); } else { - if( TextAbstraction::UNKNOWN != currentScriptRun.script ) + if(TextAbstraction::UNKNOWN != currentScriptRun.script) { // Adds white spaces between characters. currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; @@ -350,60 +380,60 @@ void MultilanguageSupport::SetScripts( const Vector& text, // Add remaining characters into the last script. currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters; - if( 0u != currentScriptRun.characterRun.numberOfCharacters ) + if(0u != currentScriptRun.characterRun.numberOfCharacters) { // Store the last run. - scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun ); + scripts.Insert(scripts.Begin() + scriptIndex, currentScriptRun); ++scriptIndex; } - if( scriptIndex < scripts.Count() ) + 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; + 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 ) + for(Vector::Iterator it = scripts.Begin() + scriptIndex, + endIt = scripts.End(); + it != endIt; + ++it) { - ScriptRun& run = *it; + ScriptRun& run = *it; run.characterRun.characterIndex = nextCharacterIndex; nextCharacterIndex += run.characterRun.numberOfCharacters; } } } -void MultilanguageSupport::ValidateFonts( const Vector& text, - const Vector& scripts, - const Vector& fontDescriptions, - const TextAbstraction::FontDescription& defaultFontDescription, - TextAbstraction::PointSize26Dot6 defaultFontPointSize, - CharacterIndex startIndex, - Length numberOfCharacters, - Vector& fonts ) +void MultilanguageSupport::ValidateFonts(const Vector& text, + const Vector& scripts, + const Vector& fontDescriptions, + const TextAbstraction::FontDescription& defaultFontDescription, + TextAbstraction::PointSize26Dot6 defaultFontPointSize, + CharacterIndex startIndex, + Length numberOfCharacters, + Vector& fonts) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->MultilanguageSupport::ValidateFonts\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->MultilanguageSupport::ValidateFonts\n"); - if( 0u == numberOfCharacters ) + if(0u == numberOfCharacters) { - DALI_LOG_INFO( gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n"); // Nothing to do if there are no characters. return; } // Find the first index where to insert the font run. FontRunIndex fontIndex = 0u; - if( 0u != startIndex ) + if(0u != startIndex) { - for( Vector::ConstIterator it = fonts.Begin(), - endIt = fonts.End(); - it != endIt; - ++it, ++fontIndex ) + 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 ) + if(startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters) { // Run found. break; @@ -414,17 +444,19 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, // Traverse the characters and validate/set the fonts. // Get the caches. - DefaultFonts** defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin(); - ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin(); + DefaultFonts** defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin(); + ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin(); // Stores the validated font runs. - fonts.Reserve( fontDescriptions.Count() ); + fonts.Reserve(fontDescriptions.Count()); // Initializes a validated font run. FontRun currentFontRun; - currentFontRun.characterRun.characterIndex = startIndex; + currentFontRun.characterRun.characterIndex = startIndex; currentFontRun.characterRun.numberOfCharacters = 0u; - currentFontRun.fontId = 0u; + currentFontRun.fontId = 0u; + currentFontRun.isBoldRequired = false; + currentFontRun.isItalicRequired = false; // Get the font client. TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get(); @@ -432,57 +464,53 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, const Character* const textBuffer = text.Begin(); // Iterators of the script runs. - Vector::ConstIterator scriptRunIt = scripts.Begin(); - Vector::ConstIterator scriptRunEndIt = scripts.End(); - bool isNewParagraphCharacter = false; + Vector::ConstIterator scriptRunIt = scripts.Begin(); + Vector::ConstIterator scriptRunEndIt = scripts.End(); + bool isNewParagraphCharacter = false; - FontId currentFontId = 0u; - FontId previousFontId = 0u; - bool isPreviousEmojiScript = false; + FontId previousEmojiFontId = 0u; + TextAbstraction::Script previousScript = TextAbstraction::UNKNOWN; - // Description of fallback font which is selected at current iteration. - TextAbstraction::FontDescription selectedFontDescription; - - CharacterIndex lastCharacter = startIndex + numberOfCharacters; - for( Length index = startIndex; index < lastCharacter; ++index ) + CharacterIndex lastCharacter = startIndex + numberOfCharacters - 1u; + for(Length index = startIndex; index <= lastCharacter; ++index) { // Get the current character. - const Character character = *( textBuffer + index ); - bool needSoftwareBoldening = false; - bool needSoftwareItalic = false; + const Character character = *(textBuffer + index); + bool isItalicRequired = false; + bool isBoldRequired = false; // new description for current character TextAbstraction::FontDescription currentFontDescription; TextAbstraction::PointSize26Dot6 currentFontPointSize = defaultFontPointSize; - bool isDefaultFont = true; - MergeFontDescriptions( fontDescriptions, - defaultFontDescription, - defaultFontPointSize, - index, - currentFontDescription, - currentFontPointSize, - isDefaultFont ); + bool isDefaultFont = true; + MergeFontDescriptions(fontDescriptions, + defaultFontDescription, + defaultFontPointSize, + index, + currentFontDescription, + currentFontPointSize, + isDefaultFont); // Get the font for the current character. - FontId fontId = fontClient.GetFontId( currentFontDescription, currentFontPointSize ); - currentFontId = fontId; + FontId fontId = fontClient.GetFontId(currentFontDescription, currentFontPointSize); // Get the script for the current character. - Script script = GetScript( index, - scriptRunIt, - scriptRunEndIt ); + Script script = GetScript(index, + scriptRunIt, + scriptRunEndIt); #ifdef DEBUG_ENABLED + if(gLogFilter->IsEnabledFor(Debug::Verbose)) { 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() ); + 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 @@ -491,44 +519,39 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, // Check first in the cache of default fonts per script and size. - FontId cachedDefaultFontId = 0u; - DefaultFonts* defaultFonts = *( defaultFontPerScriptCacheBuffer + script ); - if( NULL != defaultFonts ) + FontId cachedDefaultFontId = 0u; + DefaultFonts* defaultFonts = *(defaultFontPerScriptCacheBuffer + script); + if(NULL != defaultFonts) { // This cache stores fall-back fonts. - cachedDefaultFontId = defaultFonts->FindFont( fontClient, - currentFontDescription, - currentFontPointSize ); + cachedDefaultFontId = defaultFonts->FindFont(fontClient, + currentFontDescription, + currentFontPointSize); } // 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 ); + isValidFont = isValidCachedDefaultFont && (fontId == cachedDefaultFontId); - if( isValidFont ) + if(isValidFont) { // Check if the font supports the character. - isValidFont = fontClient.IsCharacterSupportedByFont( fontId, character ); + isValidFont = fontClient.IsCharacterSupportedByFont(fontId, character); } bool isCommonScript = false; - bool isEmojiScript = TextAbstraction::EMOJI == script; + bool isEmojiScript = TextAbstraction::IsOneOfEmojiScripts(script); - if( isEmojiScript && !isPreviousEmojiScript ) + if(isEmojiScript && (previousScript == script)) { - if( 0u != currentFontRun.characterRun.numberOfCharacters ) + // Emoji sequence should use the previous emoji font. + if(0u != previousEmojiFontId) { - // Store the font run. - fonts.Insert( fonts.Begin() + fontIndex, currentFontRun ); - ++fontIndex; + fontId = previousEmojiFontId; + isValidFont = true; } - - // Initialize the new one. - currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; - currentFontRun.characterRun.numberOfCharacters = 0u; - currentFontRun.fontId = fontId; } // If the given font is not valid, it means either: @@ -537,7 +560,7 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, // - the platform default font is different than the default font for the current script. // Need to check if the given font supports the current character. - if( !isValidFont ) // (1) + if(!isValidFont) // (1) { // Whether the current character is common for all scripts (i.e. white spaces, ...) @@ -551,210 +574,227 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, // // 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 ); + isCommonScript = TextAbstraction::IsCommonScript(character) || TextAbstraction::IsEmojiPresentationSelector(character); + + // Check in the valid fonts cache. + ValidateFontsPerScript* validateFontsPerScript = *(validFontsPerScriptCacheBuffer + script); - if( isCommonScript ) + if(NULL != validateFontsPerScript) { - if( isValidCachedDefaultFont && - ( isDefaultFont || ( currentFontId == previousFontId ) ) && - !isEmojiScript ) + // This cache stores valid fonts set by the user. + isValidFont = validateFontsPerScript->IsValidFont(fontId); + + // It may happen that a validated font for a script doesn't have all the glyphs for that script. + // i.e a font validated for the CJK script may contain glyphs for the chinese language but not for the Japanese. + if(isValidFont) { - // 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; + // Checks if the current character is supported by the font is needed. + isValidFont = fontClient.IsCharacterSupportedByFont(fontId, character); } } - else + + if(!isValidFont) // (2) { - // Check in the valid fonts cache. - ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script ); + // The selected font is not stored in any cache. - if( NULL != validateFontsPerScript ) - { - // This cache stores valid fonts set by the user. - isValidFont = validateFontsPerScript->IsValidFont( fontId ); + // Checks if the current character is supported by the selected font. + isValidFont = fontClient.IsCharacterSupportedByFont(fontId, character); - // It may happen that a validated font for a script doesn't have all the glyphs for that script. - // i.e a font validated for the CJK script may contain glyphs for the chinese language but not for the Japanese. - if( isValidFont ) + // If there is a valid font, cache it. + if(isValidFont && !isCommonScript) + { + if(NULL == validateFontsPerScript) { - // Checks if the current character is supported by the font is needed. - isValidFont = fontClient.IsCharacterSupportedByFont( fontId, character ); + validateFontsPerScript = new ValidateFontsPerScript(); + + *(validFontsPerScriptCacheBuffer + script) = validateFontsPerScript; } + + validateFontsPerScript->mValidFonts.PushBack(fontId); } - if( !isValidFont ) // (2) + if(!isValidFont && (fontId != cachedDefaultFontId) && (!TextAbstraction::IsNewParagraph(character))) // (3) { - // The selected font is not stored in any cache. - - // Checks if the current character is supported by the selected font. - isValidFont = fontClient.IsCharacterSupportedByFont( fontId, character ); + // The selected font by the user or the platform's default font has failed to validate the character. - // Emojis are present in many monochrome fonts; prefer color by default. - if( isValidFont && - isEmojiScript ) + // Checks if the previously discarted cached default font supports the character. + bool isValidCachedFont = false; + if(isValidCachedDefaultFont) { - const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character ); - - // For color emojis, the font is valid if the glyph is a color glyph (the bitmap is RGBA). - isValidFont = fontClient.IsColorGlyph( fontId, glyphIndex ); + isValidCachedFont = fontClient.IsCharacterSupportedByFont(cachedDefaultFontId, character); } - // If there is a valid font, cache it. - if( isValidFont ) + if(isValidCachedFont) { - if( NULL == validateFontsPerScript ) - { - validateFontsPerScript = new ValidateFontsPerScript(); - - *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript; - } - - validateFontsPerScript->mValidFonts.PushBack( fontId ); + // Use the cached default font for the script if there is one. + fontId = cachedDefaultFontId; + isValidFont = true; } - - if( !isValidFont && ( fontId != cachedDefaultFontId ) ) // (3) + else { - // The selected font by the user or the platform's default font has failed to validate the character. + // There is no valid cached default font for the script. - // Checks if the previously discarted cached default font supports the character. - bool isValidCachedFont = false; - if( isValidCachedDefaultFont ) - { - isValidCachedFont = fontClient.IsCharacterSupportedByFont( cachedDefaultFontId, character ); - } + DefaultFonts* defaultFontsPerScript = NULL; - if( isValidCachedFont ) + // Find a fallback-font. + fontId = fontClient.FindFallbackFont(character, + currentFontDescription, + currentFontPointSize, + false); + + if(0u == fontId) { - // Use the cached default font for the script if there is one. - fontId = cachedDefaultFontId; + fontId = fontClient.FindDefaultFont(UTF32_A, currentFontPointSize); } - else - { - // There is no valid cached default font for the script. - - DefaultFonts* defaultFontsPerScript = NULL; - // Emojis are present in many monochrome fonts; prefer color by default. - const bool preferColor = ( TextAbstraction::EMOJI == script ); - - // Find a fallback-font. - fontId = fontClient.FindFallbackFont( character, - currentFontDescription, - currentFontPointSize, - preferColor ); - - if( 0u == fontId ) + if(!isCommonScript && (script != TextAbstraction::UNKNOWN)) + { + // Cache the font if it is not an unknown script + if(NULL == defaultFontsPerScript) { - // If the system does not support a suitable font, fallback to Latin - defaultFontsPerScript = *( defaultFontPerScriptCacheBuffer + TextAbstraction::LATIN ); - if( NULL != defaultFontsPerScript ) + defaultFontsPerScript = *(defaultFontPerScriptCacheBuffer + script); + + if(NULL == defaultFontsPerScript) { - fontId = defaultFontsPerScript->FindFont( fontClient, - currentFontDescription, - currentFontPointSize ); + defaultFontsPerScript = new DefaultFonts(); + *(defaultFontPerScriptCacheBuffer + script) = defaultFontsPerScript; } } + defaultFontsPerScript->Cache(currentFontDescription, fontId); + isValidFont = true; + } + } + } // !isValidFont (3) + } // !isValidFont (2) + } // !isValidFont (1) - if( 0u == fontId ) - { - fontId = fontClient.FindDefaultFont( UTF32_A, currentFontPointSize ); - } + if(isEmojiScript && (previousScript != script)) + { + //New Emoji sequence should select font according to the variation selector (VS15 or VS16). + if(0u != currentFontRun.characterRun.numberOfCharacters) + { + // Store the font run. + fonts.Insert(fonts.Begin() + fontIndex, currentFontRun); + ++fontIndex; + } - if ( script != TextAbstraction::UNKNOWN ) - { - // Cache the font if it is not an unknown script - if( NULL == defaultFontsPerScript ) - { - defaultFontsPerScript = *( defaultFontPerScriptCacheBuffer + script ); + // Initialize the new one. + currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; + currentFontRun.characterRun.numberOfCharacters = 0u; + currentFontRun.fontId = fontId; + currentFontRun.isItalicRequired = false; + currentFontRun.isBoldRequired = false; - if( NULL == defaultFontsPerScript ) - { - defaultFontsPerScript = new DefaultFonts(); - *( defaultFontPerScriptCacheBuffer + script ) = defaultFontsPerScript; - } - } - defaultFontsPerScript->Cache( currentFontDescription, fontId ); - } - } - } // !isValidFont (3) - } // !isValidFont (2) - } // !isCommonScript - } // !isValidFont (1) + if(TextAbstraction::IsEmojiColorScript(script) || TextAbstraction::IsEmojiTextScript(script)) + { + bool isModifiedByVariationSelector = false; + GlyphIndex glyphIndexChar = fontClient.GetGlyphIndex(fontId, character); + GlyphIndex glyphIndexCharByVS = fontClient.GetGlyphIndex(fontId, character, Text::GetVariationSelectorByScript(script)); + + isModifiedByVariationSelector = glyphIndexChar != glyphIndexCharByVS; + + if(isModifiedByVariationSelector) + { + FontId requestedFontId = fontClient.FindDefaultFont(character, currentFontPointSize, IsEmojiColorScript(script)); + if(0u != requestedFontId) + { + currentFontRun.fontId = fontId = requestedFontId; + isValidFont = true; + } + } + } + } + + // Store the font id when the first character is an emoji. + if(isEmojiScript) + { + if(0u != fontId && previousScript != script) + { + previousEmojiFontId = fontId; + } + } + else + { + previousEmojiFontId = 0u; + } #ifdef DEBUG_ENABLED + if(gLogFilter->IsEnabledFor(Debug::Verbose)) { 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() ); + 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 - - if( fontId != currentFontRun.fontId ) + if(!isValidFont && !isCommonScript) { - fontClient.GetDescription(fontId,selectedFontDescription); + Dali::TextAbstraction::FontDescription descriptionForLog; + fontClient.GetDescription(fontId, descriptionForLog); + DALI_LOG_RELEASE_INFO("Validated font set fail : Character : %x, Script : %s, Font : %s \n", + character, + Dali::TextAbstraction::ScriptName[script], + descriptionForLog.path.c_str()); } - // Developer sets bold to character but selected font cannot support it - needSoftwareBoldening = ( currentFontDescription.weight >= TextAbstraction::FontWeight::BOLD ) && ( selectedFontDescription.weight < TextAbstraction::FontWeight::BOLD ); + // Whether bols style is required. + isBoldRequired = (currentFontDescription.weight >= TextAbstraction::FontWeight::BOLD); - // Developer sets italic to character but selected font cannot support it - needSoftwareItalic = ( currentFontDescription.slant == TextAbstraction::FontSlant::ITALIC ) && ( selectedFontDescription.slant < TextAbstraction::FontSlant::ITALIC ); + // Whether italic style is required. + isItalicRequired = (currentFontDescription.slant >= TextAbstraction::FontSlant::ITALIC); // The font is now validated. - if( ( fontId != currentFontRun.fontId ) || - isNewParagraphCharacter || - // If font id is same as previous but style is diffrent, initialize new one - ( ( fontId == currentFontRun.fontId ) && ( ( needSoftwareBoldening != currentFontRun.softwareBold ) || ( needSoftwareItalic != currentFontRun.softwareItalic ) ) ) ) + if((fontId != currentFontRun.fontId) || + isNewParagraphCharacter || + // If font id is same as previous but style is diffrent, initialize new one + ((fontId == currentFontRun.fontId) && ((isBoldRequired != currentFontRun.isBoldRequired) || (isItalicRequired != currentFontRun.isItalicRequired)))) { // Current run needs to be stored and a new one initialized. - if( 0u != currentFontRun.characterRun.numberOfCharacters ) + if(0u != currentFontRun.characterRun.numberOfCharacters) { // Store the font run. - fonts.Insert( fonts.Begin() + fontIndex, currentFontRun ); + fonts.Insert(fonts.Begin() + fontIndex, currentFontRun); ++fontIndex; } // Initialize the new one. - currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; + currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters; currentFontRun.characterRun.numberOfCharacters = 0u; - currentFontRun.fontId = fontId; - currentFontRun.softwareItalic = needSoftwareItalic; - currentFontRun.softwareBold = needSoftwareBoldening; + currentFontRun.fontId = fontId; + currentFontRun.isBoldRequired = isBoldRequired; + currentFontRun.isItalicRequired = isItalicRequired; } // 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; + isNewParagraphCharacter = TextAbstraction::IsNewParagraph(character); + previousScript = script; } // end traverse characters. - if( 0u != currentFontRun.characterRun.numberOfCharacters ) + if(0u != currentFontRun.characterRun.numberOfCharacters) { // Store the last run. - fonts.Insert( fonts.Begin() + fontIndex, currentFontRun ); + fonts.Insert(fonts.Begin() + fontIndex, currentFontRun); ++fontIndex; } - if( fontIndex < fonts.Count() ) + if(fontIndex < fonts.Count()) { // Update the indices of the next font runs. - const FontRun& run = *( fonts.Begin() + fontIndex - 1u ); + 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 ) + for(Vector::Iterator it = fonts.Begin() + fontIndex, + endIt = fonts.End(); + it != endIt; + ++it) { FontRun& run = *it; @@ -763,7 +803,35 @@ void MultilanguageSupport::ValidateFonts( const Vector& text, } } - DALI_LOG_INFO( gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n"); +} + +void MultilanguageSupport::AddCurrentScriptAndCreatNewScript(const Script requestedScript, + const bool isRightToLeft, + const bool addScriptCharactersToNewScript, + ScriptRun& currentScriptRun, + Length& numberOfAllScriptCharacters, + Vector& scripts, + ScriptRunIndex& scriptIndex) +{ + // Add the pending characters to the current script + currentScriptRun.characterRun.numberOfCharacters += (addScriptCharactersToNewScript ? 0u : numberOfAllScriptCharacters); + + // In-case the current script is empty then no need to add it for scripts + if(0u != currentScriptRun.characterRun.numberOfCharacters) + { + // Store the script run. + scripts.Insert(scripts.Begin() + scriptIndex, currentScriptRun); + ++scriptIndex; + } + + // Initialize the new one by the requested script + currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters; + currentScriptRun.characterRun.numberOfCharacters = (addScriptCharactersToNewScript ? numberOfAllScriptCharacters : 0u); + currentScriptRun.script = requestedScript; + numberOfAllScriptCharacters = 0u; + // Initialize whether is right to left direction + currentScriptRun.isRightToLeft = isRightToLeft; } } // namespace Internal