750fab78dd24baa0c8b7f0df0f63abd6521f965b
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / multi-language-support-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/multi-language-support-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/devel-api/adaptor-framework/singleton-service.h>
24 #include <dali/devel-api/text-abstraction/font-client.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/multi-language-helper-functions.h>
28
29 namespace Dali
30 {
31
32 namespace Toolkit
33 {
34
35 namespace
36 {
37 #if defined(DEBUG_ENABLED)
38 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_MULTI_LANGUAGE_SUPPORT");
39 #endif
40
41 const Dali::Toolkit::Text::Character UTF32_A = 0x0041;
42 }
43
44 namespace Text
45 {
46
47 namespace Internal
48 {
49
50 bool ValidateFontsPerScript::IsValidFont( FontId fontId ) const
51 {
52   for( Vector<FontId>::ConstIterator it = mValidFonts.Begin(),
53          endIt = mValidFonts.End();
54        it != endIt;
55        ++it )
56   {
57     if( fontId == *it )
58     {
59       return true;
60     }
61   }
62
63   return false;
64 }
65
66 FontId DefaultFonts::FindFont( TextAbstraction::FontClient& fontClient,
67                                const TextAbstraction::FontDescription& description,
68                                PointSize26Dot6 size ) const
69 {
70   for( Vector<FontId>::ConstIterator it = mFonts.Begin(),
71          endIt = mFonts.End();
72        it != endIt;
73        ++it )
74   {
75     const FontId fontId = *it;
76     TextAbstraction::FontDescription fontDescription;
77     fontClient.GetDescription( fontId, fontDescription );
78
79     if( ( size == fontClient.GetPointSize( fontId ) ) &&
80         ( description.weight == fontDescription.weight ) &&
81         ( description.width == fontDescription.width ) &&
82         ( description.slant == fontDescription.slant ) )
83     {
84       return fontId;
85     }
86   }
87
88   return 0u;
89 }
90
91 MultilanguageSupport::MultilanguageSupport()
92 : mDefaultFontPerScriptCache(),
93   mValidFontsPerScriptCache()
94 {
95   // Initializes the default font cache to zero (invalid font).
96   // Reserves space to cache the default fonts and access them with the script as an index.
97   mDefaultFontPerScriptCache.Resize( TextAbstraction::UNKNOWN, NULL );
98
99   // Initializes the valid fonts cache to NULL (no valid fonts).
100   // Reserves space to cache the valid fonts and access them with the script as an index.
101   mValidFontsPerScriptCache.Resize( TextAbstraction::UNKNOWN, NULL );
102 }
103
104 MultilanguageSupport::~MultilanguageSupport()
105 {
106   // Destroy the default font per script cache.
107   for( Vector<DefaultFonts*>::Iterator it = mDefaultFontPerScriptCache.Begin(),
108          endIt = mDefaultFontPerScriptCache.End();
109        it != endIt;
110        ++it )
111   {
112     delete *it;
113   }
114
115   // Destroy the valid fonts per script cache.
116   for( Vector<ValidateFontsPerScript*>::Iterator it = mValidFontsPerScriptCache.Begin(),
117          endIt = mValidFontsPerScriptCache.End();
118        it != endIt;
119        ++it )
120   {
121     delete *it;
122   }
123 }
124
125 Text::MultilanguageSupport MultilanguageSupport::Get()
126 {
127   Text::MultilanguageSupport multilanguageSupportHandle;
128
129   SingletonService service( SingletonService::Get() );
130   if( service )
131   {
132     // Check whether the singleton is already created
133     Dali::BaseHandle handle = service.GetSingleton( typeid( Text::MultilanguageSupport ) );
134     if( handle )
135     {
136       // If so, downcast the handle
137       MultilanguageSupport* impl = dynamic_cast< Internal::MultilanguageSupport* >( handle.GetObjectPtr() );
138       multilanguageSupportHandle = Text::MultilanguageSupport( impl );
139     }
140     else // create and register the object
141     {
142       multilanguageSupportHandle = Text::MultilanguageSupport( new MultilanguageSupport );
143       service.Register( typeid( multilanguageSupportHandle ), multilanguageSupportHandle );
144     }
145   }
146
147   return multilanguageSupportHandle;
148 }
149
150 void MultilanguageSupport::SetScripts( const Vector<Character>& text,
151                                        CharacterIndex startIndex,
152                                        Length numberOfCharacters,
153                                        Vector<ScriptRun>& scripts )
154 {
155   if( 0u == numberOfCharacters )
156   {
157     // Nothing to do if there are no characters.
158     return;
159   }
160
161   // Find the first index where to insert the script.
162   ScriptRunIndex scriptIndex = 0u;
163   if( 0u != startIndex )
164   {
165     for( Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
166            endIt = scripts.End();
167          it != endIt;
168          ++it, ++scriptIndex )
169     {
170       const ScriptRun& run = *it;
171       if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
172       {
173         // Run found.
174         break;
175       }
176     }
177   }
178
179   // Stores the current script run.
180   ScriptRun currentScriptRun;
181   currentScriptRun.characterRun.characterIndex = startIndex;
182   currentScriptRun.characterRun.numberOfCharacters = 0u;
183   currentScriptRun.script = TextAbstraction::UNKNOWN;
184
185   // Reserve some space to reduce the number of reallocations.
186   scripts.Reserve( text.Count() << 2u );
187
188   // Whether the first valid script needs to be set.
189   bool isFirstScriptToBeSet = true;
190
191   // Whether the first valid script is a right to left script.
192   bool isParagraphRTL = false;
193
194   // Count the number of characters which are valid for all scripts. i.e. white spaces or '\n'.
195   Length numberOfAllScriptCharacters = 0u;
196
197   // Pointers to the text buffer.
198   const Character* const textBuffer = text.Begin();
199
200   // Traverse all characters and set the scripts.
201   const Length lastCharacter = startIndex + numberOfCharacters;
202   for( Length index = startIndex; index < lastCharacter; ++index )
203   {
204     Character character = *( textBuffer + index );
205
206     // Get the script of the character.
207     Script script = TextAbstraction::GetCharacterScript( character );
208
209     // Some characters (like white spaces) are valid for many scripts. The rules to set a script
210     // for them are:
211     // - If they are at the begining of a paragraph they get the script of the first character with
212     //   a defined script. If they are at the end, they get the script of the last one.
213     // - If they are between two scripts with the same direction, they get the script of the previous
214     //   character with a defined script. If the two scripts have different directions, they get the
215     //   script of the first character of the paragraph with a defined script.
216
217     // Skip those characters valid for many scripts like white spaces or '\n'.
218     bool endOfText = index == lastCharacter;
219     while( !endOfText &&
220            ( TextAbstraction::COMMON == script ) )
221     {
222       if( TextAbstraction::EMOJI == currentScriptRun.script )
223       {
224         // Emojis doesn't mix well with characters common to all scripts. Insert the emoji run.
225         scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun );
226         ++scriptIndex;
227
228         // Initialize the new one.
229         currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
230         currentScriptRun.characterRun.numberOfCharacters = 0u;
231         currentScriptRun.script = TextAbstraction::UNKNOWN;
232         numberOfAllScriptCharacters = 0u;
233       }
234
235       // Count all these characters to be added into a script.
236       ++numberOfAllScriptCharacters;
237
238       if( TextAbstraction::IsNewParagraph( character ) )
239       {
240         // The character is a new paragraph.
241         // To know when there is a new paragraph is needed because if there is a white space
242         // between two scripts with different directions, it is added to the script with
243         // the same direction than the first script of the paragraph.
244         isFirstScriptToBeSet = true;
245
246         // Characters common to all scripts at the end of the paragraph are added to the last script.
247         currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
248
249         // Store the script run.
250         if( TextAbstraction::UNKNOWN == currentScriptRun.script )
251         {
252           currentScriptRun.script = TextAbstraction::LATIN;
253         }
254         scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun );
255         ++scriptIndex;
256
257         // Initialize the new one.
258         currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
259         currentScriptRun.characterRun.numberOfCharacters = 0u;
260         currentScriptRun.script = TextAbstraction::UNKNOWN;
261         numberOfAllScriptCharacters = 0u;
262       }
263
264       // Get the next character.
265       ++index;
266       endOfText = index == lastCharacter;
267       if( !endOfText )
268       {
269         character = *( textBuffer + index );
270         script = TextAbstraction::GetCharacterScript( character );
271       }
272     } // end while( !endOfText && ( TextAbstraction::COMMON == script ) )
273
274     if( endOfText )
275     {
276       // Last characters of the text are 'white spaces'.
277       // There is nothing else to do. Just add the remaining characters to the last script after this bucle.
278       break;
279     }
280
281     // Check if it is the first character of a paragraph.
282     if( isFirstScriptToBeSet &&
283         ( TextAbstraction::UNKNOWN != script ) &&
284         ( TextAbstraction::COMMON != script ) &&
285         ( TextAbstraction::EMOJI != script ) )
286     {
287       // Sets the direction of the first valid script.
288       isParagraphRTL = TextAbstraction::IsRightToLeftScript( script );
289       isFirstScriptToBeSet = false;
290     }
291
292     if( ( script != currentScriptRun.script ) &&
293         ( TextAbstraction::COMMON != script ) )
294     {
295       // Current run needs to be stored and a new one initialized.
296
297       if( ( isParagraphRTL == TextAbstraction::IsRightToLeftScript( currentScriptRun.script ) ) &&
298           ( TextAbstraction::UNKNOWN != currentScriptRun.script ) )
299       {
300         // Previous script has the same direction than the first script of the paragraph.
301         // All the previously skipped characters need to be added to the previous script before it's stored.
302         currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
303         numberOfAllScriptCharacters = 0u;
304       }
305       else if( ( TextAbstraction::IsRightToLeftScript( currentScriptRun.script ) == TextAbstraction::IsRightToLeftScript( script ) ) &&
306                ( TextAbstraction::UNKNOWN != currentScriptRun.script ) )
307       {
308         // Current script and previous one have the same direction.
309         // All the previously skipped characters need to be added to the previous script before it's stored.
310         currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
311         numberOfAllScriptCharacters = 0u;
312       }
313       else if( ( TextAbstraction::UNKNOWN == currentScriptRun.script ) &&
314                ( TextAbstraction::EMOJI == script ) )
315       {
316         currentScriptRun.script = TextAbstraction::LATIN;
317         currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
318         numberOfAllScriptCharacters = 0u;
319       }
320
321       if( 0u != currentScriptRun.characterRun.numberOfCharacters )
322       {
323         // Store the script run.
324         scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun );
325         ++scriptIndex;
326       }
327
328       // Initialize the new one.
329       currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
330       currentScriptRun.characterRun.numberOfCharacters = numberOfAllScriptCharacters + 1u; // Adds the white spaces which are at the begining of the script.
331       currentScriptRun.script = script;
332       numberOfAllScriptCharacters = 0u;
333     }
334     else
335     {
336       if( TextAbstraction::UNKNOWN != currentScriptRun.script )
337       {
338         // Adds white spaces between characters.
339         currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
340         numberOfAllScriptCharacters = 0u;
341       }
342
343       // Add one more character to the run.
344       ++currentScriptRun.characterRun.numberOfCharacters;
345     }
346   }
347
348   // Add remaining characters into the last script.
349   currentScriptRun.characterRun.numberOfCharacters += numberOfAllScriptCharacters;
350
351   if( 0u != currentScriptRun.characterRun.numberOfCharacters )
352   {
353     if( TextAbstraction::UNKNOWN == currentScriptRun.script )
354     {
355       // There are only white spaces in the last script. Set the latin script.
356       currentScriptRun.script = TextAbstraction::LATIN;
357     }
358
359     // Store the last run.
360     scripts.Insert( scripts.Begin() + scriptIndex, currentScriptRun );
361     ++scriptIndex;
362   }
363
364   if( scriptIndex < scripts.Count() )
365   {
366     // Update the indices of the next script runs.
367     const ScriptRun& run = *( scripts.Begin() + scriptIndex - 1u );
368     CharacterIndex nextCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
369
370     for( Vector<ScriptRun>::Iterator it = scripts.Begin() + scriptIndex,
371            endIt = scripts.End();
372          it != endIt;
373          ++it )
374     {
375       ScriptRun& run = *it;
376       run.characterRun.characterIndex = nextCharacterIndex;
377       nextCharacterIndex += run.characterRun.numberOfCharacters;
378     }
379   }
380 }
381
382 void MultilanguageSupport::ValidateFonts( const Vector<Character>& text,
383                                           const Vector<ScriptRun>& scripts,
384                                           const Vector<FontDescriptionRun>& fontDescriptions,
385                                           const TextAbstraction::FontDescription& defaultFontDescription,
386                                           TextAbstraction::PointSize26Dot6 defaultFontPointSize,
387                                           CharacterIndex startIndex,
388                                           Length numberOfCharacters,
389                                           Vector<FontRun>& fonts )
390 {
391   DALI_LOG_INFO( gLogFilter, Debug::General, "-->MultilanguageSupport::ValidateFonts\n" );
392
393   if( 0u == numberOfCharacters )
394   {
395     DALI_LOG_INFO( gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n" );
396     // Nothing to do if there are no characters.
397     return;
398   }
399
400   // Find the first index where to insert the font run.
401   FontRunIndex fontIndex = 0u;
402   if( 0u != startIndex )
403   {
404     for( Vector<FontRun>::ConstIterator it = fonts.Begin(),
405            endIt = fonts.End();
406          it != endIt;
407          ++it, ++fontIndex )
408     {
409       const FontRun& run = *it;
410       if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
411       {
412         // Run found.
413         break;
414       }
415     }
416   }
417
418   // Traverse the characters and validate/set the fonts.
419
420   // Get the caches.
421   DefaultFonts** defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin();
422   ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin();
423
424   // Stores the validated font runs.
425   fonts.Reserve( fontDescriptions.Count() );
426
427   // Initializes a validated font run.
428   FontRun currentFontRun;
429   currentFontRun.characterRun.characterIndex = startIndex;
430   currentFontRun.characterRun.numberOfCharacters = 0u;
431   currentFontRun.fontId = 0u;
432
433   // Get the font client.
434   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
435
436   const Character* const textBuffer = text.Begin();
437
438   // Iterators of the script runs.
439   Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
440   Vector<ScriptRun>::ConstIterator scriptRunEndIt = scripts.End();
441   bool isNewParagraphCharacter = false;
442
443   FontId currentFontId = 0u;
444   FontId previousFontId = 0u;
445   bool isPreviousEmojiScript = false;
446
447   // Whether it's the first set of characters to be validated.
448   // Used in case the paragraph starts with characters common to all scripts.
449   bool isFirstSetToBeValidated = true;
450
451   CharacterIndex lastCharacter = startIndex + numberOfCharacters;
452   for( Length index = startIndex; index < lastCharacter; ++index )
453   {
454     // Get the current character.
455     const Character character = *( textBuffer + index );
456
457     TextAbstraction::FontDescription currentFontDescription;
458     TextAbstraction::PointSize26Dot6 currentFontPointSize = defaultFontPointSize;
459     bool isDefaultFont = true;
460     MergeFontDescriptions( fontDescriptions,
461                            defaultFontDescription,
462                            defaultFontPointSize,
463                            index,
464                            currentFontDescription,
465                            currentFontPointSize,
466                            isDefaultFont );
467
468     // Get the font for the current character.
469     FontId fontId = fontClient.GetFontId( currentFontDescription, currentFontPointSize );
470     currentFontId = fontId;
471
472     // Get the script for the current character.
473     const Script script = GetScript( index,
474                                      scriptRunIt,
475                                      scriptRunEndIt );
476
477 #ifdef DEBUG_ENABLED
478     {
479       Dali::TextAbstraction::FontDescription description;
480       fontClient.GetDescription( fontId, description );
481
482       DALI_LOG_INFO( gLogFilter,
483                      Debug::Verbose,
484                      "  Initial font set\n  Character : %x, Script : %s, Font : %s \n",
485                      character,
486                      Dali::TextAbstraction::ScriptName[script],
487                      description.path.c_str() );
488     }
489 #endif
490
491     // Validate whether the current character is supported by the given font.
492     bool isValidFont = false;
493
494     // Check first in the cache of default fonts per script and size.
495
496     DefaultFonts* defaultFonts = *( defaultFontPerScriptCacheBuffer + script );
497     FontId cachedDefaultFontId = 0u;
498     if( NULL != defaultFonts )
499     {
500       cachedDefaultFontId = defaultFonts->FindFont( fontClient,
501                                                     currentFontDescription,
502                                                     currentFontPointSize );
503     }
504
505     // Whether the cached default font is valid.
506     const bool isValidCachedDefaultFont = 0u != cachedDefaultFontId;
507
508     // The font is valid if it matches with the default one for the current script and size and it's different than zero.
509     isValidFont = isValidCachedDefaultFont && ( fontId == cachedDefaultFontId );
510
511     bool isCommonScript = false;
512     bool isEmojiScript = TextAbstraction::EMOJI == script;
513
514     if( isEmojiScript && !isPreviousEmojiScript )
515     {
516       if( 0u != currentFontRun.characterRun.numberOfCharacters )
517       {
518         // Store the font run.
519         fonts.Insert( fonts.Begin() + fontIndex, currentFontRun );
520         ++fontIndex;
521       }
522
523       // Initialize the new one.
524       currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
525       currentFontRun.characterRun.numberOfCharacters = 0u;
526       currentFontRun.fontId = fontId;
527     }
528
529
530     // If the given font is not valid, it means either:
531     // - there is no cached font for the current script yet or,
532     // - the user has set a different font than the default one for the current script or,
533     // - the platform default font is different than the default font for the current script.
534
535     // Need to check if the given font supports the current character.
536     if( !isValidFont ) // (1)
537     {
538       // Whether the current character is common for all scripts (i.e. white spaces, ...)
539
540       // Is not desirable to cache fonts for the common script.
541       //
542       // i.e. Consider the text " à¤¹à¤¿à¤‚दी", the 'white space' has assigned the DEVANAGARI script.
543       //      The user may have set a font or the platform's default is used.
544       //
545       //      As the 'white space' is the first character, no font is cached so the font validation
546       //      retrieves a glyph from the given font.
547       //
548       //      Many fonts support 'white spaces' so probably the font set by the user or the platform's default
549       //      supports the 'white space'. However, that font may not support the DEVANAGARI script.
550       isCommonScript = TextAbstraction::IsCommonScript( character );
551
552       if( isCommonScript )
553       {
554         if( isValidCachedDefaultFont &&
555             ( isDefaultFont || ( currentFontId == previousFontId ) ) &&
556             !isEmojiScript )
557         {
558           // At this point the character common for all scripts has no font assigned.
559           // If there is a valid previously cached default font for it, use that one.
560           fontId = cachedDefaultFontId;
561           isValidFont = true;
562         }
563       }
564       else
565       {
566         // Check in the valid fonts cache.
567         ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script );
568
569         if( NULL != validateFontsPerScript )
570         {
571           isValidFont = validateFontsPerScript->IsValidFont( fontId );
572         }
573
574         if( !isValidFont ) // (2)
575         {
576           // Use the font client to validate the font.
577           const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
578
579           // The font is valid if there is a glyph for that character.
580           isValidFont = 0u != glyphIndex;
581
582           // Emojis are present in many monochrome fonts; prefer color by default.
583           if( isValidFont &&
584               isEmojiScript )
585           {
586             const PixelData bitmap = fontClient.CreateBitmap( fontId, glyphIndex );
587
588             // For color emojis, the font is valid if the bitmap is RGBA.
589             isValidFont = bitmap && ( Pixel::BGRA8888 == bitmap.GetPixelFormat() );
590           }
591
592           // If there is a valid font, cache it.
593           if( isValidFont )
594           {
595             if( NULL == validateFontsPerScript )
596             {
597               validateFontsPerScript = new ValidateFontsPerScript();
598
599               *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript;
600             }
601
602             validateFontsPerScript->mValidFonts.PushBack( fontId );
603           }
604
605           if( !isValidFont ) // (3)
606           {
607             // The given font has not been validated.
608
609             if( isValidCachedDefaultFont )
610             {
611               // Use the cached default font for the script if there is one.
612               fontId = cachedDefaultFontId;
613             }
614             else
615             {
616               // There is no valid cached default font for the script.
617
618               DefaultFonts* defaultFontsPerScript = NULL;
619
620               // Emojis are present in many monochrome fonts; prefer color by default.
621               const bool preferColor = ( TextAbstraction::EMOJI == script );
622
623               // Find a fallback-font.
624               fontId = fontClient.FindFallbackFont( character,
625                                                     currentFontDescription,
626                                                     currentFontPointSize,
627                                                     preferColor );
628
629               if( 0u == fontId )
630               {
631                 // If the system does not support a suitable font, fallback to Latin
632                 defaultFontsPerScript = *( defaultFontPerScriptCacheBuffer + TextAbstraction::LATIN );
633                 if( NULL != defaultFontsPerScript )
634                 {
635                   fontId = defaultFontsPerScript->FindFont( fontClient,
636                                                             currentFontDescription,
637                                                             currentFontPointSize );
638                 }
639               }
640
641               if( 0u == fontId )
642               {
643                 fontId = fontClient.FindDefaultFont( UTF32_A, currentFontPointSize );
644               }
645
646               // Cache the font.
647               if( NULL == defaultFontsPerScript )
648               {
649                 defaultFontsPerScript = *( defaultFontPerScriptCacheBuffer + script );
650
651                 if( NULL == defaultFontsPerScript )
652                 {
653                   defaultFontsPerScript = new DefaultFonts();
654                   *( defaultFontPerScriptCacheBuffer + script ) = defaultFontsPerScript;
655                 }
656               }
657               defaultFontsPerScript->mFonts.PushBack( fontId );
658             }
659           } // !isValidFont (3)
660         } // !isValidFont (2)
661       } // !isCommonScript
662     } // !isValidFont (1)
663
664 #ifdef DEBUG_ENABLED
665     {
666       Dali::TextAbstraction::FontDescription description;
667       fontClient.GetDescription( fontId, description );
668       DALI_LOG_INFO( gLogFilter,
669                      Debug::Verbose,
670                      "  Validated font set\n  Character : %x, Script : %s, Font : %s \n",
671                      character,
672                      Dali::TextAbstraction::ScriptName[script],
673                      description.path.c_str() );
674     }
675 #endif
676
677     if( isFirstSetToBeValidated && !isCommonScript )
678     {
679       currentFontRun.fontId = fontId;
680       isFirstSetToBeValidated = false;
681     }
682
683     // The font is now validated.
684     if( ( fontId != currentFontRun.fontId ) ||
685         isNewParagraphCharacter )
686     {
687       // Current run needs to be stored and a new one initialized.
688
689       if( 0u != currentFontRun.characterRun.numberOfCharacters )
690       {
691         // Store the font run.
692         fonts.Insert( fonts.Begin() + fontIndex, currentFontRun );
693         ++fontIndex;
694       }
695
696       // Initialize the new one.
697       currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
698       currentFontRun.characterRun.numberOfCharacters = 0u;
699       currentFontRun.fontId = fontId;
700
701       if( isNewParagraphCharacter )
702       {
703         isFirstSetToBeValidated = true;
704       }
705     }
706
707     // Add one more character to the run.
708     ++currentFontRun.characterRun.numberOfCharacters;
709
710     // Whether the current character is a new paragraph character.
711     isNewParagraphCharacter = TextAbstraction::IsNewParagraph( character );
712     previousFontId = currentFontId;
713     isPreviousEmojiScript = isEmojiScript;
714   } // end traverse characters.
715
716   if( 0u != currentFontRun.characterRun.numberOfCharacters )
717   {
718     // Store the last run.
719     fonts.Insert( fonts.Begin() + fontIndex, currentFontRun );
720     ++fontIndex;
721   }
722
723   if( fontIndex < fonts.Count() )
724   {
725     // Update the indices of the next font runs.
726     const FontRun& run = *( fonts.Begin() + fontIndex - 1u );
727     CharacterIndex nextCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
728
729     for( Vector<FontRun>::Iterator it = fonts.Begin() + fontIndex,
730            endIt = fonts.End();
731          it != endIt;
732          ++it )
733     {
734       FontRun& run = *it;
735
736       run.characterRun.characterIndex = nextCharacterIndex;
737       nextCharacterIndex += run.characterRun.numberOfCharacters;
738     }
739   }
740
741   DALI_LOG_INFO( gLogFilter, Debug::General, "<--MultilanguageSupport::ValidateFonts\n" );
742 }
743
744 } // namespace Internal
745
746 } // namespace Text
747
748 } // namespace Toolkit
749
750 } // namespace Dali