Further refactoring of text-controller-impl
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / markup-processor.cpp
1 /*
2  * Copyright (c) 2020 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 // FILE HEADER
19 #include <dali-toolkit/internal/text/markup-processor.h>
20
21 // EXTERNAL INCLUDES
22 #include <climits>  // for ULONG_MAX
23 #include <functional>
24 #include <dali/integration-api/debug.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/character-set-conversion.h>
28 #include <dali-toolkit/internal/text/markup-processor-color.h>
29 #include <dali-toolkit/internal/text/markup-processor-embedded-item.h>
30 #include <dali-toolkit/internal/text/markup-processor-font.h>
31 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
32 #include <dali-toolkit/internal/text/xhtml-entities.h>
33
34 namespace Dali
35 {
36
37 namespace Toolkit
38 {
39
40 namespace Text
41 {
42
43 namespace
44 {
45 // HTML-ISH tag and attribute constants.
46 // Note they must be lower case in order to make the comparison to work
47 // as the parser converts all the read tags to lower case.
48 const std::string XHTML_COLOR_TAG("color");
49 const std::string XHTML_FONT_TAG("font");
50 const std::string XHTML_B_TAG("b");
51 const std::string XHTML_I_TAG("i");
52 const std::string XHTML_U_TAG("u");
53 const std::string XHTML_SHADOW_TAG("shadow");
54 const std::string XHTML_GLOW_TAG("glow");
55 const std::string XHTML_OUTLINE_TAG("outline");
56 const std::string XHTML_ITEM_TAG("item");
57
58 const char LESS_THAN      = '<';
59 const char GREATER_THAN   = '>';
60 const char EQUAL          = '=';
61 const char QUOTATION_MARK = '\'';
62 const char SLASH          = '/';
63 const char BACK_SLASH     = '\\';
64 const char AMPERSAND      = '&';
65 const char HASH           = '#';
66 const char SEMI_COLON     = ';';
67 const char CHAR_ARRAY_END = '\0';
68 const char HEX_CODE       = 'x';
69
70 const char WHITE_SPACE    = 0x20;        // ASCII value of the white space.
71
72 // Range 1 0x0u < XHTML_DECIMAL_ENTITY_RANGE <= 0xD7FFu
73 // Range 2 0xE000u < XHTML_DECIMAL_ENTITY_RANGE <= 0xFFFDu
74 // Range 3 0x10000u < XHTML_DECIMAL_ENTITY_RANGE <= 0x10FFFFu
75 const unsigned long XHTML_DECIMAL_ENTITY_RANGE[] = { 0x0u, 0xD7FFu, 0xE000u, 0xFFFDu, 0x10000u, 0x10FFFFu };
76
77 const unsigned int MAX_NUM_OF_ATTRIBUTES =  5u; ///< The font tag has the 'family', 'size' 'weight', 'width' and 'slant' attrubutes.
78 const unsigned int DEFAULT_VECTOR_SIZE   = 16u; ///< Default size of run vectors.
79
80 #if defined(DEBUG_ENABLED)
81 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_MARKUP_PROCESSOR");
82 #endif
83
84 /**
85  * @brief Struct used to retrieve the style runs from the mark-up string.
86  */
87 struct StyleStack
88 {
89   typedef VectorBase::SizeType RunIndex;
90
91   Vector<RunIndex>  stack;    ///< Use a vector as a style stack. Stores the indices pointing where the run is stored inside the logical model.
92   unsigned int topIndex; ///< Points the top of the stack.
93
94   StyleStack()
95   : stack(),
96     topIndex( 0u )
97   {
98     stack.Resize( DEFAULT_VECTOR_SIZE );
99   }
100
101   void Push( RunIndex index )
102   {
103     // Check if there is space inside the style stack.
104     const VectorBase::SizeType size = stack.Count();
105     if( topIndex >= size )
106     {
107       // Resize the style stack.
108       stack.Resize( 2u * size );
109     }
110
111     // Set the run index in the top of the stack.
112     *( stack.Begin() + topIndex ) = index;
113
114     // Reposition the pointer to the top of the stack.
115     ++topIndex;
116   }
117
118   RunIndex Pop()
119   {
120     // Pop the top of the stack.
121     --topIndex;
122     return *( stack.Begin() + topIndex );
123   }
124 };
125
126 /**
127  * @brief Initializes a font run description to its defaults.
128  *
129  * @param[in,out] fontRun The font description run to initialize.
130  */
131 void Initialize( FontDescriptionRun& fontRun )
132 {
133   fontRun.characterRun.characterIndex = 0u;
134   fontRun.characterRun.numberOfCharacters = 0u;
135   fontRun.familyName = NULL;
136   fontRun.familyLength = 0u;
137   fontRun.weight = TextAbstraction::FontWeight::NORMAL;
138   fontRun.width = TextAbstraction::FontWidth::NORMAL;
139   fontRun.slant = TextAbstraction::FontSlant::NORMAL;
140   fontRun.size = 0u;
141   fontRun.familyDefined = false;
142   fontRun.weightDefined = false;
143   fontRun.widthDefined = false;
144   fontRun.slantDefined = false;
145   fontRun.sizeDefined = false;
146 }
147
148 /**
149  * @brief Initializes a color run description to its defaults.
150  *
151  * @param[in,out] colorRun The font description run to initialize.
152  */
153 void Initialize( ColorRun& colorRun )
154 {
155   colorRun.characterRun.characterIndex = 0u;
156   colorRun.characterRun.numberOfCharacters = 0u;
157 }
158
159 /**
160  * @brief Splits the tag string into the tag name and its attributes.
161  *
162  * The attributes are stored in a vector in the tag.
163  *
164  * @param[in,out] tag The tag.
165  */
166 void ParseAttributes( Tag& tag )
167 {
168   if( tag.buffer == NULL )
169   {
170     return;
171   }
172
173   tag.attributes.Resize( MAX_NUM_OF_ATTRIBUTES );
174
175   // Find first the tag name.
176   bool isQuotationOpen = false;
177
178   const char* tagBuffer = tag.buffer;
179   const char* const tagEndBuffer = tagBuffer + tag.length;
180   tag.length = 0u;
181   for( ; tagBuffer < tagEndBuffer; ++tagBuffer )
182   {
183     const char character = *tagBuffer;
184     if( WHITE_SPACE < character )
185     {
186       ++tag.length;
187     }
188     else
189     {
190       // Stops counting the length of the tag when a white space is found.
191       // @note a white space is the WHITE_SPACE character and anything below as 'tab', 'return' or 'control characters'.
192       break;
193     }
194   }
195   SkipWhiteSpace( tagBuffer, tagEndBuffer );
196
197   // Find the attributes.
198   unsigned int attributeIndex = 0u;
199   const char* nameBuffer = NULL;
200   const char* valueBuffer = NULL;
201   Length nameLength = 0u;
202   Length valueLength = 0u;
203
204   bool addToNameValue = true;
205   Length numberOfWhiteSpace = 0u;
206   for( ; tagBuffer < tagEndBuffer; ++tagBuffer )
207   {
208     const char character = *tagBuffer;
209     if( ( WHITE_SPACE >= character ) && !isQuotationOpen )
210     {
211       if( NULL != valueBuffer )
212       {
213         // Remove white spaces at the end of the value.
214         valueLength -= numberOfWhiteSpace;
215       }
216
217       if( ( NULL != nameBuffer ) && ( NULL != valueBuffer ) )
218       {
219         // Every time a white space is found, a new attribute is created and stored in the attributes vector.
220         Attribute& attribute = *( tag.attributes.Begin() + attributeIndex );
221         ++attributeIndex;
222
223         attribute.nameBuffer = nameBuffer;
224         attribute.valueBuffer = valueBuffer;
225         attribute.nameLength = nameLength;
226         attribute.valueLength = valueLength;
227
228         nameBuffer = NULL;
229         valueBuffer = NULL;
230         nameLength = 0u;
231         valueLength = 0u;
232
233         addToNameValue = true; // next read characters will be added to the name.
234       }
235     }
236     else if( EQUAL == character ) // '='
237     {
238       addToNameValue = false; // next read characters will be added to the value.
239       SkipWhiteSpace( tagBuffer, tagEndBuffer );
240     }
241     else if( QUOTATION_MARK == character ) // '\''
242     {
243       // Do not add quotation marks to neither name nor value.
244       isQuotationOpen = !isQuotationOpen;
245
246       if( isQuotationOpen )
247       {
248         ++tagBuffer;
249         SkipWhiteSpace( tagBuffer, tagEndBuffer );
250         --tagBuffer;
251       }
252     }
253     else
254     {
255       // Adds characters to the name or the value.
256       if( addToNameValue )
257       {
258         if( NULL == nameBuffer )
259         {
260           nameBuffer = tagBuffer;
261         }
262         ++nameLength;
263       }
264       else
265       {
266         if( isQuotationOpen )
267         {
268           if( WHITE_SPACE >= character )
269           {
270             ++numberOfWhiteSpace;
271           }
272           else
273           {
274             numberOfWhiteSpace = 0u;
275           }
276         }
277         if( NULL == valueBuffer )
278         {
279           valueBuffer = tagBuffer;
280         }
281         ++valueLength;
282       }
283     }
284   }
285
286   if( NULL != valueBuffer )
287   {
288     // Remove white spaces at the end of the value.
289     valueLength -= numberOfWhiteSpace;
290   }
291
292   if( ( NULL != nameBuffer ) && ( NULL != valueBuffer ) )
293   {
294     // Checks if the last attribute needs to be added.
295     Attribute& attribute = *( tag.attributes.Begin() + attributeIndex );
296     ++attributeIndex;
297
298     attribute.nameBuffer = nameBuffer;
299     attribute.valueBuffer = valueBuffer;
300     attribute.nameLength = nameLength;
301     attribute.valueLength = valueLength;
302   }
303
304   // Resize the vector of attributes.
305   tag.attributes.Resize( attributeIndex );
306 }
307
308 /**
309  * @brief It parses a tag and its attributes if the given iterator @e it is pointing at a tag beginning.
310  *
311  * @param[in,out] markupStringBuffer The mark-up string buffer. It's a const iterator pointing the current character.
312  * @param[in] markupStringEndBuffer Pointer to one character after the end of the mark-up string buffer.
313  * @param[out] tag The tag with its attributes.
314  *
315  * @return @e true if the iterator @e it is pointing a mark-up tag. Otherwise @e false.
316  */
317 bool IsTag( const char*& markupStringBuffer,
318             const char* const markupStringEndBuffer,
319             Tag& tag )
320 {
321   bool isTag = false;
322   bool isQuotationOpen = false;
323   bool attributesFound = false;
324   tag.isEndTag = false;
325   bool isPreviousLessThan = false;
326   bool isPreviousSlash = false;
327
328   const char character = *markupStringBuffer;
329   if( LESS_THAN == character ) // '<'
330   {
331     tag.buffer = NULL;
332     tag.length = 0u;
333     isPreviousLessThan = true;
334
335     // if the iterator is pointing to a '<' character, then check if it's a mark-up tag is needed.
336     ++markupStringBuffer;
337     if( markupStringBuffer < markupStringEndBuffer )
338     {
339       SkipWhiteSpace( markupStringBuffer, markupStringEndBuffer );
340
341       for( ; ( !isTag ) && ( markupStringBuffer < markupStringEndBuffer ); ++markupStringBuffer )
342       {
343         const char character = *markupStringBuffer;
344
345         if( !isQuotationOpen && ( SLASH == character ) ) // '/'
346         {
347           if (isPreviousLessThan)
348           {
349             tag.isEndTag = true;
350           }
351           else
352           {
353             // if the tag has a '/' it may be an end tag.
354             isPreviousSlash = true;
355           }
356
357           isPreviousLessThan = false;
358           if( ( markupStringBuffer + 1u < markupStringEndBuffer ) && ( WHITE_SPACE >= *( markupStringBuffer + 1u ) ) )
359           {
360             ++markupStringBuffer;
361             SkipWhiteSpace( markupStringBuffer, markupStringEndBuffer );
362             --markupStringBuffer;
363           }
364         }
365         else if( GREATER_THAN == character ) // '>'
366         {
367           isTag = true;
368           if (isPreviousSlash)
369           {
370             tag.isEndTag = true;
371           }
372
373           isPreviousSlash = false;
374           isPreviousLessThan = false;
375         }
376         else if( QUOTATION_MARK == character )
377         {
378           isQuotationOpen = !isQuotationOpen;
379           ++tag.length;
380
381           isPreviousSlash = false;
382           isPreviousLessThan = false;
383         }
384         else if( WHITE_SPACE >= character ) // ' '
385         {
386           // If the tag contains white spaces then it may have attributes.
387           if( !isQuotationOpen )
388           {
389             attributesFound = true;
390           }
391           ++tag.length;
392         }
393         else
394         {
395           if( NULL == tag.buffer )
396           {
397             tag.buffer = markupStringBuffer;
398           }
399
400           // If it's not any of the 'special' characters then just add it to the tag string.
401           ++tag.length;
402
403           isPreviousSlash = false;
404           isPreviousLessThan = false;
405         }
406       }
407     }
408
409     // If the tag string has white spaces, then parse the attributes is needed.
410     if( attributesFound )
411     {
412       ParseAttributes( tag );
413     }
414   }
415
416   return isTag;
417 }
418
419 /**
420  * @brief Returns length of XHTML entity by parsing the text. It also determines if it is XHTML entity or not.
421  *
422  * @param[in] markupStringBuffer The mark-up string buffer. It's a const iterator pointing the current character.
423  * @param[in] markupStringEndBuffer Pointing to end of mark-up string buffer.
424  *
425  * @return Length of markupText in case of XHTML entity otherwise return 0.
426  */
427 unsigned int GetXHTMLEntityLength( const char*& markupStringBuffer,
428                                    const char* const markupStringEndBuffer )
429 {
430   char character = *markupStringBuffer;
431   if( AMPERSAND == character ) // '&'
432   {
433     // if the iterator is pointing to a '&' character, then check for ';' to find end to XHTML entity.
434     ++markupStringBuffer;
435     if( markupStringBuffer < markupStringEndBuffer )
436     {
437       unsigned int len = 1u;
438       for( ; markupStringBuffer < markupStringEndBuffer ; ++markupStringBuffer )
439       {
440         character = *markupStringBuffer;
441         ++len;
442         if( SEMI_COLON == character ) // ';'
443         {
444           // found end of XHTML entity
445           ++markupStringBuffer;
446           return len;
447         }
448         else if( ( AMPERSAND == character ) || ( BACK_SLASH == character ) || ( LESS_THAN == character ))
449         {
450           return 0;
451         }
452       }
453     }
454   }
455   return 0;
456 }
457
458 /**
459  * @brief It parses a XHTML string which has hex/decimal entity and fill its corresponging utf-8 string.
460  *
461  * @param[in] markupText The mark-up text buffer.
462  * @param[out] utf-8 text Corresponding to markup Text
463  *
464  * @return true if string is successfully parsed otherwise false
465  */
466 bool XHTMLNumericEntityToUtf8 ( const char* markupText, char* utf8 )
467 {
468   bool result = false;
469
470   if( NULL != markupText )
471   {
472     bool isHex = false;
473
474     // check if hex or decimal entity
475     if( ( CHAR_ARRAY_END != *markupText ) && ( HEX_CODE == *markupText ) )
476     {
477       isHex = true;
478       ++markupText;
479     }
480
481     char* end = NULL;
482     unsigned long l = strtoul( markupText, &end, ( isHex ? 16 : 10 ) );  // l contains UTF-32 code in case of correct XHTML entity
483
484     // check for valid XHTML numeric entities (between '#' or "#x" and ';')
485     if( ( l > 0 ) && ( l < ULONG_MAX ) && ( *end == SEMI_COLON ) ) // in case wrong XHTML entity is set eg. "&#23abcdefs;" in that case *end will be 'a'
486     {
487       /* characters XML 1.1 permits */
488       if( ( ( XHTML_DECIMAL_ENTITY_RANGE[0] < l ) && ( l <= XHTML_DECIMAL_ENTITY_RANGE[1] ) ) ||
489         ( ( XHTML_DECIMAL_ENTITY_RANGE[2] <= l ) && ( l <= XHTML_DECIMAL_ENTITY_RANGE[3] ) ) ||
490         ( ( XHTML_DECIMAL_ENTITY_RANGE[4] <= l ) && ( l <= XHTML_DECIMAL_ENTITY_RANGE[5] ) ) )
491       {
492         // Convert UTF32 code to UTF8
493         Utf32ToUtf8( reinterpret_cast<const uint32_t* const>( &l ), 1, reinterpret_cast<uint8_t*>( utf8 ) );
494         result = true;
495        }
496     }
497   }
498   return result;
499 }
500
501 /**
502  * @brief Processes a particular tag for the required run (color-run or font-run).
503  *
504  * @tparam RunType Whether ColorRun or FontDescriptionRun
505  *
506  * @param[in/out] runsContainer The container containing all the runs
507  * @param[in/out] styleStack The style stack
508  * @param[in] tag The tag we are currently processing
509  * @param[in] characterIndex The current character index
510  * @param[in/out] runIndex The run index
511  * @param[in/out] tagReference The tagReference we should increment/decrement
512  * @param[in] parameterSettingFunction This function will be called to set run specific parameters
513  */
514 template <typename RunType>
515 void ProcessTagForRun(
516     Vector<RunType>& runsContainer,
517     StyleStack& styleStack,
518     const Tag& tag,
519     const CharacterIndex characterIndex,
520     StyleStack::RunIndex& runIndex,
521     int& tagReference,
522     std::function<void (const Tag&, RunType&)> parameterSettingFunction)
523 {
524   if( !tag.isEndTag )
525   {
526     // Create a new run.
527     RunType run;
528     Initialize(run);
529
530     // Fill the run with the parameters.
531     run.characterRun.characterIndex = characterIndex;
532     parameterSettingFunction(tag, run);
533
534     // Push the run in the logical model.
535     runsContainer.PushBack(run);
536
537     // Push the index of the run into the stack.
538     styleStack.Push(runIndex);
539
540     // Point the next free run.
541     ++runIndex;
542
543     // Increase reference
544     ++tagReference;
545   }
546   else
547   {
548     if( tagReference > 0 )
549     {
550       // Pop the top of the stack and set the number of characters of the run.
551       RunType& run = *( runsContainer.Begin() + styleStack.Pop() );
552       run.characterRun.numberOfCharacters = characterIndex - run.characterRun.characterIndex;
553       --tagReference;
554     }
555   }
556 }
557
558 /**
559  * @brief Processes the item tag
560  *
561  * @param[in/out] markupProcessData The markup process data
562  * @param[in] tag The current tag
563  * @param[in/out] characterIndex The current character index
564  */
565 void ProcessItemTag(
566     MarkupProcessData& markupProcessData,
567     const Tag tag,
568     CharacterIndex& characterIndex)
569 {
570   if (tag.isEndTag)
571   {
572     // Create an embedded item instance.
573     EmbeddedItem item;
574     item.characterIndex = characterIndex;
575     ProcessEmbeddedItem(tag, item);
576
577     markupProcessData.items.PushBack(item);
578
579     // Insert white space character that will be replaced by the item.
580     markupProcessData.markupProcessedText.append( 1u, WHITE_SPACE );
581     ++characterIndex;
582   }
583 }
584
585 /**
586  * @brief Resizes the model's vectors
587  *
588  * @param[in/out] markupProcessData The markup process data
589  * @param[in] fontRunIndex The font run index
590  * @param[in] colorRunIndex The color run index
591  */
592 void ResizeModelVectors(MarkupProcessData& markupProcessData, const StyleStack::RunIndex fontRunIndex, const StyleStack::RunIndex colorRunIndex)
593 {
594   markupProcessData.fontRuns.Resize( fontRunIndex );
595   markupProcessData.colorRuns.Resize( colorRunIndex );
596
597 #ifdef DEBUG_ENABLED
598   for( unsigned int i=0; i<colorRunIndex; ++i )
599   {
600     ColorRun& run = markupProcessData.colorRuns[i];
601     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "run[%d] index: %d, length: %d, color %f,%f,%f,%f\n", i, run.characterRun.characterIndex, run.characterRun.numberOfCharacters, run.color.r, run.color.g, run.color.b, run.color.a );
602   }
603 #endif
604 }
605
606 /**
607  * @brief Processes the markup string buffer
608  *
609  * @param[in/out] markupProcessData The markup process data
610  * @param[in/out] markupStringBuffer The markup string buffer pointer
611  * @param[in] markupStringEndBuffer The markup string end buffer pointer
612  * @param[in/out] characterIndex The current character index
613  */
614 void ProcessMarkupStringBuffer(
615     MarkupProcessData& markupProcessData,
616     const char*& markupStringBuffer,
617     const char* const markupStringEndBuffer,
618     CharacterIndex& characterIndex)
619 {
620   unsigned char character = *markupStringBuffer;
621   const char* markupBuffer = markupStringBuffer;
622   unsigned char count = GetUtf8Length( character );
623   char utf8[8];
624
625   if( ( BACK_SLASH == character ) && ( markupStringBuffer + 1u < markupStringEndBuffer ) )
626   {
627     // Adding < , >  or & special character.
628     const unsigned char nextCharacter = *( markupStringBuffer + 1u );
629     if( ( LESS_THAN == nextCharacter ) || ( GREATER_THAN == nextCharacter ) || ( AMPERSAND == nextCharacter ) )
630     {
631       character = nextCharacter;
632       ++markupStringBuffer;
633
634       count = GetUtf8Length( character );
635       markupBuffer = markupStringBuffer;
636     }
637   }
638   else   // checking if contains XHTML entity or not
639   {
640     const unsigned int len =  GetXHTMLEntityLength( markupStringBuffer, markupStringEndBuffer);
641
642     // Parse markupStringTxt if it contains XHTML Entity between '&' and ';'
643     if( len > 0 )
644     {
645       char* entityCode = NULL;
646       bool result = false;
647       count = 0;
648
649       // Checking if XHTML Numeric Entity
650       if( HASH == *( markupBuffer + 1u ) )
651       {
652         entityCode = &utf8[0];
653         // markupBuffer is currently pointing to '&'. By adding 2u to markupBuffer it will point to numeric string by skipping "&#'
654         result = XHTMLNumericEntityToUtf8( ( markupBuffer + 2u ), entityCode );
655       }
656       else    // Checking if XHTML Named Entity
657       {
658         entityCode = const_cast<char*> ( NamedEntityToUtf8( markupBuffer, len ) );
659         result = ( entityCode != NULL );
660       }
661       if ( result )
662       {
663         markupBuffer = entityCode; //utf8 text assigned to markupBuffer
664         character = markupBuffer[0];
665       }
666       else
667       {
668         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Not valid XHTML entity : (%.*s) \n", len, markupBuffer );
669         markupBuffer = NULL;
670       }
671     }
672     else    // in case string conatins Start of XHTML Entity('&') but not its end character(';')
673     {
674       if( character == AMPERSAND )
675       {
676         markupBuffer = NULL;
677         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Not Well formed XHTML content \n" );
678       }
679     }
680   }
681
682   if( markupBuffer != NULL )
683   {
684     const unsigned char numberOfBytes = GetUtf8Length( character );
685     markupProcessData.markupProcessedText.push_back( character );
686
687     for( unsigned char i = 1u; i < numberOfBytes; ++i )
688     {
689       ++markupBuffer;
690       markupProcessData.markupProcessedText.push_back( *markupBuffer );
691     }
692
693     ++characterIndex;
694     markupStringBuffer += count;
695   }
696 }
697
698 } // namespace
699
700 void ProcessMarkupString( const std::string& markupString, MarkupProcessData& markupProcessData )
701 {
702   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "markupString: %s\n", markupString.c_str() );
703
704   // Reserve space for the plain text.
705   const Length markupStringSize = markupString.size();
706   markupProcessData.markupProcessedText.reserve( markupStringSize );
707
708   // Stores a struct with the index to the first character of the run, the type of run and its parameters.
709   StyleStack styleStack;
710
711   // Points the next free position in the vector of runs.
712   StyleStack::RunIndex colorRunIndex = 0u;
713   StyleStack::RunIndex fontRunIndex = 0u;
714
715   // check tag reference
716   int colorTagReference = 0u;
717   int fontTagReference = 0u;
718   int iTagReference = 0u;
719   int bTagReference = 0u;
720
721   // Give an initial default value to the model's vectors.
722   markupProcessData.colorRuns.Reserve( DEFAULT_VECTOR_SIZE );
723   markupProcessData.fontRuns.Reserve( DEFAULT_VECTOR_SIZE );
724
725   // Get the mark-up string buffer.
726   const char* markupStringBuffer = markupString.c_str();
727   const char* const markupStringEndBuffer = markupStringBuffer + markupStringSize;
728
729   Tag tag;
730   CharacterIndex characterIndex = 0u;
731   for( ; markupStringBuffer < markupStringEndBuffer; )
732   {
733     tag.attributes.Clear();
734     if( IsTag( markupStringBuffer,
735                markupStringEndBuffer,
736                tag ) )
737     {
738       if( TokenComparison( XHTML_COLOR_TAG, tag.buffer, tag.length ) )
739       {
740         ProcessTagForRun<ColorRun>(
741             markupProcessData.colorRuns, styleStack, tag, characterIndex, colorRunIndex, colorTagReference,
742             [] (const Tag& tag, ColorRun& run) { ProcessColorTag( tag, run ); });
743       } // <color></color>
744       else if( TokenComparison( XHTML_I_TAG, tag.buffer, tag.length ) )
745       {
746         ProcessTagForRun<FontDescriptionRun>(
747             markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, iTagReference,
748             [] (const Tag&, FontDescriptionRun& fontRun)
749             {
750               fontRun.slant = TextAbstraction::FontSlant::ITALIC;
751               fontRun.slantDefined = true;
752             });
753       } // <i></i>
754       else if( TokenComparison( XHTML_U_TAG, tag.buffer, tag.length ) )
755       {
756         // TODO: If !tag.isEndTag, then create a new underline run.
757         //       else Pop the top of the stack and set the number of characters of the run.
758       } // <u></u>
759       else if( TokenComparison( XHTML_B_TAG, tag.buffer, tag.length ) )
760       {
761         ProcessTagForRun<FontDescriptionRun>(
762             markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, bTagReference,
763             [] (const Tag&, FontDescriptionRun& fontRun)
764             {
765               fontRun.weight = TextAbstraction::FontWeight::BOLD;
766               fontRun.weightDefined = true;
767             });
768       } // <b></b>
769       else if( TokenComparison( XHTML_FONT_TAG, tag.buffer, tag.length ) )
770       {
771         ProcessTagForRun<FontDescriptionRun>(
772             markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, fontTagReference,
773             [] (const Tag& tag, FontDescriptionRun& fontRun) { ProcessFontTag( tag, fontRun ); });
774       } // <font></font>
775       else if( TokenComparison( XHTML_SHADOW_TAG, tag.buffer, tag.length ) )
776       {
777         // TODO: If !tag.isEndTag, then create a new shadow run.
778         //       else Pop the top of the stack and set the number of characters of the run.
779       } // <shadow></shadow>
780       else if( TokenComparison( XHTML_GLOW_TAG, tag.buffer, tag.length ) )
781       {
782         // TODO: If !tag.isEndTag, then create a new glow run.
783         //       else Pop the top of the stack and set the number of characters of the run.
784       } // <glow></glow>
785       else if( TokenComparison( XHTML_OUTLINE_TAG, tag.buffer, tag.length ) )
786       {
787         // TODO: If !tag.isEndTag, then create a new outline run.
788         //       else Pop the top of the stack and set the number of characters of the run.
789       } // <outline></outline>
790       else if (TokenComparison(XHTML_ITEM_TAG, tag.buffer, tag.length))
791       {
792         ProcessItemTag(markupProcessData, tag, characterIndex);
793       }
794     }  // end if( IsTag() )
795     else if( markupStringBuffer < markupStringEndBuffer )
796     {
797       ProcessMarkupStringBuffer(markupProcessData, markupStringBuffer, markupStringEndBuffer, characterIndex);
798     }
799   }
800
801   // Resize the model's vectors.
802   ResizeModelVectors(markupProcessData, fontRunIndex, colorRunIndex);
803 }
804
805 } // namespace Text
806
807 } // namespace Toolkit
808
809 } // namespace Dali