[dali_2.3.22] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / multi-language-support-impl.cpp
index a0e6ae5..41ba5ac 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
 // EXTERNAL INCLUDES
 #include <dali/devel-api/common/singleton-service.h>
 #include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/emoji-helper.h>
 #include <dali-toolkit/internal/text/multi-language-helper-functions.h>
 
 namespace Dali
@@ -36,13 +39,150 @@ namespace
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_MULTI_LANGUAGE_SUPPORT");
 #endif
 
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_FONT_PERFORMANCE_MARKER, false);
+
 const Dali::Toolkit::Text::Character UTF32_A = 0x0041;
+
+// TODO : Customization required for these values.
+constexpr std::size_t MAX_VALIDATE_FONTS_PER_SCRIPT_CACHE_SIZE = 63;
+constexpr std::size_t MAX_DEFAULT_FONTS_CACHE_SIZE             = 15;
+
+constexpr int VALIDATE_FONTS_PER_SCRIPT_REMAIN_COUNT = 8;
+constexpr int DEFAULT_FONTS_REMAIN_COUNT             = 2;
 } // namespace
 
 namespace Text
 {
 namespace Internal
 {
+
+namespace
+{
+void CheckFontSupportsCharacter(
+  bool& isValidFont,
+  bool& isCommonScript,
+  const Character& character,
+  ValidateFontsPerScript**& validFontsPerScriptCacheBuffer,
+  const Script& script,
+  FontId& fontId,
+  TextAbstraction::FontClient& fontClient,
+  const bool isValidCachedDefaultFont,
+  const FontId& cachedDefaultFontId,
+  const TextAbstraction::FontDescription& currentFontDescription,
+  const TextAbstraction::PointSize26Dot6& currentFontPointSize,
+  DefaultFonts**& defaultFontPerScriptCacheBuffer)
+{
+  // Need to check if the given font supports the current character.
+  if(!isValidFont) // (1)
+  {
+    // Whether the current character is common for all scripts (i.e. white spaces, ...)
+
+    // Is not desirable to cache fonts for the common script.
+    //
+    // i.e. Consider the text " हिंदी", the 'white space' has assigned the DEVANAGARI script.
+    //      The user may have set a font or the platform's default is used.
+    //
+    //      As the 'white space' is the first character, no font is cached so the font validation
+    //      retrieves a glyph from the given font.
+    //
+    //      Many fonts support 'white spaces' so probably the font set by the user or the platform's default
+    //      supports the 'white space'. However, that font may not support the DEVANAGARI script.
+    isCommonScript = TextAbstraction::IsCommonScript(character) || TextAbstraction::IsEmojiPresentationSelector(character);
+
+    // Check in the valid fonts cache.
+    ValidateFontsPerScript* validateFontsPerScript = *(validFontsPerScriptCacheBuffer + script);
+
+    if(NULL != validateFontsPerScript)
+    {
+      // 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)
+      {
+        // Checks if the current character is supported by the font is needed.
+        isValidFont = fontClient.IsCharacterSupportedByFont(fontId, character);
+      }
+    }
+
+    if(!isValidFont) // (2)
+    {
+      // 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);
+
+      // If there is a valid font, cache it.
+      if(isValidFont && !isCommonScript)
+      {
+        if(NULL == validateFontsPerScript)
+        {
+          validateFontsPerScript = new ValidateFontsPerScript();
+
+          *(validFontsPerScriptCacheBuffer + script) = validateFontsPerScript;
+        }
+
+        validateFontsPerScript->Cache(fontId);
+      }
+
+      if(!isValidFont && (fontId != cachedDefaultFontId) && (!TextAbstraction::IsNewParagraph(character))) // (3)
+      {
+        // The selected font by the user or the platform's default font has failed to validate the character.
+
+        // Checks if the previously discarted cached default font supports the character.
+        bool isValidCachedFont = false;
+        if(isValidCachedDefaultFont)
+        {
+          isValidCachedFont = fontClient.IsCharacterSupportedByFont(cachedDefaultFontId, character);
+        }
+
+        if(isValidCachedFont)
+        {
+          // Use the cached default font for the script if there is one.
+          fontId      = cachedDefaultFontId;
+          isValidFont = true;
+        }
+        else
+        {
+          // There is no valid cached default font for the script.
+
+          DefaultFonts* defaultFontsPerScript = NULL;
+
+          // Find a fallback-font.
+          fontId = fontClient.FindFallbackFont(character,
+                                               currentFontDescription,
+                                               currentFontPointSize,
+                                               false);
+
+          if(0u == fontId)
+          {
+            fontId = fontClient.FindDefaultFont(UTF32_A, currentFontPointSize);
+          }
+
+          if(!isCommonScript && (script != TextAbstraction::UNKNOWN))
+          {
+            // Cache the font if it is not an unknown script
+            if(NULL == defaultFontsPerScript)
+            {
+              defaultFontsPerScript = *(defaultFontPerScriptCacheBuffer + script);
+
+              if(NULL == defaultFontsPerScript)
+              {
+                defaultFontsPerScript                       = new DefaultFonts();
+                *(defaultFontPerScriptCacheBuffer + script) = defaultFontsPerScript;
+              }
+            }
+            defaultFontsPerScript->Cache(currentFontDescription, fontId);
+            isValidFont = true;
+          }
+        }
+      } // !isValidFont (3)
+    }   // !isValidFont (2)
+  }     // !isValidFont (1)
+}
+} // unnamed namespace
+
 bool ValidateFontsPerScript::IsValidFont(FontId fontId) const
 {
   for(Vector<FontId>::ConstIterator it    = mValidFonts.Begin(),
@@ -58,6 +198,20 @@ bool ValidateFontsPerScript::IsValidFont(FontId fontId) const
 
   return false;
 }
+void ValidateFontsPerScript::Cache(FontId fontId)
+{
+  mValidFonts.PushBack(fontId);
+  if(MAX_VALIDATE_FONTS_PER_SCRIPT_CACHE_SIZE < mValidFonts.Count())
+  {
+    // Clear cache but remaind some last items.
+    const auto offset = mValidFonts.Count() - VALIDATE_FONTS_PER_SCRIPT_REMAIN_COUNT;
+    for(int i = 0; i < VALIDATE_FONTS_PER_SCRIPT_REMAIN_COUNT; ++i)
+    {
+      mValidFonts[i] = std::move(mValidFonts[offset + i]);
+    }
+    mValidFonts.Resize(VALIDATE_FONTS_PER_SCRIPT_REMAIN_COUNT);
+  }
+}
 
 FontId DefaultFonts::FindFont(TextAbstraction::FontClient&            fontClient,
                               const TextAbstraction::FontDescription& description,
@@ -89,19 +243,35 @@ void DefaultFonts::Cache(const TextAbstraction::FontDescription& description, Fo
   item.description = description;
   item.fontId      = fontId;
   mFonts.push_back(item);
+  if(MAX_DEFAULT_FONTS_CACHE_SIZE < mFonts.size())
+  {
+    // Clear cache but remaind some last items.
+    const auto offset = mFonts.size() - DEFAULT_FONTS_REMAIN_COUNT;
+    for(int i = 0; i < DEFAULT_FONTS_REMAIN_COUNT; ++i)
+    {
+      mFonts[i] = std::move(mFonts[offset + i]);
+    }
+    mFonts.resize(DEFAULT_FONTS_REMAIN_COUNT);
+  }
 }
 
 MultilanguageSupport::MultilanguageSupport()
 : mDefaultFontPerScriptCache(),
-  mValidFontsPerScriptCache()
+  mValidFontsPerScriptCache(),
+  mLocale(std::string())
 {
   // 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);
+
+  if(Dali::Adaptor::IsAvailable())
+  {
+    Dali::Adaptor::Get().LocaleChangedSignal().Connect(this, &MultilanguageSupport::OnLocaleChanged);
+  }
 }
 
 MultilanguageSupport::~MultilanguageSupport()
@@ -125,6 +295,29 @@ MultilanguageSupport::~MultilanguageSupport()
   }
 }
 
+void MultilanguageSupport::OnLocaleChanged(std::string locale)
+{
+  if(mLocale != locale)
+  {
+    mLocale = locale;
+    ClearCache();
+  }
+}
+
+void MultilanguageSupport::ClearCache()
+{
+  mDefaultFontPerScriptCache.Clear();
+  mValidFontsPerScriptCache.Clear();
+
+  mDefaultFontPerScriptCache.Resize(TextAbstraction::GetNumberOfScripts(), NULL);
+  mValidFontsPerScriptCache.Resize(TextAbstraction::GetNumberOfScripts(), NULL);
+}
+
+std::string MultilanguageSupport::GetLocale()
+{
+  return mLocale;
+}
+
 Text::MultilanguageSupport MultilanguageSupport::Get()
 {
   Text::MultilanguageSupport multilanguageSupportHandle;
@@ -204,8 +397,9 @@ void MultilanguageSupport::SetScripts(const Vector<Character>& text,
   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);
 
@@ -221,26 +415,41 @@ void MultilanguageSupport::SetScripts(const Vector<Character>& 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;
+    bool endOfText = index > lastCharacter;
+
+    //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;
+    }
+    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);
 
-      if(TextAbstraction::EMOJI == currentScriptRun.script)
-      {
-        // Emojis doesn't mix well with characters common to all scripts. Insert the emoji run.
-        scripts.Insert(scripts.Begin() + scriptIndex, currentScriptRun);
-        ++scriptIndex;
-
-        // Initialize the new one.
-        currentScriptRun.characterRun.characterIndex     = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
-        currentScriptRun.characterRun.numberOfCharacters = 0u;
-        currentScriptRun.script                          = TextAbstraction::UNKNOWN;
-        numberOfAllScriptCharacters                      = 0u;
-      }
-
       // Count all these characters to be added into a script.
       ++numberOfAllScriptCharacters;
 
@@ -252,29 +461,38 @@ void MultilanguageSupport::SetScripts(const Vector<Character>& 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;
-        // Initialize whether is right to left direction
-        currentScriptRun.isRightToLeft = false;
+        AddCurrentScriptAndCreatNewScript(TextAbstraction::UNKNOWN,
+                                          false,
+                                          false,
+                                          currentScriptRun,
+                                          numberOfAllScriptCharacters,
+                                          scripts,
+                                          scriptIndex);
       }
 
       // Get the next character.
       ++index;
-      endOfText = index == lastCharacter;
+      endOfText = index > lastCharacter;
       if(!endOfText)
       {
         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 ) )
 
@@ -289,7 +507,10 @@ void MultilanguageSupport::SetScripts(const Vector<Character>& text,
     if(isFirstScriptToBeSet &&
        (TextAbstraction::UNKNOWN != script) &&
        (TextAbstraction::COMMON != script) &&
-       (TextAbstraction::EMOJI != script))
+       (TextAbstraction::EMOJI != script) &&
+       (TextAbstraction::EMOJI_TEXT != script) &&
+       (TextAbstraction::EMOJI_COLOR != script) &&
+       (!TextAbstraction::IsSymbolScript(script)))
     {
       // Sets the direction of the first valid script.
       isParagraphRTL       = currentScriptRun.isRightToLeft || TextAbstraction::IsRightToLeftScript(script);
@@ -318,26 +539,21 @@ void MultilanguageSupport::SetScripts(const Vector<Character>& text,
         numberOfAllScriptCharacters = 0u;
       }
       else if((TextAbstraction::UNKNOWN == currentScriptRun.script) &&
-              (TextAbstraction::EMOJI == 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;
-      // Check if whether is right to left script.
-      currentScriptRun.isRightToLeft = TextAbstraction::IsRightToLeftScript(currentScriptRun.script);
+      // Adds the white spaces which are at the begining of the script.
+      numberOfAllScriptCharacters++;
+      AddCurrentScriptAndCreatNewScript(script,
+                                        TextAbstraction::IsRightToLeftScript(script),
+                                        true,
+                                        currentScriptRun,
+                                        numberOfAllScriptCharacters,
+                                        scripts,
+                                        scriptIndex);
     }
     else
     {
@@ -386,6 +602,7 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
                                          const Vector<FontDescriptionRun>&       fontDescriptions,
                                          const TextAbstraction::FontDescription& defaultFontDescription,
                                          TextAbstraction::PointSize26Dot6        defaultFontPointSize,
+                                         float                                   fontSizeScale,
                                          CharacterIndex                          startIndex,
                                          Length                                  numberOfCharacters,
                                          Vector<FontRun>&                        fonts)
@@ -399,6 +616,8 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
     return;
   }
 
+  DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_FONTS_VALIDATE");
+
   // Find the first index where to insert the font run.
   FontRunIndex fontIndex = 0u;
   if(0u != startIndex)
@@ -444,10 +663,12 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
   Vector<ScriptRun>::ConstIterator scriptRunEndIt          = scripts.End();
   bool                             isNewParagraphCharacter = false;
 
-  bool isPreviousEmojiScript = false;
+  FontId                  currentFontId       = 0u;
+  FontId                  previousFontId      = 0u;
+  TextAbstraction::Script previousScript      = TextAbstraction::UNKNOWN;
 
-  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);
@@ -461,6 +682,7 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
     MergeFontDescriptions(fontDescriptions,
                           defaultFontDescription,
                           defaultFontPointSize,
+                          fontSizeScale,
                           index,
                           currentFontDescription,
                           currentFontPointSize,
@@ -468,6 +690,7 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
 
     // Get the font for the current character.
     FontId fontId = fontClient.GetFontId(currentFontDescription, currentFontPointSize);
+    currentFontId = fontId;
 
     // Get the script for the current character.
     Script script = GetScript(index,
@@ -475,6 +698,7 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
                               scriptRunEndIt);
 
 #ifdef DEBUG_ENABLED
+    if(gLogFilter->IsEnabledFor(Debug::Verbose))
     {
       Dali::TextAbstraction::FontDescription description;
       fontClient.GetDescription(fontId, description);
@@ -515,139 +739,80 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
       isValidFont = fontClient.IsCharacterSupportedByFont(fontId, character);
     }
 
-    bool isCommonScript = false;
-    bool isEmojiScript  = TextAbstraction::EMOJI == script;
+    bool isEmojiScript = IsEmojiColorScript(script) || IsEmojiTextScript(script);
+    bool isZWJ         = TextAbstraction::IsZeroWidthJoiner(character);
 
-    if(isEmojiScript && !isPreviousEmojiScript)
+    if((previousScript == script) &&
+       (isEmojiScript || isZWJ))
     {
-      if(0u != currentFontRun.characterRun.numberOfCharacters)
+      // This sequence should use the previous font.
+      if(0u != previousFontId)
       {
-        // Store the font run.
-        fonts.Insert(fonts.Begin() + fontIndex, currentFontRun);
-        ++fontIndex;
+        fontId      = previousFontId;
+        isValidFont = true;
       }
+    }
 
-      // 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(TextAbstraction::IsSpace(character) &&
+       TextAbstraction::HasLigatureMustBreak(script) &&
+       isValidCachedDefaultFont &&
+       (isDefaultFont || (currentFontId == previousFontId)))
+    {
+      fontId      = cachedDefaultFontId;
+      isValidFont = true;
     }
 
+    // This is valid after CheckFontSupportsCharacter();
+    bool isCommonScript = false;
+
     // If the given font is not valid, it means either:
     // - there is no cached font for the current script yet or,
     // - the user has set a different font than the default one for the current script or,
     // - the platform default font is different than the default font for the current script.
 
     // Need to check if the given font supports the current character.
-    if(!isValidFont) // (1)
+    CheckFontSupportsCharacter(isValidFont, isCommonScript, character, validFontsPerScriptCacheBuffer, script, fontId, fontClient,
+                               isValidCachedDefaultFont, cachedDefaultFontId, currentFontDescription, currentFontPointSize, defaultFontPerScriptCacheBuffer);
+
+    if(isEmojiScript && (previousScript != script))
     {
-      // Whether the current character is common for all scripts (i.e. white spaces, ...)
-
-      // Is not desirable to cache fonts for the common script.
-      //
-      // i.e. Consider the text " हिंदी", the 'white space' has assigned the DEVANAGARI script.
-      //      The user may have set a font or the platform's default is used.
-      //
-      //      As the 'white space' is the first character, no font is cached so the font validation
-      //      retrieves a glyph from the given font.
-      //
-      //      Many fonts support 'white spaces' so probably the font set by the user or the platform's default
-      //      supports the 'white space'. However, that font may not support the DEVANAGARI script.
-      isCommonScript = TextAbstraction::IsCommonScript(character);
-
-      // Check in the valid fonts cache.
-      ValidateFontsPerScript* validateFontsPerScript = *(validFontsPerScriptCacheBuffer + script);
-
-      if(NULL != validateFontsPerScript)
+      //New Emoji sequence should select font according to the variation selector (VS15 or VS16).
+      if(0u != currentFontRun.characterRun.numberOfCharacters)
       {
-        // 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)
-        {
-          // Checks if the current character is supported by the font is needed.
-          isValidFont = fontClient.IsCharacterSupportedByFont(fontId, character);
-        }
+        // Store the font run.
+        fonts.Insert(fonts.Begin() + fontIndex, currentFontRun);
+        ++fontIndex;
       }
 
-      if(!isValidFont) // (2)
+      // 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(TextAbstraction::IsEmojiColorScript(script) || TextAbstraction::IsEmojiTextScript(script))
       {
-        // The selected font is not stored in any cache.
+        bool       isModifiedByVariationSelector = false;
+        GlyphIndex glyphIndexChar                = fontClient.GetGlyphIndex(fontId, character);
+        GlyphIndex glyphIndexCharByVS            = fontClient.GetGlyphIndex(fontId, character, Text::GetVariationSelectorByScript(script));
 
-        // Checks if the current character is supported by the selected font.
-        isValidFont = fontClient.IsCharacterSupportedByFont(fontId, character);
+        isModifiedByVariationSelector = glyphIndexChar != glyphIndexCharByVS;
 
-        // If there is a valid font, cache it.
-        if(isValidFont && !isCommonScript)
+        if(isModifiedByVariationSelector)
         {
-          if(NULL == validateFontsPerScript)
+          FontId requestedFontId = fontClient.FindDefaultFont(character, currentFontPointSize, IsEmojiColorScript(script));
+          if(0u != requestedFontId)
           {
-            validateFontsPerScript = new ValidateFontsPerScript();
-
-            *(validFontsPerScriptCacheBuffer + script) = validateFontsPerScript;
+            currentFontRun.fontId = fontId = requestedFontId;
+            isValidFont                    = true;
           }
-
-          validateFontsPerScript->mValidFonts.PushBack(fontId);
         }
-
-        if(!isValidFont && (fontId != cachedDefaultFontId) && (!TextAbstraction::IsNewParagraph(character))) // (3)
-        {
-          // The selected font by the user or the platform's default font has failed to validate the character.
-
-          // Checks if the previously discarted cached default font supports the character.
-          bool isValidCachedFont = false;
-          if(isValidCachedDefaultFont)
-          {
-            isValidCachedFont = fontClient.IsCharacterSupportedByFont(cachedDefaultFontId, character);
-          }
-
-          if(isValidCachedFont)
-          {
-            // Use the cached default font for the script if there is one.
-            fontId = cachedDefaultFontId;
-          }
-          else
-          {
-            // There is no valid cached default font for the script.
-
-            DefaultFonts* defaultFontsPerScript = NULL;
-
-            // Find a fallback-font.
-            fontId = fontClient.FindFallbackFont(character,
-                                                 currentFontDescription,
-                                                 currentFontPointSize,
-                                                 false);
-
-            if(0u == fontId)
-            {
-              fontId = fontClient.FindDefaultFont(UTF32_A, currentFontPointSize);
-            }
-
-            if(!isCommonScript && (script != TextAbstraction::UNKNOWN))
-            {
-              // Cache the font if it is not an unknown script
-              if(NULL == defaultFontsPerScript)
-              {
-                defaultFontsPerScript = *(defaultFontPerScriptCacheBuffer + script);
-
-                if(NULL == defaultFontsPerScript)
-                {
-                  defaultFontsPerScript                       = new DefaultFonts();
-                  *(defaultFontPerScriptCacheBuffer + script) = defaultFontsPerScript;
-                }
-              }
-              defaultFontsPerScript->Cache(currentFontDescription, fontId);
-            }
-          }
-        } // !isValidFont (3)
-      }   // !isValidFont (2)
-    }     // !isValidFont (1)
+      }
+    }
 
 #ifdef DEBUG_ENABLED
+    if(gLogFilter->IsEnabledFor(Debug::Verbose))
     {
       Dali::TextAbstraction::FontDescription description;
       fontClient.GetDescription(fontId, description);
@@ -659,6 +824,15 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
                     description.path.c_str());
     }
 #endif
+    if(!isValidFont && !isCommonScript)
+    {
+      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());
+    }
 
     // Whether bols style is required.
     isBoldRequired = (currentFontDescription.weight >= TextAbstraction::FontWeight::BOLD);
@@ -694,7 +868,9 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
 
     // Whether the current character is a new paragraph character.
     isNewParagraphCharacter = TextAbstraction::IsNewParagraph(character);
-    isPreviousEmojiScript   = isEmojiScript;
+    previousScript          = script;
+    currentFontId           = fontId;
+    previousFontId          = currentFontId;
   } // end traverse characters.
 
   if(0u != currentFontRun.characterRun.numberOfCharacters)
@@ -725,6 +901,34 @@ void MultilanguageSupport::ValidateFonts(const Vector<Character>&
   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<ScriptRun>& 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
 
 } // namespace Text