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