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