2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/multi-language-support-impl.h>
22 #include <dali/devel-api/common/singleton-service.h>
23 #include <dali/devel-api/text-abstraction/font-client.h>
24 #include <dali/integration-api/debug.h>
27 #include <dali-toolkit/internal/text/emoji-helper.h>
28 #include <dali-toolkit/internal/text/multi-language-helper-functions.h>
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_MULTI_LANGUAGE_SUPPORT");
40 const Dali::Toolkit::Text::Character UTF32_A = 0x0041;
47 bool ValidateFontsPerScript::IsValidFont(FontId fontId) const
49 for(Vector<FontId>::ConstIterator it = mValidFonts.Begin(),
50 endIt = mValidFonts.End();
63 FontId DefaultFonts::FindFont(TextAbstraction::FontClient& fontClient,
64 const TextAbstraction::FontDescription& description,
65 PointSize26Dot6 size) const
67 for(std::vector<CacheItem>::const_iterator it = mFonts.begin(),
72 const CacheItem& item = *it;
74 if(((TextAbstraction::FontWeight::NONE == description.weight) || (description.weight == item.description.weight)) &&
75 ((TextAbstraction::FontWidth::NONE == description.width) || (description.width == item.description.width)) &&
76 ((TextAbstraction::FontSlant::NONE == description.slant) || (description.slant == item.description.slant)) &&
77 (size == fontClient.GetPointSize(item.fontId)) &&
78 (description.family.empty() || (description.family == item.description.family)))
87 void DefaultFonts::Cache(const TextAbstraction::FontDescription& description, FontId fontId)
90 item.description = description;
92 mFonts.push_back(item);
95 MultilanguageSupport::MultilanguageSupport()
96 : mDefaultFontPerScriptCache(),
97 mValidFontsPerScriptCache()
99 // Initializes the default font cache to zero (invalid font).
100 // Reserves space to cache the default fonts and access them with the script as an index.
101 mDefaultFontPerScriptCache.Resize(TextAbstraction::GetNumberOfScripts(), NULL);
103 // Initializes the valid fonts cache to NULL (no valid fonts).
104 // Reserves space to cache the valid fonts and access them with the script as an index.
105 mValidFontsPerScriptCache.Resize(TextAbstraction::GetNumberOfScripts(), NULL);
108 MultilanguageSupport::~MultilanguageSupport()
110 // Destroy the default font per script cache.
111 for(Vector<DefaultFonts*>::Iterator it = mDefaultFontPerScriptCache.Begin(),
112 endIt = mDefaultFontPerScriptCache.End();
119 // Destroy the valid fonts per script cache.
120 for(Vector<ValidateFontsPerScript*>::Iterator it = mValidFontsPerScriptCache.Begin(),
121 endIt = mValidFontsPerScriptCache.End();
129 Text::MultilanguageSupport MultilanguageSupport::Get()
131 Text::MultilanguageSupport multilanguageSupportHandle;
133 SingletonService service(SingletonService::Get());
136 // Check whether the singleton is already created
137 Dali::BaseHandle handle = service.GetSingleton(typeid(Text::MultilanguageSupport));
140 // If so, downcast the handle
141 MultilanguageSupport* impl = dynamic_cast<Internal::MultilanguageSupport*>(handle.GetObjectPtr());
142 multilanguageSupportHandle = Text::MultilanguageSupport(impl);
144 else // create and register the object
146 multilanguageSupportHandle = Text::MultilanguageSupport(new MultilanguageSupport);
147 service.Register(typeid(multilanguageSupportHandle), multilanguageSupportHandle);
151 return multilanguageSupportHandle;
154 void MultilanguageSupport::SetScripts(const Vector<Character>& text,
155 CharacterIndex startIndex,
156 Length numberOfCharacters,
157 Vector<ScriptRun>& scripts)
159 if(0u == numberOfCharacters)
161 // Nothing to do if there are no characters.
165 // Find the first index where to insert the script.
166 ScriptRunIndex scriptIndex = 0u;
169 for(Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
170 endIt = scripts.End();
174 const ScriptRun& run = *it;
175 if(startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters)
183 // Stores the current script run.
184 ScriptRun currentScriptRun;
185 currentScriptRun.characterRun.characterIndex = startIndex;
186 currentScriptRun.characterRun.numberOfCharacters = 0u;
187 currentScriptRun.script = TextAbstraction::UNKNOWN;
189 // Reserve some space to reduce the number of reallocations.
190 scripts.Reserve(text.Count() << 2u);
192 // Whether the first valid script needs to be set.
193 bool isFirstScriptToBeSet = true;
195 // Whether the first valid script is a right to left script.
196 bool isParagraphRTL = false;
198 // Count the number of characters which are valid for all scripts. i.e. white spaces or '\n'.
199 Length numberOfAllScriptCharacters = 0u;
201 // Pointers to the text buffer.
202 const Character* const textBuffer = text.Begin();
204 // Initialize whether is right to left direction
205 currentScriptRun.isRightToLeft = false;
207 // Traverse all characters and set the scripts.
208 const Length lastCharacter = startIndex + numberOfCharacters - 1u;
210 for(Length index = startIndex; index <= lastCharacter; ++index)
212 Character character = *(textBuffer + index);
214 // Get the script of the character.
215 Script script = TextAbstraction::GetCharacterScript(character);
217 // Some characters (like white spaces) are valid for many scripts. The rules to set a script
219 // - If they are at the begining of a paragraph they get the script of the first character with
220 // a defined script. If they are at the end, they get the script of the last one.
221 // - If they are between two scripts with the same direction, they get the script of the previous
222 // character with a defined script. If the two scripts have different directions, they get the
223 // script of the first character of the paragraph with a defined script.
225 // Skip those characters valid for many scripts like white spaces or '\n'.
226 bool endOfText = index > lastCharacter;
228 //Handle all Emoji Sequence cases
229 if(IsNewSequence(textBuffer, currentScriptRun.script, index, lastCharacter, script))
231 AddCurrentScriptAndCreatNewScript(script,
235 numberOfAllScriptCharacters,
239 else if(IsScriptChangedToFollowSequence(currentScriptRun.script, character, script))
241 currentScriptRun.script = script;
243 else if(IsOneOfEmojiScripts(currentScriptRun.script) && (TextAbstraction::COMMON == script))
245 // Emojis doesn't mix well with characters common to all scripts. Insert the emoji run.
246 AddCurrentScriptAndCreatNewScript(TextAbstraction::UNKNOWN,
250 numberOfAllScriptCharacters,
256 (TextAbstraction::COMMON == script))
258 // Check if whether is right to left markup and Keeps true if the previous value was true.
259 currentScriptRun.isRightToLeft = currentScriptRun.isRightToLeft || TextAbstraction::IsRightToLeftMark(character);
261 // Count all these characters to be added into a script.
262 ++numberOfAllScriptCharacters;
264 if(TextAbstraction::IsNewParagraph(character))
266 // The character is a new paragraph.
267 // To know when there is a new paragraph is needed because if there is a white space
268 // between two scripts with different directions, it is added to the script with
269 // the same direction than the first script of the paragraph.
270 isFirstScriptToBeSet = true;
272 AddCurrentScriptAndCreatNewScript(TextAbstraction::UNKNOWN,
276 numberOfAllScriptCharacters,
281 // Get the next character.
283 endOfText = index > lastCharacter;
286 character = *(textBuffer + index);
287 script = TextAbstraction::GetCharacterScript(character);
289 //Handle all Emoji Sequence cases
290 if(IsNewSequence(textBuffer, currentScriptRun.script, index, lastCharacter, script))
292 AddCurrentScriptAndCreatNewScript(script,
296 numberOfAllScriptCharacters,
300 else if(IsScriptChangedToFollowSequence(currentScriptRun.script, character, script))
302 currentScriptRun.script = script;
305 } // end while( !endOfText && ( TextAbstraction::COMMON == script ) )
309 // Last characters of the text are 'white spaces'.
310 // There is nothing else to do. Just add the remaining characters to the last script after this bucle.
314 // Check if it is the first character of a paragraph.
315 if(isFirstScriptToBeSet &&
316 (TextAbstraction::UNKNOWN != script) &&
317 (TextAbstraction::COMMON != script) &&
318 (TextAbstraction::EMOJI != script) &&
319 (TextAbstraction::EMOJI_TEXT != script) &&
320 (TextAbstraction::EMOJI_COLOR != script) &&
321 (!TextAbstraction::IsSymbolScript(script)))
323 // Sets the direction of the first valid script.
324 isParagraphRTL = currentScriptRun.isRightToLeft || TextAbstraction::IsRightToLeftScript(script);
325 isFirstScriptToBeSet = false;
328 if((script != currentScriptRun.script) &&
329 (TextAbstraction::COMMON != script))
331 // Current run needs to be stored and a new one initialized.
333 if((isParagraphRTL == TextAbstraction::IsRightToLeftScript(currentScriptRun.script)) &&
334 (TextAbstraction::UNKNOWN != currentScriptRun.script))
336 // Previous script has the same direction than the first script of the paragraph.
337 // All the previously skipped characters need to be added to the previous script before it's stored.
338 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
339 numberOfAllScriptCharacters = 0u;
341 else if((TextAbstraction::IsRightToLeftScript(currentScriptRun.script) == TextAbstraction::IsRightToLeftScript(script)) &&
342 (TextAbstraction::UNKNOWN != currentScriptRun.script))
344 // Current script and previous one have the same direction.
345 // All the previously skipped characters need to be added to the previous script before it's stored.
346 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
347 numberOfAllScriptCharacters = 0u;
349 else if((TextAbstraction::UNKNOWN == currentScriptRun.script) &&
350 (TextAbstraction::IsSymbolOrEmojiOrTextScript(script)))
352 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
353 numberOfAllScriptCharacters = 0u;
356 // Adds the white spaces which are at the begining of the script.
357 numberOfAllScriptCharacters++;
358 AddCurrentScriptAndCreatNewScript(script,
359 TextAbstraction::IsRightToLeftScript(script),
362 numberOfAllScriptCharacters,
368 if(TextAbstraction::UNKNOWN != currentScriptRun.script)
370 // Adds white spaces between characters.
371 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
372 numberOfAllScriptCharacters = 0u;
375 // Add one more character to the run.
376 ++currentScriptRun.characterRun.numberOfCharacters;
380 // Add remaining characters into the last script.
381 currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
383 if(0u != currentScriptRun.characterRun.numberOfCharacters)
385 // Store the last run.
386 scripts.Insert(scripts.Begin() + scriptIndex, currentScriptRun);
390 if(scriptIndex < scripts.Count())
392 // Update the indices of the next script runs.
393 const ScriptRun& run = *(scripts.Begin() + scriptIndex - 1u);
394 CharacterIndex nextCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
396 for(Vector<ScriptRun>::Iterator it = scripts.Begin() + scriptIndex,
397 endIt = scripts.End();
401 ScriptRun& run = *it;
402 run.characterRun.characterIndex = nextCharacterIndex;
403 nextCharacterIndex += run.characterRun.numberOfCharacters;
408 void MultilanguageSupport::ValidateFonts(const Vector<Character>& text,
409 const Vector<ScriptRun>& scripts,
410 const Vector<FontDescriptionRun>& fontDescriptions,
411 const TextAbstraction::FontDescription& defaultFontDescription,
412 TextAbstraction::PointSize26Dot6 defaultFontPointSize,
413 CharacterIndex startIndex,
414 Length numberOfCharacters,
415 Vector<FontRun>& fonts)
417 DALI_LOG_INFO(gLogFilter, Debug::General, "-->MultilanguageSupport::ValidateFonts\n");
419 if(0u == numberOfCharacters)
421 DALI_LOG_INFO(gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n");
422 // Nothing to do if there are no characters.
426 // Find the first index where to insert the font run.
427 FontRunIndex fontIndex = 0u;
430 for(Vector<FontRun>::ConstIterator it = fonts.Begin(),
435 const FontRun& run = *it;
436 if(startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters)
444 // Traverse the characters and validate/set the fonts.
447 DefaultFonts** defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin();
448 ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin();
450 // Stores the validated font runs.
451 fonts.Reserve(fontDescriptions.Count());
453 // Initializes a validated font run.
454 FontRun currentFontRun;
455 currentFontRun.characterRun.characterIndex = startIndex;
456 currentFontRun.characterRun.numberOfCharacters = 0u;
457 currentFontRun.fontId = 0u;
458 currentFontRun.isBoldRequired = false;
459 currentFontRun.isItalicRequired = false;
461 // Get the font client.
462 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
464 const Character* const textBuffer = text.Begin();
466 // Iterators of the script runs.
467 Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
468 Vector<ScriptRun>::ConstIterator scriptRunEndIt = scripts.End();
469 bool isNewParagraphCharacter = false;
471 FontId previousEmojiFontId = 0u;
472 TextAbstraction::Script previousScript = TextAbstraction::UNKNOWN;
474 CharacterIndex lastCharacter = startIndex + numberOfCharacters - 1u;
475 for(Length index = startIndex; index <= lastCharacter; ++index)
477 // Get the current character.
478 const Character character = *(textBuffer + index);
479 bool isItalicRequired = false;
480 bool isBoldRequired = false;
482 // new description for current character
483 TextAbstraction::FontDescription currentFontDescription;
484 TextAbstraction::PointSize26Dot6 currentFontPointSize = defaultFontPointSize;
485 bool isDefaultFont = true;
486 MergeFontDescriptions(fontDescriptions,
487 defaultFontDescription,
488 defaultFontPointSize,
490 currentFontDescription,
491 currentFontPointSize,
494 // Get the font for the current character.
495 FontId fontId = fontClient.GetFontId(currentFontDescription, currentFontPointSize);
497 // Get the script for the current character.
498 Script script = GetScript(index,
503 if(gLogFilter->IsEnabledFor(Debug::Verbose))
505 Dali::TextAbstraction::FontDescription description;
506 fontClient.GetDescription(fontId, description);
508 DALI_LOG_INFO(gLogFilter,
510 " Initial font set\n Character : %x, Script : %s, Font : %s \n",
512 Dali::TextAbstraction::ScriptName[script],
513 description.path.c_str());
517 // Validate whether the current character is supported by the given font.
518 bool isValidFont = false;
520 // Check first in the cache of default fonts per script and size.
522 FontId cachedDefaultFontId = 0u;
523 DefaultFonts* defaultFonts = *(defaultFontPerScriptCacheBuffer + script);
524 if(NULL != defaultFonts)
526 // This cache stores fall-back fonts.
527 cachedDefaultFontId = defaultFonts->FindFont(fontClient,
528 currentFontDescription,
529 currentFontPointSize);
532 // Whether the cached default font is valid.
533 const bool isValidCachedDefaultFont = 0u != cachedDefaultFontId;
535 // The font is valid if it matches with the default one for the current script and size and it's different than zero.
536 isValidFont = isValidCachedDefaultFont && (fontId == cachedDefaultFontId);
540 // Check if the font supports the character.
541 isValidFont = fontClient.IsCharacterSupportedByFont(fontId, character);
544 bool isCommonScript = false;
545 bool isEmojiScript = TextAbstraction::IsEmojiScript(script) || TextAbstraction::IsEmojiColorScript(script) || TextAbstraction::IsEmojiTextScript(script);
547 if(isEmojiScript && (previousScript == script))
549 // Emoji sequence should use the previous emoji font.
550 if(0u != previousEmojiFontId)
552 fontId = previousEmojiFontId;
557 // If the given font is not valid, it means either:
558 // - there is no cached font for the current script yet or,
559 // - the user has set a different font than the default one for the current script or,
560 // - the platform default font is different than the default font for the current script.
562 // Need to check if the given font supports the current character.
563 if(!isValidFont) // (1)
565 // Whether the current character is common for all scripts (i.e. white spaces, ...)
567 // Is not desirable to cache fonts for the common script.
569 // i.e. Consider the text " हिंदी", the 'white space' has assigned the DEVANAGARI script.
570 // The user may have set a font or the platform's default is used.
572 // As the 'white space' is the first character, no font is cached so the font validation
573 // retrieves a glyph from the given font.
575 // Many fonts support 'white spaces' so probably the font set by the user or the platform's default
576 // supports the 'white space'. However, that font may not support the DEVANAGARI script.
577 isCommonScript = TextAbstraction::IsCommonScript(character) || TextAbstraction::IsEmojiPresentationSelector(character);
579 // Check in the valid fonts cache.
580 ValidateFontsPerScript* validateFontsPerScript = *(validFontsPerScriptCacheBuffer + script);
582 if(NULL != validateFontsPerScript)
584 // This cache stores valid fonts set by the user.
585 isValidFont = validateFontsPerScript->IsValidFont(fontId);
587 // It may happen that a validated font for a script doesn't have all the glyphs for that script.
588 // i.e a font validated for the CJK script may contain glyphs for the chinese language but not for the Japanese.
591 // Checks if the current character is supported by the font is needed.
592 isValidFont = fontClient.IsCharacterSupportedByFont(fontId, character);
596 if(!isValidFont) // (2)
598 // The selected font is not stored in any cache.
600 // Checks if the current character is supported by the selected font.
601 isValidFont = fontClient.IsCharacterSupportedByFont(fontId, character);
603 // If there is a valid font, cache it.
604 if(isValidFont && !isCommonScript)
606 if(NULL == validateFontsPerScript)
608 validateFontsPerScript = new ValidateFontsPerScript();
610 *(validFontsPerScriptCacheBuffer + script) = validateFontsPerScript;
613 validateFontsPerScript->mValidFonts.PushBack(fontId);
616 if(!isValidFont && (fontId != cachedDefaultFontId) && (!TextAbstraction::IsNewParagraph(character))) // (3)
618 // The selected font by the user or the platform's default font has failed to validate the character.
620 // Checks if the previously discarted cached default font supports the character.
621 bool isValidCachedFont = false;
622 if(isValidCachedDefaultFont)
624 isValidCachedFont = fontClient.IsCharacterSupportedByFont(cachedDefaultFontId, character);
627 if(isValidCachedFont)
629 // Use the cached default font for the script if there is one.
630 fontId = cachedDefaultFontId;
634 // There is no valid cached default font for the script.
636 DefaultFonts* defaultFontsPerScript = NULL;
638 // Find a fallback-font.
639 fontId = fontClient.FindFallbackFont(character,
640 currentFontDescription,
641 currentFontPointSize,
646 fontId = fontClient.FindDefaultFont(UTF32_A, currentFontPointSize);
649 if(!isCommonScript && (script != TextAbstraction::UNKNOWN))
651 // Cache the font if it is not an unknown script
652 if(NULL == defaultFontsPerScript)
654 defaultFontsPerScript = *(defaultFontPerScriptCacheBuffer + script);
656 if(NULL == defaultFontsPerScript)
658 defaultFontsPerScript = new DefaultFonts();
659 *(defaultFontPerScriptCacheBuffer + script) = defaultFontsPerScript;
662 defaultFontsPerScript->Cache(currentFontDescription, fontId);
665 } // !isValidFont (3)
666 } // !isValidFont (2)
667 } // !isValidFont (1)
669 if(isEmojiScript && (previousScript != script))
671 //New Emoji sequence should select font according to the variation selector (VS15 or VS16).
672 if(0u != currentFontRun.characterRun.numberOfCharacters)
674 // Store the font run.
675 fonts.Insert(fonts.Begin() + fontIndex, currentFontRun);
679 // Initialize the new one.
680 currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
681 currentFontRun.characterRun.numberOfCharacters = 0u;
682 currentFontRun.fontId = fontId;
683 currentFontRun.isItalicRequired = false;
684 currentFontRun.isBoldRequired = false;
686 if(TextAbstraction::IsEmojiColorScript(script) || TextAbstraction::IsEmojiTextScript(script))
688 bool isModifiedByVariationSelector = false;
689 GlyphIndex glyphIndexChar = fontClient.GetGlyphIndex(fontId, character);
690 GlyphIndex glyphIndexCharByVS = fontClient.GetGlyphIndex(fontId, character, Text::GetVariationSelectorByScript(script));
692 isModifiedByVariationSelector = glyphIndexChar != glyphIndexCharByVS;
694 if(isModifiedByVariationSelector)
696 FontId requestedFontId = fontClient.FindDefaultFont(character, currentFontPointSize, IsEmojiColorScript(script));
697 if(0u != requestedFontId)
699 currentFontRun.fontId = fontId = requestedFontId;
706 // Store the font id when the first character is an emoji.
709 if(0u != fontId && previousScript != script)
711 previousEmojiFontId = fontId;
716 previousEmojiFontId = 0u;
720 if(gLogFilter->IsEnabledFor(Debug::Verbose))
722 Dali::TextAbstraction::FontDescription description;
723 fontClient.GetDescription(fontId, description);
724 DALI_LOG_INFO(gLogFilter,
726 " Validated font set\n Character : %x, Script : %s, Font : %s \n",
728 Dali::TextAbstraction::ScriptName[script],
729 description.path.c_str());
733 // Whether bols style is required.
734 isBoldRequired = (currentFontDescription.weight >= TextAbstraction::FontWeight::BOLD);
736 // Whether italic style is required.
737 isItalicRequired = (currentFontDescription.slant >= TextAbstraction::FontSlant::ITALIC);
739 // The font is now validated.
740 if((fontId != currentFontRun.fontId) ||
741 isNewParagraphCharacter ||
742 // If font id is same as previous but style is diffrent, initialize new one
743 ((fontId == currentFontRun.fontId) && ((isBoldRequired != currentFontRun.isBoldRequired) || (isItalicRequired != currentFontRun.isItalicRequired))))
745 // Current run needs to be stored and a new one initialized.
747 if(0u != currentFontRun.characterRun.numberOfCharacters)
749 // Store the font run.
750 fonts.Insert(fonts.Begin() + fontIndex, currentFontRun);
754 // Initialize the new one.
755 currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
756 currentFontRun.characterRun.numberOfCharacters = 0u;
757 currentFontRun.fontId = fontId;
758 currentFontRun.isBoldRequired = isBoldRequired;
759 currentFontRun.isItalicRequired = isItalicRequired;
762 // Add one more character to the run.
763 ++currentFontRun.characterRun.numberOfCharacters;
765 // Whether the current character is a new paragraph character.
766 isNewParagraphCharacter = TextAbstraction::IsNewParagraph(character);
767 previousScript = script;
768 } // end traverse characters.
770 if(0u != currentFontRun.characterRun.numberOfCharacters)
772 // Store the last run.
773 fonts.Insert(fonts.Begin() + fontIndex, currentFontRun);
777 if(fontIndex < fonts.Count())
779 // Update the indices of the next font runs.
780 const FontRun& run = *(fonts.Begin() + fontIndex - 1u);
781 CharacterIndex nextCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
783 for(Vector<FontRun>::Iterator it = fonts.Begin() + fontIndex,
790 run.characterRun.characterIndex = nextCharacterIndex;
791 nextCharacterIndex += run.characterRun.numberOfCharacters;
795 DALI_LOG_INFO(gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n");
798 void MultilanguageSupport::AddCurrentScriptAndCreatNewScript(const Script requestedScript,
799 const bool isRightToLeft,
800 const bool addScriptCharactersToNewScript,
801 ScriptRun& currentScriptRun,
802 Length& numberOfAllScriptCharacters,
803 Vector<ScriptRun>& scripts,
804 ScriptRunIndex& scriptIndex)
806 // Add the pending characters to the current script
807 currentScriptRun.characterRun.numberOfCharacters += (addScriptCharactersToNewScript ? 0u : numberOfAllScriptCharacters);
809 // In-case the current script is empty then no need to add it for scripts
810 if(0u != currentScriptRun.characterRun.numberOfCharacters)
812 // Store the script run.
813 scripts.Insert(scripts.Begin() + scriptIndex, currentScriptRun);
817 // Initialize the new one by the requested script
818 currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
819 currentScriptRun.characterRun.numberOfCharacters = (addScriptCharactersToNewScript ? numberOfAllScriptCharacters : 0u);
820 currentScriptRun.script = requestedScript;
821 numberOfAllScriptCharacters = 0u;
822 // Initialize whether is right to left direction
823 currentScriptRun.isRightToLeft = isRightToLeft;
826 } // namespace Internal
830 } // namespace Toolkit